diff --git a/.gitattributes b/.gitattributes index e67ac20..79f5f48 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,9 @@ nebula_app/src/platform/pi_session.sh text eol=lf scripts/tests/fixtures/ai-hooks-v1.5.0/* -text # Embedded remote hooks keep POSIX newlines in Windows binaries. nebula_app/res/hooks/* text eol=lf +# Checksum-pinned offline reader distributions retain their upstream bytes. +mobile/android/app/src/main/assets/reader/katex/** -text +mobile/android/app/src/main/assets/reader/mermaid/** -text -whitespace +mobile/android/app/src/main/assets/reader/highlight.min.js -text +mobile/android/app/src/main/assets/reader/reader-compat.js -text +mobile/android/third_party/licenses/ZXing-Core-Apache-2.0.txt -text diff --git a/.github/workflows/android-release.yml b/.github/workflows/android-release.yml new file mode 100644 index 0000000..459929b --- /dev/null +++ b/.github/workflows/android-release.yml @@ -0,0 +1,216 @@ +name: Android Preview release package + +on: + workflow_call: + inputs: + version: + required: true + type: string + +permissions: + contents: read + +env: + PYTHONUTF8: "1" + RUST_TOOLCHAIN: "1.97.1" + CARGO_TERM_COLOR: always + +jobs: + relay: + name: Embedded relay (${{ matrix.arch }}) + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-24.04 + arch: x86_64 + target: x86_64-unknown-linux-musl + - runner: ubuntu-24.04-arm + arch: aarch64 + target: aarch64-unknown-linux-musl + runs-on: ${{ matrix.runner }} + timeout-minutes: 45 + env: + CARGO_BUILD_TARGET: ${{ matrix.target }} + CC: musl-gcc + RUSTFLAGS: -C linker=musl-gcc + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install native static-link tools + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends musl-tools + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} + targets: ${{ matrix.target }} + - uses: ./.github/actions/rust-cache + id: rust-cache + with: + key: android-relay + target: target/${{ matrix.target }}/release + - name: Build and package same-source relay + env: + RELAY_ARCH: ${{ matrix.arch }} + RELAY_TARGET: ${{ matrix.target }} + run: | + cargo build --locked --release -p pebrel-mobile-link --features relay --bin pebrel-relay + python3 mobile/tools/package_native_relay.py \ + --binary "target/$RELAY_TARGET/release/pebrel-relay" \ + --output relay-assets --arch "$RELAY_ARCH" --commit "$GITHUB_SHA" + - name: Save compiled relay + if: ${{ steps.rust-cache.outputs.cache-hit != 'true' }} + uses: actions/cache/save@v4 + with: + path: target/${{ matrix.target }}/release + key: ${{ steps.rust-cache.outputs.cache-key }} + - uses: actions/upload-artifact@v4 + with: + name: android-relay-${{ matrix.arch }} + path: relay-assets + if-no-files-found: error + retention-days: 7 + + apk: + name: Android universal Preview APK + needs: relay + runs-on: ubuntu-24.04 + timeout-minutes: 120 + env: + PACKAGE_VERSION: ${{ inputs.version }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "17" + - uses: android-actions/setup-android@v3 + with: + packages: 'platform-tools platforms;android-35 build-tools;35.0.0 ndk;27.2.12479018 cmake;3.22.1' + - uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: "8.11.1" + cache-read-only: false + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} + targets: aarch64-linux-android,x86_64-linux-android + - name: Select the pinned NDK + run: echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/27.2.12479018" >> "$GITHUB_ENV" + - uses: ./.github/actions/rust-cache + id: rust-cache + with: + key: android-ssh + target: | + mobile/ssh/target/aarch64-linux-android/release + mobile/ssh/target/x86_64-linux-android/release + extra-key: ndk-27.2.12479018 + - uses: actions/cache@v4 + with: + path: 'mobile/android/ghostty/build/upstream' + key: android-vt-linux-${{ hashFiles('mobile/android/ghostty/UPSTREAM.json', 'mobile/tools/build_ghostty.py') }} + - name: Build both Android native ABIs + run: | + python3 mobile/tools/build_ghostty.py + python3 mobile/tools/build_russh.py + - name: Save compiled Android transport + if: ${{ steps.rust-cache.outputs.cache-hit != 'true' }} + uses: actions/cache/save@v4 + with: + path: | + mobile/ssh/target/aarch64-linux-android/release + mobile/ssh/target/x86_64-linux-android/release + key: ${{ steps.rust-cache.outputs.cache-key }} + - uses: actions/download-artifact@v4 + with: + pattern: android-relay-* + merge-multiple: true + path: mobile/android/app/build/generated/nativeRelayAssets/native-relay + - name: Run unit tests and build the optimized Preview variant + run: | + gradle --no-daemon -p mobile/android \ + :app:testDebugUnitTest :app:lintPreview \ + :app:assemblePreview :app:assemblePreviewAndroidTest + - name: Prepare the isolated OpenSSH fixture + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends openssh-server + sudo useradd --create-home --shell /bin/bash pebreltest + echo 'pebreltest:pebrel-test-only' | sudo chpasswd + mkdir -p "$RUNNER_TEMP/android-ssh" + ssh-keygen -t ed25519 -N '' -f "$RUNNER_TEMP/android-ssh/host_key" + sudo mkdir -p /run/sshd + cat > "$RUNNER_TEMP/android-ssh/sshd_config" <> "$GITHUB_ENV" + - name: Enable emulator acceleration + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: Test native terminal, SSH and SFTP in the Preview APK + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 35 + target: google_apis + arch: x86_64 + disable-animations: true + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + script: | + gradle --no-daemon -p mobile/android :app:connectedPreviewAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=io.github.kuddev.pebrel.mobile.GhosttyEngineTest,io.github.kuddev.pebrel.mobile.SshIntegrationTest#sftpRoundTripPagingAndCancellationKeepTheRealShellUsable,io.github.kuddev.pebrel.mobile.SshIntegrationTest#realPasswordShellStreamsOutputThroughGhostty,io.github.kuddev.pebrel.mobile.SshIntegrationTest#wrongPasswordReportsAuthenticationAndWipesSecret,io.github.kuddev.pebrel.mobile.SshIntegrationTest#changedHostKeyNeverReachesAuthentication,io.github.kuddev.pebrel.mobile.SshIntegrationTest#declinedTrustNeverAuthenticates,io.github.kuddev.pebrel.mobile.SshIntegrationTest#execKeepsStderrOutOfRpcStdoutAndPreservesExitCode -Pandroid.testInstrumentationRunnerArguments.sshFingerprint="$SSH_FINGERPRINT" -Pandroid.testInstrumentationRunnerArguments.sftpFixtureDirectory=paging + - name: Verify signing, identity, native payload and test results + run: | + python3 scripts/android_release.py --version "$PACKAGE_VERSION" --commit "$GITHUB_SHA" \ + --sdk "$ANDROID_HOME" --output dist --report android-evidence/android-report.json + - name: Upload Android Preview asset + uses: actions/upload-artifact@v4 + with: + compression-level: 0 + name: stable-package-android + path: dist/*.apk + if-no-files-found: error + retention-days: 14 + - name: Upload Android release evidence + if: always() + uses: actions/upload-artifact@v4 + with: + name: stable-evidence-android + path: android-evidence/android-report.json + if-no-files-found: warn + retention-days: 14 + - name: Upload Android diagnostic reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: android-test-reports + path: | + mobile/android/app/build/reports + mobile/android/app/build/test-results + mobile/android/app/build/outputs/androidTest-results + if-no-files-found: warn + retention-days: 14 + - name: Stop the test SSH service + if: always() + run: | + if [[ -f "$RUNNER_TEMP/android-ssh/sshd.pid" ]]; then + sudo kill "$(cat "$RUNNER_TEMP/android-ssh/sshd.pid")" + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 42817c0..84b5706 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -106,6 +106,7 @@ jobs: python3 scripts/check_platform_cfg.py python3 -m unittest \ scripts.tests.test_stable_release \ + scripts.tests.test_android_release \ scripts.tests.test_ci_native_tests \ scripts.tests.test_preview_release \ scripts.tests.test_macos_dmg \ @@ -584,7 +585,7 @@ jobs: shell: pwsh run: | ./scripts/package-release.ps1 -Version $env:PACKAGE_VERSION -Architecture arm64 -OutputDirectory dist -TargetDirectory target - ./scripts/build-installer.ps1 -Version $env:PACKAGE_VERSION -Architecture arm64 -OutputDirectory dist -TargetDirectory target -SkipBuild -Force + ./scripts/build-installer.ps1 -Version $env:PACKAGE_VERSION -Architecture arm64 -OutputDirectory dist -TargetDirectory target -Force - name: Keep Windows PowerShell alive on the ARM64 runner image shell: pwsh run: ./scripts/conformance/windows_arm64_module_path.ps1 @@ -617,10 +618,18 @@ jobs: if-no-files-found: warn retention-days: 14 + android: + name: Android Preview + if: ${{ !inputs.windows_only }} + needs: prepare + uses: ./.github/workflows/android-release.yml + with: + version: ${{ needs.prepare.outputs.version }} + aggregate: name: Verify stable assets and upload complete set if: ${{ !inputs.windows_only }} - needs: [prepare, linux, macos, windows, windows-arm64] + needs: [prepare, linux, macos, windows, windows-arm64, android] runs-on: ubuntu-22.04 env: PACKAGE_VERSION: ${{ needs.prepare.outputs.version }} @@ -649,6 +658,8 @@ jobs: from scripts.preview_release import validate_evidence validate_evidence(Path("stable-evidence"), os.environ["SOURCE_COMMIT"], windows_arm64=True) PY + python3 scripts/android_release.py --version "$PACKAGE_VERSION" --commit "$SOURCE_COMMIT" \ + --output release-dist --report stable-evidence/android-report.json --verify-evidence python3 scripts/stable_release.py release-dist \ --version "$PACKAGE_VERSION" \ --mark-platform-previews \ @@ -717,7 +728,7 @@ jobs: gh release create "$tag" \ release-dist/*.AppImage release-dist/*.deb release-dist/*.dmg \ release-dist/*.tar.gz release-dist/*.zip release-dist/*-setup.exe \ - release-dist/SHA256SUMS \ + release-dist/*.apk release-dist/SHA256SUMS \ --target "$GITHUB_SHA" --title "Pebrel $PACKAGE_VERSION" \ --notes-file release-dist/RELEASE_NOTES.md gh api "repos/$GITHUB_REPOSITORY/releases/tags/$tag" >"$RUNNER_TEMP/stable-release.json" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4754cc8..183f9dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,313 @@ Every release entry is provided in English and Simplified Chinese. 每个版本条目均同时提供英文和简体中文说明。 +## 2.0.0 - 2026-09-28 + +### English + +Pebrel 2.0 adds phone access to desktop terminals, Agent conversations, files and Git, with tasks continuing on the computer. The Android companion also offers local and direct-SSH terminals. Desktop updates include guided encrypted backup and restore, new automation commands, and improvements to input, rendering, startup and localization. + +#### Added + +##### Phone connections and desktop access + +- **Android companion Preview and connection onboarding.** Start by connecting a computer, adding an SSH host or opening a local terminal. The introduction can be skipped; cancelling a connection form returns to the choices, while completing or skipping the introduction is remembered across launches. +- **Pair by QR code, short code or complete invitation.** Desktop Settings generates a one-use invitation valid for ten minutes. On a discoverable local network, an eight-digit code offers another way to retrieve the invitation; scanning and pasting remain available when discovery is restricted. +- **Desktop-approved pairing.** Compare the verification code on both devices and approve the connection on the computer. Each paired phone receives an independent credential for subsequent connections. +- **LAN and self-hosted relay connection choices.** Connect directly to a reachable LAN or Tailscale address, or configure a self-hosted relay for use across networks. Phone-to-computer application traffic is end-to-end encrypted; a relay forwards that encrypted traffic rather than processing terminal content. +- **Per-phone access management.** See paired devices and their connection state, choose view-only or input access, and revoke an individual device. Pausing Phone Remote disconnects sessions while retaining enrolled devices for later use; closing Settings leaves active phone connections running. +- **Self-hosted relay management from the phone.** Use SSH to install, inspect, start, stop or uninstall the native relay on supported Linux systemd/OpenRC hosts. Setup reports actual stages and readiness. The desktop can import relay connection details from a file or clipboard, or read them from a saved SSH host. +- **Desktop workspace navigation.** Browse the computer's windows, tabs and panes from the phone. With the corresponding permission, switch or close tabs, create terminals and open files directly in the existing desktop workspace. +- **Online task notifications.** Receive system notifications for desktop task completion and requests for attention while connected. Android also offers an explicit foreground-service option for keeping active connections in the background, with a persistent notification and an entry to system notification settings. + +##### Android terminals and command input + +- **On-device local terminals.** Open an Android shell with an application-owned home directory, independently of a desktop connection. Available commands follow the device environment. +- **Direct SSH terminal sessions.** Save hosts, connect with password or server-approved passwordless authentication, confirm new host fingerprints and see the actual connection stage. Available session modes also support attaching to compatible session tools already installed on the server. +- **Compose and direct-input modes.** Compose a multiline draft locally before sending it, or switch to direct terminal input. Drafts belong to their session and are cleared only after an acknowledged submission, so switching views does not silently submit or discard unfinished text. +- **Touch-accessible terminal keys.** The command toolbar provides Ctrl+C, Escape, Tab, arrow keys and keyboard controls. Keys sent to the computer use the target terminal's input mode. +- **Session command history.** Reuse previous submissions from the composer and filter suggestions as you type. Selecting a suggestion fills the draft; execution still requires an explicit send action. Suggestions can be disabled in Settings. +- **File attachments for local and direct-SSH terminals.** Select a file through Android's document picker, import or upload it, and insert its quoted path into the composer. The path is added to a draft rather than executed as a shell command; this attachment path accepts files up to 16 MiB. +- **Optional on-device voice input.** Download the speech model, then hold to record and transcribe into an editable draft on the device. Sending remains a separate action. The model can be removed, and leaving the recording view stops recording. + +##### Conversations, documents and remote files + +- **A conversation view for an existing Agent session.** Read supported Codex and Claude native messages from the desktop session, including user messages, replies and tool results. Switching between terminal and conversation views keeps the same running CLI and context rather than launching another Agent. +- **Interactive confirmation choices.** Answer the current session's recognized questions and expand or collapse tool results. Buttons correspond to that specific question, keeping ordinary numbered text separate from actionable choices. +- **Color terminal output on the phone.** Desktop screen snapshots preserve text colors, background colors, styles, wide and combining characters, and cursor placement. The phone reads the live screen even when the desktop user is looking at an older scrollback position. +- **Phone-width terminal reflow.** Rewrap supported desktop screen rows to fit the phone without resizing the computer's PTY or changing its layout. A switch retains the original grid presentation for fixed-layout terminal applications. +- **Desktop file tabs on the phone.** Read supported text, Markdown and image files in dedicated tabs. If a file changes during loading, the reader reports the change instead of mixing content from different revisions. +- **SFTP browsing and transfers in direct SSH sessions.** Browse paged directories, create folders, rename or delete entries, upload files and download through Android's file picker. The SFTP browser shares the authenticated SSH connection while using a separate file channel; closing a file tab leaves the terminal session open. +- **A shared offline document reader.** Desktop files, SFTP files and Agent messages use the same reading components. Markdown includes tables, task lists, strikethrough, syntax-highlighted code, inline/display math and Mermaid diagrams, with rendering libraries and fonts bundled locally rather than fetched from a CDN when opening a document. +- **Document navigation, copying and image inspection.** Follow supported relative links into file tabs, open external links through the system, copy original code without line numbers or fences, and zoom or reset an image in its own viewer. Copy actions provide visible feedback without inserting extra space into the document. + +##### Git from the connected workspace + +- **Repository browsing on the phone.** View changed files, staged and unstaged differences, branch information and paged commit history for the selected desktop pane's working directory. Open file and commit diffs directly in the Git view. +- **Git operations.** Stage or unstage changes, commit, fetch, pull and push. Sync runs fetch, fast-forward-only pull and push in order, retaining the record of completed steps if a later step fails. Writes require input permission and a current repository snapshot; uncertain results are not automatically retried. + +##### Encrypted backup and restore + +- **A guided Backup and Restore page.** The desktop setup flow walks through storage selection, connection checking, a backup password and content selection. Configure an existing folder, WebDAV location, S3-compatible bucket or a remote directory on a saved SSH host. +- **Per-backup content selection.** Choose appearance, terminal and assistant settings, SSH host profiles, workspace layout, imported fonts and history categories. The interface shows the selected content size and remembers the selection after a successful backup. +- **Encrypt before storing or uploading.** Backup archives are encrypted on the computer. SSH private keys and secrets from the system credential store are excluded; command history is a separate selectable category. The backup password is retained only for the current application run, not written into the saved configuration. +- **Portable archive import and export.** Export selected contents as a `.pebrel-backup` file for copying to another computer, or import and unlock an archive with its backup password. Remote storage is optional for this file-based workflow. +- **Selective restore with undo.** Authenticate and inspect an archive before choosing what to restore. Unselected items remain unchanged. A local encrypted recovery copy preserves the previous files, including whether a file existed, so the restore can be undone from the current Settings session. + +##### Automation and desktop options + +- **Name individual split panes.** Double-click a pane title to name it; Enter or focus loss saves, Escape cancels, and clearing the name restores the automatic title. Names follow panes across moves and session restoration. + +- **Expanded Runtime API.** Automation clients can target tabs by stable identifiers, open and read supported file tabs, retrieve terminal screen snapshots, read supported Agent conversations and perform scoped Git operations. Existing plain-text terminal reads remain available. +- **Native and Lua plugin commands.** `pebrel plugin check` validates a local command package; `pebrel plugin run` executes one declared Runtime request or Lua handler and returns a structured result. Commands run on demand: native requests skip Lua initialization, and Lua invocations release their VM on completion. This release covers command plugins, not resident plugin panels. +- **A switch for Ctrl+wheel font zoom.** Settings → Appearance → Terminal appearance can disable the gesture completely: it changes neither font size nor scroll position. It remains enabled by default, and ordinary scrolling plus the font-size keyboard shortcuts keep their existing behavior. Addresses [#329](https://github.com/Kuddev/pebrel/issues/329). +- **Native Windows ARM64 installer support.** Installer builds and in-app updates now select the matching ARM64 package, alongside portable distribution. x64 support is unchanged; historical 1.9.0/1.9.1 ARM64 releases remain ZIP-only. + +#### Fixed + +##### Desktop input, rendering and window behavior + +- **Windows color emoji rendered as black shapes.** Corrected color-layer enumeration and transparent compositing initialization, restoring the colored glyphs and their transparent edges. Addresses [#317](https://github.com/Kuddev/pebrel/issues/317). +- **Ctrl+C lost its control character in Windows input mode.** The missing character is restored so applications such as `adb shell` receive the interrupt, with key-down, key-up and modifier information preserved. Addresses [#213](https://github.com/Kuddev/pebrel/issues/213). +- **Ctrl+/ lost its slash-key identity through ConPTY.** Windows input records now use the current keyboard layout's slash mapping, allowing native console applications to distinguish the shortcut correctly. The existing legacy byte encoding and ordinary slash text input are retained. +- **macOS Command shortcuts stayed active after being cleared.** Clearing an action now also clears its built-in Command alias; restoring defaults reinstates both. For older saved configurations, clear the affected row once more after upgrading. Command+K unbinding removes the picker shortcut without assigning a clear-screen action. Addresses [#238](https://github.com/Kuddev/pebrel/issues/238). +- **Command+Q did not quit the GPUI application.** The shortcut now uses the normal quit path, saving workspace state and drafts before ending terminal sessions. +- **Color-report escape sequences appeared at shell prompts.** Changing the color-notification subscription no longer sends an immediate report, addressing the reported Fish/SSH prompt noise. Explicit queries and actual light/dark changes retain their responses. Addresses [#283](https://github.com/Kuddev/pebrel/issues/283). +- **SFTP conflict dialogs remained open after an action.** Skip, Keep Both and Overwrite now close the dialog after submitting the choice. Cancel closes it without starting a transfer. Enter or Space activates the focused button; Enter without a focused button leaves the dialog open. Addresses [#230](https://github.com/Kuddev/pebrel/issues/230). +- **Advanced theme colors were displayed but not saved.** Selection background, selection foreground and cursor text color now apply to the terminal and remain saved when reopening the editor. The cursor text preview also reflects the effective color. +- **The update dialog became transparent with the terminal.** Its background remains opaque when terminal opacity is lowered, keeping update details and actions readable. +- **Windows startup briefly showed an empty, incorrectly sized window.** Normal foreground startup prepares the initial terminal geometry before showing the window and presents its first frame before activation, avoiding the visible resize jump. Background and Quick Terminal startup keep their separate behavior. +- **WSL received a Windows-form Hook executable path.** The path is translated when passed into WSL, allowing guest-side integration to locate the Hook executable. +- **Windows packages depended on build-machine shader files.** ZIP and installer packaging now requires release-mode binaries with embedded shaders, removing the debug renderer's dependency on source files from the build machine. + +##### Agent state and reading position + +- **A completed Pi tool turn was marked as a failure.** A host-finished `toolUse` turn now produces the completion outcome and notification instead of an erroneous failed badge. Truncated, aborted and error outcomes retain their distinct meanings. +- **Pi redraws displaced the text being read.** In supported Pi Hook sessions, synchronized redraws preserve the reading position when the previous content has a unique match. Scrolling, input, resizing and screen changes take priority over restoring the old position. +- **Codex `/plan` left the mobile conversation stuck in a running state.** Sending `/plan` by itself now allows continued input after the mode switch. Normal prompts and commands that start tasks retain their existing running/completion behavior. +- **Injected context appeared as conversation messages.** Supported Codex records now distinguish actual user messages from injected context and deduplicate repeated assistant/tool records. User-authored Markdown, XML and repeated questions remain intact. + +##### Mobile connections and session switching + +- **Changing a LAN address invalidated an already paired phone.** Address relocation now retains the computer's trusted key and phone grants while renewing the certificate for the new address. The phone can update the saved endpoint through an explicit address editor without scanning and approving the same computer again. +- **Stale requests after switching mobile sessions.** Late reads and confirmation actions are rejected when their original connection, pane or conversation has changed. Reconnecting does not resend a command that may already have been delivered. + +#### Improved + +##### Desktop usability and localization + +- **A cleaner tray menu when no Agent is running.** The empty Agent message and its extra separator are hidden; Show and Quit remain available. Running Agent entries keep their existing status display. Addresses [#324](https://github.com/Kuddev/pebrel/issues/324). + +- **Independent SSH host copies.** Copy immediately saves a separate host with the same connection settings. Editing the copy's address, credentials, proxy/jump settings or organization leaves the source unchanged; profile exchange preserves its connection target. +- **Local startup directories are remembered.** After a local terminal starts successfully, its initial directory enters the existing history and can be selected to open another terminal there. This addition does not record SSH or WSL guest paths as host-local directories. Addresses [#237](https://github.com/Kuddev/pebrel/issues/237). +- **Sidebar resizing is available by default.** Drag the existing divider to adjust the sidebar without first enabling the option. An explicitly disabled resize setting is still respected. +- **More workspace controls follow the selected language.** Toolbars, pane actions, local/remote file panels and close confirmations use the shared translation catalog rather than fixed Chinese labels. macOS shortcut hints identify Command as `Cmd` instead of `Win`. +- **Expanded Japanese and Traditional Chinese translations.** Japanese adds missing desktop catalog entries; Traditional Chinese uses Taiwan terminology for tabs, customization and file management. Text awaiting translation continues to use the existing fallback. +- **Acrylic blur at screen edges and when inactive.** On Windows 11 22H2+ with a matching Windows App Runtime 1.8 installed, Acrylic better preserves blur near screen edges and when focus is lost. Aero and older configurations retain their existing behavior. Addresses [#236](https://github.com/Kuddev/pebrel/issues/236). + +##### Mobile interaction and continuity + +- **Clearer pairing layout.** The pairing card groups controls in order: connection type, network interface and port, then instructions. The old QR code is hidden during an address change and replaced when the new invitation is ready. +- **Pairing stays on one page.** The camera preview is embedded beside the scan/code/paste choices. Changing methods, leaving the page or backgrounding the app releases camera use; code and paste remain usable when camera permission is declined. +- **LAN recovery no longer depends on keeping Settings open.** An enabled desktop LAN route retries a missing listener and preserves an automatically chosen port across restarts. The phone can look for a previously trusted computer while its disconnected workspace is in the foreground; manual address editing remains available when multicast discovery is restricted. +- **Long conversations remain readable during updates.** Incoming messages preserve the visible message anchor and tool-folding state when reading earlier content. Automatic following applies when already at the bottom, rather than pulling the reader away from an older reply. +- **Mobile display and input preferences are explicit.** Choose terminal themes and fonts, adjust font size, select cursor shape/blinking and enable or disable pinch zoom. The composer, history list and terminal viewport account for keyboard space, and terminal font selection is independent of system UI text scaling. +- **More predictable file transfers.** Directory loading, previews and transfers support cancellation. Downloads stream to the selected destination, uploads preserve existing targets, and file-channel timeouts leave a still-usable shell open. + +##### Performance and installation ownership + +- **Less embedded font data.** The desktop embeds only the default Maple Mono Normal NF CN font. Font rasterization and loading of system or imported fonts retain their existing behavior. + +- **Fewer settings reads during redraws.** Theme, language and sidebar-resize queries in the affected GPUI views use in-memory settings instead of repeatedly reading and parsing files. Saved settings and system appearance changes still refresh the views. +- **Startup does less blocking preparation.** Icon decoding/scaling and local directory-history recording are performed off the UI thread. The first visible window does not wait for all icon work, and cancelled or replaced icon requests do not overwrite the current workspace. +- **Bounded background reading.** Conversation reads and document processing run in the background, with size limits on screen snapshots, row updates, image previews and reader caches. Phone-width reflow reuses unchanged frames instead of repeating the work on every paint. +- **Channel-aware Windows updates.** Scoop-marked and Windows-packaged installations leave updates to their owning channel and show that channel in Settings. Regular installations retain the standalone updater. MSIX packaging and Scoop manifest generation are also supported. + +##### Compatibility and Preview scope + +- Android, desktop and relay packages retain separate version identities. The native relay uses protocol v2 and requires a matching phone client; older v1 combinations remain separate. +- Short-code lookup and automatic LAN rediscovery require local-network discovery. QR/full invitations and manual address editing remain available. Tailscale requires a reachable address between the two devices. +- Desktop-owned SSH conversation reading requires Python 3 on the remote host and a reported native transcript path. +- Task notifications are delivered while online, without offline replay. Android background sessions remain subject to system process management. +- MSIX and Scoop packaging support does not indicate Store publication or completed channel-installation acceptance. Final assets and checksums follow the release build. + +### 中文 + +Pebrel 2.0 支持从手机访问桌面终端、Agent 对话、文件与 Git,任务继续在电脑上执行。Android 配套端同时提供本地终端与直连 SSH 终端。桌面端新增引导式加密备份与恢复、扩展自动化命令,并改进输入、渲染、启动和多语言体验。 + +#### 新增 + +##### 手机连接与桌面访问 + +- **Android 配套端 Preview 与首次连接引导。** 首次进入时,可以选择连接电脑、添加 SSH 主机或打开本地终端,也可以跳过。取消连接表单后回到选择页;完成或跳过引导的状态会保存,后续启动不必重复选择。 +- **扫码、短码与完整邀请三种配对入口。** 桌面设置页生成十分钟有效的一次性邀请;在支持局域网发现的网络中,也可以输入八位数字取得邀请。网络限制设备发现时,仍可扫描二维码或粘贴完整邀请。 +- **电脑端确认配对。** 对照两端验证码,再在电脑上批准连接。每台已配对手机获得独立凭据,用于后续连接。 +- **局域网与自建中继两种连接方式。** 可以直连手机可达的局域网或 Tailscale 地址,也可以配置自建中继跨网络访问。手机与电脑之间的应用数据采用端到端加密,中继转发的是加密流量,不处理终端正文。 +- **按手机管理访问权限。** 查看已配对设备及在线状态,分别设置“仅查看”或“允许输入”,也可以撤销某一台手机的授权。暂停手机连接会断开会话但保留配对记录;关闭设置页则保留已经运行的连接。 +- **从手机管理自建中继。** 通过 SSH 在支持的 Linux systemd/OpenRC 主机上安装、查看、启动、停止或卸载原生中继,安装过程显示实际阶段与就绪状态。桌面端支持从文件、剪贴板导入连接信息,或从已保存的 SSH 主机读取配置。 +- **桌面工作区导航。** 从手机查看电脑的窗口、标签和分屏,并在权限允许时切换或关闭标签、新建终端、打开文件,直接操作现有桌面工作区。 +- **在线任务通知。** 连接期间通过系统通知接收桌面任务完成和需要处理的状态。Android 还提供显式的前台服务开关,用常驻通知维持后台活动连接,并可直接进入系统通知设置。 + +##### Android 终端与命令输入 + +- **设备本地终端。** 使用 Android Shell 和应用自己的主目录,独立于电脑连接运行;可用命令由设备环境决定。 +- **直连 SSH 终端。** 保存主机,使用密码或服务端认可的免密方式连接,确认首次遇到的主机指纹,并查看实际连接阶段。可选的会话模式也支持接入服务器上已经安装的兼容会话保持工具。 +- **编辑后发送与直接输入两种模式。** 可以先在手机本地编辑多行草稿,再显式发送;也可以切换到直接终端输入。草稿按会话保存,收到提交确认后才清除,切换视图不会悄悄提交或丢弃尚未完成的内容。 +- **触屏终端按键。** 工具栏提供 Ctrl+C、Esc、Tab、方向键及键盘控制。发送到电脑的按键遵循目标终端的输入模式。 +- **按会话使用命令历史。** 在编辑区复用先前提交的内容,并随输入过滤候选。选择历史记录只填入草稿,执行仍需显式发送;历史建议可以在设置中关闭。 +- **本地与直连 SSH 终端的文件附件。** 使用 Android 文件选择器导入或上传文件,再把带引号的路径放入编辑区。添加附件只修改草稿,不自动执行 Shell 命令;该附件入口接受最大 16 MiB 的文件。 +- **可选的端侧语音输入。** 下载语音模型后,长按录音,在设备上识别为可编辑草稿,发送仍由用户确认。模型支持删除,离开录音视图时停止录音。 + +##### 对话、文档与远程文件 + +- **已有 Agent 会话的对话视图。** 从桌面会话读取受支持的 Codex、Claude 原生消息,展示用户输入、助手回复和工具结果。终端视图与对话视图共用同一个正在运行的 CLI 及其上下文,不额外启动另一位 Agent。 +- **可操作的确认选项。** 回答当前会话中识别出的问题,并展开或收起工具结果。按钮对应具体问题,普通编号正文与可操作选项保持区分。 +- **手机端彩色终端画面。** 桌面屏幕快照保留文字与背景颜色、样式、宽字符、组合字符和光标位置。即使电脑端正在查看较早的滚动历史,手机仍读取当前活动屏幕。 +- **按手机宽度重排终端内容。** 将支持的桌面屏幕行重新排到手机可用宽度,不改变电脑 PTY 尺寸或桌面布局。对于依赖固定网格的终端程序,也可以切回原始网格显示。 +- **手机端桌面文件标签。** 在独立标签中阅读受支持的文本、Markdown 和图片文件;加载期间文件发生变化时提示更新,不混用不同版本的内容。 +- **直连 SSH 会话中的 SFTP 浏览与传输。** 分页浏览目录、新建文件夹、重命名或删除条目、上传文件,并通过 Android 文件选择器下载。文件浏览器复用已有 SSH 认证,但使用独立文件通道;关闭文件标签后,终端会话继续保留。 +- **共用的离线文档阅读器。** 桌面文件、SFTP 文件和 Agent 消息使用同一套阅读组件,支持 Markdown 表格、任务列表、删除线、代码高亮、行内/块级公式与 Mermaid 图表。渲染库及字体随应用携带,打开文档时无需临时从 CDN 获取。 +- **文档导航、代码复制与图片查看。** 支持将适用的相对链接打开为文件标签、通过系统打开外部链接、复制不含行号及围栏的原始代码,以及在独立视图中缩放或重置图片。复制后有可见反馈,不额外撑开正文布局。 + +##### 在连接的工作区中操作 Git + +- **手机端仓库浏览。** 针对选定桌面分屏的工作目录,查看变更文件、暂存与未暂存差异、分支信息及分页提交历史,在 Git 视图中直接打开文件和提交差异。 +- **Git 操作。** 支持暂存、取消暂存、提交、fetch、pull 和 push。同步按“fetch → 仅快进 pull → push”执行,后续步骤失败时保留已完成步骤的记录。写操作要求输入权限与最新仓库快照,结果不确定时不自动重发。 + +##### 加密备份与恢复 + +- **引导式“备份与恢复”设置页。** 按存储位置、连接检查、备份密码和内容选择逐步完成配置。支持已有文件夹、WebDAV、S3 兼容存储,以及已保存 SSH 主机上的远程目录。 +- **按次选择备份内容。** 按需选择外观、终端与助手设置、SSH 主机配置、工作区布局、导入字体及历史类别。界面显示所选内容大小,并在备份成功后记住选择。 +- **先在电脑加密,再保存或上传。** SSH 私钥和系统凭据库中的秘密不纳入归档;命令历史作为独立类别选择。备份密码只在本次应用运行期间保留,不写入保存的配置文件。 +- **便于跨电脑迁移的归档导入与导出。** 将所选内容导出为 `.pebrel-backup` 文件,或选择归档并用备份密码解锁。通过文件交换时,无需先配置远程存储。 +- **分项恢复,并可撤回本次恢复。** 先验证、解锁并查看归档,再勾选需要恢复的内容,未勾选项保持不变。覆盖前保存本机加密恢复副本,并记录原文件是否存在,可在当前设置会话中撤回此次恢复。 + +##### 自动化与桌面选项 + +- **为分屏窗格单独命名。** 双击窗格标题编辑名称,Enter 或失焦保存、Esc 取消,清空后恢复自动标题。名称随窗格移动,并随会话保存和恢复。 + +- **扩展 Runtime API。** 自动化客户端可通过稳定标识定位标签、打开和读取受支持的文件标签、获取终端屏幕快照、读取受支持的 Agent 对话,以及执行限定范围的 Git 操作。原有纯文本终端读取继续保留。 +- **原生与 Lua 插件命令。** `pebrel plugin check` 检查本地命令包,`pebrel plugin run` 执行一个已声明的 Runtime 请求或 Lua 处理器,并返回结构化结果。命令按需运行,原生请求跳过 Lua 初始化,Lua 调用结束后释放虚拟机;本版支持命令插件,不包含常驻插件面板。 +- **Ctrl+滚轮字号缩放开关。** 在“设置 → 外观 → 终端外观”中关闭后,该手势既不改变字号,也不滚动终端。默认继续开启,普通滚轮和字号快捷键保持原有行为。对应 [#329](https://github.com/Kuddev/pebrel/issues/329)。 +- **原生 Windows ARM64 安装器支持。** 安装器构建与应用内更新支持匹配的 ARM64 包,与便携版配套。x64 支持保持不变;历史 1.9.0/1.9.1 的 ARM64 资产仍仅包含 ZIP。 + +#### 修复 + +##### 桌面输入、渲染与窗口行为 + +- **Windows 彩色 emoji 显示成黑色轮廓。** 修正彩色字形图层的枚举与透明合成初始化,恢复表情颜色及透明边缘。对应 [#317](https://github.com/Kuddev/pebrel/issues/317)。 +- **Windows 输入模式丢失 Ctrl+C 控制字符。** 补回遗漏的控制字符,让 `adb shell` 等程序正确接收中断,同时保留按下、抬起及修饰键信息。对应 [#213](https://github.com/Kuddev/pebrel/issues/213)。 +- **Ctrl+/ 经过 ConPTY 后丢失斜杠按键身份。** Windows 输入记录按当前键盘布局解析斜杠,让原生控制台程序正确区分快捷键;原有传统字节编码与普通斜杠文本输入继续保留。 +- **macOS 清除快捷键后,Command 绑定仍生效。** 清除动作时同步清除内置 Command 别名,恢复默认时一并恢复。使用旧配置的用户需在升级后重新清除一次对应设置行;解绑 Command+K 仅移除选择器快捷键,不另行指定清屏动作。对应 [#238](https://github.com/Kuddev/pebrel/issues/238)。 +- **Command+Q 没有退出 GPUI 应用。** 现在走正常退出流程,先保存工作区状态与草稿,再结束终端会话。 +- **Shell 提示符出现配色报告转义序列。** 调整配色通知订阅时不再立即发送报告,修正 Fish/SSH 提示符乱码涉及的订阅行为;显式查询和实际明暗变化继续得到响应。对应 [#283](https://github.com/Kuddev/pebrel/issues/283)。 +- **SFTP 同名冲突弹窗在选择后残留。** “跳过”“保留两者”和“覆盖”在提交选择后关闭弹窗,“取消”只关闭弹窗、不启动传输。Enter 或空格激活当前聚焦按钮;未聚焦按钮时,Enter 保留弹窗。对应 [#230](https://github.com/Kuddev/pebrel/issues/230)。 +- **高级主题颜色修改后未保存。** 选区背景色、选区前景色与光标文字色正确应用到终端,并在重新打开编辑器时保留;光标文字预览同步显示生效颜色。 +- **更新弹窗随终端一起变透明。** 降低终端不透明度后,更新弹窗仍保持不透明底色,让版本信息与操作按钮保持可读。 +- **Windows 启动时先出现空窗口,再跳变尺寸。** 正常前台启动会在显示窗口前准备首屏终端几何,并在激活前呈现首帧,避免可见的尺寸跳变;后台启动与 Quick Terminal 保留各自的行为。 +- **WSL 收到 Windows 形式的 Hook 程序路径。** 路径传入 WSL 时转换为对应形式,供来宾侧集成定位 Hook 程序。 +- **Windows 分发包依赖构建机上的着色器文件。** ZIP 与安装器打包要求使用嵌入着色器的 release 二进制,消除 debug 渲染器对构建机源文件的依赖。 + +##### Agent 状态与阅读位置 + +- **Pi 已结束的工具调用轮次被标成失败。** 由宿主结束的 `toolUse` 轮次现在产生完成结果与通知,不再显示错误的失败标记;截断、中止和报错仍保持各自含义。 +- **Pi 重绘打断阅读位置。** 在受支持的 Pi Hook 会话中,同步重绘后若能唯一匹配原内容,便保留阅读位置。用户滚动、输入、调整尺寸和切换屏幕优先于旧位置恢复。 +- **Codex `/plan` 让手机对话停在“运行中”。** 单独发送 `/plan` 后,模式切换完成即可继续输入;普通提问及启动任务的命令保留原有运行与完成状态。 +- **注入上下文被显示为对话消息。** 受支持的 Codex 记录区分真实用户消息与注入上下文,并去重助手/工具的重复记录;用户编写的 Markdown、XML 和重复提问保持完整。 + +##### 手机连接与会话切换 + +- **更换局域网地址后,已配对手机失去连接凭据。** 地址变更时保留电脑的可信密钥与手机授权,为新地址更新证书。手机可通过显式地址编辑入口更新已保存端点,无需重新扫描并再次批准同一台电脑。 +- **手机会话切换后仍有过期请求。** 原连接、分屏或对话发生变化后,丢弃迟到的读取结果与确认操作;重连不重发可能已经送达的命令。 + +#### 改进 + +##### 桌面使用与多语言 + +- **无 Agent 时精简托盘菜单。** 隐藏空 Agent 提示及多余分隔线,保留显示和退出命令;有 Agent 时继续显示原有条目与状态。对应 [#324](https://github.com/Kuddev/pebrel/issues/324)。 + +- **独立的 SSH 主机副本。** “复制”立即保存具有相同连接设置的独立主机。编辑副本的地址、凭据、代理/跳板或组织信息不影响原主机,配置交换保留其连接目标。 +- **记住本地终端的初始目录。** 本地终端成功启动后,将初始目录加入既有历史,后续可以从中选择并在该目录打开新终端;这项补充不会把 SSH 或 WSL 来宾路径记录为本机目录。对应 [#237](https://github.com/Kuddev/pebrel/issues/237)。 +- **默认可以拖动侧栏宽度。** 无需先启用设置即可拖动既有分隔线调整侧栏;已经显式关闭拖宽的配置继续生效。 +- **更多工作区控件跟随所选语言。** 工具栏、分屏操作、本地/远程文件面板与关闭确认使用共享翻译目录,减少固定中文文案;macOS 快捷键提示使用 `Cmd`,不再把 Command 显示为 `Win`。 +- **扩充日语与繁体中文翻译。** 日语补充缺失的桌面消息目录条目,繁体中文按台湾用语修订标签、自定义与文件管理等措辞。待翻译文字继续使用既有回退。 +- **改善 Acrylic 边缘与失焦模糊。** Windows 11 22H2+ 安装架构匹配的 Windows App Runtime 1.8 后,Acrylic 更好地保持屏幕边缘及失焦时的模糊效果。Aero 与较旧配置保留原有行为。对应 [#236](https://github.com/Kuddev/pebrel/issues/236)。 + +##### 手机交互与连续使用 + +- **更清晰的配对布局。** 配对卡片内依次排列连接类型、网卡与端口、操作步骤。地址变更期间隐藏旧二维码,新邀请准备完成后再显示。 +- **在同一页完成配对方式切换。** 相机预览内嵌在扫码/短码/粘贴入口旁;切换方式、离开页面或应用进入后台时释放相机。未授予相机权限时,仍可使用短码和粘贴。 +- **局域网恢复不再依赖设置页常开。** 已启用的桌面局域网连接会重试缺失监听,并在重启时保留自动选定的端口。手机可在断开的工作区处于前台时查找此前信任的电脑;多播发现受限时仍可手动编辑地址。 +- **长对话更新时保留阅读位置。** 查看较早消息时,新增回复保留可见消息锚点与工具折叠状态;只有原本位于底部时才继续跟随,不把正在阅读旧回复的用户拉回最新消息。 +- **明确的移动端显示与输入偏好。** 可选择终端主题和字体、调整字号、设置光标形状与闪烁,并启用或关闭双指缩放。编辑区、历史列表和终端视口按键盘可用空间排列,终端字体与系统界面文字缩放各自生效。 +- **更可控的文件传输。** 目录加载、预览与传输支持取消;下载流式写入选定目标,上传保留已有目标文件,文件通道超时不关闭仍可用的 Shell。 + +##### 性能与安装来源 + +- **减少内嵌字体数据。** 桌面端仅内嵌默认的 Maple Mono Normal NF CN 字体,保持原有字体光栅化,以及系统和导入字体的加载行为。 + +- **减少重绘期间的设置读取。** 相关 GPUI 视图的主题、语言与侧栏拖宽查询读取内存设置,避免反复读盘和解析文件;保存设置及系统外观变化仍正常刷新界面。 +- **减少启动阶段的阻塞准备。** 图标解码、缩放及本地目录历史记录移到 UI 线程之外;首次显示不必等全部图标准备完成,取消或被替换的图标请求也不会覆盖当前工作区。 +- **有界的后台阅读处理。** 对话读取与文档处理在后台进行,屏幕快照、行更新、图片预览及阅读缓存设有大小上限。手机宽度重排复用未变化的画面,避免每次绘制重复计算。 +- **按 Windows 安装渠道更新。** 带有 Scoop 标记或 Windows 包身份的安装由对应渠道管理更新,并在设置中显示来源;普通安装继续使用独立更新器。同时支持 MSIX 打包与 Scoop 清单生成。 + +##### 兼容性与 Preview 范围 + +- Android、桌面应用与中继分别维护版本。原生中继使用 v2 协议,需搭配相应手机客户端;旧版 v1 组合单独使用。 +- 短码查询与局域网自动重新发现依赖本地网络发现;扫码、完整邀请及手动编辑地址仍可使用。Tailscale 需要两端地址实际可达。 +- 读取桌面 SSH 会话的对话,要求远端安装 Python 3,并上报原生会话记录路径。 +- 任务通知在联网期间送达,不含离线补发;Android 后台会话仍受系统进程管理约束。 +- MSIX 与 Scoop 打包支持不等同于商店上架或渠道安装验收完成。最终资产与校验值随发布构建确定。 + +### Contributors + +Thanks to everyone who contributed to this update. / 感谢参与本次更新的贡献者。 + +

+@WilliamWang1721 avatar +@JoyElliot avatar +@thynomex avatar +@Cec1c avatar +@AnxForever avatar +@galact-byte avatar +@zhbjerry avatar +@keys-cherish avatar +@tavricccc avatar +@Tettu0530 avatar +@Sakyvo avatar +@wyr-zal avatar +@Kuddev avatar +

+ +- **[@WilliamWang1721](https://github.com/WilliamWang1721)** — Rendering settings reads, startup-directory history, SSH host duplication, default sidebar resizing, Windows ARM64 installation, advanced theme colors, Ctrl+/ and color emoji; Pi helper regression coverage. / 渲染设置读取、启动目录历史、SSH 主机复制、默认侧栏拖宽、Windows ARM64 安装、高级主题颜色、Ctrl+/ 与彩色 emoji,以及 Pi 辅助进程回归覆盖。([#140](https://github.com/Kuddev/pebrel/pull/140), [#243](https://github.com/Kuddev/pebrel/pull/243), [#267](https://github.com/Kuddev/pebrel/pull/267), [#282](https://github.com/Kuddev/pebrel/pull/282), [#284](https://github.com/Kuddev/pebrel/pull/284), [#299](https://github.com/Kuddev/pebrel/pull/299), [#300](https://github.com/Kuddev/pebrel/pull/300), [#304](https://github.com/Kuddev/pebrel/pull/304), [#318](https://github.com/Kuddev/pebrel/pull/318)) +- **[@JoyElliot](https://github.com/JoyElliot)** — Windows Acrylic and first-frame behavior, Win32 control characters, SFTP conflict dialogs and pane titles. / Windows Acrylic 与首帧呈现、Win32 控制字符、SFTP 冲突弹窗及窗格标题。([#250](https://github.com/Kuddev/pebrel/pull/250), [#251](https://github.com/Kuddev/pebrel/pull/251), [#310](https://github.com/Kuddev/pebrel/pull/310), [#311](https://github.com/Kuddev/pebrel/pull/311), [#333](https://github.com/Kuddev/pebrel/pull/333)) +- **[@thynomex](https://github.com/thynomex)** — Workspace controls and confirmation localization. / 工作区控件与确认文案本地化。([#133](https://github.com/Kuddev/pebrel/pull/133)) +- **[@Cec1c](https://github.com/Cec1c)** — Cursor-motion foundations: preference handling, trajectory logic and tests. / 光标运动基础:设置处理、轨迹逻辑与测试。([#293](https://github.com/Kuddev/pebrel/pull/293)) +- **[@AnxForever](https://github.com/AnxForever)** — Hook executable path translation across WSL. / 跨 WSL 的 Hook 程序路径转换。([#290](https://github.com/Kuddev/pebrel/pull/290)) +- **[@galact-byte](https://github.com/galact-byte)** — Pi reading-position preservation during synchronized redraws. / Pi 同步重绘期间的阅读位置保持。([#234](https://github.com/Kuddev/pebrel/pull/234)) +- **[@zhbjerry](https://github.com/zhbjerry)** — macOS configurable Command shortcuts and normal quit. / macOS Command 快捷键配置与正常退出。([#248](https://github.com/Kuddev/pebrel/pull/248)) +- **[@keys-cherish](https://github.com/keys-cherish)** — Readable update dialogs at low terminal opacity. / 低终端不透明度下更新弹窗的可读性。([#305](https://github.com/Kuddev/pebrel/pull/305)) +- **[@tavricccc](https://github.com/tavricccc)** — Traditional Chinese catalog and Taiwan terminology. / 繁体中文消息目录与台湾用语。([#301](https://github.com/Kuddev/pebrel/pull/301)) +- **[@Tettu0530](https://github.com/Tettu0530)** — Japanese message-catalog expansion. / 日语消息目录扩充。([#314](https://github.com/Kuddev/pebrel/pull/314)) +- **[@Sakyvo](https://github.com/Sakyvo)** — Completed Pi tool-turn outcomes and notifications. / Pi 已结束工具轮次的结果与通知。([#321](https://github.com/Kuddev/pebrel/pull/321)) +- **[@wyr-zal](https://github.com/wyr-zal)** — Configurable Ctrl+wheel font zoom. / 可配置的 Ctrl+滚轮字号缩放。([#331](https://github.com/Kuddev/pebrel/pull/331)) +- **[@Kuddev](https://github.com/Kuddev)** — Desktop/mobile integration, backup and restore, automation, review and release preparation; color-report correction and reviewed integration batches. / 桌面与移动端整合、备份恢复、自动化、审查与发布准备,以及配色报告修正和审核后的批次整合。([#302](https://github.com/Kuddev/pebrel/pull/302), [#337](https://github.com/Kuddev/pebrel/pull/337), [#338](https://github.com/Kuddev/pebrel/pull/338)) + +--- + +**SHA256** + +- `Pebrel-v2.0.0-linux-x64-preview.AppImage`: `52941463b7b558c63fc1717d88a2d758bb67f9f1bd468c2a489be9cd7fab25d9` +- `Pebrel-v2.0.0-linux-x64-preview.deb`: `d2c8dcea66e2106f01e590730a18133c16fca08b2cdb9eab4d5cb922f80b914c` +- `Pebrel-v2.0.0-linux-x64-preview.tar.gz`: `0318dcafdb79de1a2c11bdfa61b03e12f0c227ffa55960e7fa6fc486aa5a6501` +- `Pebrel-v2.0.0-macos-arm64-preview.dmg`: `eb3a4f758f53e2571bcd9c0cbbb2560c8d43c2e4446cadac65487594a5ccc778` +- `Pebrel-v2.0.0-macos-x64-preview.dmg`: `6698b0e04b395ca99a1e33fbb3382d9c95886f07e80c85c6fb9ac794150e3e3e` +- `Pebrel-v2.0.0-windows-x64.zip`: `1bac7a1caa37b9c414667c0ce60cc132e2fde8a7d9a1ced5eecf7b5dda53419a` +- `Pebrel-v2.0.0-windows-x64-setup.exe`: `4c3ff71c6924a022717e97316acc32dd10db6972d91f33427ca502fbb7d79316` +- `Pebrel-v2.0.0-windows-arm64.zip`: `9127fa9891560a47a9d4d0458d0b6094e95ccd0fbc73623aa077618323456334` +- `Pebrel-v2.0.0-windows-arm64-setup.exe`: `234cf07fd4b9c25caebd6b55138c6d97087cccf0a7152c79882d4c35cb2bdb52` +- `Pebrel-v2.0.0-android-universal-preview.apk`: `dcef9e724e1500663613a367b41acccf84d45bf55c46e28a9ad5b1508662828c` + ## 1.9.1 - 2026-09-24 ### English diff --git a/Cargo.lock b/Cargo.lock index b7ba63d..eccf68c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,6 +133,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common 0.1.7", + "generic-array 0.14.7", +] + [[package]] name = "aead" version = "0.6.1" @@ -173,7 +183,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf011db2e21ce0d575593d749db5554b47fed37aff429e4dc50bc91ac93a028" dependencies = [ - "aead", + "aead 0.6.1", "aes 0.9.1", "cipher 0.5.2", "ctr", @@ -374,7 +384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7af50940b73bf4e16c15c448a2b121c63f2d68e3e54b6a8731673cb4aa0cdff5" dependencies = [ "base64ct", - "blake2", + "blake2 0.11.0-rc.6", "cpufeatures 0.3.0", "password-hash", ] @@ -439,6 +449,45 @@ dependencies = [ "zbus", ] +[[package]] +name = "asn1-rs" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom 7.1.3", + "num-traits", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -702,6 +751,61 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "base64 0.22.1", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1 0.10.7", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.76" @@ -735,6 +839,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "base64ct" version = "1.8.3" @@ -801,6 +911,9 @@ name = "bit-vec" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" +dependencies = [ + "serde", +] [[package]] name = "bit_field" @@ -832,6 +945,15 @@ dependencies = [ "no_std_io2", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "blake2" version = "0.11.0-rc.6" @@ -1139,6 +1261,17 @@ dependencies = [ "libc", ] +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher 0.4.4", + "cpufeatures 0.2.17", +] + [[package]] name = "chacha20" version = "0.10.1" @@ -1152,6 +1285,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead 0.5.2", + "chacha20 0.9.1", + "cipher 0.4.4", + "poly1305 0.8.0", + "zeroize", +] + [[package]] name = "chematic-core" version = "1.0.9" @@ -1906,6 +2052,20 @@ dependencies = [ "serde", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "fiat-crypto 0.2.9", + "rustc_version", + "subtle", +] + [[package]] name = "curve25519-dalek" version = "5.0.0-rc.1" @@ -1916,7 +2076,7 @@ dependencies = [ "cpufeatures 0.3.0", "curve25519-dalek-derive", "digest 0.11.3", - "fiat-crypto", + "fiat-crypto 0.3.0", "rustc_version", "subtle", "zeroize", @@ -1981,6 +2141,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom 7.1.3", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "deranged" version = "0.5.8" @@ -2192,7 +2366,7 @@ version = "3.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1685663e23882cd8517dcbcb1c23a6ebff4433c22dfb681d760219b62cd1b849" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 5.0.0-rc.1", "ed25519", "rand_core 0.10.1", "serde", @@ -2455,11 +2629,11 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.4.3", ] [[package]] @@ -2487,6 +2661,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "fiat-crypto" version = "0.3.0" @@ -3815,6 +3995,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hybrid-array" version = "0.4.13" @@ -3841,6 +4027,7 @@ dependencies = [ "http", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -4010,6 +4197,26 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "if-addrs" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf39cc0423ee66021dc5eccface85580e4a001e0c5288bae8bea7ecb69225e90" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "if-addrs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0a05c691e1fae256cf7013d99dad472dc52d5543322761f83ec8d47eab40d2b" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "ignore" version = "0.4.33" @@ -4429,9 +4636,9 @@ checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libfuzzer-sys" @@ -4725,6 +4932,12 @@ dependencies = [ "xml5ever", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "maybe-rayon" version = "0.1.1" @@ -4751,6 +4964,21 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebb8d8732c6a6df3d8f032a82911cfc747e00efb95cc46e8d0acd5b5b88570c" +[[package]] +name = "mdns-sd" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1067efe2aadc6967f84c95977dca910e98f3edfd6403efc8fa8508d289ce012d" +dependencies = [ + "fastrand", + "flume", + "if-addrs 0.15.0", + "log", + "mio", + "socket-pktinfo", + "socket2", +] + [[package]] name = "media" version = "0.1.0" @@ -4839,14 +5067,14 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" dependencies = [ "libc", "log", "wasi", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4984,13 +5212,13 @@ dependencies = [ [[package]] name = "nebula" -version = "1.9.1" +version = "2.0.0" dependencies = [ "ab_glyph_rasterizer", "aes-gcm", "ahash", "argon2", - "base64", + "base64 0.22.1", "bitflags 2.13.0", "chematic-depict", "chematic-smiles", @@ -5031,6 +5259,7 @@ dependencies = [ "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "parking_lot", + "pebrel-mobile-link", "png 0.17.16", "polling", "pulldown-cmark", @@ -5047,7 +5276,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_yaml", - "sha1", + "sha1 0.11.0", "sha2 0.11.0", "signal-hook", "tauri-winrt-notification", @@ -5069,7 +5298,7 @@ dependencies = [ [[package]] name = "nebula-completions" -version = "1.9.1" +version = "2.0.0" dependencies = [ "lscolors", "nu-ansi-term", @@ -5090,15 +5319,15 @@ dependencies = [ [[package]] name = "nebula-settings" -version = "1.9.1" +version = "2.0.0" [[package]] name = "nebula-split" -version = "1.9.1" +version = "2.0.0" [[package]] name = "nebula_config" -version = "1.9.1" +version = "2.0.0" dependencies = [ "log", "nebula_config_derive", @@ -5108,7 +5337,7 @@ dependencies = [ [[package]] name = "nebula_config_derive" -version = "1.9.1" +version = "2.0.0" dependencies = [ "log", "nebula_config", @@ -5125,9 +5354,9 @@ version = "0.6.0" [[package]] name = "nebula_terminal" -version = "1.9.1" +version = "2.0.0" dependencies = [ - "base64", + "base64 0.22.1", "bitflags 2.13.0", "home", "libc", @@ -5891,6 +6120,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -5938,6 +6176,12 @@ dependencies = [ "zvariant", ] +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "open" version = "5.4.2" @@ -6027,7 +6271,7 @@ checksum = "492f329d7eb11d22dadc988626b9ea1f503b4ab043a8b1e4e2cc4ae45dabdd70" dependencies = [ "ecdsa", "elliptic-curve", - "fiat-crypto", + "fiat-crypto 0.3.0", "primefield", "primeorder", "sha2 0.11.0", @@ -6157,6 +6401,45 @@ dependencies = [ "hmac 0.13.0", ] +[[package]] +name = "pebrel-mobile-link" +version = "0.1.0" +dependencies = [ + "axum", + "base64 0.22.1", + "futures-util", + "getrandom 0.3.4", + "hyper-util", + "if-addrs 0.14.0", + "libc", + "mdns-sd", + "qrcode", + "rcgen", + "rustls-pemfile", + "serde", + "serde_json", + "sha2 0.10.9", + "snow", + "subtle", + "thiserror 2.0.18", + "tokio", + "tokio-rustls", + "tokio-tungstenite", + "tokio-util", + "x509-parser", + "zeroize", +] + +[[package]] +name = "pem" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120" +dependencies = [ + "base64 0.23.1", + "serde_core", +] + [[package]] name = "pem-rfc7468" version = "1.0.0" @@ -6414,6 +6697,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash 0.5.1", +] + [[package]] name = "poly1305" version = "0.9.1" @@ -6421,7 +6715,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c" dependencies = [ "cpufeatures 0.3.0", - "universal-hash", + "universal-hash 0.6.1", "zeroize", ] @@ -6442,7 +6736,7 @@ checksum = "b20f20e954175de5f463f67781b35583397d916b1d148738923711b2ad16bee8" dependencies = [ "cpubits", "cpufeatures 0.3.0", - "universal-hash", + "universal-hash 0.6.1", ] [[package]] @@ -6685,6 +6979,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "qrcode" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec" + [[package]] name = "quick-error" version = "1.2.3" @@ -6819,7 +7119,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ - "chacha20", + "chacha20 0.10.1", "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -6995,6 +7295,20 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rcgen" +version = "0.14.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8774e05a7d0de114588e6a28fe7e71694b82614ed569d86d8b389dfbc98b8ad8" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "x509-parser", + "yasna", +] + [[package]] name = "read-fonts" version = "0.37.0" @@ -7291,7 +7605,7 @@ dependencies = [ "cipher 0.5.2", "crypto-bigint", "ctr", - "curve25519-dalek", + "curve25519-dalek 5.0.0-rc.1", "data-encoding", "delegate", "der", @@ -7332,7 +7646,7 @@ dependencies = [ "salsa20", "scrypt", "sec1", - "sha1", + "sha1 0.11.0", "sha2 0.11.0", "sha3", "signature", @@ -7343,7 +7657,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "typenum", - "universal-hash", + "universal-hash 0.6.1", "zeroize", ] @@ -7502,6 +7816,15 @@ dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom 7.1.3", +] + [[package]] name = "rustix" version = "0.38.44" @@ -7844,7 +8167,7 @@ checksum = "7bd22781911de0ca6debda95f073c8f18bec65d1a94f1fa9573f3102e514cea4" dependencies = [ "ahash", "annotate-snippets", - "base64", + "base64 0.22.1", "encoding_rs_io", "getrandom 0.3.4", "granit-parser", @@ -8004,6 +8327,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + [[package]] name = "sha1" version = "0.11.0" @@ -8255,6 +8589,33 @@ dependencies = [ "serde_core", ] +[[package]] +name = "snow" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "599b506ccc4aff8cf7844bc42cf783009a434c1e26c964432560fb6d6ad02d82" +dependencies = [ + "blake2 0.10.6", + "chacha20poly1305", + "curve25519-dalek 4.1.3", + "getrandom 0.3.4", + "ring", + "rustc_version", + "sha2 0.10.9", + "subtle", +] + +[[package]] +name = "socket-pktinfo" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612942246d0cc239cfd83af1dfd39be47f649208a3524e5e9da651910128e0ac" +dependencies = [ + "libc", + "socket2", + "windows-sys 0.61.2", +] + [[package]] name = "socket2" version = "0.6.4" @@ -8331,14 +8692,14 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d801accda99469cde6d73da741422610fdf6508a72d9a69d1b55cb241c720597" dependencies = [ - "aead", + "aead 0.6.1", "aes 0.9.1", "aes-gcm", - "chacha20", + "chacha20 0.10.1", "cipher 0.5.2", "ctutils", "des", - "poly1305", + "poly1305 0.9.1", "ssh-encoding", "zeroize", ] @@ -8376,7 +8737,7 @@ dependencies = [ "rand_core 0.10.1", "rsa", "sec1", - "sha1", + "sha1 0.11.0", "sha2 0.11.0", "signature", "ssh-cipher", @@ -8993,6 +9354,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -9125,6 +9502,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -9583,6 +9961,24 @@ dependencies = [ "core_maths", ] +[[package]] +name = "tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.5", + "rustls", + "rustls-pki-types", + "sha1 0.10.7", + "thiserror 2.0.18", +] + [[package]] name = "typeid" version = "1.0.3" @@ -9690,6 +10086,16 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common 0.1.7", + "subtle", +] + [[package]] name = "universal-hash" version = "0.6.1" @@ -9718,7 +10124,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ - "base64", + "base64 0.22.1", "cookie_store", "flate2", "log", @@ -9739,7 +10145,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" dependencies = [ - "base64", + "base64 0.22.1", "http", "httparse", "log", @@ -9763,7 +10169,7 @@ version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" dependencies = [ - "base64", + "base64 0.22.1", "data-url", "flate2", "fontdb", @@ -9790,7 +10196,7 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e419dff010bb12512b0ae9e3d2f318dfbdf0167fde7eb05465134d4e8756076f" dependencies = [ - "base64", + "base64 0.22.1", "data-url", "flate2", "fontdb", @@ -11234,6 +11640,24 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" +[[package]] +name = "x509-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom 7.1.3", + "oid-registry", + "ring", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + [[package]] name = "xcb" version = "1.7.1" @@ -11334,6 +11758,16 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" +[[package]] +name = "yasna" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282" +dependencies = [ + "bit-vec 0.9.1", + "time", +] + [[package]] name = "yazi" version = "0.2.1" @@ -11509,7 +11943,7 @@ name = "zed-reqwest" version = "0.12.15-zed" source = "git+https://github.com/zed-industries/reqwest.git?rev=c15662463bda39148ba154100dd44d3fba5873a4#c15662463bda39148ba154100dd44d3fba5873a4" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", diff --git a/Cargo.toml b/Cargo.toml index 85cbe90..9dfffd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "nebula_gpui", "nebula_settings", "nebula_split", + "mobile/link", ] resolver = "2" diff --git a/architecture/dependencies.toml b/architecture/dependencies.toml index 4615804..e685d3a 100644 --- a/architecture/dependencies.toml +++ b/architecture/dependencies.toml @@ -3,7 +3,7 @@ renderer_packages = ["gpui", "gpui_platform", "gpui-component", "gpui-component- [crates.nebula_app] layer = "application" -dependencies = ["nebula_terminal", "nebula_config", "nebula_config_derive", "nebula_settings", "nebula_split", "nebula-completions"] +dependencies = ["nebula_terminal", "nebula_config", "nebula_config_derive", "nebula_settings", "nebula_split", "nebula-completions", "mobile/link"] build-dependencies = ["nebula_settings"] dev-dependencies = [] @@ -35,3 +35,6 @@ zero_production_dependencies = true [crates.nebula_gpui] layer = "lab" + +[crates."mobile/link"] +layer = "core" diff --git a/architecture/file-budgets.txt b/architecture/file-budgets.txt index 41d6c5f..4e7083e 100644 --- a/architecture/file-budgets.txt +++ b/architecture/file-budgets.txt @@ -8,6 +8,7 @@ root nebula_hook root nebula_gpui root nebula_settings root nebula_split +root mobile/link root scripts root tools nebula_app/src/display/chrome.rs 2442 diff --git a/architecture/notes/mobile/android/2026-09-27-connection-onboarding.md b/architecture/notes/mobile/android/2026-09-27-connection-onboarding.md new file mode 100644 index 0000000..41f7bdd --- /dev/null +++ b/architecture/notes/mobile/android/2026-09-27-connection-onboarding.md @@ -0,0 +1,43 @@ +# Connection onboarding completion + +## Status +Implemented; device acceptance remains separate from automated UI checks. + +## Context +The first-launch connection choice offers a computer, an SSH host, a local +terminal, and an explicit skip. Returning from a cancelled connection form must +not discard the choice page, and skipping must not show it again on every launch. + +## Evidence +`MainActivity.Workspace` owns navigation and existing connection forms. +`session/DisplayPreferences.kt` owns small persisted UI preferences. +`ui/HomeSessionsTest.kt` exercises entry actions and preference reloads. + +## Decision +Store one boolean, `connection_onboarding_completed`, in the existing UI +preferences. Mark completion after a valid computer invitation enters its +connection flow, after an SSH host is saved, after local-terminal creation, or +when the user explicitly skips. Cancelling forms or storage permission leaves +the choice page active. This flag means the introduction was completed, not +that a network connection succeeded. + +## Rejected alternatives +Inferring completion from an empty host/session list would repeatedly interrupt +users who deliberately skipped. A new onboarding store or navigation framework +would duplicate existing ownership for a single preference. + +## Consequences +The new flag defaults to false. Existing session, credential and terminal +preference formats are unchanged. Home creation actions reuse the same flows. + +## Validation +The existing `HomeSessionsTest` covers the four home actions, three onboarding +choices, explicit skip, large-text scrolling and persisted completion. +Actual pairing, SSH authentication and remote deployment remain their existing +flows and require their own live endpoints for end-to-end acceptance. + +## Supersedes +None. + +## Revisit when +Onboarding gains resumable multi-step setup or a user-requested replay flow. diff --git a/architecture/notes/mobile/android/2026-09-27-inline-pairing-scanner.md b/architecture/notes/mobile/android/2026-09-27-inline-pairing-scanner.md new file mode 100644 index 0000000..29bdc27 --- /dev/null +++ b/architecture/notes/mobile/android/2026-09-27-inline-pairing-scanner.md @@ -0,0 +1,64 @@ +# Inline pairing camera ownership + +## Status +Implemented; physical-device QR decoding and computer approval require separate acceptance. + +## Context +The connection page offers scanning, an eight-digit code and pasted invitations +in one place. Launching a separate full-screen scanner hides that context. +Embedding the preview transfers permission and camera lifetime ownership from +the scanner Activity to the pairing view. + +## Evidence +- [ZXing 4.3.0 embedding guidance](https://github.com/journeyapps/zxing-android-embedded/blob/v4.3.0/EMBEDDING.md) + assigns permission and camera ownership to the embedding caller. It describes + preview overflow in dialogs with SurfaceView and the TextureView solution. +- [`InlinePairingScanner.kt`](../../../../mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/InlinePairingScanner.kt) + owns the Android view and lifecycle observer. +- [`RelayForm.kt`](../../../../mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/RelayForm.kt) + composes the scanner only for the active scanning method and uses the existing + invitation parser before entering the connection flow. + +## Decision +Reuse the existing ZXing dependency with a TextureView-backed BarcodeView. +Only a resumed scanner with camera permission starts preview and single-result +decoding. Switching methods, leaving the page or pausing the Activity stops +decoding and releases the camera. Disposed or stopped views discard queued +results; the current callback handles an accepted result once. + +Permission denial and camera failure remain local states with retry or app +settings actions. Code entry and paste do not depend on camera permission. +Scanner hints sit below the fixed preview so larger system text does not cover +the viewfinder. Pairing parsing, discovery, verification and approval retain +their existing owners and protocols. + +## Rejected alternatives +- A separate capture Activity would retain the full-screen interruption. +- A new camera dependency would duplicate a capability already provided by + ZXing without removing the caller's lifecycle responsibility. +- Keeping the preview alive under inactive methods would consume the camera + after the user had moved to an input method that does not need it. +- Blocking `pauseAndWait()` on every transition would stall the UI thread. + The existing camera worker serializes close/open operations; this view does + not immediately hand the camera to an unrelated capture implementation. + +## Consequences +Camera cleanup is now part of the pairing view's contract, including tab changes +and background transitions. No new persisted settings, dependencies or fallback +pairing protocols are introduced. TextureView's compositing cost is accepted +for a bounded preview that must respect the surrounding Compose controls. + +## Validation +The existing `HomeSessionsTest` covers method exclusivity, a bounded scanner +region, invitation validation, back navigation and one accessible numeric +input at enlarged text size. Android API 28 emulator checks cover actual touch +switching and camera service ownership across method changes, backgrounding and dismissal. +These checks do not establish physical-camera optical decoding or a completed +connection to a real computer. + +## Supersedes +None. + +## Revisit when +The app replaces ZXing, adds concurrent camera consumers or changes pairing-page +navigation in a way that outlives its current Activity/view lifecycle. diff --git a/architecture/notes/mobile/android/2026-09-28-shared-reader-lifetime.md b/architecture/notes/mobile/android/2026-09-28-shared-reader-lifetime.md new file mode 100644 index 0000000..e79bba1 --- /dev/null +++ b/architecture/notes/mobile/android/2026-09-28-shared-reader-lifetime.md @@ -0,0 +1,66 @@ +# 文件与对话共用阅读器的资源边界 + +## Status + +Implemented. + +## Context + +电脑文件、SFTP 文件和 Agent 消息都需要 Markdown、代码复制及链接跳转。 +分别维护三个渲染器会让中文、代码块、主题和复制反馈逐渐不一致;直接让远端正文 +控制 WebView 的导航或资源加载,又会把文件阅读变成不受控的浏览器入口。 + +## Evidence + +- [ReaderDocument](../../../../mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderDocument.kt) + 用 CommonMark 解析,并维护原始代码、链接与标题目录的独立映射。 +- [ReaderWebView](../../../../mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderWebView.kt) + 只加载 APK 中固定的阅读器 CSS / JavaScript;文件和内容访问关闭,网络请求不直接出站。 +- [桌面文件读取](../../../../mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopTabs.kt) + 与 [SFTP 读取](../../../../mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SftpClient.kt) + 分别保留自己的传输、修订与生命周期合同,不在阅读器里混合两种协议。 + +## Decision + +- 共用正文呈现、代码复制及图片缩放组件;导航、权限、下载和文件 Tab 的所有权仍由 + 相应页面维护。手机切换文件 Tab 不等价于关闭其 SSH 连接或电脑终端。 +- Markdown 和图片解码离开主线程。文件预览有明确字节预算,图片先读取尺寸并采样; + 超大文件的传输使用流式下载,不通过 WebView 绕过预览预算。 +- 正文中的 HTML 转义;链接由原生页面处理,相对文件链接进入文件 Tab,适用的外部 + 链接交给系统处理。正文不能自行读取本地文件或加载任意网络资源。 +- 复制使用原始代码,不附加行号、语言名和 Markdown fence;文件与对话共用就地 + 成功反馈和可取消的恢复计时器。 +- 对话增量更新保留可见消息锚点和工具折叠状态;仅在原本位于底部时跟随新消息。 + 缓存只在内存中保留最近四个对话视图,每个视图受消息数和文本字节预算限制;重新 + 进入时不恢复旧写入许可。文件内容跟随其页面生命周期,不另建永久文件缓存。 +- 原生 WebView 使用视图边界裁剪,并在页面完成前显示覆盖式加载进度。裁剪修复的是 + AndroidView 原生合成层越界,不通过关闭整应用硬件加速改变终端渲染。 + +## Rejected alternatives + +- 三套独立 Markdown / 复制实现:相同操作会形成不同的反馈和错误语义。 +- 允许 WebView 自行下载文档资源:跳过现有 SSH / 电脑权限、修订和取消边界。 +- 把全部历史和图片常驻缓存:文件及 Agent 消息的体积不受用户滚动次数限制。 +- 为避免工具栏被遮挡而关闭全局硬件加速:会波及无关的终端与界面绘制。 + +## Consequences + +当前 Markdown 范围是 CommonMark 加表格、删除线和任务列表;图片链接打开独立图片 +Tab,而非让 HTML 发起不受控资源请求。数学公式和 Mermaid 需要另行定义受控渲染 +资源与交互语义,不能把基础 Markdown 支持等同于所有扩展已兼容。 + +## Validation + +雷电与真实 SSH 文件连接验证了中文 Markdown、代码与图片独立 Tab、链接跳转、复制 +原始代码、图片等比缩放 / 重置和关闭文件不结束连接。WebView 冷启动期间可见加载 +进度,加载完成后顶部工具栏保持可见。对话的滚动与消息收发需要各自真实场景验收, +不由文件页面的渲染验证替代。 + +## Supersedes + +None. + +## Revisit when + +新增 Markdown 扩展、内嵌远端图片、离线文件缓存或流式对话渲染时,重新核对资源预算、 +链接权限、正文更新锚点和页面销毁策略;保持呈现复用与传输所有权分离。 diff --git a/architecture/notes/mobile/android/reader/2026-09-28-offline-markdown-extensions.md b/architecture/notes/mobile/android/reader/2026-09-28-offline-markdown-extensions.md new file mode 100644 index 0000000..9aa66fb --- /dev/null +++ b/architecture/notes/mobile/android/reader/2026-09-28-offline-markdown-extensions.md @@ -0,0 +1,55 @@ +# Offline mathematical and Mermaid rendering + +## Status +Implemented; parser tests and LDPlayer formula/diagram rendering verified. + +## Context +File and Agent readers share CommonMark and a restricted WebView. Math needs to +be protected before Markdown consumes underscores and escapes. Runtime CDN loads +would break offline reading and expand the document's access beyond local assets. + +## Evidence +- `ReaderMath.kt` uses CommonMark 0.30's actual inline/block extension interfaces. +- `ReaderDocument.kt` escapes document HTML and preserves copy targets separately. +- `reader/extensions-vendor.json` records package integrity and asset hashes. +- LDPlayer WebView 91 rejected Mermaid's `class { static {} }` at parse time. + The same browser reported missing `Object.hasOwn` and `structuredClone` APIs. + +## Decision +- Bundle KaTeX 0.18.9 and Mermaid 11.17.2 with licenses and local font assets. +- Use esbuild 0.28.2 for syntax lowering and selected core-js 3.50.0 polyfills. + Explicitly lower class static blocks: this WebView contradicted the compiler's + version-based support inference. Keep the complete Mermaid implementation. +- Lock the reproducible build inputs under `third_party/reader-build`. +- Keep network/file access disabled and an explicit asset allowlist/CSP. + KaTeX uses `trust=false`; Mermaid uses strict security and bounded text/edges. +- Render diagrams serially, ignore results belonging to removed documents, and + bound the SVG cache. Keep source available when parsing/rendering fails. +- Use dedicated code-surface tokens. A theme's general document container may + intentionally equal its background, which is unsuitable as the code-block fill. + +## Rejected alternatives +- Post-processing arbitrary rendered Markdown text for math: loses original + escapes, treats code/currency as formulas and corrupts copy semantics. +- Downgrading Mermaid to avoid a browser syntax error: gives up library fixes + rather than adapting the known deployment target. +- Loading remote libraries or granting documents local filesystem access. + +## Consequences +The APK includes bounded extra JS/font resources. Source copies preserve the +CommonMark source text, whose line endings are normalized by that parser. +Math and diagrams still require a functioning system WebView; one emulator is +not evidence of universal WebView compatibility. + +## Validation +Existing reader tests cover escaped HTML, math/code/currency separation, exact +copy data and Mermaid source/CSP. Real LDPlayer screenshots show inline/display +math, a flowchart and a sequence diagram with Chinese labels. The code-surface +regression has a targeted theme test; visual acceptance is recorded separately. + +## Supersedes +None. + +## Revisit when +The supported WebView baseline changes or upstream syntax/API support makes the +explicit compatibility transforms unnecessary. diff --git a/architecture/notes/mobile/link/2026-09-26-native-phone-pairing.md b/architecture/notes/mobile/link/2026-09-26-native-phone-pairing.md new file mode 100644 index 0000000..1cd21c4 --- /dev/null +++ b/architecture/notes/mobile/link/2026-09-26-native-phone-pairing.md @@ -0,0 +1,95 @@ +# Native phone pairing and short-code discovery + +## Status + +Implemented in the working tree. Native protocol tests and product compilation are +verified; physical-phone network acceptance remains separate. + +## Context + +Phone Remote needs an actual Off → Pairing → Paired lifecycle, not a settings-only +QR display. The desktop remains the execution authority. LAN and a separately +hosted relay must share device grants and input permission without sharing relay +credentials with the runtime API. + +Manual pairing needs both a complete invitation and an eight-digit code. A short +decimal code has insufficient entropy to replace a device credential or Noise PSK. + +## Evidence + +- `mobile/link/src/endpoint/host.rs` owns enrollment, persistence-before-ack, + per-device permission and revocation. +- `mobile/link/src/endpoint/transport.rs` requires Noise authentication, desktop + approval and an encrypted credential acknowledgment before opening Runtime. +- `mobile/link/src/endpoint/pairing_code.rs` limits a short code to five attempts, + one redemption and the original invitation's validity. +- `mobile/link/src/endpoint/discovery.rs` advertises a v2 TLS endpoint, name and + public-key fingerprint, never the invitation or device secret. +- Android's NSD service-info callback is available from API 34. API 26–33 resolve + services one at a time; concurrent legacy resolution can return ALREADY_ACTIVE. +- The runtime snapshot subscription publishes state changes, not an unconditional + polling heartbeat. Permission UI therefore needs a separate live update. + +## Decision + +1. Keep pairing and Noise framing in the shared Rust link crate. Kotlin uses its + existing JNI bridge, and GPUI calls the process-owned desktop service. +2. Use 600-second one-use invitations and a bounded 120-second approval window. + Rejecting or replacing an invitation also cancels its outstanding approvals. +3. An eight-digit code retrieves the full invitation over pinned TLS on the local + network. It does not grant access. mDNS fingerprints are untrusted bootstrap + metadata: the user compares the locally derived six-digit Noise code on both + devices and approves on the desktop to bind the intended computer and phone. +4. Retain QR and complete-invitation import when multicast discovery is blocked or + when pairing across networks. Discovery is optional; the native TLS service is + not torn down if advertisement registration fails. +5. Persist host grants before returning the new per-device secret. Revoke or change + permissions only after credential-store success. A watch notification updates + the phone's input UI even while the terminal is idle; every runtime request + still checks the current authorization independently. +6. Keep Android discovery and lookup scoped to the pairing form. Closing the form + stops discovery and cancels its socket; waiting for desktop approval remains a + live foreground session rather than triggering reconnect or service shutdown. +7. Pause disconnects listeners and sessions but retains enrolled devices. Settings + view disposal does not stop approved sessions. + +## Rejected alternatives + +- Deriving the Noise PSK from eight digits: enables offline guessing and merges + bootstrap usability with the long-term authorization boundary. +- Advertising invitation secrets in mDNS: exposes enrollment material to everyone + on the subnet without an explicit user pairing action. +- Automatically enrolling after scanning: the QR alone must not open Runtime. +- Reimplementing Noise in Kotlin or treating a server-supplied code as verification: + both create a second authority for cryptographic state. +- Waiting for a new terminal snapshot before updating permissions: leaves an idle + phone showing writable input after permission has changed. +- Automatic v2-to-v1 fallback: silently removes v2 enrollment properties. + +## Consequences + +Multicast discovery depends on local network policy. Manual full invitations remain +available. A discovered fingerprint alone is not a verified computer identity. +LAN and relay endpoints can coexist, with an existing LAN session taking priority +for the same device. Automatic phone-side route racing/failover is not implemented. +The relay currently admits one mobile socket per room; native LAN admits multiple +enrolled phones. OS credential-store size limits can reject additional enrollment +without storing secrets in plaintext. + +## Validation + +Focused link tests exercise real loopback TLS, code redemption and rejection, +expiry/attempt limits, Noise code agreement, approval, credential persistence, +permission changes, refresh, pause and revocation. Android tests use TLS mock +servers and the actual client adapter; they do not replace a physical-phone test. +GPUI layout and clipboard checks are separate from protocol and compilation checks. + +## Supersedes + +None. + +## Revisit when + +A verified cross-network short-code service, multi-phone relay rooms or automatic +transport switching becomes an explicit requirement; preserve the same enrollment +and per-device authorization boundaries when adding it. diff --git a/architecture/notes/mobile/ssh/2026-09-28-sftp-channel-ownership.md b/architecture/notes/mobile/ssh/2026-09-28-sftp-channel-ownership.md new file mode 100644 index 0000000..d8bd83b --- /dev/null +++ b/architecture/notes/mobile/ssh/2026-09-28-sftp-channel-ownership.md @@ -0,0 +1,62 @@ +# SFTP 子通道与传输取消的所有权 + +## Status + +Implemented. + +## Context + +SSH 终端需要同时浏览目录和传输文件。如果把文件请求放进终端命令循环,文件请求 +等待服务器时会阻塞按键和输出;若每次打开文件树都新建 SSH 连接,又会重复认证并 +分裂主机指纹和连接生命周期。 + +## Evidence + +- [SSH 传输循环](../../../../mobile/ssh/src/transport.rs)已经持有认证后的 + `russh::client::Handle`,可以从同一连接打开独立 subsystem channel。 +- [SFTP 适配](../../../../mobile/ssh/src/sftp.rs)使用 `russh-sftp` 的 v3 协议实现, + 自己只维护目录游标、传输句柄、修订检查和上传临时文件。 +- [Android 客户端](../../../../mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SftpClient.kt) + 在 I/O 调度器调用 JNI;协程取消不代表已经发出的原生请求尚未创建句柄。 + +## Decision + +- 复用 SSH 认证与主机指纹,按需建立 SFTP 子通道。终端接收、发送和 SFTP 分别拥有 + 独立 future,外层统一跟随 SSH 连接结束。 +- 文件请求顺序处理,分块为 32 KiB;目录每页最多 256 条,原生层只持有一个目录 + 游标和最多四个传输句柄。新目录请求释放旧游标,空闲句柄由周期清理回收。 +- 原生句柄 ID 单调递增,文件通道重开后不复用;旧取消操作不会关闭新传输。 +- JNI 返回前的取消仍取回已创建的句柄,并在 `NonCancellable` 清理段归还。UI 页面 + 释放时先取消分页和上传,再关闭自己持有的游标。 +- 上传写入同目录的独占临时文件,核验实际大小后执行不覆盖目标的 SFTP v3 rename。 + 取消尽力关闭句柄并删除本次临时文件,不覆盖已有目标,不重放未知结果的写入。 +- 预览限制为 16 MiB;下载通过 Android 文件选择器提供的流分块写入,不先把大文件 + 整体载入 JVM。文件通道超时只回收文件通道,不主动结束仍可用的 shell。 + +## Rejected alternatives + +- 另开一条 SSH 连接处理每次文件操作:重复认证、重复凭据状态,且关闭语义不一致。 +- 在 PTY 上执行 `ls` / `cat` 并解析输出:文件名、编码、终端控制符和 shell 配置会改变协议。 +- 直接写目标文件或使用覆盖式 rename:失败、取消与目标冲突会损坏已有内容。 +- 用固定句柄槽位作为公开 ID:通道重建后的迟到取消可能命中新操作。 + +## Consequences + +同一主机的 shell 与文件操作共享 SSH 连接,但不共享终端字节流。目录游标有空闲 +期限,过期后需就地刷新;该恢复操作不是重新连接 SSH。断网后服务端遗留的临时文件 +不保证立即清理,界面不得把未确认的传输显示为成功。 + +## Validation + +原生测试覆盖请求参数、句柄和协议边界。Android `SshIntegrationTest` 通过真实 +OpenSSH 验证目录分页、UTF-8 文件名、分块往返、取消、目标不覆盖、重命名和删除, +并在传输期间验证 shell 仍可收发。手机 UI 另行验证系统文件选择器与文件 Tab 操作。 + +## Supersedes + +None. + +## Revisit when + +新增多文件队列、并发目录窗口、断点续传或覆盖上传时,重新定义传输所有权与用户确认 +语义;保持 SSH 认证的单一来源,并用真实终端收发证明文件操作未阻塞 PTY。 diff --git a/architecture/notes/nebula_app/ai_hook/2026-09-28-local-mode-command-submission.md b/architecture/notes/nebula_app/ai_hook/2026-09-28-local-mode-command-submission.md new file mode 100644 index 0000000..cdd9aa5 --- /dev/null +++ b/architecture/notes/nebula_app/ai_hook/2026-09-28-local-mode-command-submission.md @@ -0,0 +1,57 @@ +# 本地模式命令与真实任务提交的生命周期区别 + +## Status + +Implemented; targeted tests and the live mobile conversation path verified. + +## Context + +手机对话页发送 Codex `/plan` 后,CLI 已切换模式并回到输入提示,但对话页仍显示运行中, +后续消息的发送入口持续禁用。该命令只改变本地交互模式,没有任务完成 hook。 + +## Evidence + +- `AgentActivity::submitted()` 会建立 `pending_submit` 屏障,防止旧输入提示在任务刚提交时 + 被误判为完成。这是普通任务需要保留的行为。 +- 实际 Codex 0.155.1 接收 `/plan` 后显示 Plan mode 和空输入提示;Runtime 仍保持 + `running / state_source=process`,等待的任务事件并不存在。 +- `gpui_shell/terminal/view/runtime.rs` 的文本和粘贴入口原先都无差别建立该屏障。 + +## Decision + +由共享生命周期的 `submitted_text(program, text)` 区分这条已经核实的本地命令。 +仅 Codex 的完整输入去除首尾空白后等于 `/plan` 时调用已有 `input_sent()`,允许后续 +屏幕观察,但不制造新任务,也不改变正在运行的任务状态。 + +Runtime 普通输入和粘贴入口共用这一规则。其他命令、其他提供者和包含额外任务正文的 +多行输入继续使用 `submitted()`;手机视图不建立第二套命令或任务状态机。 + +## Rejected alternatives + +- 移除全部提交屏障:旧屏幕中的输入提示可能提前结束真实任务。 +- 把所有斜杠命令当作本地模式操作:`/review` 等命令本身会启动工作。 +- 手机发送 `/plan` 后强制显示完成:掩盖共享状态错误,也可能错误结束已有任务。 +- 通过超时解除运行状态:时间流逝不能证明任务已经结束。 + +## Consequences + +命令语义留在共享 Agent 生命周期,终端和手机入口保持一致。本次不推断未核实的其他 +斜杠命令,不把传输成功当作任务完成。 + +## Validation + +13 项生命周期测试通过。相关回归覆盖原生 hook 已接入和未接入的空闲会话、普通任务 +提交屏障、运行中模式切换、其他提供者以及带任务正文的输入。 + +真实手机对话页发送 `/plan` 后保持可继续输入;随后在同一原生会话提交中文问题, +收到可点击选项,选择后收到对应助手回答。正常任务仍经过 running、attention 和 +finished 的真实状态变化。 + +## Supersedes + +None. Extends the shared ownership established in `2026-09-19-agent-activity-authority.md`. + +## Revisit when + +提供者改变模式命令的事件语义,或出现另一条已复现的本地命令时,重新核对真实 CLI +事件与输入路径;保持精确分类,不扩展为未经验证的通用斜杠命令规则。 diff --git a/architecture/notes/nebula_app/assistant_answer/2026-09-28-native-conversation-identity.md b/architecture/notes/nebula_app/assistant_answer/2026-09-28-native-conversation-identity.md new file mode 100644 index 0000000..5b0539e --- /dev/null +++ b/architecture/notes/nebula_app/assistant_answer/2026-09-28-native-conversation-identity.md @@ -0,0 +1,70 @@ +# 活体 Agent 对话投影的身份与异步读取 + +## Status + +Implemented. + +## Context + +手机需要阅读 Agent 的消息、工具结果和确认选项,但切换到对话视图不应启动另一个 +Agent,也不应把旧会话的输入交给同一 pane 中后来启动的进程。原生记录可能较大, +在 UI 回调中读取会让桌面 hover、输入和手机请求一起等待文件 I/O。 + +## Evidence + +- [身份和请求合同](../../../../nebula_app/src/runtime_api/conversation.rs)将 + `kind`、`session_id`、`epoch` 作为同一会话的联合身份;首次读取可解析 epoch,写入必须携带它。 +- [原生记录投影](../../../../nebula_app/src/assistant_answer/conversation.rs)核验 + Codex / Claude 的原生会话标识,按边界读取 JSONL,不将推理记录作为可见消息输出。 +- 实测 Codex 0.155.1 的 `response_item/message/role=user` 同时包含环境注入和真正输入; + 可见用户消息另有 `event_msg/item_completed/UserMessage` 事件。助手完成事件的 + `AgentMessage` 内容使用 `Text`,与已有助手消息记录重复出现。 +- [终端适配](../../../../nebula_app/src/gpui_shell/terminal/view/conversation.rs)已持有 + 活体 Agent 身份、原生记录位置和桌面确认状态,另建一套屏幕识别会产生两个权威来源。 + +## Decision + +- 对话仅是当前 CLI 的另一视图,读取原生消息;消息发送仍进入该活体终端的已有提交路径。 +- Codex 用户消息以明确的 `UserMessage` 或旧式 `user_message` 事件为依据,不把 + 原始上下文中的 `role=user` 全部提升为聊天消息。助手消息兼容 `AgentMessage/Text` + 并去重;保留用户主动发送的 XML、Markdown 和重复提问,不按正文关键词删除内容。 +- 在 UI 线程捕获身份与执行上下文,在后台读取原生记录,回到 UI 线程再次核验身份后 + 才发布结果。关闭 pane 或替换会话使旧请求失效,而非转发给当前焦点。 +- 选项复用 `capture_confirmation()` / `answer_choice()` 的问题身份、一次性消费和 + 按键语义;普通编号列表不提升为可操作授权选项。 +- 手机端将变更请求串行化,并以变更版本丢弃发送前发起的迟到读取。发送后先清除旧 + 选项和发送许可,再等待新的状态。网络结果不确定时不自动重放用户输入。 +- Ctrl+C、Esc、Tab 和方向键走同一身份约束下的明确按键请求,不把正文当作按键脚本。 + +## Rejected alternatives + +- 为手机新开一个 Agent:会话上下文、权限和任务生命周期都会分叉。 +- 在 UI 回调中直接扫描原生记录:把磁盘或 guest I/O 延迟带入窗口交互。 +- 为手机复制确认文本解析器:会与桌面的问题轮次、重复问题及单次消费规则漂移。 +- 按 `AGENTS`、XML 标签或文件路径过滤用户正文:既会误删真实输入,也依赖注入文本的写法。 +- 网络恢复后重试发送:成功回执丢失时会重复提交真实任务。 + +## Consequences + +当前原生投影覆盖电脑本地 / 已有 guest 执行上下文中的 Codex 与 Claude。 +桌面 SSH pane 的记录属于远端主机,不能套用本机记录路径;该路径明确返回未就绪。 +手机缓存不保存可复用的 epoch、旧确认按钮或写入许可,重新进入时须重新协商。 + +## Validation + +现有 Rust 测试覆盖原生身份不匹配、UTF-8 与分页边界、部分 JSONL 写入、工具结果去重、 +省略推理记录、请求参数约束,以及同一确认问题跨轮次后的旧按钮失效。 +Android `DesktopRecoveryTest` 覆盖迟到读取与变更互斥、身份绑定及未知结果不重放。 +原生投影的 5 项定向测试通过。雷电模拟器与同一个原生 Codex 会话实际完成中文收发、 +两轮选项提交、工具记录展开和收起、Markdown 代码复制以及终端/对话视图往返。 +复制结果与原生代码块的 UTF-8 字节一致;阅读旧消息期间收到新回复,正文截图位置保持不变。 +这些真实验收不覆盖其他提供者版本、桌面 SSH 远端记录或所有 Android WebView 版本。 + +## Supersedes + +None. + +## Revisit when + +新增原生消息格式、远端 Agent 记录读取、实时增量事件或新的确认控件时,重新核对 +记录所有权和输入语义;优先扩展现有提供者适配,而非在手机建立第二套 Agent 状态机。 diff --git a/architecture/notes/nebula_app/assistant_answer/conversation/2026-09-28-ssh-transcript-owner.md b/architecture/notes/nebula_app/assistant_answer/conversation/2026-09-28-ssh-transcript-owner.md new file mode 100644 index 0000000..98e09a5 --- /dev/null +++ b/architecture/notes/nebula_app/assistant_answer/conversation/2026-09-28-ssh-transcript-owner.md @@ -0,0 +1,54 @@ +# Read remote transcripts through the owning SSH connection + +## Status +Implemented; targeted Rust and real-OpenSSH file-capture checks passed. + +## Context +Desktop SSH panes already receive authenticated Hook identities, but conversation +reads rejected every SSH source. Reading a remote absolute path on the desktop, +or opening a new connection by hostname during a delayed read, would target the +wrong environment or lose the original pane's ownership boundary. + +## Evidence +- `TerminalView::handle_ai_hook` records the provider's native session/file identity. +- `ssh_session::transcript` exposes an opaque reader for one live shell lifetime. +- `assistant_answer/conversation/remote.rs` and `remote_read.py` capture bytes; + the existing shared native-record projector validates and renders them. + +## Decision +- Publish the reader only after the SSH shell is opened. Capture the exact + authenticated transport with a weak reference and a per-pane close signal. +- Never resolve a hostname, reauthenticate or select a replacement pooled + connection from this read capability. Closing the shell cancels its reads. +- Send a fixed Python read program over a separate exec channel. Arguments are + encoded as JSON/base64, never inserted as shell source or into the live PTY. +- Bound the header/page and total duration; verify the opened file and path + metadata before returning bytes. Detect replacements/truncation as changes. +- Keep reads on the background executor and check native conversation identity + again on the UI thread before publishing the page or enabling input. + +## Rejected alternatives +- Treating a remote path as a host-local path. +- Reusing the general authenticated-connect helper: it can open another + connection or prompt for credentials after the original pane has ended. +- Guessing the newest conversation from a directory or terminal screen text. + +## Consequences +The remote host needs Python 3 and a Hook-reported native transcript path. +Missing metadata is reported as unavailable, not substituted with another Agent. +This capability serves desktop-owned SSH panes projected to mobile. It is not a +claim that mobile-direct SSH now has its own Hook installer or conversation index. + +## Validation +Rust conversation tests cover record/session validation and remote response +bounds. A reader-lifetime test rejects ended/missing transports without opening +a replacement. Six real-OpenSSH checks covered UTF-8/quoted paths, bounded tail +and header reads, earlier pages, out-of-range cursors and invalid file types. +Full desktop-SSH-to-phone Agent interaction remains a distinct device acceptance. + +## Supersedes +None. + +## Revisit when +Mobile-direct SSH adopts the same Hook/record contract, or providers expose a +structured native transcript API that replaces bounded file capture. diff --git a/architecture/notes/nebula_app/encrypted_backup/2026-09-27-first-release-restore.md b/architecture/notes/nebula_app/encrypted_backup/2026-09-27-first-release-restore.md new file mode 100644 index 0000000..3bf1b9b --- /dev/null +++ b/architecture/notes/nebula_app/encrypted_backup/2026-09-27-first-release-restore.md @@ -0,0 +1,64 @@ +# 首次备份上线:草稿提交与可撤回的分项恢复 + +## Status + +Proposed — 本地实现,待维护者审查。 + +## Context + +备份导航此前隐藏。用户确认备份从未正式上线,要求直接实现 HTML 原型的四步引导、 +按次选择内容和恢复撤回,不承担旧备份兼容或迁移。 + +## Evidence + +- `nebula_app/src/gpui_shell/settings_pane/backup.rs`:连接检查与完成时提交草稿。 +- `nebula_app/src/gpui_shell/settings_pane/backup/operations.rs`:异步操作与恢复生命周期。 +- `nebula_app/src/encrypted_backup.rs`:归档白名单、认证解密、设置敏感字段过滤。 +- `nebula_app/src/encrypted_backup/recovery.rs`:恢复前保存原始文件及存在状态。 +- `nebula_app/src/backup_remote/listing.rs`:协议返回的大小随记录保留,不捏造设备信息。 + +## Decision + +1. 首次上线只接受 `PEBRBAK1` 归档和当前 Pebrel 文件名,不增加旧格式探测或迁移。 + 本机配置文件的读取仍沿用配置域既有的身份规则,不改应用其他功能。 +2. 四步向导使用独立草稿。连接检查只读取已有目录,成功后才能进入密码步骤。 + 修改存储时先检查,再显式提交配置及系统凭据;取消不落盘。 +3. 内容选择在成功备份后持久化;首次推荐选择只在 UI 的推荐函数定义。 + 加密口令使用进程内 `Zeroizing` 缓存,输入控件在完成操作后清空,不保存到配置。 +4. 恢复先认证整个归档,列出真实类别,再只覆盖勾选项。覆盖前把目标的原始字节 + 及不存在状态写入本机加密恢复点。撤回可以删除恢复时新建的文件。 +5. 恢复点不是可交换的普通备份;撤回句柄约束本次目标集合,不扫描历史恢复点或迁移 + 旧备份。句柄及其口令由设置页拥有;操作失败时留下恢复点文件供故障调查。 +6. 抽屉由设置实体拥有,使用组件库的焦点陷阱与设置页既有 deferred overlay 模式。 + 不在窗口全局安装 Sheet,避免设置页关闭后残留弹层;异步结果通过弱实体及序号归属。 + +## Rejected alternatives + +- 继续常驻九个开关或省掉向导:不符合批准的原型。 +- 边输入边写配置/凭据:取消会改动真实目的地,连接检查也会污染原状态。 +- 用普通导出归档作为撤回:普通导出过滤 SSH 偏好,且没有“原文件不存在”的信息, + 撤回会丢原始字节或留下恢复中新建的文件。 +- 给未解密的远端记录填入当前设备名或示例大小:会把未知元数据伪装为真实事实。 +- 增加旧版归档读取器、版本分派或迁移层:用户明确确认没有已发布备份需要兼容。 + +## Consequences + +网络、加解密和内容统计在后台执行,渲染只消费准备好的状态。所有真实存储错误均反馈到 +当前页面/抽屉。目录检查只证明当前读取成功,不把它当成写入权限保证。 + +设备信息只在本次上传或成功解密后显示;其他加密归档显示类型标签,不额外下载全部归档。 +本机恢复点保持加密落盘;撤回入口及口令随设置页释放,不引入跨启动恢复注册表。 + +## Validation + +回归覆盖归档认证、白名单、类别筛选、精确撤回、新建文件删除、协议列表大小,及真实 +GPUI 控件的四步前进/返回、密码不一致、连接失败与抽屉取消。运行结果在本次交付说明中列出。 + +## Supersedes + +None。 + +## Revisit when + +备份正式发布后需要变更格式,或产品明确要求跨启动的恢复点管理时,另行定义合同; +不提前构建版本兼容层。操作系统凭据存储能力扩展也由凭据适配层负责。 diff --git a/architecture/notes/nebula_app/mobile_connection/2026-09-26-process-owned-phone-settings.md b/architecture/notes/nebula_app/mobile_connection/2026-09-26-process-owned-phone-settings.md new file mode 100644 index 0000000..3b3ab17 --- /dev/null +++ b/architecture/notes/nebula_app/mobile_connection/2026-09-26-process-owned-phone-settings.md @@ -0,0 +1,74 @@ +# Process-owned phone service and configuration transactions + +## Status + +Implemented in the working tree; product compilation is verified. Runtime UI and +platform-specific credential failure acceptance remain separate checks. + +## Context + +The settings page can be closed while a phone is connected. Its UI lifetime must +not own listeners, approved device grants or background configuration writes. +Changing one connection route must not restart the unchanged route. A cancelled +settings operation must not replace a newer user choice. + +## Evidence + +- `nebula_app/src/mobile_connection.rs` owns LAN/relay handles, serialized changes + and the generation used to reject stale operations. +- `mobile_connection/preferences.rs` stores one configuration credential, keeping + preferences and route credentials in the same OS-store transaction. +- `gpui_shell/settings_pane/mobile.rs` renders prepared snapshots, owns only UI + tasks/feedback and cancels its outstanding configuration generation on drop. +- `runtime_api/mobile_bridge.rs` reuses the existing runtime endpoint and request + allowlist instead of exposing the desktop endpoint token to a phone. + +## Decision + +Use a process-owned manager and two-worker network runtime. Run I/O and credential +operations outside rendering callbacks. UI cancellation increments an atomic +generation rather than waiting for a network or credential-store lock. + +Start changed routes as inactive candidates. Commit the complete configuration +once, check the generation, then enable their Runtime scope. Failure restores the +previous configuration and only the displaced routes. Restoration failure remains +visible as a failed/missing connection; it is not reported as successful rollback. + +Scope credentials to the configuration directory and elevation boundary. A disabled +startup loads preferences without creating a new host identity. Preserve pairing +credentials across pause and process restarts, but not across unrelated isolated +configuration instances. + +## Rejected alternatives + +- Owning listeners in the settings view: closing settings would disconnect phones. +- Saving preferences and credentials independently: partial writes leave the UI + pointing at an old relay token or a different listener certificate. +- Cancelling by taking the manager lock in a UI callback: a slow credential write + can freeze rendering and delay cancellation. +- Restarting both routes for every preference change: interrupts unrelated active + phones when merely changing notification or default-permission settings. + +## Consequences + +Snapshot queries clone active handles before querying the host. A slow host lock +does not extend a manager-held lock into the UI. The existing credential store +remains the secret authority and storage errors remain actionable failures. +Current runtime capabilities are advertised truthfully: the restored bridge does +not claim screen-delta reads while the resident runtime only supports text reads. + +## Validation + +Native link tests cover persistence-before-grant, failed permission writes, +revocation and pause behavior. Product compilation covers the GPUI/service +boundary. Full platform-store rollback and real settings interaction require +their own acceptance evidence; protocol tests do not establish those results. + +## Supersedes + +None. + +## Revisit when + +The existing runtime gains native screen reads or the configuration credential +outgrows the platform's blob limit; migrate explicitly without plaintext fallback. diff --git a/architecture/notes/nebula_app/mobile_connection/2026-09-28-lan-address-recovery.md b/architecture/notes/nebula_app/mobile_connection/2026-09-28-lan-address-recovery.md new file mode 100644 index 0000000..083db2d --- /dev/null +++ b/architecture/notes/nebula_app/mobile_connection/2026-09-28-lan-address-recovery.md @@ -0,0 +1,107 @@ +# Preserve paired computer identity across LAN address changes + +## Status + +Implemented in the working tree. Native TLS and Android recovery tests are verified; +desktop restart and emulator network acceptance are recorded separately. + +## Context + +A saved LAN address can disappear after changing networks. The settings selector +can show a current interface even when startup failed to bind the saved address. +Previously, selecting the new address generated a new TLS key, room and bearer +token. An already approved phone therefore lost its transport credentials even +though the desktop still retained its device grant. + +## Evidence + +- `mobile_connection.rs::start_route` previously regenerated `LanCredentials` + whenever the address or requested port changed. +- `preview/lan.rs` stores the TLS private key, certificate, room and transport token + independently of `endpoint/host.rs`'s Noise identity and per-phone grants. +- `endpoint/discovery.rs` already advertises the endpoint and its SPKI fingerprint. +- Android's `PinnedDesktopTls` pins SPKI and retains normal SAN/hostname validation. + Simply changing a URL while retaining the old IP certificate does not work. +- [rcgen 0.14.10](https://docs.rs/rcgen/0.14.10/rcgen/struct.CertificateParams.html#method.self_signed) + supports issuing a new certificate with an existing signing key. +- [Android NSD](https://developer.android.com/develop/connectivity/wifi/use-nsd) + requires discovery to be stopped when its owning lifecycle no longer needs it. + +## Decision + +Relocate LAN credentials by issuing a certificate for the new IP using the existing +private key. Preserve SPKI, room, transport token and device authorization. Retain +an automatically selected port on restart; explicit port changes remain explicit. + +Use the existing process-level startup thread as a five-second LAN recovery owner. +Only enabled LAN routes enumerate interfaces, off the UI thread. Keep the selected +address while it exists; otherwise choose from the existing ordered interface +list. Missing or failed listeners are retried without requiring settings to remain +open. No probe packets or new external dependency are introduced. + +Recovery tries the existing serialized operation lock. It reads preferences under +that ownership, never advances the user-operation generation and uses the same +transaction/rollback path. A concurrent user operation can cancel recovery rather +than having old preferences overwrite the user's selection. + +On Android, reuse NSD only while a foreground, previously connected LAN workspace +needs recovery. Match the already saved SPKI, not the advertised name. Discovery +can change only the endpoint: TLS pin, room, bearer token, Noise host and grant +remain the saved values. TLS, Noise and the first valid Runtime snapshot must +succeed before replacing the saved address. Reuse the computer ID and draft; +never replay commands. Backgrounding, closing the workspace or recovering all +connections stops discovery. Identity failures still stop automatic retries. + +Saved-computer and connected-workspace menus also share an explicit address editor +(`ComputerAddressDialog`). `RelayProfile::atLanAddress` validates the address and +retains all saved identities and credentials. It enters the same connection path +as discovery: only a successful authenticated snapshot replaces the saved address. +This allows recovery on networks where multicast discovery does not arrive. + +## Rejected alternatives + +- Regenerating transport credentials on every network change: turns an address + change into another pairing and invalidates approved phones. +- Disabling hostname verification: hides an invalid certificate rather than + renewing its SAN with the already trusted key. +- Trusting an mDNS name or newly advertised pin: untrusted local metadata is not + permission to replace an existing computer identity. +- Refreshing only from the settings page: recovery would depend on a view lifetime. +- Persisting an address as soon as it is discovered: an unreachable or spoofed + announcement could replace the last authenticated connection. + +## Consequences + +Multicast reachability still depends on the network and emulator. This does not +add LAN/relay racing or cross-network discovery. Older desktop builds that already +discarded their previous LAN key do not regain that key through this change. +The process owns one background monitor; no per-view network service is added. + +## Validation + +`preview::lan::tests` verifies real TLS on a new interface, unchanged credentials, +updated SAN, listener restart and rejection of wrong credentials/old epochs. +Android `RelayConnectionTest`, `DesktopRecoveryTest` and `DesktopReconnectTest` +verify endpoint selection, rejected pins/invitations, save-after-snapshot, +workspace/draft retention, stale callbacks, background disposal and no input replay. +The address-editor changes passed 47 focused Android tests across +`RelayConnectionTest`, `DesktopRecoveryTest` and `HomeSessionsTest`. +Emulator acceptance used the real address-editor entry, rejected an invalid input, +then connected at the changed LAN address without scanning or granting access again. +The saved list retained one entry per existing desktop identity. Two later isolated +desktop restarts also recovered the open phone connection at the same endpoint. +These checks do not establish automatic mDNS address discovery in the emulator or +physical-phone multicast reachability. + +## Supersedes + +Extends `2026-09-26-process-owned-phone-settings.md` with LAN recovery ownership. +Supersedes the pairing-form-only discovery scope in +`architecture/notes/mobile/link/2026-09-26-native-phone-pairing.md`, decision 6; +the enrollment and cryptographic boundaries in that note remain unchanged. + +## Revisit when + +LAN/relay route racing, an OS network-change subscription, or recovery without +multicast becomes an explicit requirement. Preserve the existing identity and +authentication-before-persistence boundaries. diff --git a/architecture/notes/nebula_app/plugins/2026-09-28-bounded-command-foundation.md b/architecture/notes/nebula_app/plugins/2026-09-28-bounded-command-foundation.md new file mode 100644 index 0000000..98951f3 --- /dev/null +++ b/architecture/notes/nebula_app/plugins/2026-09-28-bounded-command-foundation.md @@ -0,0 +1,71 @@ +# Bounded native/Lua command foundation + +## Status + +Implemented command slice. It is not a resident UI host. + +## Context + +The user selected performance and memory as the primary constraints and rejected adding TS/JS, +browser or additional execution engines. Resource contributions should cost no plugin VM; +executable contributions can reuse the existing Lua 5.4 dependency. + +## Evidence + +- `Cargo.toml` already declares mlua with Lua 5.4 and serialization; the lockfile pins 0.11.6. +- `config/lua/runtime.rs` owns configuration evaluation, not persistent plugin state. +- `runtime_api` already owns commands, targets, outcomes and local discovery. +- The existing general CLI response reader accumulates a line without a consumer-specific size cap. +- `TerminalView::drop` closes its session, so UI replacement has a separate ownership requirement. +- mlua 0.11.6 documents memory limits and instruction-hook yields on managed Lua threads. + +## Decision + +Deliver one real public entry: `pebrel plugin check/run`. A bounded TOML manifest selects a native +Runtime request or a named Lua handler. A native request returns before any Lua initialization, +including in a mixed package. No GUI startup wiring, directory watcher, worker or global cache is added. + +Lua initialization and the selected handler share an instruction/call/deadline budget. Native requests +yield out of Lua before reaching the existing Runtime. A command scope owns and drops the VM; +conversion has its own depth, text, node and allocation bounds rather than relying on the Lua limit. + +The Runtime adapter reuses existing envelope and discovery types, caps frames, checks response +identity, preserves input-outcome uncertainty and bounds I/O by a cumulative deadline. The existing +general CLI and subscription readers are not rewritten as part of this consumer-specific boundary. +The shared response model preserves an explicit JSON null result, distinguishing it from an absent +result field without duplicating or reparsing the response envelope in this consumer. + +## Rejected alternatives + +- A new JS engine or browser: contrary to the chosen runtime and resource budget. +- Reusing the configuration VM: couples two independent lifetimes and expands existing APIs. +- Creating resident workers or registries before any consumer needs them: idle cost and unused state. +- Only a metadata checker: does not exercise an actual command through the existing public control plane. +- Wiring an unverified executor into paint or replacing TerminalView: risks input latency or session loss. + +## Consequences + +The first resource contribution is a declared native command, not a new theme/UI installation path. +Persistent activation, package management, events, multiple Lua modules and UI slots remain unimplemented. +The synchronous command adapter is explicitly not reusable as a GUI/worker callback; future resident +work must retain the bounded data/execution contracts while supplying an owned asynchronous driver. + +## Validation + +Windows GPUI product build and ten isolated public-CLI smoke cases passed, including native/Lua +dispatch, UTF-8 data, preserved error receipts, missing runtime, mismatched reply identity and oversized +reply rejection, plus an explicit null result. All 15 plugin regressions, 9 Runtime client regressions, +the public CLI parser regression and the existing configuration-Lua foundation regression passed +(26 focused Windows tests). The current tree passed the architecture checker and focused formatting. + +No GUI throughput, process RSS, peak allocation or cross-platform result is claimed here. +Reported Lua memory is one VM's allocator usage only. + +## Supersedes + +None. The rejected exploratory TS proposal is not an accepted implementation decision. + +## Revisit when + +A real UI/event consumer is ready, a measured workload needs a revised budget, or plugin execution +requires stronger process isolation. No additional runtime follows automatically from those needs. diff --git a/architecture/notes/nebula_app/runtime_api/2026-09-28-mobile-color-grid.md b/architecture/notes/nebula_app/runtime_api/2026-09-28-mobile-color-grid.md new file mode 100644 index 0000000..3a0cbf6 --- /dev/null +++ b/architecture/notes/nebula_app/runtime_api/2026-09-28-mobile-color-grid.md @@ -0,0 +1,55 @@ +# Mobile terminal grid and phone-only reflow + +## Status +Implemented; targeted protocol and Android rendering tests run locally. + +## Context +The phone already had a grid decoder and painter, but `pane.read` rejected the +`screen` parameter. Plain text discarded colors and cursor information. Mirroring +desktop columns also made phone readers shrink text to see complete long rows. + +## Evidence +- `runtime_api/terminal_read.rs` owns bounded terminal projections. +- `runtime_api/mobile_screen.rs` owns each link's acknowledged row baseline. +- Android `DesktopScreenFrame.kt`, `DesktopScreenSync.kt`, `TerminalReflow.kt`, + and `TerminalSnapshotView.kt` consume the same immutable cell data. + +## Decision +- Keep the existing text-tail contract unchanged. `screen=true` is additive; + callers not requesting it receive the previous response shape. +- Advertise `pane.read.screen.v1`. Negotiate row deltas only when the resident + runtime advertises this capability; do not advertise an unimplemented stream. +- Read the live grid independently of desktop scrollback offset. Preserve the + theme/OSC palette, wide and combining glyphs, styles, inverse colors and cursor. +- Limit snapshots to 400 columns, 200 rows, 40,000 cells and 128 KiB of glyphs. + Oversized snapshots return an explicit error rather than silently cropping. +- Carry optional per-row soft-wrap metadata. Phone reflow joins those physical + rows and wraps cells to the available width without changing desktop PTY size. +- Reflow is cached by immutable source frame and available column count, not + repeated in the paint callback. A menu switch retains the original grid view. + +## Rejected alternatives +- Replaying captured ANSI: unnecessary terminal-control interpretation on a + read-only projection and poor snapshot/delta recovery semantics. +- Resizing the shared desktop PTY to phone width: changes the computer's layout + and affects another user's terminal interaction. +- Treating all rows as hard lines: loses logical continuity at desktop wraps. + +## Consequences +Very wide fixed-layout TUIs may be better viewed with phone reflow disabled. +Old screen producers without wrap metadata can still wrap physical rows locally. +Blank grid space below a cursor must not push the actual prompt above the viewport. + +## Validation +Targeted Rust screen tests cover soft/hard wraps, styles, palette, Unicode, cursor, +limits and opt-in behavior. Android tests cover delta revisions, CJK/combining/ +emoji reflow, cursor placement, unchanged source grids, keyboard height and clipping. +Color output and phone-width wrapping were inspected in the LDPlayer preview APK. +This does not claim physical-phone haptic or all-device IME acceptance. + +## Supersedes +None. + +## Revisit when +A negotiated event-driven grid stream replaces the current bounded polling path, +or a richer screen protocol needs explicit history and viewport ownership. diff --git a/architecture/notes/nebula_app/ssh_profiles/2026-09-28-independent-host-copies.md b/architecture/notes/nebula_app/ssh_profiles/2026-09-28-independent-host-copies.md new file mode 100644 index 0000000..7a6bb8e --- /dev/null +++ b/architecture/notes/nebula_app/ssh_profiles/2026-09-28-independent-host-copies.md @@ -0,0 +1,73 @@ +# Independent identities for saved SSH host copies + +## Status +Implemented in the working tree. Focused tests, a GPUI product build and native +copy/persistence/edit acceptance passed; a separate menu teardown failure remains. + +## Context +The host menu's Copy action opened a draft editor and cleared its address. +The requested behavior is to immediately save another host with the same connection +settings. Profiles, list entries, proxy secrets and passwords previously all used +the connection destination as their key, so saving the same destination again +would replace the original host. + +## Evidence +`SshProfiles::upsert` replaces a profile with the same destination. +`SshHostLists::merged` deduplicates entries by that identity. +`SshDestination::original` also scopes credentials and connection-pool routes. + +## Decision +Keep existing identities unchanged. New copies receive a random application-owned +identity and an optional `targets` entry in the same profile document. Its value +is the connection address or OpenSSH alias, never another copy's identity. +The native SSH resolver expands the connection target while retaining the copy's +identity for authentication and session ownership. The editor and host search +use the actual target. Editing or deleting a copy does not mutate its source. +The system-SSH wrapper likewise forwards the real address (normalizing explicit +ports for OpenSSH), while its AskPass environment retains the copy's identity. + +Copy authentication settings, key references, proxy/jump settings and organization. +Read and write secrets only through the existing system credential adapter, using +new credential keys. Save profile metadata after credential writes succeed; remove +newly written credentials if saving fails. The existing profile lock and stale +snapshot check remain authoritative. Perform this work on the background executor, +and show success only after persistence completes. Preserve distinct targets in +credential-free CSV exchange through an optional `connect_to` column. + +## Rejected alternatives +Saving another profile under the original address overwrites it. Opening an editor +or clearing the address fails the requested interaction. Writing generated aliases +into the user's OpenSSH configuration would modify a separate source of truth. + +## Consequences +Existing profiles require no migration. Versions that do not understand `targets` +cannot connect the new copies; downgrading is not a supported copy transport. +OpenSSH aliases retain their existing dependence on the user's SSH configuration. + +## Validation +43 focused tests passed, including independent metadata/targets, copies of copies, +CSV roundtrips, credential rollback, native destination resolution, system-SSH +argument forwarding, hidden settings navigation/search and keyboard menu failure. +The GPUI product build passed after regenerable Cargo outputs were cleaned. + +Native Windows mouse and keyboard actions created copies immediately without an +editor. Two successive mouse copies preserved the original profile and targets. +A fresh product process loaded both copies; editing one copy's address retained +its identity and left the source and sibling unchanged. The built executable also +expanded both copied endpoints correctly through the real OpenSSH `-G` command. +This does not claim a new remote authentication or SFTP acceptance run. + +The ignored native preview test fails its exit-time PopupMenu leak assertion. +Opening and dismissing a right-click menu without invoking Copy or writing any +profile reproduced the failure. The pinned gpui-component ContextMenu subscription +captures its own shared state strongly; changing preview shutdown ordering did +not resolve the failure. Leak detection remains enabled. This separate dependency +lifecycle issue is not treated as a failed host copy or a passing native test. +The workspace architecture gate still reports its existing workspace.rs line +budget violation; no budget or unrelated source was changed to hide it. + +## Supersedes +None. + +## Revisit when +The shared host model adopts explicit identities for all saved profiles. diff --git a/architecture/notes/packaging/2026-09-28-android-preview-release.md b/architecture/notes/packaging/2026-09-28-android-preview-release.md new file mode 100644 index 0000000..a513657 --- /dev/null +++ b/architecture/notes/packaging/2026-09-28-android-preview-release.md @@ -0,0 +1,85 @@ +# Android Preview in the desktop release transaction + +## Status + +Implemented and released in 2.0.0. Hosted build and x86_64 emulator validation passed; +physical-phone acceptance remains separate. + +## Context + +The 2.0 release includes an Android companion. Existing users have installed a +Preview application, so CI must not replace its package identity or signing key +with a newly generated debug/release identity. A desktop-only checksum manifest +would also omit the APK from the final publication checks. + +## Evidence + +- `mobile/android/app/build.gradle.kts` owns the Preview variant and its signing configuration. +- The current optimized Preview APK verifies with certificate SHA256 + `93587953edfe9611601768833b5f008ff700d85c2486ddcfb7f7ea82b2de827b`. +- Android Gradle Plugin 8.10 requires Gradle 8.11.1 and JDK 17: + https://developer.android.com/build/releases/past-releases/agp-8-10-0-release-notes +- Android's 16 KiB alignment check uses `zipalign -c -P 16 4`: + https://developer.android.com/tools/zipalign + +## Decision + +Keep the `.preview` application ID and current Preview certificate. Advance the +version code from 17 to 18 and use `2.0.0-preview` as the visible Android version. +This remains Preview distribution, not a production signing or Store release. + +The release workflow calls one Android packaging workflow. Build both Android +ABIs and the two embedded Linux relay binaries from the same source commit. +Native musl relay builds retain systemd/OpenRC portability without substituting +old deployment assets. Reuse existing native composition and relay validators. + +Run the complete debug unit-test suite and Preview lint, then exercise the optimized APK's terminal and +selected real OpenSSH/SFTP paths on an x86_64 emulator. Persistent-session tools +and optical QR pairing remain separate acceptance scenarios. + +Compose JVM tests use the existing debug-only test Activity manifest. The first +hosted attempt ran them as Preview and 50 tests failed before UI assertions with +`Unable to resolve activity` for `androidx.activity.ComponentActivity`. Run the +same complete unit-test sources under debug; keep native instrumentation on the +optimized Preview APK. Adding the test Activity to the distributed Preview would +unnecessarily change its manifest. + +Collect the APK only after checks pass. Verify the compiled package name, +incremented version, signer, archive alignment and both test reports. Bind the +evidence to the source commit and actual APK hash. The aggregate job requires +Android success and includes the APK in SHA256SUMS and publication verification. + +## Rejected alternatives + +- Generate a CI debug key: breaks update identity for the current Preview. +- Publish the unconfigured production variant: changes the package and signing contract. +- Upload an APK as optional evidence: permits a nominally successful release with no Android asset. +- Reuse a local APK or old embedded relay: breaks source-to-artifact traceability. +- Add Android work to every desktop test matrix: duplicates release work outside this request. + +## Consequences + +The current Preview identity is intentionally distinct from an older Preview +certificate. Retaining this identity establishes continuity with the current +Preview, not automatic migration from every historically distributed APK. +The shared release manifest requires Android from 2.0.0 onward and preserves +the exact historical desktop-only asset sets. + +## Validation + +Focused Python tests cover signer/package/version mismatches, missing or failed +tests, wrong source/hash evidence and a missing or malformed APK. The +[Android release job](https://github.com/Kuddev/pebrel/actions/runs/36452556963/job/109035735360) +passed 142 unit tests and 18 x86_64 emulator tests, then verified the Preview +identity, signer, native payload and source-bound evidence. The downloaded public +APK matches that evidence. Physical-phone coverage remains separate. + +## Supersedes + +None. + +## Revisit when + +Android moves to production signing, a Store channel or an explicit certificate +migration. Preserve installed-user identity deliberately rather than replacing +the key as an incidental packaging change. diff --git a/architecture/notes/packaging/2026-09-28-windows-debug-shader-packaging.md b/architecture/notes/packaging/2026-09-28-windows-debug-shader-packaging.md new file mode 100644 index 0000000..107a9da --- /dev/null +++ b/architecture/notes/packaging/2026-09-28-windows-debug-shader-packaging.md @@ -0,0 +1,69 @@ +# Windows packages require embedded GPUI shaders + +## Status + +Cause reproduced with the distributed debug binary. The extracted release package +passes the same missing-source check. ZIP and installer entry points now reject +the debug configuration; local debug builds remain available. + +## Context + +A Windows portable package started on the build machine but failed on another +machine before opening a window. The panic reported a DirectWrite initialization +failure caused by a missing path. Checking executable freshness, architecture and +successful startup on the build machine did not establish portability. + +## Evidence + +- Pinned GPUI revision `7c662384cd77c1d1e1566206920e3a420ea6767b` selects + `shader_resources::build_shader_blob` when `debug_assertions` is enabled. +- That branch canonicalizes `CARGO_MANIFEST_DIR/src/color_text_raster.hlsl` before + initializing DirectWrite's emoji rasterization shaders. General rendering also + reads `src/shaders.hlsl` from the same build-time source location. +- The release branch instead uses shader byte arrays generated by the dependency's + build script and embedded in the executable. +- Redirecting only the test process's HLSL source reads to a nonexistent path + reproduced the reported `DirectWriteTextSystem` / `os error 3` panic. No shared + dependency files or permissions were changed for this check. + +## Decision + +Restrict `package-release.ps1` and `build-installer.ps1` to the release +configuration. Keep `build-windows-product.ps1 -Configuration debug` for local +development. Use the same fresh release binaries for both distribution formats. + +## Rejected alternatives + +- Distributing debug binaries because they open on the developer machine: the + source checkout hides the external resource dependency. +- Recreating the build machine's absolute Cargo paths on recipient machines. +- Copying HLSL files beside the executable: the pinned debug loader does not + resolve them relative to the executable. +- Patching the GPUI dependency merely to make local debug builds distributable: + the existing release mode already embeds the required resources. + +## Consequences + +Distribution requires the release compiler/linker work and Windows SDK shader +compiler. Developer builds remain useful but cannot be packaged by these entry +points. Ordinary release startup still depends on supported Windows graphics APIs. + +## Validation + +The existing product-build script tests retain both release and debug build +coverage, and now verify that ZIP and installer entry points reject debug before +building or collecting files. The missing-source failure was reproduced in an +isolated debug process. Under the same process-local redirection, the extracted +release executable opened its native window, exposed the correct Runtime PID, +rendered Chinese text and color emoji, and shut down normally without reading +source HLSL. Its executable and bundled runtime hashes match the release build. +This is a targeted missing-resource check, not all-machine graphics certification. + +## Supersedes + +None. + +## Revisit when + +The pinned GPUI debug renderer gains a relocatable resource contract and that +contract is verified outside its source checkout. diff --git a/architecture/notes/packaging/2026-09-28-windows-distribution-ownership.md b/architecture/notes/packaging/2026-09-28-windows-distribution-ownership.md new file mode 100644 index 0000000..91c6389 --- /dev/null +++ b/architecture/notes/packaging/2026-09-28-windows-distribution-ownership.md @@ -0,0 +1,102 @@ +# Windows distribution ownership + +## Status + +Implemented in the working tree; product release and Store certification pending. + +## Context + +Scoop manages versioned portable directories; Windows packages have a read-only +installation directory. The existing standalone updater downloads an Inno +installer and uses an adjacent startup lock. Applying that ownership model to +either channel wastes downloads and, for MSIX, attempts an invalid directory write. + +## Evidence + +- `nebula_app/src/platform/update_installation.rs` requires an Inno uninstaller + before installation handoff; old portable copies already reject final handoff. + The missing behavior was earlier channel-aware discovery/download and explanation, + not a demonstrated successful overwrite of a Scoop install. +- `update_download/handoff.rs::installation_in_progress` used an installation-side + lifetime lock before resident startup. +- `GetCurrentPackageFullName` reports real Windows package identity. A package + identity does not prove that a package was acquired through Microsoft Store. +- `scripts/package-release.ps1` is the existing validated Windows payload authority. + +## Decision + +Detect package identity first and an installation-local `pebrel-distribution` +marker second. Scoop's pre-install hook writes the exact `scoop` marker. Missing +markers preserve the direct-distribution behavior; invalid/unreadable markers or +identity failures disable standalone updating. The marker grants no capabilities. + +Cache the result once, initialized during the GUI startup ownership check. No +terminal render/input callback scans the filesystem or queries package metadata. +MSIX/Scoop do not start the standalone update-check worker, hydrate its cached +download, make a new download, schedule an installer or prepare its handoff. +They also skip the adjacent lock. Settings explain the owning channel and leave +the user's persisted update preferences untouched. + +MSIX packaging wraps the existing portable packaging entry rather than duplicating +its payload list and freshness contract. Required Partner Center identity is an +input; generated MSIX remains unsigned. The initial package exposes the existing +native program and CLI alias, without declaring unimplemented Explorer integrations. +The console-subsystem alias requires `SupportsMultipleInstances=true` under SDK +semantic validation. This permits each CLI invocation to start its own process; +existing application/runtime logic still owns GUI and session handoff. + +Scoop manifests are generated offline from published Release metadata; exact +architecture asset names remain owned by `scripts/stable_release.py`. The current +manifest targets existing v1.9.1 ZIPs; source updater improvements require a new +application release and are not attributed to those old binaries. + +## Rejected alternatives + +- A shared user setting for installation source: simultaneous installations would + overwrite each other's update ownership. +- Inferring Scoop or Store from a directory-name substring: custom paths and + copied directories make that unreliable. +- Packaging an Inno setup inside Scoop, or launching it for a Store package: + that transfers ownership to a different installer. +- A new resident distribution service or scripting runtime: no lifecycle need. +- Treating every MSIX identity as proof of Store acquisition: sideloads exist. +- Duplicating the release payload and skipping MakeAppx validation: both allow + divergence that the existing packaging contracts already prevent. + +## Consequences + +Channel detection is a small cold-path addition using an existing windows-sys +feature; no new crate, worker, VM or terminal hot-path callback is added. The +application package owner controls updates; plugin distribution remains separate. + +Scoop's bucket publication and real installation acceptance are separate from +manifest generation. Store identity, signing for local acceptance, packaged OS +integration and certification are separate from generating a valid XML package. + +Windows-only PowerShell scripts containing Chinese comments use UTF-8 with BOM +so Windows PowerShell 5.1 does not parse them using its legacy ANSI code page. + +## Validation + +Focused Rust tests cover marker parsing and installation-local ownership. A +test-only child-process case exercises real marker detection and the early update +boundaries with isolated process state. Existing updater tests remain in place. + +Scoop generation was compared with the real v1.9.1 metadata, validated against the +upstream JSON schema, and checked with invalid draft/asset/digest inputs. Its +pre-install hook was executed only in a temporary directory. + +Windows SDK 10.0.26100 MakeAppx accepted both x64 and ARM64 layout fixtures with +semantic validation enabled. Invalid identities and mismatched architecture were +rejected. These layout fixtures are not executable product packages or Store +acceptance results. Existing package architecture tests were also run. + +## Supersedes + +None. + +## Revisit when + +Real Store acceptance reveals a packaged data/OS integration requirement, another +distribution manager is supported, or the application introduces an explicitly +owned portable in-place updater. diff --git a/architecture/notes/scripts/naming/2026-09-28-android-release-build-references.md b/architecture/notes/scripts/naming/2026-09-28-android-release-build-references.md new file mode 100644 index 0000000..9ddcdcc --- /dev/null +++ b/architecture/notes/scripts/naming/2026-09-28-android-release-build-references.md @@ -0,0 +1,50 @@ +# Android release cache references + +## Status + +Implemented with targeted positive and negative checks. + +## Context + +The Android release workflow caches the existing native dependency build directory +and keys that cache by its pinned upstream manifest. These paths are build inputs, +not product comparisons, but the naming checker recognized them only in the +already-established Gradle and Python build entry points. + +## Evidence + +The staged check rejected both the cache path and its `hashFiles` input in +`.github/workflows/android-release.yml`. The exact reference forms already exist +in `MOBILE_BUILD_REFERENCE_PATTERNS`. The naming tests now include both rejected +workflow lines as reproducible cases. + +## Decision + +Apply those same exact-reference patterns to the Android release workflow. +Continue checking the rest of each line, including appended descriptions and +comments. No file, directory or arbitrary quoted string is exempted. + +## Rejected alternatives + +- Remove the cache to satisfy a text checker: discards useful build reuse. +- Rename a dependency directory just for the checker: unrelated source churn. +- Exempt all workflow text: permits unrelated comparisons in public CI metadata. + +## Consequences + +Only the established native build references gain another legitimate consumer. +Ordinary mentions and the same references in unrelated documentation still fail. + +## Validation + +The existing naming suite covers both workflow references, appended comparison +text, unrelated metadata and references outside their owning paths. + +## Supersedes + +Extends the consumer scope of `2026-09-28-mobile-integration-references.md`. + +## Revisit when + +Another real build consumer needs these paths; require a concrete failing example +and retain the checks on all remaining text. diff --git a/architecture/notes/scripts/naming/2026-09-28-mobile-integration-references.md b/architecture/notes/scripts/naming/2026-09-28-mobile-integration-references.md new file mode 100644 index 0000000..ab5d7cb --- /dev/null +++ b/architecture/notes/scripts/naming/2026-09-28-mobile-integration-references.md @@ -0,0 +1,44 @@ +# Mobile dependency and session references in name checks + +## Status +Implemented with positive and negative regression coverage. + +## Context +The naming policy rejects unrelated product comparisons while preserving legal +attribution and functional compatibility identifiers. Android introduces native +dependency metadata, Gradle module references and remote session commands that +were absent from the original Rust-focused compatibility rules. + +## Evidence +The staged-name check rejected the actual Gradle dependency directive, the native +C++ include, pinned source URLs and remote session commands. Removing those +identifiers would break the build or change the program executed on an SSH host. +The owning sources are `mobile/android/app/build.gradle.kts`, the terminal JNI +adapter and `mobile/android/app/src/main/.../connection/SshSessionMode.kt`. + +## Decision +Keep the existing policy and add recognition for the necessary reference forms +at their owning paths. Remove only each matched reference before scanning the +remaining line. Preserve third-party notices and remove incidental references +from ordinary descriptions, diagnostics and fixture labels. + +## Rejected alternatives +- Renaming dependency headers or remote executable commands to pass a text check. +- Exempting whole source directories, files or all quoted strings. +- Removing license and source attribution from the Android distribution. + +## Consequences +The same line can contain both a legitimate reference and a prohibited comparison; +the latter still fails. New integrations need evidence before extending these +forms. Source URLs remain bounded to dependency metadata, not ordinary prose. + +## Validation +The existing naming test module covers real build/session references, appended +comparisons, unrelated metadata fields and the same text outside its owning path. +The staged and pending-commit checks remain required. + +## Supersedes +None. + +## Revisit when +New dependency metadata or remote command interfaces require additional forms. diff --git a/assets/fonts/JetBrainsMono-Regular.ttf b/assets/fonts/JetBrainsMono-Regular.ttf new file mode 100644 index 0000000..dff66cc Binary files /dev/null and b/assets/fonts/JetBrainsMono-Regular.ttf differ diff --git a/docs/release-notes/v2.0.0.md b/docs/release-notes/v2.0.0.md new file mode 100644 index 0000000..9486cd1 --- /dev/null +++ b/docs/release-notes/v2.0.0.md @@ -0,0 +1,306 @@ +# Pebrel 2.0.0 + +## English + +Pebrel 2.0 adds phone access to desktop terminals, Agent conversations, files and Git, with tasks continuing on the computer. The Android companion also offers local and direct-SSH terminals. Desktop updates include guided encrypted backup and restore, new automation commands, and improvements to input, rendering, startup and localization. + +### Added + +#### Phone connections and desktop access + +- **Android companion Preview and connection onboarding.** Start by connecting a computer, adding an SSH host or opening a local terminal. The introduction can be skipped; cancelling a connection form returns to the choices, while completing or skipping the introduction is remembered across launches. +- **Pair by QR code, short code or complete invitation.** Desktop Settings generates a one-use invitation valid for ten minutes. On a discoverable local network, an eight-digit code offers another way to retrieve the invitation; scanning and pasting remain available when discovery is restricted. +- **Desktop-approved pairing.** Compare the verification code on both devices and approve the connection on the computer. Each paired phone receives an independent credential for subsequent connections. +- **LAN and self-hosted relay connection choices.** Connect directly to a reachable LAN or Tailscale address, or configure a self-hosted relay for use across networks. Phone-to-computer application traffic is end-to-end encrypted; a relay forwards that encrypted traffic rather than processing terminal content. +- **Per-phone access management.** See paired devices and their connection state, choose view-only or input access, and revoke an individual device. Pausing Phone Remote disconnects sessions while retaining enrolled devices for later use; closing Settings leaves active phone connections running. +- **Self-hosted relay management from the phone.** Use SSH to install, inspect, start, stop or uninstall the native relay on supported Linux systemd/OpenRC hosts. Setup reports actual stages and readiness. The desktop can import relay connection details from a file or clipboard, or read them from a saved SSH host. +- **Desktop workspace navigation.** Browse the computer's windows, tabs and panes from the phone. With the corresponding permission, switch or close tabs, create terminals and open files directly in the existing desktop workspace. +- **Online task notifications.** Receive system notifications for desktop task completion and requests for attention while connected. Android also offers an explicit foreground-service option for keeping active connections in the background, with a persistent notification and an entry to system notification settings. + +#### Android terminals and command input + +- **On-device local terminals.** Open an Android shell with an application-owned home directory, independently of a desktop connection. Available commands follow the device environment. +- **Direct SSH terminal sessions.** Save hosts, connect with password or server-approved passwordless authentication, confirm new host fingerprints and see the actual connection stage. Available session modes also support attaching to compatible session tools already installed on the server. +- **Compose and direct-input modes.** Compose a multiline draft locally before sending it, or switch to direct terminal input. Drafts belong to their session and are cleared only after an acknowledged submission, so switching views does not silently submit or discard unfinished text. +- **Touch-accessible terminal keys.** The command toolbar provides Ctrl+C, Escape, Tab, arrow keys and keyboard controls. Keys sent to the computer use the target terminal's input mode. +- **Session command history.** Reuse previous submissions from the composer and filter suggestions as you type. Selecting a suggestion fills the draft; execution still requires an explicit send action. Suggestions can be disabled in Settings. +- **File attachments for local and direct-SSH terminals.** Select a file through Android's document picker, import or upload it, and insert its quoted path into the composer. The path is added to a draft rather than executed as a shell command; this attachment path accepts files up to 16 MiB. +- **Optional on-device voice input.** Download the speech model, then hold to record and transcribe into an editable draft on the device. Sending remains a separate action. The model can be removed, and leaving the recording view stops recording. + +#### Conversations, documents and remote files + +- **A conversation view for an existing Agent session.** Read supported Codex and Claude native messages from the desktop session, including user messages, replies and tool results. Switching between terminal and conversation views keeps the same running CLI and context rather than launching another Agent. +- **Interactive confirmation choices.** Answer the current session's recognized questions and expand or collapse tool results. Buttons correspond to that specific question, keeping ordinary numbered text separate from actionable choices. +- **Color terminal output on the phone.** Desktop screen snapshots preserve text colors, background colors, styles, wide and combining characters, and cursor placement. The phone reads the live screen even when the desktop user is looking at an older scrollback position. +- **Phone-width terminal reflow.** Rewrap supported desktop screen rows to fit the phone without resizing the computer's PTY or changing its layout. A switch retains the original grid presentation for fixed-layout terminal applications. +- **Desktop file tabs on the phone.** Read supported text, Markdown and image files in dedicated tabs. If a file changes during loading, the reader reports the change instead of mixing content from different revisions. +- **SFTP browsing and transfers in direct SSH sessions.** Browse paged directories, create folders, rename or delete entries, upload files and download through Android's file picker. The SFTP browser shares the authenticated SSH connection while using a separate file channel; closing a file tab leaves the terminal session open. +- **A shared offline document reader.** Desktop files, SFTP files and Agent messages use the same reading components. Markdown includes tables, task lists, strikethrough, syntax-highlighted code, inline/display math and Mermaid diagrams, with rendering libraries and fonts bundled locally rather than fetched from a CDN when opening a document. +- **Document navigation, copying and image inspection.** Follow supported relative links into file tabs, open external links through the system, copy original code without line numbers or fences, and zoom or reset an image in its own viewer. Copy actions provide visible feedback without inserting extra space into the document. + +#### Git from the connected workspace + +- **Repository browsing on the phone.** View changed files, staged and unstaged differences, branch information and paged commit history for the selected desktop pane's working directory. Open file and commit diffs directly in the Git view. +- **Git operations.** Stage or unstage changes, commit, fetch, pull and push. Sync runs fetch, fast-forward-only pull and push in order, retaining the record of completed steps if a later step fails. Writes require input permission and a current repository snapshot; uncertain results are not automatically retried. + +#### Encrypted backup and restore + +- **A guided Backup and Restore page.** The desktop setup flow walks through storage selection, connection checking, a backup password and content selection. Configure an existing folder, WebDAV location, S3-compatible bucket or a remote directory on a saved SSH host. +- **Per-backup content selection.** Choose appearance, terminal and assistant settings, SSH host profiles, workspace layout, imported fonts and history categories. The interface shows the selected content size and remembers the selection after a successful backup. +- **Encrypt before storing or uploading.** Backup archives are encrypted on the computer. SSH private keys and secrets from the system credential store are excluded; command history is a separate selectable category. The backup password is retained only for the current application run, not written into the saved configuration. +- **Portable archive import and export.** Export selected contents as a `.pebrel-backup` file for copying to another computer, or import and unlock an archive with its backup password. Remote storage is optional for this file-based workflow. +- **Selective restore with undo.** Authenticate and inspect an archive before choosing what to restore. Unselected items remain unchanged. A local encrypted recovery copy preserves the previous files, including whether a file existed, so the restore can be undone from the current Settings session. + +#### Automation and desktop options + +- **Name individual split panes.** Double-click a pane title to name it; Enter or focus loss saves, Escape cancels, and clearing the name restores the automatic title. Names follow panes across moves and session restoration. + +- **Expanded Runtime API.** Automation clients can target tabs by stable identifiers, open and read supported file tabs, retrieve terminal screen snapshots, read supported Agent conversations and perform scoped Git operations. Existing plain-text terminal reads remain available. +- **Native and Lua plugin commands.** `pebrel plugin check` validates a local command package; `pebrel plugin run` executes one declared Runtime request or Lua handler and returns a structured result. Commands run on demand: native requests skip Lua initialization, and Lua invocations release their VM on completion. This release covers command plugins, not resident plugin panels. +- **A switch for Ctrl+wheel font zoom.** Settings → Appearance → Terminal appearance can disable the gesture completely: it changes neither font size nor scroll position. It remains enabled by default, and ordinary scrolling plus the font-size keyboard shortcuts keep their existing behavior. Addresses [#329](https://github.com/Kuddev/pebrel/issues/329). +- **Native Windows ARM64 installer support.** Installer builds and in-app updates now select the matching ARM64 package, alongside portable distribution. x64 support is unchanged; historical 1.9.0/1.9.1 ARM64 releases remain ZIP-only. + +### Fixed + +#### Desktop input, rendering and window behavior + +- **Windows color emoji rendered as black shapes.** Corrected color-layer enumeration and transparent compositing initialization, restoring the colored glyphs and their transparent edges. Addresses [#317](https://github.com/Kuddev/pebrel/issues/317). +- **Ctrl+C lost its control character in Windows input mode.** The missing character is restored so applications such as `adb shell` receive the interrupt, with key-down, key-up and modifier information preserved. Addresses [#213](https://github.com/Kuddev/pebrel/issues/213). +- **Ctrl+/ lost its slash-key identity through ConPTY.** Windows input records now use the current keyboard layout's slash mapping, allowing native console applications to distinguish the shortcut correctly. The existing legacy byte encoding and ordinary slash text input are retained. +- **macOS Command shortcuts stayed active after being cleared.** Clearing an action now also clears its built-in Command alias; restoring defaults reinstates both. For older saved configurations, clear the affected row once more after upgrading. Command+K unbinding removes the picker shortcut without assigning a clear-screen action. Addresses [#238](https://github.com/Kuddev/pebrel/issues/238). +- **Command+Q did not quit the GPUI application.** The shortcut now uses the normal quit path, saving workspace state and drafts before ending terminal sessions. +- **Color-report escape sequences appeared at shell prompts.** Changing the color-notification subscription no longer sends an immediate report, addressing the reported Fish/SSH prompt noise. Explicit queries and actual light/dark changes retain their responses. Addresses [#283](https://github.com/Kuddev/pebrel/issues/283). +- **SFTP conflict dialogs remained open after an action.** Skip, Keep Both and Overwrite now close the dialog after submitting the choice. Cancel closes it without starting a transfer. Enter or Space activates the focused button; Enter without a focused button leaves the dialog open. Addresses [#230](https://github.com/Kuddev/pebrel/issues/230). +- **Advanced theme colors were displayed but not saved.** Selection background, selection foreground and cursor text color now apply to the terminal and remain saved when reopening the editor. The cursor text preview also reflects the effective color. +- **The update dialog became transparent with the terminal.** Its background remains opaque when terminal opacity is lowered, keeping update details and actions readable. +- **Windows startup briefly showed an empty, incorrectly sized window.** Normal foreground startup prepares the initial terminal geometry before showing the window and presents its first frame before activation, avoiding the visible resize jump. Background and Quick Terminal startup keep their separate behavior. +- **WSL received a Windows-form Hook executable path.** The path is translated when passed into WSL, allowing guest-side integration to locate the Hook executable. +- **Windows packages depended on build-machine shader files.** ZIP and installer packaging now requires release-mode binaries with embedded shaders, removing the debug renderer's dependency on source files from the build machine. + +#### Agent state and reading position + +- **A completed Pi tool turn was marked as a failure.** A host-finished `toolUse` turn now produces the completion outcome and notification instead of an erroneous failed badge. Truncated, aborted and error outcomes retain their distinct meanings. +- **Pi redraws displaced the text being read.** In supported Pi Hook sessions, synchronized redraws preserve the reading position when the previous content has a unique match. Scrolling, input, resizing and screen changes take priority over restoring the old position. +- **Codex `/plan` left the mobile conversation stuck in a running state.** Sending `/plan` by itself now allows continued input after the mode switch. Normal prompts and commands that start tasks retain their existing running/completion behavior. +- **Injected context appeared as conversation messages.** Supported Codex records now distinguish actual user messages from injected context and deduplicate repeated assistant/tool records. User-authored Markdown, XML and repeated questions remain intact. + +#### Mobile connections and session switching + +- **Changing a LAN address invalidated an already paired phone.** Address relocation now retains the computer's trusted key and phone grants while renewing the certificate for the new address. The phone can update the saved endpoint through an explicit address editor without scanning and approving the same computer again. +- **Stale requests after switching mobile sessions.** Late reads and confirmation actions are rejected when their original connection, pane or conversation has changed. Reconnecting does not resend a command that may already have been delivered. + +### Improved + +#### Desktop usability and localization + +- **A cleaner tray menu when no Agent is running.** The empty Agent message and its extra separator are hidden; Show and Quit remain available. Running Agent entries keep their existing status display. Addresses [#324](https://github.com/Kuddev/pebrel/issues/324). + +- **Independent SSH host copies.** Copy immediately saves a separate host with the same connection settings. Editing the copy's address, credentials, proxy/jump settings or organization leaves the source unchanged; profile exchange preserves its connection target. +- **Local startup directories are remembered.** After a local terminal starts successfully, its initial directory enters the existing history and can be selected to open another terminal there. This addition does not record SSH or WSL guest paths as host-local directories. Addresses [#237](https://github.com/Kuddev/pebrel/issues/237). +- **Sidebar resizing is available by default.** Drag the existing divider to adjust the sidebar without first enabling the option. An explicitly disabled resize setting is still respected. +- **More workspace controls follow the selected language.** Toolbars, pane actions, local/remote file panels and close confirmations use the shared translation catalog rather than fixed Chinese labels. macOS shortcut hints identify Command as `Cmd` instead of `Win`. +- **Expanded Japanese and Traditional Chinese translations.** Japanese adds missing desktop catalog entries; Traditional Chinese uses Taiwan terminology for tabs, customization and file management. Text awaiting translation continues to use the existing fallback. +- **Acrylic blur at screen edges and when inactive.** On Windows 11 22H2+ with a matching Windows App Runtime 1.8 installed, Acrylic better preserves blur near screen edges and when focus is lost. Aero and older configurations retain their existing behavior. Addresses [#236](https://github.com/Kuddev/pebrel/issues/236). + +#### Mobile interaction and continuity + +- **Clearer pairing layout.** The pairing card groups controls in order: connection type, network interface and port, then instructions. The old QR code is hidden during an address change and replaced when the new invitation is ready. +- **Pairing stays on one page.** The camera preview is embedded beside the scan/code/paste choices. Changing methods, leaving the page or backgrounding the app releases camera use; code and paste remain usable when camera permission is declined. +- **LAN recovery no longer depends on keeping Settings open.** An enabled desktop LAN route retries a missing listener and preserves an automatically chosen port across restarts. The phone can look for a previously trusted computer while its disconnected workspace is in the foreground; manual address editing remains available when multicast discovery is restricted. +- **Long conversations remain readable during updates.** Incoming messages preserve the visible message anchor and tool-folding state when reading earlier content. Automatic following applies when already at the bottom, rather than pulling the reader away from an older reply. +- **Mobile display and input preferences are explicit.** Choose terminal themes and fonts, adjust font size, select cursor shape/blinking and enable or disable pinch zoom. The composer, history list and terminal viewport account for keyboard space, and terminal font selection is independent of system UI text scaling. +- **More predictable file transfers.** Directory loading, previews and transfers support cancellation. Downloads stream to the selected destination, uploads preserve existing targets, and file-channel timeouts leave a still-usable shell open. + +#### Performance and installation ownership + +- **Less embedded font data.** The desktop embeds only the default Maple Mono Normal NF CN font. Font rasterization and loading of system or imported fonts retain their existing behavior. + +- **Fewer settings reads during redraws.** Theme, language and sidebar-resize queries in the affected GPUI views use in-memory settings instead of repeatedly reading and parsing files. Saved settings and system appearance changes still refresh the views. +- **Startup does less blocking preparation.** Icon decoding/scaling and local directory-history recording are performed off the UI thread. The first visible window does not wait for all icon work, and cancelled or replaced icon requests do not overwrite the current workspace. +- **Bounded background reading.** Conversation reads and document processing run in the background, with size limits on screen snapshots, row updates, image previews and reader caches. Phone-width reflow reuses unchanged frames instead of repeating the work on every paint. +- **Channel-aware Windows updates.** Scoop-marked and Windows-packaged installations leave updates to their owning channel and show that channel in Settings. Regular installations retain the standalone updater. MSIX packaging and Scoop manifest generation are also supported. + +#### Compatibility and Preview scope + +- Android, desktop and relay packages retain separate version identities. The native relay uses protocol v2 and requires a matching phone client; older v1 combinations remain separate. +- Short-code lookup and automatic LAN rediscovery require local-network discovery. QR/full invitations and manual address editing remain available. Tailscale requires a reachable address between the two devices. +- Desktop-owned SSH conversation reading requires Python 3 on the remote host and a reported native transcript path. +- Task notifications are delivered while online, without offline replay. Android background sessions remain subject to system process management. +- MSIX and Scoop packaging support does not indicate Store publication or completed channel-installation acceptance. Final assets and checksums follow the release build. + +## 中文 + +Pebrel 2.0 支持从手机访问桌面终端、Agent 对话、文件与 Git,任务继续在电脑上执行。Android 配套端同时提供本地终端与直连 SSH 终端。桌面端新增引导式加密备份与恢复、扩展自动化命令,并改进输入、渲染、启动和多语言体验。 + +### 新增 + +#### 手机连接与桌面访问 + +- **Android 配套端 Preview 与首次连接引导。** 首次进入时,可以选择连接电脑、添加 SSH 主机或打开本地终端,也可以跳过。取消连接表单后回到选择页;完成或跳过引导的状态会保存,后续启动不必重复选择。 +- **扫码、短码与完整邀请三种配对入口。** 桌面设置页生成十分钟有效的一次性邀请;在支持局域网发现的网络中,也可以输入八位数字取得邀请。网络限制设备发现时,仍可扫描二维码或粘贴完整邀请。 +- **电脑端确认配对。** 对照两端验证码,再在电脑上批准连接。每台已配对手机获得独立凭据,用于后续连接。 +- **局域网与自建中继两种连接方式。** 可以直连手机可达的局域网或 Tailscale 地址,也可以配置自建中继跨网络访问。手机与电脑之间的应用数据采用端到端加密,中继转发的是加密流量,不处理终端正文。 +- **按手机管理访问权限。** 查看已配对设备及在线状态,分别设置“仅查看”或“允许输入”,也可以撤销某一台手机的授权。暂停手机连接会断开会话但保留配对记录;关闭设置页则保留已经运行的连接。 +- **从手机管理自建中继。** 通过 SSH 在支持的 Linux systemd/OpenRC 主机上安装、查看、启动、停止或卸载原生中继,安装过程显示实际阶段与就绪状态。桌面端支持从文件、剪贴板导入连接信息,或从已保存的 SSH 主机读取配置。 +- **桌面工作区导航。** 从手机查看电脑的窗口、标签和分屏,并在权限允许时切换或关闭标签、新建终端、打开文件,直接操作现有桌面工作区。 +- **在线任务通知。** 连接期间通过系统通知接收桌面任务完成和需要处理的状态。Android 还提供显式的前台服务开关,用常驻通知维持后台活动连接,并可直接进入系统通知设置。 + +#### Android 终端与命令输入 + +- **设备本地终端。** 使用 Android Shell 和应用自己的主目录,独立于电脑连接运行;可用命令由设备环境决定。 +- **直连 SSH 终端。** 保存主机,使用密码或服务端认可的免密方式连接,确认首次遇到的主机指纹,并查看实际连接阶段。可选的会话模式也支持接入服务器上已经安装的兼容会话保持工具。 +- **编辑后发送与直接输入两种模式。** 可以先在手机本地编辑多行草稿,再显式发送;也可以切换到直接终端输入。草稿按会话保存,收到提交确认后才清除,切换视图不会悄悄提交或丢弃尚未完成的内容。 +- **触屏终端按键。** 工具栏提供 Ctrl+C、Esc、Tab、方向键及键盘控制。发送到电脑的按键遵循目标终端的输入模式。 +- **按会话使用命令历史。** 在编辑区复用先前提交的内容,并随输入过滤候选。选择历史记录只填入草稿,执行仍需显式发送;历史建议可以在设置中关闭。 +- **本地与直连 SSH 终端的文件附件。** 使用 Android 文件选择器导入或上传文件,再把带引号的路径放入编辑区。添加附件只修改草稿,不自动执行 Shell 命令;该附件入口接受最大 16 MiB 的文件。 +- **可选的端侧语音输入。** 下载语音模型后,长按录音,在设备上识别为可编辑草稿,发送仍由用户确认。模型支持删除,离开录音视图时停止录音。 + +#### 对话、文档与远程文件 + +- **已有 Agent 会话的对话视图。** 从桌面会话读取受支持的 Codex、Claude 原生消息,展示用户输入、助手回复和工具结果。终端视图与对话视图共用同一个正在运行的 CLI 及其上下文,不额外启动另一位 Agent。 +- **可操作的确认选项。** 回答当前会话中识别出的问题,并展开或收起工具结果。按钮对应具体问题,普通编号正文与可操作选项保持区分。 +- **手机端彩色终端画面。** 桌面屏幕快照保留文字与背景颜色、样式、宽字符、组合字符和光标位置。即使电脑端正在查看较早的滚动历史,手机仍读取当前活动屏幕。 +- **按手机宽度重排终端内容。** 将支持的桌面屏幕行重新排到手机可用宽度,不改变电脑 PTY 尺寸或桌面布局。对于依赖固定网格的终端程序,也可以切回原始网格显示。 +- **手机端桌面文件标签。** 在独立标签中阅读受支持的文本、Markdown 和图片文件;加载期间文件发生变化时提示更新,不混用不同版本的内容。 +- **直连 SSH 会话中的 SFTP 浏览与传输。** 分页浏览目录、新建文件夹、重命名或删除条目、上传文件,并通过 Android 文件选择器下载。文件浏览器复用已有 SSH 认证,但使用独立文件通道;关闭文件标签后,终端会话继续保留。 +- **共用的离线文档阅读器。** 桌面文件、SFTP 文件和 Agent 消息使用同一套阅读组件,支持 Markdown 表格、任务列表、删除线、代码高亮、行内/块级公式与 Mermaid 图表。渲染库及字体随应用携带,打开文档时无需临时从 CDN 获取。 +- **文档导航、代码复制与图片查看。** 支持将适用的相对链接打开为文件标签、通过系统打开外部链接、复制不含行号及围栏的原始代码,以及在独立视图中缩放或重置图片。复制后有可见反馈,不额外撑开正文布局。 + +#### 在连接的工作区中操作 Git + +- **手机端仓库浏览。** 针对选定桌面分屏的工作目录,查看变更文件、暂存与未暂存差异、分支信息及分页提交历史,在 Git 视图中直接打开文件和提交差异。 +- **Git 操作。** 支持暂存、取消暂存、提交、fetch、pull 和 push。同步按“fetch → 仅快进 pull → push”执行,后续步骤失败时保留已完成步骤的记录。写操作要求输入权限与最新仓库快照,结果不确定时不自动重发。 + +#### 加密备份与恢复 + +- **引导式“备份与恢复”设置页。** 按存储位置、连接检查、备份密码和内容选择逐步完成配置。支持已有文件夹、WebDAV、S3 兼容存储,以及已保存 SSH 主机上的远程目录。 +- **按次选择备份内容。** 按需选择外观、终端与助手设置、SSH 主机配置、工作区布局、导入字体及历史类别。界面显示所选内容大小,并在备份成功后记住选择。 +- **先在电脑加密,再保存或上传。** SSH 私钥和系统凭据库中的秘密不纳入归档;命令历史作为独立类别选择。备份密码只在本次应用运行期间保留,不写入保存的配置文件。 +- **便于跨电脑迁移的归档导入与导出。** 将所选内容导出为 `.pebrel-backup` 文件,或选择归档并用备份密码解锁。通过文件交换时,无需先配置远程存储。 +- **分项恢复,并可撤回本次恢复。** 先验证、解锁并查看归档,再勾选需要恢复的内容,未勾选项保持不变。覆盖前保存本机加密恢复副本,并记录原文件是否存在,可在当前设置会话中撤回此次恢复。 + +#### 自动化与桌面选项 + +- **为分屏窗格单独命名。** 双击窗格标题编辑名称,Enter 或失焦保存、Esc 取消,清空后恢复自动标题。名称随窗格移动,并随会话保存和恢复。 + +- **扩展 Runtime API。** 自动化客户端可通过稳定标识定位标签、打开和读取受支持的文件标签、获取终端屏幕快照、读取受支持的 Agent 对话,以及执行限定范围的 Git 操作。原有纯文本终端读取继续保留。 +- **原生与 Lua 插件命令。** `pebrel plugin check` 检查本地命令包,`pebrel plugin run` 执行一个已声明的 Runtime 请求或 Lua 处理器,并返回结构化结果。命令按需运行,原生请求跳过 Lua 初始化,Lua 调用结束后释放虚拟机;本版支持命令插件,不包含常驻插件面板。 +- **Ctrl+滚轮字号缩放开关。** 在“设置 → 外观 → 终端外观”中关闭后,该手势既不改变字号,也不滚动终端。默认继续开启,普通滚轮和字号快捷键保持原有行为。对应 [#329](https://github.com/Kuddev/pebrel/issues/329)。 +- **原生 Windows ARM64 安装器支持。** 安装器构建与应用内更新支持匹配的 ARM64 包,与便携版配套。x64 支持保持不变;历史 1.9.0/1.9.1 的 ARM64 资产仍仅包含 ZIP。 + +### 修复 + +#### 桌面输入、渲染与窗口行为 + +- **Windows 彩色 emoji 显示成黑色轮廓。** 修正彩色字形图层的枚举与透明合成初始化,恢复表情颜色及透明边缘。对应 [#317](https://github.com/Kuddev/pebrel/issues/317)。 +- **Windows 输入模式丢失 Ctrl+C 控制字符。** 补回遗漏的控制字符,让 `adb shell` 等程序正确接收中断,同时保留按下、抬起及修饰键信息。对应 [#213](https://github.com/Kuddev/pebrel/issues/213)。 +- **Ctrl+/ 经过 ConPTY 后丢失斜杠按键身份。** Windows 输入记录按当前键盘布局解析斜杠,让原生控制台程序正确区分快捷键;原有传统字节编码与普通斜杠文本输入继续保留。 +- **macOS 清除快捷键后,Command 绑定仍生效。** 清除动作时同步清除内置 Command 别名,恢复默认时一并恢复。使用旧配置的用户需在升级后重新清除一次对应设置行;解绑 Command+K 仅移除选择器快捷键,不另行指定清屏动作。对应 [#238](https://github.com/Kuddev/pebrel/issues/238)。 +- **Command+Q 没有退出 GPUI 应用。** 现在走正常退出流程,先保存工作区状态与草稿,再结束终端会话。 +- **Shell 提示符出现配色报告转义序列。** 调整配色通知订阅时不再立即发送报告,修正 Fish/SSH 提示符乱码涉及的订阅行为;显式查询和实际明暗变化继续得到响应。对应 [#283](https://github.com/Kuddev/pebrel/issues/283)。 +- **SFTP 同名冲突弹窗在选择后残留。** “跳过”“保留两者”和“覆盖”在提交选择后关闭弹窗,“取消”只关闭弹窗、不启动传输。Enter 或空格激活当前聚焦按钮;未聚焦按钮时,Enter 保留弹窗。对应 [#230](https://github.com/Kuddev/pebrel/issues/230)。 +- **高级主题颜色修改后未保存。** 选区背景色、选区前景色与光标文字色正确应用到终端,并在重新打开编辑器时保留;光标文字预览同步显示生效颜色。 +- **更新弹窗随终端一起变透明。** 降低终端不透明度后,更新弹窗仍保持不透明底色,让版本信息与操作按钮保持可读。 +- **Windows 启动时先出现空窗口,再跳变尺寸。** 正常前台启动会在显示窗口前准备首屏终端几何,并在激活前呈现首帧,避免可见的尺寸跳变;后台启动与 Quick Terminal 保留各自的行为。 +- **WSL 收到 Windows 形式的 Hook 程序路径。** 路径传入 WSL 时转换为对应形式,供来宾侧集成定位 Hook 程序。 +- **Windows 分发包依赖构建机上的着色器文件。** ZIP 与安装器打包要求使用嵌入着色器的 release 二进制,消除 debug 渲染器对构建机源文件的依赖。 + +#### Agent 状态与阅读位置 + +- **Pi 已结束的工具调用轮次被标成失败。** 由宿主结束的 `toolUse` 轮次现在产生完成结果与通知,不再显示错误的失败标记;截断、中止和报错仍保持各自含义。 +- **Pi 重绘打断阅读位置。** 在受支持的 Pi Hook 会话中,同步重绘后若能唯一匹配原内容,便保留阅读位置。用户滚动、输入、调整尺寸和切换屏幕优先于旧位置恢复。 +- **Codex `/plan` 让手机对话停在“运行中”。** 单独发送 `/plan` 后,模式切换完成即可继续输入;普通提问及启动任务的命令保留原有运行与完成状态。 +- **注入上下文被显示为对话消息。** 受支持的 Codex 记录区分真实用户消息与注入上下文,并去重助手/工具的重复记录;用户编写的 Markdown、XML 和重复提问保持完整。 + +#### 手机连接与会话切换 + +- **更换局域网地址后,已配对手机失去连接凭据。** 地址变更时保留电脑的可信密钥与手机授权,为新地址更新证书。手机可通过显式地址编辑入口更新已保存端点,无需重新扫描并再次批准同一台电脑。 +- **手机会话切换后仍有过期请求。** 原连接、分屏或对话发生变化后,丢弃迟到的读取结果与确认操作;重连不重发可能已经送达的命令。 + +### 改进 + +#### 桌面使用与多语言 + +- **无 Agent 时精简托盘菜单。** 隐藏空 Agent 提示及多余分隔线,保留显示和退出命令;有 Agent 时继续显示原有条目与状态。对应 [#324](https://github.com/Kuddev/pebrel/issues/324)。 + +- **独立的 SSH 主机副本。** “复制”立即保存具有相同连接设置的独立主机。编辑副本的地址、凭据、代理/跳板或组织信息不影响原主机,配置交换保留其连接目标。 +- **记住本地终端的初始目录。** 本地终端成功启动后,将初始目录加入既有历史,后续可以从中选择并在该目录打开新终端;这项补充不会把 SSH 或 WSL 来宾路径记录为本机目录。对应 [#237](https://github.com/Kuddev/pebrel/issues/237)。 +- **默认可以拖动侧栏宽度。** 无需先启用设置即可拖动既有分隔线调整侧栏;已经显式关闭拖宽的配置继续生效。 +- **更多工作区控件跟随所选语言。** 工具栏、分屏操作、本地/远程文件面板与关闭确认使用共享翻译目录,减少固定中文文案;macOS 快捷键提示使用 `Cmd`,不再把 Command 显示为 `Win`。 +- **扩充日语与繁体中文翻译。** 日语补充缺失的桌面消息目录条目,繁体中文按台湾用语修订标签、自定义与文件管理等措辞。待翻译文字继续使用既有回退。 +- **改善 Acrylic 边缘与失焦模糊。** Windows 11 22H2+ 安装架构匹配的 Windows App Runtime 1.8 后,Acrylic 更好地保持屏幕边缘及失焦时的模糊效果。Aero 与较旧配置保留原有行为。对应 [#236](https://github.com/Kuddev/pebrel/issues/236)。 + +#### 手机交互与连续使用 + +- **更清晰的配对布局。** 配对卡片内依次排列连接类型、网卡与端口、操作步骤。地址变更期间隐藏旧二维码,新邀请准备完成后再显示。 +- **在同一页完成配对方式切换。** 相机预览内嵌在扫码/短码/粘贴入口旁;切换方式、离开页面或应用进入后台时释放相机。未授予相机权限时,仍可使用短码和粘贴。 +- **局域网恢复不再依赖设置页常开。** 已启用的桌面局域网连接会重试缺失监听,并在重启时保留自动选定的端口。手机可在断开的工作区处于前台时查找此前信任的电脑;多播发现受限时仍可手动编辑地址。 +- **长对话更新时保留阅读位置。** 查看较早消息时,新增回复保留可见消息锚点与工具折叠状态;只有原本位于底部时才继续跟随,不把正在阅读旧回复的用户拉回最新消息。 +- **明确的移动端显示与输入偏好。** 可选择终端主题和字体、调整字号、设置光标形状与闪烁,并启用或关闭双指缩放。编辑区、历史列表和终端视口按键盘可用空间排列,终端字体与系统界面文字缩放各自生效。 +- **更可控的文件传输。** 目录加载、预览与传输支持取消;下载流式写入选定目标,上传保留已有目标文件,文件通道超时不关闭仍可用的 Shell。 + +#### 性能与安装来源 + +- **减少内嵌字体数据。** 桌面端仅内嵌默认的 Maple Mono Normal NF CN 字体,保持原有字体光栅化,以及系统和导入字体的加载行为。 + +- **减少重绘期间的设置读取。** 相关 GPUI 视图的主题、语言与侧栏拖宽查询读取内存设置,避免反复读盘和解析文件;保存设置及系统外观变化仍正常刷新界面。 +- **减少启动阶段的阻塞准备。** 图标解码、缩放及本地目录历史记录移到 UI 线程之外;首次显示不必等全部图标准备完成,取消或被替换的图标请求也不会覆盖当前工作区。 +- **有界的后台阅读处理。** 对话读取与文档处理在后台进行,屏幕快照、行更新、图片预览及阅读缓存设有大小上限。手机宽度重排复用未变化的画面,避免每次绘制重复计算。 +- **按 Windows 安装渠道更新。** 带有 Scoop 标记或 Windows 包身份的安装由对应渠道管理更新,并在设置中显示来源;普通安装继续使用独立更新器。同时支持 MSIX 打包与 Scoop 清单生成。 + +#### 兼容性与 Preview 范围 + +- Android、桌面应用与中继分别维护版本。原生中继使用 v2 协议,需搭配相应手机客户端;旧版 v1 组合单独使用。 +- 短码查询与局域网自动重新发现依赖本地网络发现;扫码、完整邀请及手动编辑地址仍可使用。Tailscale 需要两端地址实际可达。 +- 读取桌面 SSH 会话的对话,要求远端安装 Python 3,并上报原生会话记录路径。 +- 任务通知在联网期间送达,不含离线补发;Android 后台会话仍受系统进程管理约束。 +- MSIX 与 Scoop 打包支持不等同于商店上架或渠道安装验收完成。最终资产与校验值随发布构建确定。 + +## Contributors + +Thanks to everyone who contributed to this update. / 感谢参与本次更新的贡献者。 + +

+@WilliamWang1721 avatar +@JoyElliot avatar +@thynomex avatar +@Cec1c avatar +@AnxForever avatar +@galact-byte avatar +@zhbjerry avatar +@keys-cherish avatar +@tavricccc avatar +@Tettu0530 avatar +@Sakyvo avatar +@wyr-zal avatar +@Kuddev avatar +

+ +- **[@WilliamWang1721](https://github.com/WilliamWang1721)** — Rendering settings reads, startup-directory history, SSH host duplication, default sidebar resizing, Windows ARM64 installation, advanced theme colors, Ctrl+/ and color emoji; Pi helper regression coverage. / 渲染设置读取、启动目录历史、SSH 主机复制、默认侧栏拖宽、Windows ARM64 安装、高级主题颜色、Ctrl+/ 与彩色 emoji,以及 Pi 辅助进程回归覆盖。([#140](https://github.com/Kuddev/pebrel/pull/140), [#243](https://github.com/Kuddev/pebrel/pull/243), [#267](https://github.com/Kuddev/pebrel/pull/267), [#282](https://github.com/Kuddev/pebrel/pull/282), [#284](https://github.com/Kuddev/pebrel/pull/284), [#299](https://github.com/Kuddev/pebrel/pull/299), [#300](https://github.com/Kuddev/pebrel/pull/300), [#304](https://github.com/Kuddev/pebrel/pull/304), [#318](https://github.com/Kuddev/pebrel/pull/318)) +- **[@JoyElliot](https://github.com/JoyElliot)** — Windows Acrylic and first-frame behavior, Win32 control characters, SFTP conflict dialogs and pane titles. / Windows Acrylic 与首帧呈现、Win32 控制字符、SFTP 冲突弹窗及窗格标题。([#250](https://github.com/Kuddev/pebrel/pull/250), [#251](https://github.com/Kuddev/pebrel/pull/251), [#310](https://github.com/Kuddev/pebrel/pull/310), [#311](https://github.com/Kuddev/pebrel/pull/311), [#333](https://github.com/Kuddev/pebrel/pull/333)) +- **[@thynomex](https://github.com/thynomex)** — Workspace controls and confirmation localization. / 工作区控件与确认文案本地化。([#133](https://github.com/Kuddev/pebrel/pull/133)) +- **[@Cec1c](https://github.com/Cec1c)** — Cursor-motion foundations: preference handling, trajectory logic and tests. / 光标运动基础:设置处理、轨迹逻辑与测试。([#293](https://github.com/Kuddev/pebrel/pull/293)) +- **[@AnxForever](https://github.com/AnxForever)** — Hook executable path translation across WSL. / 跨 WSL 的 Hook 程序路径转换。([#290](https://github.com/Kuddev/pebrel/pull/290)) +- **[@galact-byte](https://github.com/galact-byte)** — Pi reading-position preservation during synchronized redraws. / Pi 同步重绘期间的阅读位置保持。([#234](https://github.com/Kuddev/pebrel/pull/234)) +- **[@zhbjerry](https://github.com/zhbjerry)** — macOS configurable Command shortcuts and normal quit. / macOS Command 快捷键配置与正常退出。([#248](https://github.com/Kuddev/pebrel/pull/248)) +- **[@keys-cherish](https://github.com/keys-cherish)** — Readable update dialogs at low terminal opacity. / 低终端不透明度下更新弹窗的可读性。([#305](https://github.com/Kuddev/pebrel/pull/305)) +- **[@tavricccc](https://github.com/tavricccc)** — Traditional Chinese catalog and Taiwan terminology. / 繁体中文消息目录与台湾用语。([#301](https://github.com/Kuddev/pebrel/pull/301)) +- **[@Tettu0530](https://github.com/Tettu0530)** — Japanese message-catalog expansion. / 日语消息目录扩充。([#314](https://github.com/Kuddev/pebrel/pull/314)) +- **[@Sakyvo](https://github.com/Sakyvo)** — Completed Pi tool-turn outcomes and notifications. / Pi 已结束工具轮次的结果与通知。([#321](https://github.com/Kuddev/pebrel/pull/321)) +- **[@wyr-zal](https://github.com/wyr-zal)** — Configurable Ctrl+wheel font zoom. / 可配置的 Ctrl+滚轮字号缩放。([#331](https://github.com/Kuddev/pebrel/pull/331)) +- **[@Kuddev](https://github.com/Kuddev)** — Desktop/mobile integration, backup and restore, automation, review and release preparation; color-report correction and reviewed integration batches. / 桌面与移动端整合、备份恢复、自动化、审查与发布准备,以及配色报告修正和审核后的批次整合。([#302](https://github.com/Kuddev/pebrel/pull/302), [#337](https://github.com/Kuddev/pebrel/pull/337), [#338](https://github.com/Kuddev/pebrel/pull/338)) + +--- + +**SHA256** + +- `Pebrel-v2.0.0-linux-x64-preview.AppImage`: `52941463b7b558c63fc1717d88a2d758bb67f9f1bd468c2a489be9cd7fab25d9` +- `Pebrel-v2.0.0-linux-x64-preview.deb`: `d2c8dcea66e2106f01e590730a18133c16fca08b2cdb9eab4d5cb922f80b914c` +- `Pebrel-v2.0.0-linux-x64-preview.tar.gz`: `0318dcafdb79de1a2c11bdfa61b03e12f0c227ffa55960e7fa6fc486aa5a6501` +- `Pebrel-v2.0.0-macos-arm64-preview.dmg`: `eb3a4f758f53e2571bcd9c0cbbb2560c8d43c2e4446cadac65487594a5ccc778` +- `Pebrel-v2.0.0-macos-x64-preview.dmg`: `6698b0e04b395ca99a1e33fbb3382d9c95886f07e80c85c6fb9ac794150e3e3e` +- `Pebrel-v2.0.0-windows-x64.zip`: `1bac7a1caa37b9c414667c0ce60cc132e2fde8a7d9a1ced5eecf7b5dda53419a` +- `Pebrel-v2.0.0-windows-x64-setup.exe`: `4c3ff71c6924a022717e97316acc32dd10db6972d91f33427ca502fbb7d79316` +- `Pebrel-v2.0.0-windows-arm64.zip`: `9127fa9891560a47a9d4d0458d0b6094e95ccd0fbc73623aa077618323456334` +- `Pebrel-v2.0.0-windows-arm64-setup.exe`: `234cf07fd4b9c25caebd6b55138c6d97087cccf0a7152c79882d4c35cb2bdb52` +- `Pebrel-v2.0.0-android-universal-preview.apk`: `dcef9e724e1500663613a367b41acccf84d45bf55c46e28a9ad5b1508662828c` diff --git a/docs/runtime-api-v1.schema.json b/docs/runtime-api-v1.schema.json index 5af2c7f..8abc6fb 100644 --- a/docs/runtime-api-v1.schema.json +++ b/docs/runtime-api-v1.schema.json @@ -86,7 +86,23 @@ "pane.send_key", "pane.run", "pane.exec", - "pane.wait" + "pane.wait", + "git.status", + "git.diff", + "git.history", + "git.stage", + "git.unstage", + "git.commit", + "git.fetch", + "git.pull", + "git.push", + "tab.focus", + "tab.open", + "tab.read", + "conversation.read", + "conversation.send", + "conversation.choose", + "conversation.key" ] }, "params": { @@ -96,120 +112,675 @@ "additionalProperties": false, "allOf": [ { - "if": { "properties": { "method": { "enum": ["runtime.describe", "runtime.snapshot", "window.create"] } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/empty_params" } } } + "if": { + "properties": { + "method": { + "enum": [ + "runtime.describe", + "runtime.snapshot", + "window.create" + ] + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/empty_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "runtime.orchestrate" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/orchestrate_params" } } } + "if": { + "properties": { + "method": { + "const": "runtime.orchestrate" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/orchestrate_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "events.subscribe" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/subscribe_params" } } } + "if": { + "properties": { + "method": { + "const": "events.subscribe" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/subscribe_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "agents.list" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/window_params" } } } + "if": { + "properties": { + "method": { + "const": "agents.list" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/window_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "agent.start" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/agent_start_params" } } } + "if": { + "properties": { + "method": { + "const": "agent.start" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/agent_start_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "agent.fork" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/agent_fork_params" } } } + "if": { + "properties": { + "method": { + "const": "agent.fork" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/agent_fork_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "agent.get" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/agent_target_params" } } } + "if": { + "properties": { + "method": { + "const": "agent.get" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/agent_target_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "agent.prompt" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/agent_prompt_params" } } } + "if": { + "properties": { + "method": { + "const": "agent.prompt" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/agent_prompt_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "agent.delegate" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/agent_delegate_params" } } } + "if": { + "properties": { + "method": { + "const": "agent.delegate" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/agent_delegate_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "agent.paste" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/agent_paste_params" } } } + "if": { + "properties": { + "method": { + "const": "agent.paste" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/agent_paste_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "agent.read" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/agent_read_params" } } } + "if": { + "properties": { + "method": { + "const": "agent.read" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/agent_read_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "agent.wait" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/agent_wait_params" } } } + "if": { + "properties": { + "method": { + "const": "agent.wait" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/agent_wait_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "window.focus" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/focus_params" } } } + "if": { + "properties": { + "method": { + "const": "window.focus" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/focus_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "window.close" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/window_params" } } } + "if": { + "properties": { + "method": { + "const": "window.close" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/window_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "tab.new" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/tab_new_params" } } } + "if": { + "properties": { + "method": { + "const": "tab.new" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/tab_new_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "tab.close" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/tab_target_params" } } } + "if": { + "properties": { + "method": { + "const": "tab.close" + } + } + }, + "then": { + "properties": { + "params": { + "oneOf": [ + { + "$ref": "#/$defs/tab_target_params" + }, + { + "$ref": "#/$defs/tab_identity_params" + } + ] + } + } + } }, { - "if": { "properties": { "method": { "const": "tab.rename" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/tab_rename_params" } } } + "if": { + "properties": { + "method": { + "const": "tab.rename" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/tab_rename_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "tab.move" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/tab_move_params" } } } + "if": { + "properties": { + "method": { + "const": "tab.move" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/tab_move_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "pane.split" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/split_params" } } } + "if": { + "properties": { + "method": { + "const": "pane.split" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/split_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "pane.zoom" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/pane_zoom_params" } } } + "if": { + "properties": { + "method": { + "const": "pane.zoom" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/pane_zoom_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "pane.resize" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/pane_resize_params" } } } + "if": { + "properties": { + "method": { + "const": "pane.resize" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/pane_resize_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "pane.prompt" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/prompt_params" } } } + "if": { + "properties": { + "method": { + "const": "pane.prompt" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/prompt_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "pane.paste" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/paste_params" } } } + "if": { + "properties": { + "method": { + "const": "pane.paste" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/paste_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "pane.read" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/read_params" } } } + "if": { + "properties": { + "method": { + "const": "pane.read" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/read_params" + } + } + } }, { - "if": { "properties": { "method": { "enum": ["pane.close", "pane.procs"] } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/pane_target_params" } } } + "if": { + "properties": { + "method": { + "enum": [ + "pane.close", + "pane.procs" + ] + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/pane_target_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "pane.send_key" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/send_key_params" } } } + "if": { + "properties": { + "method": { + "const": "pane.send_key" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/send_key_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "pane.run" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/run_params" } } } + "if": { + "properties": { + "method": { + "const": "pane.run" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/run_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "pane.exec" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/exec_params" } } } + "if": { + "properties": { + "method": { + "const": "pane.exec" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/exec_params" + } + } + } }, { - "if": { "properties": { "method": { "const": "pane.wait" } } }, - "then": { "properties": { "params": { "$ref": "#/$defs/wait_params" } } } + "if": { + "properties": { + "method": { + "const": "pane.wait" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/wait_params" + } + } + } + }, + { + "if": { + "properties": { + "method": { + "enum": [ + "git.status", + "git.diff", + "git.history", + "git.stage", + "git.unstage", + "git.commit", + "git.fetch", + "git.pull", + "git.push" + ] + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/git_params" + } + } + } + }, + { + "if": { + "properties": { + "method": { + "enum": [ + "git.stage", + "git.unstage", + "git.commit", + "git.fetch", + "git.pull", + "git.push" + ] + } + } + }, + "then": { + "properties": { + "params": { + "required": [ + "revision" + ], + "properties": { + "revision": { + "type": "string", + "pattern": "^[0-9a-fA-F]{64}$" + } + } + } + } + } + }, + { + "if": { + "properties": { + "method": { + "const": "git.commit" + } + } + }, + "then": { + "properties": { + "params": { + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string", + "pattern": "\\S" + } + } + } + } + } + }, + { + "if": { + "properties": { + "method": { + "const": "tab.focus" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/tab_identity_params" + } + } + } + }, + { + "if": { + "properties": { + "method": { + "const": "tab.open" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/tab_open_params" + } + } + } + }, + { + "if": { + "properties": { + "method": { + "const": "tab.read" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/tab_read_params" + } + } + } + }, + { + "if": { + "properties": { + "method": { + "const": "conversation.read" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/conversation_read_params" + } + } + } + }, + { + "if": { + "properties": { + "method": { + "const": "conversation.send" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/conversation_send_params" + } + } + } + }, + { + "if": { + "properties": { + "method": { + "const": "conversation.choose" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/conversation_choose_params" + } + } + } + }, + { + "if": { + "properties": { + "method": { + "const": "conversation.key" + } + } + }, + "then": { + "properties": { + "params": { + "$ref": "#/$defs/conversation_key_params" + } + } + } } ] }, @@ -218,48 +789,93 @@ "maxProperties": 0 }, "nullable_window_id": { - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "minimum": 0 }, "window_params": { "type": "object", "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "cwd": { "type": ["string", "null"] }, - "shell": { "type": ["string", "null"] } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "shell": { + "type": [ + "string", + "null" + ] + } }, "additionalProperties": false }, "subscribe_params": { "type": "object", "properties": { - "since_revision": { "type": ["integer", "null"], "minimum": 0 } + "since_revision": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + } }, "additionalProperties": false }, "orchestrate_params": { "type": "object", - "required": ["steps"], + "required": [ + "steps" + ], "properties": { "steps": { "type": "array", "minItems": 1, "maxItems": 32, - "items": { "$ref": "#/$defs/orchestrate_step" } + "items": { + "$ref": "#/$defs/orchestrate_step" + } }, - "on_error": { "enum": ["stop", "continue"], "default": "stop" } + "on_error": { + "enum": [ + "stop", + "continue" + ], + "default": "stop" + } }, "additionalProperties": false }, "orchestrate_step": { "oneOf": [ - { "$ref": "#/$defs/orchestrate_new_tab" }, - { "$ref": "#/$defs/orchestrate_focus" }, - { "$ref": "#/$defs/orchestrate_split" }, - { "$ref": "#/$defs/orchestrate_prompt" }, - { "$ref": "#/$defs/orchestrate_run" }, - { "$ref": "#/$defs/orchestrate_agent_launch" }, - { "$ref": "#/$defs/orchestrate_wait" } + { + "$ref": "#/$defs/orchestrate_new_tab" + }, + { + "$ref": "#/$defs/orchestrate_focus" + }, + { + "$ref": "#/$defs/orchestrate_split" + }, + { + "$ref": "#/$defs/orchestrate_prompt" + }, + { + "$ref": "#/$defs/orchestrate_run" + }, + { + "$ref": "#/$defs/orchestrate_agent_launch" + }, + { + "$ref": "#/$defs/orchestrate_wait" + } ] }, "orchestrate_step_id": { @@ -270,19 +886,33 @@ "oneOf": [ { "type": "object", - "required": ["step", "field"], + "required": [ + "step", + "field" + ], "properties": { - "step": { "$ref": "#/$defs/orchestrate_step_id" }, - "field": { "const": "pane_id" } + "step": { + "$ref": "#/$defs/orchestrate_step_id" + }, + "field": { + "const": "pane_id" + } }, "additionalProperties": false }, { "type": "object", - "required": ["pane_id"], + "required": [ + "pane_id" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + } }, "additionalProperties": false } @@ -290,225 +920,614 @@ }, "orchestrate_new_tab": { "type": "object", - "required": ["id", "op"], + "required": [ + "id", + "op" + ], "properties": { - "id": { "$ref": "#/$defs/orchestrate_step_id" }, - "op": { "const": "new_tab" }, - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "cwd": { "type": ["string", "null"] } + "id": { + "$ref": "#/$defs/orchestrate_step_id" + }, + "op": { + "const": "new_tab" + }, + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "cwd": { + "type": [ + "string", + "null" + ] + } }, "additionalProperties": false }, "orchestrate_focus": { "type": "object", - "required": ["id", "op", "target"], + "required": [ + "id", + "op", + "target" + ], "properties": { - "id": { "$ref": "#/$defs/orchestrate_step_id" }, - "op": { "const": "focus" }, - "target": { "$ref": "#/$defs/orchestrate_pane_target" } + "id": { + "$ref": "#/$defs/orchestrate_step_id" + }, + "op": { + "const": "focus" + }, + "target": { + "$ref": "#/$defs/orchestrate_pane_target" + } }, "additionalProperties": false }, "orchestrate_split": { "type": "object", - "required": ["id", "op", "direction"], + "required": [ + "id", + "op", + "direction" + ], "properties": { - "id": { "$ref": "#/$defs/orchestrate_step_id" }, - "op": { "const": "split" }, - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "target": { "$ref": "#/$defs/orchestrate_pane_target" }, - "direction": { "enum": ["left_right", "top_bottom"] } + "id": { + "$ref": "#/$defs/orchestrate_step_id" + }, + "op": { + "const": "split" + }, + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "target": { + "$ref": "#/$defs/orchestrate_pane_target" + }, + "direction": { + "enum": [ + "left_right", + "top_bottom" + ] + } }, "additionalProperties": false }, "orchestrate_prompt": { "type": "object", - "required": ["id", "op", "target", "text"], + "required": [ + "id", + "op", + "target", + "text" + ], "properties": { - "id": { "$ref": "#/$defs/orchestrate_step_id" }, - "op": { "const": "prompt" }, - "target": { "$ref": "#/$defs/orchestrate_pane_target" }, - "text": { "type": "string", "minLength": 1, "maxLength": 32768 }, - "submit": { "type": "boolean", "default": true } + "id": { + "$ref": "#/$defs/orchestrate_step_id" + }, + "op": { + "const": "prompt" + }, + "target": { + "$ref": "#/$defs/orchestrate_pane_target" + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + }, + "submit": { + "type": "boolean", + "default": true + } }, "additionalProperties": false }, "orchestrate_run": { "type": "object", - "required": ["id", "op", "target", "command"], + "required": [ + "id", + "op", + "target", + "command" + ], "properties": { - "id": { "$ref": "#/$defs/orchestrate_step_id" }, - "op": { "const": "run" }, - "target": { "$ref": "#/$defs/orchestrate_pane_target" }, - "command": { "type": "string", "minLength": 1, "maxLength": 32768 }, - "wait": { "type": "boolean", "default": true }, - "timeout_ms": { "type": "integer", "minimum": 1, "maximum": 86400000, "default": 30000 }, - "tail_lines": { "type": "integer", "minimum": 1, "maximum": 2000 } + "id": { + "$ref": "#/$defs/orchestrate_step_id" + }, + "op": { + "const": "run" + }, + "target": { + "$ref": "#/$defs/orchestrate_pane_target" + }, + "command": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + }, + "wait": { + "type": "boolean", + "default": true + }, + "timeout_ms": { + "type": "integer", + "minimum": 1, + "maximum": 86400000, + "default": 30000 + }, + "tail_lines": { + "type": "integer", + "minimum": 1, + "maximum": 2000 + } }, "additionalProperties": false }, "orchestrate_agent_launch": { "type": "object", - "required": ["id", "op", "target", "name", "kind", "initial_prompt"], + "required": [ + "id", + "op", + "target", + "name", + "kind", + "initial_prompt" + ], "properties": { - "id": { "$ref": "#/$defs/orchestrate_step_id" }, - "op": { "const": "agent_launch" }, - "target": { "$ref": "#/$defs/orchestrate_pane_target" }, - "name": { "type": "string", "minLength": 1, "maxLength": 64 }, - "kind": { "$ref": "#/$defs/launch_agent_kind" }, - "resume_session_id": { "type": ["string", "null"] }, - "initial_prompt": { "type": "string", "minLength": 1, "maxLength": 32768 }, - "ready_timeout_ms": { "type": "integer", "minimum": 1, "maximum": 86400000, "default": 10000 } + "id": { + "$ref": "#/$defs/orchestrate_step_id" + }, + "op": { + "const": "agent_launch" + }, + "target": { + "$ref": "#/$defs/orchestrate_pane_target" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "kind": { + "$ref": "#/$defs/launch_agent_kind" + }, + "resume_session_id": { + "type": [ + "string", + "null" + ] + }, + "initial_prompt": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + }, + "ready_timeout_ms": { + "type": "integer", + "minimum": 1, + "maximum": 86400000, + "default": 10000 + } }, "additionalProperties": false }, "orchestrate_wait": { "type": "object", - "required": ["id", "op", "target", "state"], + "required": [ + "id", + "op", + "target", + "state" + ], "properties": { - "id": { "$ref": "#/$defs/orchestrate_step_id" }, - "op": { "const": "wait" }, - "target": { "$ref": "#/$defs/orchestrate_pane_target" }, - "state": { "$ref": "#/$defs/wait_state" }, - "timeout_ms": { "type": "integer", "minimum": 1, "maximum": 86400000, "default": 30000 }, - "tail_lines": { "type": "integer", "minimum": 1, "maximum": 2000 } + "id": { + "$ref": "#/$defs/orchestrate_step_id" + }, + "op": { + "const": "wait" + }, + "target": { + "$ref": "#/$defs/orchestrate_pane_target" + }, + "state": { + "$ref": "#/$defs/wait_state" + }, + "timeout_ms": { + "type": "integer", + "minimum": 1, + "maximum": 86400000, + "default": 30000 + }, + "tail_lines": { + "type": "integer", + "minimum": 1, + "maximum": 2000 + } }, "additionalProperties": false }, "agent_start_params": { "type": "object", - "required": ["name", "kind"], + "required": [ + "name", + "kind" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": ["integer", "null"], "minimum": 0 }, - "name": { "type": "string", "minLength": 1, "maxLength": 64 }, - "kind": { "$ref": "#/$defs/launch_agent_kind" }, - "cwd": { "type": ["string", "null"] }, - "resume_session_id": { "type": ["string", "null"] } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "kind": { + "$ref": "#/$defs/launch_agent_kind" + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "resume_session_id": { + "type": [ + "string", + "null" + ] + } }, "additionalProperties": false }, "agent_fork_params": { "type": "object", - "required": ["name", "kind"], + "required": [ + "name", + "kind" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "source_pane_id": { "type": ["integer", "null"], "minimum": 0 }, - "source_cwd": { "type": ["string", "null"] }, - "name": { "type": "string", "minLength": 1, "maxLength": 64 }, - "kind": { "$ref": "#/$defs/launch_agent_kind" }, - "resume_session_id": { "type": ["string", "null"] }, - "branch": { "type": ["string", "null"] }, - "base": { "type": ["string", "null"] }, - "path": { "type": ["string", "null"] }, - "allow_dirty_source": { "type": "boolean", "default": false } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "source_pane_id": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "source_cwd": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "kind": { + "$ref": "#/$defs/launch_agent_kind" + }, + "resume_session_id": { + "type": [ + "string", + "null" + ] + }, + "branch": { + "type": [ + "string", + "null" + ] + }, + "base": { + "type": [ + "string", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "allow_dirty_source": { + "type": "boolean", + "default": false + } }, "anyOf": [ - { "required": ["source_pane_id"], "properties": { "source_pane_id": { "type": "integer" } } }, - { "required": ["source_cwd"], "properties": { "source_cwd": { "type": "string", "minLength": 1 } } } + { + "required": [ + "source_pane_id" + ], + "properties": { + "source_pane_id": { + "type": "integer" + } + } + }, + { + "required": [ + "source_cwd" + ], + "properties": { + "source_cwd": { + "type": "string", + "minLength": 1 + } + } + } ], "additionalProperties": false }, "launch_agent_kind": { - "enum": ["claude", "codex", "opencode", "cursor", "pi", "omp", "kimi"], + "enum": [ + "claude", + "codex", + "opencode", + "cursor", + "pi", + "omp", + "kimi" + ], "description": "Agent kinds with an officially verified interactive cold-start command." }, "agent_target_params": { "type": "object", - "required": ["agent"], + "required": [ + "agent" + ], "properties": { - "agent": { "type": "string", "minLength": 1, "maxLength": 128 }, - "generation": { "type": ["integer", "null"], "minimum": 1 } + "agent": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "generation": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } }, "additionalProperties": false }, "agent_prompt_params": { "type": "object", - "required": ["agent", "text"], + "required": [ + "agent", + "text" + ], "properties": { - "agent": { "type": "string", "minLength": 1, "maxLength": 128 }, - "generation": { "type": ["integer", "null"], "minimum": 1 }, - "text": { "type": "string", "minLength": 1, "maxLength": 32768 }, - "submit": { "type": "boolean", "default": true } + "agent": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "generation": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + }, + "submit": { + "type": "boolean", + "default": true + } }, "additionalProperties": false }, "agent_delegate_params": { "type": "object", - "required": ["agent", "text", "origin_pane_id"], + "required": [ + "agent", + "text", + "origin_pane_id" + ], "properties": { - "agent": { "type": "string", "minLength": 1, "maxLength": 128 }, - "generation": { "type": ["integer", "null"], "minimum": 1 }, - "text": { "type": "string", "minLength": 1, "maxLength": 32768 }, - "origin_pane_id": { "type": "integer", "minimum": 1 } + "agent": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "generation": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + }, + "origin_pane_id": { + "type": "integer", + "minimum": 1 + } }, "additionalProperties": false }, "agent_paste_params": { "type": "object", - "required": ["agent", "text"], + "required": [ + "agent", + "text" + ], "properties": { - "agent": { "type": "string", "minLength": 1, "maxLength": 128 }, - "generation": { "type": ["integer", "null"], "minimum": 1 }, - "text": { "type": "string", "minLength": 1, "maxLength": 32768 }, - "submit": { "type": "boolean", "default": true } + "agent": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "generation": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + }, + "submit": { + "type": "boolean", + "default": true + } }, "additionalProperties": false }, "agent_read_params": { "type": "object", - "required": ["agent"], + "required": [ + "agent" + ], "properties": { - "agent": { "type": "string", "minLength": 1, "maxLength": 128 }, - "generation": { "type": ["integer", "null"], "minimum": 1 }, - "lines": { "type": "integer", "minimum": 1, "maximum": 2000, "default": 120 } + "agent": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "generation": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "lines": { + "type": "integer", + "minimum": 1, + "maximum": 2000, + "default": 120 + } }, "additionalProperties": false }, "agent_wait_params": { "type": "object", - "required": ["agent", "generation", "state", "timeout_ms"], + "required": [ + "agent", + "generation", + "state", + "timeout_ms" + ], "properties": { - "agent": { "type": "string", "minLength": 1, "maxLength": 128 }, - "generation": { "type": "integer", "minimum": 1 }, - "state": { "$ref": "#/$defs/wait_state" }, - "timeout_ms": { "type": "integer", "minimum": 1, "maximum": 86400000 }, - "after_seq": { "type": ["integer", "null"], "minimum": 0 } + "agent": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "generation": { + "type": "integer", + "minimum": 1 + }, + "state": { + "$ref": "#/$defs/wait_state" + }, + "timeout_ms": { + "type": "integer", + "minimum": 1, + "maximum": 86400000 + }, + "after_seq": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + } }, "additionalProperties": false }, "focus_params": { "type": "object", "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": ["integer", "null"], "minimum": 0 } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + } }, "additionalProperties": false }, "tab_new_params": { "type": "object", "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "cwd": { "type": ["string", "null"] }, - "shell": { "type": ["string", "null"] } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "shell": { + "type": [ + "string", + "null" + ] + } }, "additionalProperties": false }, "tab_target_params": { "type": "object", - "required": ["tab_index"], + "required": [ + "tab_index" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "tab_index": { "type": "integer", "minimum": 0 } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "tab_index": { + "type": "integer", + "minimum": 0 + } }, "additionalProperties": false }, "tab_rename_params": { "type": "object", - "required": ["tab_index", "name"], + "required": [ + "tab_index", + "name" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "tab_index": { "type": "integer", "minimum": 0 }, + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "tab_index": { + "type": "integer", + "minimum": 0 + }, "name": { "type": "string", "maxLength": 256, @@ -520,155 +1539,464 @@ }, "tab_move_params": { "type": "object", - "required": ["tab_index", "to_index"], + "required": [ + "tab_index", + "to_index" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "tab_index": { "type": "integer", "minimum": 0 }, - "to_index": { "type": "integer", "minimum": 0 } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "tab_index": { + "type": "integer", + "minimum": 0 + }, + "to_index": { + "type": "integer", + "minimum": 0 + } }, "additionalProperties": false }, "split_params": { "type": "object", - "required": ["direction"], + "required": [ + "direction" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": ["integer", "null"], "minimum": 0 }, - "direction": { "enum": ["left_right", "top_bottom"] } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "direction": { + "enum": [ + "left_right", + "top_bottom" + ] + } }, "additionalProperties": false }, "prompt_params": { "type": "object", - "required": ["pane_id", "text"], + "required": [ + "pane_id", + "text" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 }, - "text": { "type": "string", "minLength": 1, "maxLength": 32768 }, - "submit": { "type": "boolean", "default": true } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + }, + "submit": { + "type": "boolean", + "default": true + } }, "additionalProperties": false }, "paste_params": { "type": "object", - "required": ["pane_id", "text"], + "required": [ + "pane_id", + "text" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 }, - "text": { "type": "string", "minLength": 1, "maxLength": 32768 }, - "submit": { "type": "boolean", "default": true } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + }, + "submit": { + "type": "boolean", + "default": true + } }, "additionalProperties": false }, "read_params": { "type": "object", - "required": ["pane_id"], + "required": [ + "pane_id" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 }, - "lines": { "type": "integer", "minimum": 1, "maximum": 2000, "default": 120 } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "lines": { + "type": "integer", + "minimum": 1, + "maximum": 2000, + "default": 120 + }, + "screen": { + "type": "boolean", + "default": false, + "description": "Include the live screen v1 color grid, independently of the text tail and desktop scrollback offset." + } }, "additionalProperties": false }, "pane_target_params": { "type": "object", - "required": ["pane_id"], + "required": [ + "pane_id" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + } }, "additionalProperties": false }, "pane_zoom_params": { "type": "object", - "required": ["pane_id", "zoomed"], + "required": [ + "pane_id", + "zoomed" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 }, - "zoomed": { "type": "boolean" } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "zoomed": { + "type": "boolean" + } }, "additionalProperties": false }, "pane_resize_params": { "type": "object", - "required": ["pane_id", "ratio"], + "required": [ + "pane_id", + "ratio" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 }, - "ratio": { "type": "number", "minimum": 0.05, "maximum": 0.95 } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "ratio": { + "type": "number", + "minimum": 0.05, + "maximum": 0.95 + } }, "additionalProperties": false }, "send_key_params": { "type": "object", - "required": ["pane_id", "key"], + "required": [ + "pane_id", + "key" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 }, + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, "key": { "enum": [ - "escape", "enter", "tab", "backspace", "up", "down", "left", "right", - "home", "end", "insert", "delete", "page_up", "page_down", - "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", - "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", - "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" + "escape", + "enter", + "tab", + "backspace", + "up", + "down", + "left", + "right", + "home", + "end", + "insert", + "delete", + "page_up", + "page_down", + "f1", + "f2", + "f3", + "f4", + "f5", + "f6", + "f7", + "f8", + "f9", + "f10", + "f11", + "f12", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z" ] }, "modifiers": { "type": "object", "properties": { - "shift": { "type": "boolean", "default": false }, - "alt": { "type": "boolean", "default": false }, - "control": { "type": "boolean", "default": false } + "shift": { + "type": "boolean", + "default": false + }, + "alt": { + "type": "boolean", + "default": false + }, + "control": { + "type": "boolean", + "default": false + } }, "additionalProperties": false }, - "repeat": { "type": "integer", "minimum": 1, "maximum": 64, "default": 1 } + "repeat": { + "type": "integer", + "minimum": 1, + "maximum": 64, + "default": 1 + } }, "additionalProperties": false }, "run_params": { "type": "object", - "required": ["pane_id", "command"], + "required": [ + "pane_id", + "command" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 }, - "command": { "type": "string", "minLength": 1, "maxLength": 32768 }, - "wait": { "type": "boolean", "default": true }, - "timeout_ms": { "type": "integer", "minimum": 1, "maximum": 86400000, "default": 30000 } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "command": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + }, + "wait": { + "type": "boolean", + "default": true + }, + "timeout_ms": { + "type": "integer", + "minimum": 1, + "maximum": 86400000, + "default": 30000 + } }, "additionalProperties": false }, "exec_params": { "type": "object", - "required": ["pane_id", "argv"], + "required": [ + "pane_id", + "argv" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 }, + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, "argv": { "type": "array", "minItems": 1, "maxItems": 256, - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "Direct program argv; no shell expansion is performed. The server enforces a 32768-byte aggregate limit." }, - "timeout_ms": { "type": "integer", "minimum": 1, "maximum": 86400000, "default": 30000 }, - "max_output_bytes": { "type": "integer", "minimum": 1, "maximum": 16777216, "default": 1048576 } + "timeout_ms": { + "type": "integer", + "minimum": 1, + "maximum": 86400000, + "default": 30000 + }, + "max_output_bytes": { + "type": "integer", + "minimum": 1, + "maximum": 16777216, + "default": 1048576 + } + }, + "additionalProperties": false + }, + "git_params": { + "type": "object", + "description": "Git runs on the explicitly selected computer pane. The server rechecks its current cwd, resolves the repository root and checks the supplied status revision before a write. Read-only mobile channels allow status, diff and history only. Requests have a 25-second total execution deadline and a 256-KiB stdout limit.", + "required": [ + "window_id", + "pane_id", + "expected_cwd" + ], + "properties": { + "window_id": { + "type": "integer", + "minimum": 1 + }, + "pane_id": { + "type": "integer", + "minimum": 1 + }, + "expected_cwd": { + "type": "string", + "minLength": 1, + "maxLength": 4096, + "pattern": "^[^\\u0000]+$", + "description": "Exact cwd from the pane snapshot; at most 4096 UTF-8 bytes." + }, + "path": { + "type": "string", + "maxLength": 4096, + "pattern": "^[^\\u0000]*$", + "default": "", + "description": "Exact repository-relative change path, at most 4096 UTF-8 bytes. Never interpreted as a shell command or glob." + }, + "staged": { + "type": "boolean", + "default": false, + "description": "Select the index diff rather than the worktree diff." + }, + "all": { + "type": "boolean", + "default": false, + "description": "Stage or unstage all changes in the repository, including when the pane cwd is a subdirectory." + }, + "revision": { + "type": "string", + "default": "", + "description": "Opaque revision returned by git.status. Required for mutations; an outdated revision produces git_stale before execution." + }, + "message": { + "type": "string", + "maxLength": 16384, + "pattern": "^[^\\u0000]*$", + "default": "", + "description": "Commit message, at most 16384 UTF-8 bytes. Only staged changes are committed." + }, + "commit": { + "type": "string", + "pattern": "^(?:|[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$", + "default": "", + "description": "A full object ID selects the commit diff instead of a current change." + }, + "offset": { + "type": "integer", + "minimum": 0, + "maximum": 10000, + "default": 0, + "description": "History offset; each response returns at most 50 commits and has_more." + } }, "additionalProperties": false }, "wait_state": { - "enum": ["idle", "running", "waiting_input", "attention", "finished", "failed", "settled"] + "enum": [ + "idle", + "running", + "waiting_input", + "attention", + "finished", + "failed", + "settled" + ] }, "wait_params": { "type": "object", - "required": ["pane_id", "state", "timeout_ms"], + "required": [ + "pane_id", + "state", + "timeout_ms" + ], "properties": { - "window_id": { "$ref": "#/$defs/nullable_window_id" }, - "pane_id": { "type": "integer", "minimum": 0 }, - "state": { "$ref": "#/$defs/wait_state" }, - "timeout_ms": { "type": "integer", "minimum": 1, "maximum": 86400000 }, - "after_seq": { "type": ["integer", "null"], "minimum": 0 } + "window_id": { + "$ref": "#/$defs/nullable_window_id" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "state": { + "$ref": "#/$defs/wait_state" + }, + "timeout_ms": { + "type": "integer", + "minimum": 1, + "maximum": 86400000 + }, + "after_seq": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + } }, "additionalProperties": false }, @@ -783,7 +2111,9 @@ }, "pane_lifecycles": { "type": "array", - "items": { "$ref": "#/$defs/pane_lifecycle" } + "items": { + "$ref": "#/$defs/pane_lifecycle" + } } }, "additionalProperties": false @@ -893,6 +2223,14 @@ "items": { "$ref": "#/$defs/pane" } + }, + "tab_id": { + "type": "string", + "pattern": "^[a-fA-F0-9]{32}$", + "description": "Ephemeral identity, preserved by reorder and split. Absent on older runtimes." + }, + "file": { + "$ref": "#/$defs/tab_file" } }, "additionalProperties": false @@ -1076,64 +2414,182 @@ }, "worktree": { "type": "object", - "required": ["repo_root", "source_root", "path", "branch", "base_commit", "created"], + "required": [ + "repo_root", + "source_root", + "path", + "branch", + "base_commit", + "created" + ], "properties": { - "repo_root": { "type": "string", "minLength": 1 }, - "source_root": { "type": "string", "minLength": 1 }, - "path": { "type": "string", "minLength": 1 }, - "branch": { "type": "string", "minLength": 1 }, - "base_commit": { "type": "string", "minLength": 1 }, - "created": { "const": true } + "repo_root": { + "type": "string", + "minLength": 1 + }, + "source_root": { + "type": "string", + "minLength": 1 + }, + "path": { + "type": "string", + "minLength": 1 + }, + "branch": { + "type": "string", + "minLength": 1 + }, + "base_commit": { + "type": "string", + "minLength": 1 + }, + "created": { + "const": true + } }, "additionalProperties": false }, "managed_agent": { "type": "object", - "required": ["agent_id", "generation", "name", "kind", "window_id", "pane_id", "active", "observed"], + "required": [ + "agent_id", + "generation", + "name", + "kind", + "window_id", + "pane_id", + "active", + "observed" + ], "properties": { - "agent_id": { "type": "string", "minLength": 1 }, - "generation": { "type": "integer", "minimum": 1 }, - "name": { "type": "string", "minLength": 1, "maxLength": 64 }, - "kind": { "type": "string", "minLength": 1 }, - "window_id": { "type": "integer", "minimum": 0 }, - "pane_id": { "type": "integer", "minimum": 0 }, - "session_id": { "type": "string" }, - "worktree": { "$ref": "#/$defs/worktree" }, - "active": { "type": "boolean" }, - "observed": { "type": "boolean" }, - "closed_reason": { "type": "string" } + "agent_id": { + "type": "string", + "minLength": 1 + }, + "generation": { + "type": "integer", + "minimum": 1 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "kind": { + "type": "string", + "minLength": 1 + }, + "window_id": { + "type": "integer", + "minimum": 0 + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "session_id": { + "type": "string" + }, + "worktree": { + "$ref": "#/$defs/worktree" + }, + "active": { + "type": "boolean" + }, + "observed": { + "type": "boolean" + }, + "closed_reason": { + "type": "string" + } }, "additionalProperties": false }, "pane_lifecycle": { "type": "object", - "required": ["sequence", "window_id", "pane_id", "event"], + "required": [ + "sequence", + "window_id", + "pane_id", + "event" + ], "properties": { - "sequence": { "type": "integer", "minimum": 1 }, - "window_id": { "type": "integer", "minimum": 0 }, - "pane_id": { "type": "integer", "minimum": 0 }, - "event": { "enum": ["closed", "exited"] } + "sequence": { + "type": "integer", + "minimum": 1 + }, + "window_id": { + "type": "integer", + "minimum": 0 + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "event": { + "enum": [ + "closed", + "exited" + ] + } }, "additionalProperties": false }, "pane_run": { "type": "object", - "required": ["run_id", "phase"], + "required": [ + "run_id", + "phase" + ], "properties": { - "run_id": { "type": "integer", "minimum": 1 }, - "phase": { "enum": ["submitted", "started"] } + "run_id": { + "type": "integer", + "minimum": 1 + }, + "phase": { + "enum": [ + "submitted", + "started" + ] + } }, "additionalProperties": false }, "run_outcome": { "type": "object", - "required": ["run_id", "state", "exit_code", "exit_code_capability"], + "required": [ + "run_id", + "state", + "exit_code", + "exit_code_capability" + ], "properties": { - "run_id": { "type": "integer", "minimum": 1 }, - "state": { "enum": ["finished", "failed", "unavailable"] }, - "exit_code": { "type": ["integer", "null"] }, - "exit_code_capability": { "enum": ["supported", "unavailable"] }, - "unavailable_reason": { "type": "string" } + "run_id": { + "type": "integer", + "minimum": 1 + }, + "state": { + "enum": [ + "finished", + "failed", + "unavailable" + ] + }, + "exit_code": { + "type": [ + "integer", + "null" + ] + }, + "exit_code_capability": { + "enum": [ + "supported", + "unavailable" + ] + }, + "unavailable_reason": { + "type": "string" + } }, "additionalProperties": false }, @@ -1154,31 +2610,75 @@ "state_change_seq" ], "properties": { - "window_id": { "type": "integer", "minimum": 0 }, - "tab_index": { "type": "integer", "minimum": 0 }, - "tab_active": { "type": "boolean" }, - "pane_id": { "type": "integer", "minimum": 0 }, - "pane_active": { "type": "boolean" }, - "title": { "type": "string" }, - "cwd": { "type": "string" }, - "branch": { "type": "string" }, - "ssh_destination": { "type": ["string", "null"] }, - "agent": { "$ref": "#/$defs/agent" }, - "task_state": { - "enum": ["idle", "running", "waiting_input", "attention", "finished", "failed"] + "window_id": { + "type": "integer", + "minimum": 0 }, - "state_change_seq": { "type": "integer", "minimum": 1 } + "tab_index": { + "type": "integer", + "minimum": 0 + }, + "tab_active": { + "type": "boolean" + }, + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "pane_active": { + "type": "boolean" + }, + "title": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "branch": { + "type": "string" + }, + "ssh_destination": { + "type": [ + "string", + "null" + ] + }, + "agent": { + "$ref": "#/$defs/agent" + }, + "task_state": { + "enum": [ + "idle", + "running", + "waiting_input", + "attention", + "finished", + "failed" + ] + }, + "state_change_seq": { + "type": "integer", + "minimum": 1 + } }, "additionalProperties": false }, "agents_result": { "type": "object", - "required": ["revision", "agents"], + "required": [ + "revision", + "agents" + ], "properties": { - "revision": { "type": "integer", "minimum": 1 }, + "revision": { + "type": "integer", + "minimum": 1 + }, "agents": { "type": "array", - "items": { "$ref": "#/$defs/agent_pane" } + "items": { + "$ref": "#/$defs/agent_pane" + } } }, "additionalProperties": false @@ -1197,20 +2697,496 @@ "exited" ], "properties": { - "window_id": { "type": "integer", "minimum": 0 }, - "pane_id": { "type": "integer", "minimum": 0 }, - "text": { "type": "string" }, - "requested_lines": { "type": "integer", "minimum": 1, "maximum": 2000 }, - "returned_lines": { "type": "integer", "minimum": 0, "maximum": 2000 }, - "history_available": { "type": "integer", "minimum": 0 }, - "truncated": { "type": "boolean" }, - "task_state": { - "enum": ["idle", "running", "waiting_input", "attention", "finished", "failed"] + "window_id": { + "type": "integer", + "minimum": 0 }, - "exited": { "type": "boolean" }, - "exit_reason": { "type": "string" } + "pane_id": { + "type": "integer", + "minimum": 0 + }, + "text": { + "type": "string" + }, + "requested_lines": { + "type": "integer", + "minimum": 1, + "maximum": 2000 + }, + "returned_lines": { + "type": "integer", + "minimum": 0, + "maximum": 2000 + }, + "history_available": { + "type": "integer", + "minimum": 0 + }, + "truncated": { + "type": "boolean" + }, + "task_state": { + "enum": [ + "idle", + "running", + "waiting_input", + "attention", + "finished", + "failed" + ] + }, + "exited": { + "type": "boolean" + }, + "exit_reason": { + "type": "string" + }, + "screen": { + "$ref": "#/$defs/terminal_screen_v1" + } }, "additionalProperties": false + }, + "tab_identity_params": { + "type": "object", + "required": [ + "window_id", + "tab_id" + ], + "properties": { + "window_id": { + "type": "integer", + "minimum": 1 + }, + "tab_id": { + "type": "string", + "pattern": "^[a-fA-F0-9]{32}$" + } + }, + "additionalProperties": false + }, + "tab_read_params": { + "type": "object", + "required": [ + "window_id", + "tab_id" + ], + "properties": { + "window_id": { + "type": "integer", + "minimum": 1 + }, + "tab_id": { + "type": "string", + "pattern": "^[a-fA-F0-9]{32}$" + }, + "offset": { + "type": "integer", + "minimum": 0, + "maximum": 16777216 + }, + "limit": { + "type": "integer", + "minimum": 4, + "maximum": 65536 + }, + "revision": { + "type": "string", + "minLength": 1, + "maxLength": 128 + } + }, + "additionalProperties": false, + "allOf": [ + { + "if": { + "required": [ + "offset" + ], + "properties": { + "offset": { + "minimum": 1 + } + } + }, + "then": { + "required": [ + "revision" + ] + } + } + ] + }, + "tab_open_params": { + "type": "object", + "required": [ + "window_id", + "path" + ], + "properties": { + "window_id": { + "type": "integer", + "minimum": 1 + }, + "path": { + "type": "string", + "minLength": 1, + "maxLength": 4096, + "description": "Absolute path on the computer; only in-app readable files are accepted." + } + }, + "additionalProperties": false + }, + "tab_file": { + "type": "object", + "required": [ + "path", + "remote", + "dirty", + "saving", + "ready" + ], + "properties": { + "path": { + "type": "string" + }, + "remote": { + "type": "boolean" + }, + "dirty": { + "type": "boolean" + }, + "saving": { + "type": "boolean" + }, + "ready": { + "type": "boolean" + }, + "revision": { + "type": "integer", + "minimum": 0 + } + }, + "additionalProperties": false + }, + "conversation_identity": { + "type": "object", + "required": [ + "kind", + "session_id" + ], + "properties": { + "kind": { + "enum": [ + "codex", + "claude" + ] + }, + "session_id": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "epoch": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + } + }, + "additionalProperties": false + }, + "conversation_read_params": { + "type": "object", + "required": [ + "window_id", + "pane_id", + "identity" + ], + "properties": { + "window_id": { + "type": "integer", + "minimum": 1 + }, + "pane_id": { + "type": "integer", + "minimum": 1 + }, + "identity": { + "$ref": "#/$defs/conversation_identity" + }, + "before": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "revision": { + "type": [ + "string", + "null" + ], + "maxLength": 128 + } + }, + "additionalProperties": false + }, + "conversation_send_params": { + "type": "object", + "required": [ + "window_id", + "pane_id", + "identity", + "text" + ], + "properties": { + "window_id": { + "type": "integer", + "minimum": 1 + }, + "pane_id": { + "type": "integer", + "minimum": 1 + }, + "identity": { + "allOf": [ + { + "$ref": "#/$defs/conversation_identity" + }, + { + "required": [ + "epoch" + ], + "properties": { + "epoch": { + "type": "integer", + "minimum": 0 + } + } + } + ] + }, + "text": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false + }, + "conversation_choose_params": { + "type": "object", + "required": [ + "window_id", + "pane_id", + "identity", + "prompt_id", + "option" + ], + "properties": { + "window_id": { + "type": "integer", + "minimum": 1 + }, + "pane_id": { + "type": "integer", + "minimum": 1 + }, + "identity": { + "allOf": [ + { + "$ref": "#/$defs/conversation_identity" + }, + { + "required": [ + "epoch" + ], + "properties": { + "epoch": { + "type": "integer", + "minimum": 0 + } + } + } + ] + }, + "prompt_id": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "option": { + "type": "integer", + "minimum": 0, + "maximum": 8 + } + }, + "additionalProperties": false + }, + "conversation_key_params": { + "type": "object", + "required": [ + "window_id", + "pane_id", + "identity", + "key" + ], + "properties": { + "window_id": { + "type": "integer", + "minimum": 1 + }, + "pane_id": { + "type": "integer", + "minimum": 1 + }, + "identity": { + "allOf": [ + { + "$ref": "#/$defs/conversation_identity" + }, + { + "required": [ + "epoch" + ], + "properties": { + "epoch": { + "type": "integer", + "minimum": 0 + } + } + } + ] + }, + "key": { + "type": "string", + "enum": [ + "Ctrl+C", + "Esc", + "Tab", + "←", + "→", + "↑", + "↓" + ] + } + }, + "additionalProperties": false + }, + "terminal_screen_v1": { + "type": "object", + "required": [ + "version", + "columns", + "rows", + "palette", + "cursor" + ], + "properties": { + "version": { + "const": 1 + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 400 + }, + "rows": { + "type": "array", + "minItems": 1, + "maxItems": 200, + "items": { + "type": "array", + "minItems": 1, + "maxItems": 400, + "items": { + "type": "array", + "prefixItems": [ + { + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + { + "type": "integer", + "minimum": 1, + "maximum": 2 + }, + { + "type": "integer", + "minimum": -269, + "maximum": 16777215 + }, + { + "type": "integer", + "minimum": -269, + "maximum": 16777215 + }, + { + "type": "integer", + "minimum": 0, + "maximum": 63 + } + ], + "minItems": 5, + "maxItems": 5 + } + } + }, + "palette": { + "type": "array", + "maxItems": 269, + "items": { + "type": "array", + "prefixItems": [ + { + "type": "integer", + "minimum": 0, + "maximum": 268 + }, + { + "type": "integer", + "minimum": 0, + "maximum": 16777215 + } + ], + "minItems": 2, + "maxItems": 2 + } + }, + "cursor": { + "type": "array", + "prefixItems": [ + { + "type": "integer", + "minimum": 0, + "maximum": 399 + }, + { + "type": "integer", + "minimum": 0 + }, + { + "type": "integer", + "minimum": 0, + "maximum": 1 + } + ], + "minItems": 3, + "maxItems": 3 + }, + "wrapped": { + "type": "array", + "minItems": 1, + "maxItems": 200, + "items": { + "type": "boolean" + }, + "description": "Optional soft-wrap flag per physical row, preserving logical lines during phone-only reflow." + } + }, + "additionalProperties": false, + "description": "At most 40000 cells and 128 KiB UTF-8 glyphs; each glyph <=256 UTF-8 bytes. Row widths must equal columns. Color <0 references -(palette index+1); otherwise RGB. Flags: bold=1, italic=2, underline=4, strike=8, dim=16, hidden=32. Cursor is [x,y,visible]. Oversized grids return screen_too_large, never a silently cropped frame." } } } diff --git a/extra/completions/_pebrel b/extra/completions/_pebrel index 65c9783..fc0fc68 100644 --- a/extra/completions/_pebrel +++ b/extra/completions/_pebrel @@ -1122,6 +1122,72 @@ esac ;; esac ;; +(plugin) +_arguments "${_arguments_options[@]}" : \ +'--pretty[Pretty-print the JSON result]' \ +'-h[Print help]' \ +'--help[Print help]' \ +":: :_pebrel__plugin_commands" \ +"*::: :->plugin" \ +&& ret=0 + + case $state in + (plugin) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:pebrel-plugin-command-$line[1]:" + case $line[1] in + (check) +_arguments "${_arguments_options[@]}" : \ +'--pretty[Pretty-print the JSON result]' \ +'-h[Print help]' \ +'--help[Print help]' \ +':path:_files' \ +&& ret=0 +;; +(run) +_arguments "${_arguments_options[@]}" : \ +'--args=[JSON object overriding this command'\''s default parameters]:ARGS:_default' \ +'--timeout-ms=[]:TIMEOUT_MS:_default' \ +'--pretty[Pretty-print the JSON result]' \ +'-h[Print help]' \ +'--help[Print help]' \ +':path:_files' \ +':command:_default' \ +&& ret=0 +;; +(help) +_arguments "${_arguments_options[@]}" : \ +":: :_pebrel__plugin__help_commands" \ +"*::: :->help" \ +&& ret=0 + + case $state in + (help) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:pebrel-plugin-help-command-$line[1]:" + case $line[1] in + (check) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; +(run) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; +(help) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; + esac + ;; +esac +;; + esac + ;; +esac +;; (help) _arguments "${_arguments_options[@]}" : \ ":: :_pebrel__help_commands" \ @@ -1446,6 +1512,30 @@ _arguments "${_arguments_options[@]}" : \ ;; esac ;; +(plugin) +_arguments "${_arguments_options[@]}" : \ +":: :_pebrel__help__plugin_commands" \ +"*::: :->plugin" \ +&& ret=0 + + case $state in + (plugin) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:pebrel-help-plugin-command-$line[1]:" + case $line[1] in + (check) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; +(run) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; + esac + ;; +esac +;; (help) _arguments "${_arguments_options[@]}" : \ && ret=0 @@ -1470,6 +1560,7 @@ _pebrel_commands() { 'agent:Inspect and drive the AI agents running in panes\: list, send, read, wait' \ 'migrate:Migrate the configuration file' \ 'config:Validate or create the Pebrel configuration' \ +'plugin:Check or invoke a local plugin package without starting a GUI or plugin daemon' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'pebrel commands' commands "$@" @@ -2015,6 +2106,7 @@ _pebrel__help_commands() { 'agent:Inspect and drive the AI agents running in panes\: list, send, read, wait' \ 'migrate:Migrate the configuration file' \ 'config:Validate or create the Pebrel configuration' \ +'plugin:Check or invoke a local plugin package without starting a GUI or plugin daemon' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'pebrel help commands' commands "$@" @@ -2346,6 +2438,24 @@ _pebrel__help__pane__zoom_commands() { local commands; commands=() _describe -t commands 'pebrel help pane zoom commands' commands "$@" } +(( $+functions[_pebrel__help__plugin_commands] )) || +_pebrel__help__plugin_commands() { + local commands; commands=( +'check:Validate package metadata and entry paths without executing Lua' \ +'run:Invoke one declared native or Lua command; no resident plugin is started' \ + ) + _describe -t commands 'pebrel help plugin commands' commands "$@" +} +(( $+functions[_pebrel__help__plugin__check_commands] )) || +_pebrel__help__plugin__check_commands() { + local commands; commands=() + _describe -t commands 'pebrel help plugin check commands' commands "$@" +} +(( $+functions[_pebrel__help__plugin__run_commands] )) || +_pebrel__help__plugin__run_commands() { + local commands; commands=() + _describe -t commands 'pebrel help plugin run commands' commands "$@" +} (( $+functions[_pebrel__help__tab_commands] )) || _pebrel__help__tab_commands() { local commands; commands=( @@ -2514,6 +2624,49 @@ _pebrel__pane__zoom_commands() { local commands; commands=() _describe -t commands 'pebrel pane zoom commands' commands "$@" } +(( $+functions[_pebrel__plugin_commands] )) || +_pebrel__plugin_commands() { + local commands; commands=( +'check:Validate package metadata and entry paths without executing Lua' \ +'run:Invoke one declared native or Lua command; no resident plugin is started' \ +'help:Print this message or the help of the given subcommand(s)' \ + ) + _describe -t commands 'pebrel plugin commands' commands "$@" +} +(( $+functions[_pebrel__plugin__check_commands] )) || +_pebrel__plugin__check_commands() { + local commands; commands=() + _describe -t commands 'pebrel plugin check commands' commands "$@" +} +(( $+functions[_pebrel__plugin__help_commands] )) || +_pebrel__plugin__help_commands() { + local commands; commands=( +'check:Validate package metadata and entry paths without executing Lua' \ +'run:Invoke one declared native or Lua command; no resident plugin is started' \ +'help:Print this message or the help of the given subcommand(s)' \ + ) + _describe -t commands 'pebrel plugin help commands' commands "$@" +} +(( $+functions[_pebrel__plugin__help__check_commands] )) || +_pebrel__plugin__help__check_commands() { + local commands; commands=() + _describe -t commands 'pebrel plugin help check commands' commands "$@" +} +(( $+functions[_pebrel__plugin__help__help_commands] )) || +_pebrel__plugin__help__help_commands() { + local commands; commands=() + _describe -t commands 'pebrel plugin help help commands' commands "$@" +} +(( $+functions[_pebrel__plugin__help__run_commands] )) || +_pebrel__plugin__help__run_commands() { + local commands; commands=() + _describe -t commands 'pebrel plugin help run commands' commands "$@" +} +(( $+functions[_pebrel__plugin__run_commands] )) || +_pebrel__plugin__run_commands() { + local commands; commands=() + _describe -t commands 'pebrel plugin run commands' commands "$@" +} (( $+functions[_pebrel__tab_commands] )) || _pebrel__tab_commands() { local commands; commands=( diff --git a/extra/completions/pebrel.bash b/extra/completions/pebrel.bash index ef364ee..01db0b9 100644 --- a/extra/completions/pebrel.bash +++ b/extra/completions/pebrel.bash @@ -37,6 +37,9 @@ _pebrel() { pebrel,pane) cmd="pebrel__pane" ;; + pebrel,plugin) + cmd="pebrel__plugin" + ;; pebrel,tab) cmd="pebrel__tab" ;; @@ -316,6 +319,9 @@ _pebrel() { pebrel__help,pane) cmd="pebrel__help__pane" ;; + pebrel__help,plugin) + cmd="pebrel__help__plugin" + ;; pebrel__help,tab) cmd="pebrel__help__tab" ;; @@ -466,6 +472,12 @@ _pebrel() { pebrel__help__pane,zoom) cmd="pebrel__help__pane__zoom" ;; + pebrel__help__plugin,check) + cmd="pebrel__help__plugin__check" + ;; + pebrel__help__plugin,run) + cmd="pebrel__help__plugin__run" + ;; pebrel__help__tab,close) cmd="pebrel__help__tab__close" ;; @@ -538,6 +550,24 @@ _pebrel() { pebrel__pane__help,zoom) cmd="pebrel__pane__help__zoom" ;; + pebrel__plugin,check) + cmd="pebrel__plugin__check" + ;; + pebrel__plugin,help) + cmd="pebrel__plugin__help" + ;; + pebrel__plugin,run) + cmd="pebrel__plugin__run" + ;; + pebrel__plugin__help,check) + cmd="pebrel__plugin__help__check" + ;; + pebrel__plugin__help,help) + cmd="pebrel__plugin__help__help" + ;; + pebrel__plugin__help,run) + cmd="pebrel__plugin__help__run" + ;; pebrel__tab,close) cmd="pebrel__tab__close" ;; @@ -581,7 +611,7 @@ _pebrel() { case "${cmd}" in pebrel) - opts="-q -v -e -T -o -h -V --print-events --ref-test --embed --gpui --config-file --socket --daemon --working-directory --shell --hold --command --title --class --option --help --version ctl env window tab pane agent migrate config help" + opts="-q -v -e -T -o -h -V --print-events --ref-test --embed --gpui --config-file --socket --daemon --working-directory --shell --hold --command --title --class --option --help --version ctl env window tab pane agent migrate config plugin help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2500,7 +2530,7 @@ _pebrel() { return 0 ;; pebrel__help) - opts="ctl env window tab pane agent migrate config help" + opts="ctl env window tab pane agent migrate config plugin help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3283,6 +3313,48 @@ _pebrel() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + pebrel__help__plugin) + opts="check run" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__help__plugin__check) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__help__plugin__run) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; pebrel__help__tab) opts="close rename move" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -3820,6 +3892,112 @@ _pebrel() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + pebrel__plugin) + opts="-h --pretty --help check run help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__check) + opts="-h --pretty --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__help) + opts="check run help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__help__check) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__help__help) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__help__run) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__run) + opts="-h --args --timeout-ms --pretty --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --args) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --timeout-ms) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; pebrel__tab) opts="-h --help close rename move help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then diff --git a/extra/completions/pebrel.fish b/extra/completions/pebrel.fish index ff552e0..474debf 100644 --- a/extra/completions/pebrel.fish +++ b/extra/completions/pebrel.fish @@ -50,6 +50,7 @@ complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "pane" -d 'Inspect and complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "agent" -d 'Inspect and drive the AI agents running in panes: list, send, read, wait' complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "migrate" -d 'Migrate the configuration file' complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "config" -d 'Validate or create the Pebrel configuration' +complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "plugin" -d 'Check or invoke a local plugin package without starting a GUI or plugin daemon' complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c pebrel -n "__fish_pebrel_using_subcommand ctl; and not __fish_seen_subcommand_from describe snapshot orchestrate agents agent-start agent-fork agent-get agent-prompt agent-paste agent-read agent-wait subscribe new-window close-window focus new-tab close-tab rename-tab move-tab split close-pane zoom-pane resize-pane prompt paste read procs send-key run exec-pane wait help" -l timeout-ms -d 'Maximum time to wait for a command response' -r complete -c pebrel -n "__fish_pebrel_using_subcommand ctl; and not __fish_seen_subcommand_from describe snapshot orchestrate agents agent-start agent-fork agent-get agent-prompt agent-paste agent-read agent-wait subscribe new-window close-window focus new-tab close-tab rename-tab move-tab split close-pane zoom-pane resize-pane prompt paste read procs send-key run exec-pane wait help" -l pretty -d 'Pretty-print one-shot JSON responses. Streaming subscriptions stay JSON Lines' @@ -515,15 +516,30 @@ complete -c pebrel -n "__fish_pebrel_using_subcommand config; and __fish_seen_su complete -c pebrel -n "__fish_pebrel_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "check" -d 'Validate a Lua, TOML, or YAML configuration without opening the GUI' complete -c pebrel -n "__fish_pebrel_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "init" -d 'Create an annotated Lua configuration template' complete -c pebrel -n "__fish_pebrel_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config help" -f -a "ctl" -d 'Agent-oriented terminal control: split panes, run commands, start Codex/Claude, send prompts, wait for state changes, and read verified terminal output' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config help" -f -a "env" -d 'Report this pane\'s terminal identity, the control-plane path, and every command available to it. Answers even with no runtime reachable, so an agent can always discover what it has instead of guessing' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config help" -f -a "window" -d 'Control terminal windows' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config help" -f -a "tab" -d 'Control tabs within one terminal window' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config help" -f -a "pane" -d 'Inspect and drive terminal panes: list, read, send, paste, wait, and change layout' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config help" -f -a "agent" -d 'Inspect and drive the AI agents running in panes: list, send, read, wait' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config help" -f -a "migrate" -d 'Migrate the configuration file' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config help" -f -a "config" -d 'Validate or create the Pebrel configuration' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and not __fish_seen_subcommand_from check run help" -l pretty -d 'Pretty-print the JSON result' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and not __fish_seen_subcommand_from check run help" -s h -l help -d 'Print help' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and not __fish_seen_subcommand_from check run help" -f -a "check" -d 'Validate package metadata and entry paths without executing Lua' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and not __fish_seen_subcommand_from check run help" -f -a "run" -d 'Invoke one declared native or Lua command; no resident plugin is started' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and not __fish_seen_subcommand_from check run help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from check" -l pretty -d 'Pretty-print the JSON result' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from check" -s h -l help -d 'Print help' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from run" -l args -d 'JSON object overriding this command\'s default parameters' -r +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from run" -l timeout-ms -r +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from run" -l pretty -d 'Pretty-print the JSON result' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from run" -s h -l help -d 'Print help' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from help" -f -a "check" -d 'Validate package metadata and entry paths without executing Lua' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from help" -f -a "run" -d 'Invoke one declared native or Lua command; no resident plugin is started' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin help" -f -a "ctl" -d 'Agent-oriented terminal control: split panes, run commands, start Codex/Claude, send prompts, wait for state changes, and read verified terminal output' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin help" -f -a "env" -d 'Report this pane\'s terminal identity, the control-plane path, and every command available to it. Answers even with no runtime reachable, so an agent can always discover what it has instead of guessing' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin help" -f -a "window" -d 'Control terminal windows' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin help" -f -a "tab" -d 'Control tabs within one terminal window' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin help" -f -a "pane" -d 'Inspect and drive terminal panes: list, read, send, paste, wait, and change layout' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin help" -f -a "agent" -d 'Inspect and drive the AI agents running in panes: list, send, read, wait' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin help" -f -a "migrate" -d 'Migrate the configuration file' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin help" -f -a "config" -d 'Validate or create the Pebrel configuration' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin help" -f -a "plugin" -d 'Check or invoke a local plugin package without starting a GUI or plugin daemon' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from ctl" -f -a "describe" -d 'Describe the protocol version, runtime version, and available capabilities' complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from ctl" -f -a "snapshot" -d 'Read the authoritative window, tab, pane, and task-state projection' complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from ctl" -f -a "orchestrate" -d 'Execute one typed multi-step terminal workflow in a single Runtime request' @@ -576,3 +592,5 @@ complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subc complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from agent" -f -a "wait" -d 'Block until an agent\'s turn ends' complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "check" -d 'Validate a Lua, TOML, or YAML configuration without opening the GUI' complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "init" -d 'Create an annotated Lua configuration template' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from plugin" -f -a "check" -d 'Validate package metadata and entry paths without executing Lua' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from plugin" -f -a "run" -d 'Invoke one declared native or Lua command; no resident plugin is started' diff --git a/extra/completions/windows/_pebrel b/extra/completions/windows/_pebrel index d56bdde..31d7b53 100644 --- a/extra/completions/windows/_pebrel +++ b/extra/completions/windows/_pebrel @@ -1121,6 +1121,72 @@ esac ;; esac ;; +(plugin) +_arguments "${_arguments_options[@]}" : \ +'--pretty[Pretty-print the JSON result]' \ +'-h[Print help]' \ +'--help[Print help]' \ +":: :_pebrel__plugin_commands" \ +"*::: :->plugin" \ +&& ret=0 + + case $state in + (plugin) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:pebrel-plugin-command-$line[1]:" + case $line[1] in + (check) +_arguments "${_arguments_options[@]}" : \ +'--pretty[Pretty-print the JSON result]' \ +'-h[Print help]' \ +'--help[Print help]' \ +':path:_files' \ +&& ret=0 +;; +(run) +_arguments "${_arguments_options[@]}" : \ +'--args=[JSON object overriding this command'\''s default parameters]:ARGS:_default' \ +'--timeout-ms=[]:TIMEOUT_MS:_default' \ +'--pretty[Pretty-print the JSON result]' \ +'-h[Print help]' \ +'--help[Print help]' \ +':path:_files' \ +':command:_default' \ +&& ret=0 +;; +(help) +_arguments "${_arguments_options[@]}" : \ +":: :_pebrel__plugin__help_commands" \ +"*::: :->help" \ +&& ret=0 + + case $state in + (help) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:pebrel-plugin-help-command-$line[1]:" + case $line[1] in + (check) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; +(run) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; +(help) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; + esac + ;; +esac +;; + esac + ;; +esac +;; (notify-test) _arguments "${_arguments_options[@]}" : \ '-h[Print help]' \ @@ -1468,6 +1534,30 @@ _arguments "${_arguments_options[@]}" : \ ;; esac ;; +(plugin) +_arguments "${_arguments_options[@]}" : \ +":: :_pebrel__help__plugin_commands" \ +"*::: :->plugin" \ +&& ret=0 + + case $state in + (plugin) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:pebrel-help-plugin-command-$line[1]:" + case $line[1] in + (check) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; +(run) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; + esac + ;; +esac +;; (notify-test) _arguments "${_arguments_options[@]}" : \ && ret=0 @@ -1504,6 +1594,7 @@ _pebrel_commands() { 'agent:Inspect and drive the AI agents running in panes\: list, send, read, wait' \ 'migrate:Migrate the configuration file' \ 'config:Validate or create the Pebrel configuration' \ +'plugin:Check or invoke a local plugin package without starting a GUI or plugin daemon' \ 'notify-test:Test system notification (toast) delivery' \ 'setup-ai:Install (or --remove) AI hooks plus the Pebrel Runtime Skill for Codex and Claude Code' \ 'ssh:SSH with Pebrel shell integration bootstrapped on the remote host, so tab icons / spinner / cwd track the program running over the connection (claude, vim, cargo…). All arguments are forwarded to the system \`ssh\`' \ @@ -2052,6 +2143,7 @@ _pebrel__help_commands() { 'agent:Inspect and drive the AI agents running in panes\: list, send, read, wait' \ 'migrate:Migrate the configuration file' \ 'config:Validate or create the Pebrel configuration' \ +'plugin:Check or invoke a local plugin package without starting a GUI or plugin daemon' \ 'notify-test:Test system notification (toast) delivery' \ 'setup-ai:Install (or --remove) AI hooks plus the Pebrel Runtime Skill for Codex and Claude Code' \ 'ssh:SSH with Pebrel shell integration bootstrapped on the remote host, so tab icons / spinner / cwd track the program running over the connection (claude, vim, cargo…). All arguments are forwarded to the system \`ssh\`' \ @@ -2391,6 +2483,24 @@ _pebrel__help__pane__zoom_commands() { local commands; commands=() _describe -t commands 'pebrel help pane zoom commands' commands "$@" } +(( $+functions[_pebrel__help__plugin_commands] )) || +_pebrel__help__plugin_commands() { + local commands; commands=( +'check:Validate package metadata and entry paths without executing Lua' \ +'run:Invoke one declared native or Lua command; no resident plugin is started' \ + ) + _describe -t commands 'pebrel help plugin commands' commands "$@" +} +(( $+functions[_pebrel__help__plugin__check_commands] )) || +_pebrel__help__plugin__check_commands() { + local commands; commands=() + _describe -t commands 'pebrel help plugin check commands' commands "$@" +} +(( $+functions[_pebrel__help__plugin__run_commands] )) || +_pebrel__help__plugin__run_commands() { + local commands; commands=() + _describe -t commands 'pebrel help plugin run commands' commands "$@" +} (( $+functions[_pebrel__help__setup-ai_commands] )) || _pebrel__help__setup-ai_commands() { local commands; commands=() @@ -2574,6 +2684,49 @@ _pebrel__pane__zoom_commands() { local commands; commands=() _describe -t commands 'pebrel pane zoom commands' commands "$@" } +(( $+functions[_pebrel__plugin_commands] )) || +_pebrel__plugin_commands() { + local commands; commands=( +'check:Validate package metadata and entry paths without executing Lua' \ +'run:Invoke one declared native or Lua command; no resident plugin is started' \ +'help:Print this message or the help of the given subcommand(s)' \ + ) + _describe -t commands 'pebrel plugin commands' commands "$@" +} +(( $+functions[_pebrel__plugin__check_commands] )) || +_pebrel__plugin__check_commands() { + local commands; commands=() + _describe -t commands 'pebrel plugin check commands' commands "$@" +} +(( $+functions[_pebrel__plugin__help_commands] )) || +_pebrel__plugin__help_commands() { + local commands; commands=( +'check:Validate package metadata and entry paths without executing Lua' \ +'run:Invoke one declared native or Lua command; no resident plugin is started' \ +'help:Print this message or the help of the given subcommand(s)' \ + ) + _describe -t commands 'pebrel plugin help commands' commands "$@" +} +(( $+functions[_pebrel__plugin__help__check_commands] )) || +_pebrel__plugin__help__check_commands() { + local commands; commands=() + _describe -t commands 'pebrel plugin help check commands' commands "$@" +} +(( $+functions[_pebrel__plugin__help__help_commands] )) || +_pebrel__plugin__help__help_commands() { + local commands; commands=() + _describe -t commands 'pebrel plugin help help commands' commands "$@" +} +(( $+functions[_pebrel__plugin__help__run_commands] )) || +_pebrel__plugin__help__run_commands() { + local commands; commands=() + _describe -t commands 'pebrel plugin help run commands' commands "$@" +} +(( $+functions[_pebrel__plugin__run_commands] )) || +_pebrel__plugin__run_commands() { + local commands; commands=() + _describe -t commands 'pebrel plugin run commands' commands "$@" +} (( $+functions[_pebrel__setup-ai_commands] )) || _pebrel__setup-ai_commands() { local commands; commands=() diff --git a/extra/completions/windows/pebrel.bash b/extra/completions/windows/pebrel.bash index 33b7722..af4bdbe 100644 --- a/extra/completions/windows/pebrel.bash +++ b/extra/completions/windows/pebrel.bash @@ -40,6 +40,9 @@ _pebrel() { pebrel,pane) cmd="pebrel__pane" ;; + pebrel,plugin) + cmd="pebrel__plugin" + ;; pebrel,setup-ai) cmd="pebrel__setup__ai" ;; @@ -328,6 +331,9 @@ _pebrel() { pebrel__help,pane) cmd="pebrel__help__pane" ;; + pebrel__help,plugin) + cmd="pebrel__help__plugin" + ;; pebrel__help,setup-ai) cmd="pebrel__help__setup__ai" ;; @@ -484,6 +490,12 @@ _pebrel() { pebrel__help__pane,zoom) cmd="pebrel__help__pane__zoom" ;; + pebrel__help__plugin,check) + cmd="pebrel__help__plugin__check" + ;; + pebrel__help__plugin,run) + cmd="pebrel__help__plugin__run" + ;; pebrel__help__tab,close) cmd="pebrel__help__tab__close" ;; @@ -556,6 +568,24 @@ _pebrel() { pebrel__pane__help,zoom) cmd="pebrel__pane__help__zoom" ;; + pebrel__plugin,check) + cmd="pebrel__plugin__check" + ;; + pebrel__plugin,help) + cmd="pebrel__plugin__help" + ;; + pebrel__plugin,run) + cmd="pebrel__plugin__run" + ;; + pebrel__plugin__help,check) + cmd="pebrel__plugin__help__check" + ;; + pebrel__plugin__help,help) + cmd="pebrel__plugin__help__help" + ;; + pebrel__plugin__help,run) + cmd="pebrel__plugin__help__run" + ;; pebrel__tab,close) cmd="pebrel__tab__close" ;; @@ -599,7 +629,7 @@ _pebrel() { case "${cmd}" in pebrel) - opts="-q -v -e -T -o -h -V --print-events --ref-test --embed --gpui --config-file --daemon --working-directory --shell --hold --command --title --class --option --help --version ctl env window tab pane agent migrate config notify-test setup-ai ssh help" + opts="-q -v -e -T -o -h -V --print-events --ref-test --embed --gpui --config-file --daemon --working-directory --shell --hold --command --title --class --option --help --version ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2503,7 +2533,7 @@ _pebrel() { return 0 ;; pebrel__help) - opts="ctl env window tab pane agent migrate config notify-test setup-ai ssh help" + opts="ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3300,6 +3330,48 @@ _pebrel() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + pebrel__help__plugin) + opts="check run" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__help__plugin__check) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__help__plugin__run) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; pebrel__help__setup__ai) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -3879,6 +3951,112 @@ _pebrel() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + pebrel__plugin) + opts="-h --pretty --help check run help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__check) + opts="-h --pretty --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__help) + opts="check run help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__help__check) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__help__help) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__help__run) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pebrel__plugin__run) + opts="-h --args --timeout-ms --pretty --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --args) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --timeout-ms) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; pebrel__setup__ai) opts="-h --remove --ssh --wsl --wsl-user --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then diff --git a/extra/completions/windows/pebrel.fish b/extra/completions/windows/pebrel.fish index f1244f5..6306151 100644 --- a/extra/completions/windows/pebrel.fish +++ b/extra/completions/windows/pebrel.fish @@ -49,6 +49,7 @@ complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "pane" -d 'Inspect and complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "agent" -d 'Inspect and drive the AI agents running in panes: list, send, read, wait' complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "migrate" -d 'Migrate the configuration file' complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "config" -d 'Validate or create the Pebrel configuration' +complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "plugin" -d 'Check or invoke a local plugin package without starting a GUI or plugin daemon' complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "notify-test" -d 'Test system notification (toast) delivery' complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "setup-ai" -d 'Install (or --remove) AI hooks plus the Pebrel Runtime Skill for Codex and Claude Code' complete -c pebrel -n "__fish_pebrel_needs_command" -f -a "ssh" -d 'SSH with Pebrel shell integration bootstrapped on the remote host, so tab icons / spinner / cwd track the program running over the connection (claude, vim, cargo…). All arguments are forwarded to the system `ssh`' @@ -517,6 +518,20 @@ complete -c pebrel -n "__fish_pebrel_using_subcommand config; and __fish_seen_su complete -c pebrel -n "__fish_pebrel_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "check" -d 'Validate a Lua, TOML, or YAML configuration without opening the GUI' complete -c pebrel -n "__fish_pebrel_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "init" -d 'Create an annotated Lua configuration template' complete -c pebrel -n "__fish_pebrel_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and not __fish_seen_subcommand_from check run help" -l pretty -d 'Pretty-print the JSON result' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and not __fish_seen_subcommand_from check run help" -s h -l help -d 'Print help' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and not __fish_seen_subcommand_from check run help" -f -a "check" -d 'Validate package metadata and entry paths without executing Lua' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and not __fish_seen_subcommand_from check run help" -f -a "run" -d 'Invoke one declared native or Lua command; no resident plugin is started' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and not __fish_seen_subcommand_from check run help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from check" -l pretty -d 'Pretty-print the JSON result' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from check" -s h -l help -d 'Print help' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from run" -l args -d 'JSON object overriding this command\'s default parameters' -r +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from run" -l timeout-ms -r +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from run" -l pretty -d 'Pretty-print the JSON result' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from run" -s h -l help -d 'Print help' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from help" -f -a "check" -d 'Validate package metadata and entry paths without executing Lua' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from help" -f -a "run" -d 'Invoke one declared native or Lua command; no resident plugin is started' +complete -c pebrel -n "__fish_pebrel_using_subcommand plugin; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c pebrel -n "__fish_pebrel_using_subcommand notify-test" -s h -l help -d 'Print help' complete -c pebrel -n "__fish_pebrel_using_subcommand setup-ai" -l ssh -d 'Install or remove hooks on an SSH host (alias or user@host)' -r complete -c pebrel -n "__fish_pebrel_using_subcommand setup-ai" -l wsl -d 'Install or remove hooks in a WSL distribution\'s own user configuration' -r @@ -524,18 +539,19 @@ complete -c pebrel -n "__fish_pebrel_using_subcommand setup-ai" -l wsl-user -d ' complete -c pebrel -n "__fish_pebrel_using_subcommand setup-ai" -l remove -d 'Remove Pebrel-managed hooks for every supported agent and preserve other integrations. Without this flag, enable Claude Code and Codex plus agents explicitly enabled in settings' complete -c pebrel -n "__fish_pebrel_using_subcommand setup-ai" -s h -l help -d 'Print help' complete -c pebrel -n "__fish_pebrel_using_subcommand ssh" -s h -l help -d 'Print help' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "ctl" -d 'Agent-oriented terminal control: split panes, run commands, start Codex/Claude, send prompts, wait for state changes, and read verified terminal output' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "env" -d 'Report this pane\'s terminal identity, the control-plane path, and every command available to it. Answers even with no runtime reachable, so an agent can always discover what it has instead of guessing' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "window" -d 'Control terminal windows' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "tab" -d 'Control tabs within one terminal window' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "pane" -d 'Inspect and drive terminal panes: list, read, send, paste, wait, and change layout' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "agent" -d 'Inspect and drive the AI agents running in panes: list, send, read, wait' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "migrate" -d 'Migrate the configuration file' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "config" -d 'Validate or create the Pebrel configuration' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "notify-test" -d 'Test system notification (toast) delivery' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "setup-ai" -d 'Install (or --remove) AI hooks plus the Pebrel Runtime Skill for Codex and Claude Code' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "ssh" -d 'SSH with Pebrel shell integration bootstrapped on the remote host, so tab icons / spinner / cwd track the program running over the connection (claude, vim, cargo…). All arguments are forwarded to the system `ssh`' -complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config notify-test setup-ai ssh help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "ctl" -d 'Agent-oriented terminal control: split panes, run commands, start Codex/Claude, send prompts, wait for state changes, and read verified terminal output' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "env" -d 'Report this pane\'s terminal identity, the control-plane path, and every command available to it. Answers even with no runtime reachable, so an agent can always discover what it has instead of guessing' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "window" -d 'Control terminal windows' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "tab" -d 'Control tabs within one terminal window' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "pane" -d 'Inspect and drive terminal panes: list, read, send, paste, wait, and change layout' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "agent" -d 'Inspect and drive the AI agents running in panes: list, send, read, wait' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "migrate" -d 'Migrate the configuration file' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "config" -d 'Validate or create the Pebrel configuration' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "plugin" -d 'Check or invoke a local plugin package without starting a GUI or plugin daemon' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "notify-test" -d 'Test system notification (toast) delivery' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "setup-ai" -d 'Install (or --remove) AI hooks plus the Pebrel Runtime Skill for Codex and Claude Code' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "ssh" -d 'SSH with Pebrel shell integration bootstrapped on the remote host, so tab icons / spinner / cwd track the program running over the connection (claude, vim, cargo…). All arguments are forwarded to the system `ssh`' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and not __fish_seen_subcommand_from ctl env window tab pane agent migrate config plugin notify-test setup-ai ssh help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from ctl" -f -a "describe" -d 'Describe the protocol version, runtime version, and available capabilities' complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from ctl" -f -a "snapshot" -d 'Read the authoritative window, tab, pane, and task-state projection' complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from ctl" -f -a "orchestrate" -d 'Execute one typed multi-step terminal workflow in a single Runtime request' @@ -588,3 +604,5 @@ complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subc complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from agent" -f -a "wait" -d 'Block until an agent\'s turn ends' complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "check" -d 'Validate a Lua, TOML, or YAML configuration without opening the GUI' complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "init" -d 'Create an annotated Lua configuration template' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from plugin" -f -a "check" -d 'Validate package metadata and entry paths without executing Lua' +complete -c pebrel -n "__fish_pebrel_using_subcommand help; and __fish_seen_subcommand_from plugin" -f -a "run" -d 'Invoke one declared native or Lua command; no resident plugin is started' diff --git a/mobile/.gitignore b/mobile/.gitignore new file mode 100644 index 0000000..96b63c5 --- /dev/null +++ b/mobile/.gitignore @@ -0,0 +1,8 @@ +**/.gradle/ +**/build/ +**/.cxx/ +**/local.properties +**/*.jks + +!relay/package.json +!relay/package-lock.json diff --git a/mobile/android/.gitignore b/mobile/android/.gitignore new file mode 100644 index 0000000..fb78732 --- /dev/null +++ b/mobile/android/.gitignore @@ -0,0 +1,9 @@ +.gradle/ +.kotlin/ +**/build/ +local.properties +*.jks +*.keystore +.idea/ +# Deliberately public test identity; never signs the production application ID. +!pebrel-preview.p12 diff --git a/mobile/android/app/build.gradle.kts b/mobile/android/app/build.gradle.kts new file mode 100644 index 0000000..47b459d --- /dev/null +++ b/mobile/android/app/build.gradle.kts @@ -0,0 +1,116 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("org.jetbrains.kotlin.plugin.compose") + id("org.jetbrains.kotlin.plugin.serialization") +} + +android { + namespace = "io.github.kuddev.pebrel.mobile" + compileSdk = 35 + ndkVersion = "27.2.12479018" + defaultConfig { + applicationId = "io.github.kuddev.pebrel.mobile" + minSdk = 26 + targetSdk = 35 + versionCode = 18 + versionName = "2.0.0-preview" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + ndk { abiFilters += listOf("arm64-v8a", "x86_64") } + } + buildFeatures { compose = true } + compileOptions { + isCoreLibraryDesugaringEnabled = true + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { jvmTarget = "17" } + signingConfigs { + create("pebrelPreview") { + // Public development identity. Never use this key for the release package. + storeFile = rootProject.file("pebrel-preview.p12") + storeType = "PKCS12" + storePassword = "android" + keyAlias = "pebrel-preview" + keyPassword = "android" + } + } + buildTypes { + release { + isMinifyEnabled = true + isShrinkResources = true + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + create("preview") { + initWith(getByName("release")) + applicationIdSuffix = ".preview" + signingConfig = signingConfigs.getByName("pebrelPreview") + matchingFallbacks += "release" + testProguardFiles("test-proguard-rules.pro") + proguardFiles("preview-proguard-rules.pro") + } + } + packaging { resources.excludes += setOf("META-INF/DEPENDENCIES", "META-INF/versions/9/OSGI-INF/MANIFEST.MF") } + lint { abortOnError = true } + testBuildType = "preview" + testOptions { unitTests.isIncludeAndroidResources = true } + sourceSets["main"].assets.srcDir("build/generated/pebrelAssets") + sourceSets["main"].assets.srcDir("build/generated/nativeRelayAssets") + sourceSets["main"].res.srcDir("build/generated/pebrelResources") +} + +dependencies { + // CommonMark 0.30 uses Java collection factories; support the same parser on API 26+. + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5") + implementation("org.commonmark:commonmark:0.30.0") + implementation("org.commonmark:commonmark-ext-gfm-tables:0.30.0") + implementation("org.commonmark:commonmark-ext-gfm-strikethrough:0.30.0") + implementation("org.commonmark:commonmark-ext-task-list-items:0.30.0") + implementation(project(":ghostty")) + implementation(project(":ssh")) + implementation(project(":voice")) + implementation(platform("androidx.compose:compose-bom:2025.05.01")) + implementation("androidx.activity:activity-compose:1.10.1") + implementation("androidx.compose.material3:material3") + implementation("androidx.compose.material:material-icons-core") + implementation("androidx.lifecycle:lifecycle-runtime-compose:2.9.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1") + implementation("com.squareup.okhttp3:okhttp:4.12.0") + implementation("com.journeyapps:zxing-android-embedded:4.3.0") + testImplementation("junit:junit:4.13.2") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2") + testImplementation("org.robolectric:robolectric:4.14.1") + testImplementation("com.squareup.okhttp3:mockwebserver:4.12.0") + testImplementation("com.squareup.okhttp3:okhttp-tls:4.12.0") + testImplementation("androidx.compose.ui:ui-test-junit4") + debugImplementation("androidx.compose.ui:ui-test-manifest") + androidTestImplementation("androidx.test:runner:1.6.2") + androidTestImplementation("androidx.test.ext:junit:1.2.1") + androidTestImplementation("androidx.test:rules:1.6.1") + androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0") + // AndroidX Test references these annotations when its APK is shrunk by R8. + androidTestImplementation("com.google.errorprone:error_prone_annotations:2.36.0") +} + +val generateAssets by tasks.registering(Exec::class) { + val root = rootProject.projectDir.resolve("../..").canonicalFile + inputs.files(root.resolve("nebula_settings/src/lib.rs"), root.resolve("nebula_settings/src/themes.rs"), + root.resolve("nebula_app/src/display/ui/os_icons.rs"), root.resolve("assets/fonts/MapleMono-NF-CN-Regular.ttf"), + root.resolve("assets/fonts/JetBrainsMono-Regular.ttf"), root.resolve("extra/logo/nebula-titanium.png")) + inputs.dir(root.resolve("mobile/tools")) + inputs.files(fileTree(root.resolve("mobile/relay")) { include("*.mjs", "*.json", "*.md", "*.yaml", "Caddyfile", "Dockerfile") }) + inputs.file(root.resolve("mobile/protocol/bridge-policy.json")) + inputs.file(root.resolve("LICENSE")) + inputs.dir(root.resolve("mobile/android/third_party/licenses")) + inputs.files(root.resolve("mobile/android/third_party/THIRD-PARTY-NOTICES.md"), + root.resolve("mobile/android/ghostty/UPSTREAM.json")) + inputs.dir(root.resolve("mobile/android/ghostty/build/upstream/arm64-v8a/licenses")) + outputs.dir(layout.buildDirectory.dir("generated/pebrelAssets")) + outputs.dir(layout.buildDirectory.dir("generated/pebrelResources")) + val python = if (System.getProperty("os.name").startsWith("Windows")) "python" else "python3" + commandLine(python, root.resolve("mobile/tools/generate_assets.py"), + "--output", layout.buildDirectory.dir("generated/pebrelAssets").get().asFile, + "--resources", layout.buildDirectory.dir("generated/pebrelResources").get().asFile) +} +tasks.named("preBuild") { dependsOn(generateAssets) } diff --git a/mobile/android/app/preview-proguard-rules.pro b/mobile/android/app/preview-proguard-rules.pro new file mode 100644 index 0000000..5188312 --- /dev/null +++ b/mobile/android/app/preview-proguard-rules.pro @@ -0,0 +1,41 @@ +# AndroidJUnitRunner shares this dependency with the optimized target APK. +# Preserve its binary API so instrumentation can start without altering release rules. +-keep class androidx.tracing.** { *; } +# These APIs are called across the instrumentation/target APK boundary. +-keep class io.github.kuddev.pebrel.terminal.GhosttyCore { public *; } +-keep class io.github.kuddev.pebrel.terminal.TerminalFrame { public *; } +-keep class io.github.kuddev.pebrel.terminal.TerminalSession { public *; } +-keep class io.github.kuddev.pebrel.terminal.TerminalCallbacks { public *; } +-keep interface io.github.kuddev.pebrel.terminal.SessionTransport { *; } +-keep class io.github.kuddev.pebrel.terminal.LocalPtyTransport { public *; } +-keep class io.github.kuddev.pebrel.terminal.GhosttyView { public *; } + +# The test runner and optimized target share Kotlin runtime classes. R8 may +# otherwise remove or rename APIs used only by the separate instrumentation APK. +-keep class kotlin.** { *; } +# Resource IDs are accessed from the separate UI instrumentation APK. +-keep class io.github.kuddev.pebrel.mobile.R$string { public static ; } +# Real OpenSSH regression uses these app-owned APIs across the test APK boundary. +-keep class io.github.kuddev.pebrel.mobile.connection.HostProfile { public *; } +-keep class io.github.kuddev.pebrel.mobile.connection.SshConnection { public *; } +-keep class io.github.kuddev.pebrel.mobile.connection.SshTerminalTransport { public *; } +-keep class io.github.kuddev.pebrel.mobile.connection.SshFailure { public *; } +-keep enum io.github.kuddev.pebrel.mobile.connection.SshStage { *; } +-keep enum io.github.kuddev.pebrel.mobile.connection.SshFailureKind { *; } +# The SFTP instrumentation exercises suspend APIs from a separate APK. Preserve +# only its javap-verified public boundary; production release shrinking is unchanged. +-keep,includedescriptorclasses class io.github.kuddev.pebrel.mobile.connection.SftpClient { public *; } +-keep class io.github.kuddev.pebrel.mobile.connection.SftpClientKt { public *; } +-keep class io.github.kuddev.pebrel.mobile.connection.SftpEntry { public *; } +-keep class io.github.kuddev.pebrel.mobile.connection.SftpListing { public *; } +-keep class io.github.kuddev.pebrel.mobile.connection.SftpContent { public *; } +-keep,includedescriptorclasses class kotlinx.coroutines.BuildersKt { public *; } +-keep,includedescriptorclasses class kotlinx.coroutines.CompletableDeferredKt { public *; } +-keep,includedescriptorclasses class kotlinx.coroutines.DelayKt { public *; } +-keep,includedescriptorclasses class kotlinx.coroutines.Dispatchers { public *; } +-keep,includedescriptorclasses class kotlinx.coroutines.JobKt { public *; } +-keep,includedescriptorclasses class kotlinx.coroutines.TimeoutKt { public *; } +-keep interface kotlinx.coroutines.CompletableDeferred { *; } +-keep interface kotlinx.coroutines.Deferred { *; } +-keep interface kotlinx.coroutines.CoroutineScope { *; } +-keep interface kotlinx.coroutines.Job { *; } diff --git a/mobile/android/app/proguard-rules.pro b/mobile/android/app/proguard-rules.pro new file mode 100644 index 0000000..961ec11 --- /dev/null +++ b/mobile/android/app/proguard-rules.pro @@ -0,0 +1,2 @@ +-keepattributes Signature,InnerClasses,EnclosingMethod +# JNI exports and the typed native exception are retained by :ssh consumer rules. diff --git a/mobile/android/app/src/androidTest/java/io/github/kuddev/pebrel/mobile/GhosttyEngineTest.kt b/mobile/android/app/src/androidTest/java/io/github/kuddev/pebrel/mobile/GhosttyEngineTest.kt new file mode 100644 index 0000000..7ed524e --- /dev/null +++ b/mobile/android/app/src/androidTest/java/io/github/kuddev/pebrel/mobile/GhosttyEngineTest.kt @@ -0,0 +1,265 @@ +package io.github.kuddev.pebrel.mobile + +import android.view.KeyEvent +import android.content.Intent +import android.view.inputmethod.EditorInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import io.github.kuddev.pebrel.terminal.* +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.OutputStream +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +/** Exercises the actual JNI library in the optimized APK, not a mocked parser. */ +@RunWith(AndroidJUnit4::class) +class GhosttyEngineTest { + @Test fun utf8ChunksWideCellsAndCombiningCharacters() = GhosttyCore(24, 6).use { core -> + val bytes = "A中文 e\u0301 😀".toByteArray() + bytes.forEach { core.feed(byteArrayOf(it)) } + val frame = core.snapshot() + assertTrue(frame.text().startsWith("A中文 e\u0301 😀")) + val cells = requireNotNull(frame.rows[0]).cells + assertEquals(2, cells[1 * 6 + 2]) + assertEquals(0, cells[2 * 6 + 2]) + } + + @Test fun trueColorInverseAndDirtyRows() = GhosttyCore(20, 6).use { core -> + core.feed("\u001b[38;2;10;20;30mX\u001b[0m\r\nsecond".toByteArray()) + val first = core.snapshot() + assertEquals(0xff0a141e.toInt(), requireNotNull(first.rows[0]).cells[3]) + val stable = core.snapshot() + assertSame(first.rows[0], stable.rows[0]) + core.feed("!".toByteArray()) + val changed = core.snapshot() + assertTrue(changed.text().contains("second!")) + assertEquals(first.rows[0]?.text, changed.rows[0]?.text) + } + + @Test fun alternateScreenResizeAndRestore() = GhosttyCore(20, 6).use { core -> + core.feed("primary\u001b[?1049h\u001b[Hfull-screen".toByteArray()) + assertTrue(core.snapshot().text().contains("full-screen")) + core.resize(30, 8, 10, 20) + core.feed("\u001b[?1049l".toByteArray()) + val frame = core.snapshot() + assertEquals(30, frame.columns) + assertEquals(8, frame.rows.size) + assertTrue(frame.text().contains("primary")) + assertFalse(frame.text().contains("full-screen")) + } + + @Test fun terminalQueriesAndTitleReachHost() = GhosttyCore(20, 6).use { core -> + val reply = core.feed("abc\u001b[6n\u001b]2;测试标题\u0007".toByteArray()).toString(Charsets.UTF_8) + assertEquals("\u001b[1;4R", reply) + assertEquals("测试标题", core.takeTitle()) + assertNull(core.takeTitle()) + } + + @Test fun keyEncodingTracksApplicationModeAndPasteCannotEscape() = GhosttyCore().use { core -> + assertEquals("\u001b[A", core.key(KeyEvent.KEYCODE_DPAD_UP, 0, 1).toString(Charsets.UTF_8)) + core.feed("\u001b[?1h\u001b[?2004h".toByteArray()) + assertEquals("\u001bOA", core.key(KeyEvent.KEYCODE_DPAD_UP, 0, 1).toString(Charsets.UTF_8)) + assertEquals("\u0003", core.key(KeyEvent.KEYCODE_C, 2, 1, "c", 99).toString(Charsets.UTF_8)) + assertEquals("\u001b[200~中[201~文\u001b[201~", core.paste("中\u001b[201~文").toString(Charsets.UTF_8)) + } + + @Test fun scrollbackAndNativeLifetime() { + repeat(20) { + val core = GhosttyCore(20, 4) + core.feed((0..30).joinToString("\r\n") { "line-$it" }.toByteArray()) + assertTrue(core.snapshot().text().contains("line-30")) + core.scroll(-20) + assertFalse(core.snapshot().text().contains("line-30")) + core.scroll(Int.MAX_VALUE) + assertTrue(core.snapshot().text().contains("line-30")) + core.close() + core.close() + assertTrue(runCatching { core.snapshot() }.isFailure) + } + } + + @Test fun localPtyRunsCommandThroughGhostty() { + val target = InstrumentationRegistry.getInstrumentation().targetContext + val ready = CountDownLatch(1) + val session = TerminalSession(LocalPtyTransport(target.filesDir.absolutePath), object : TerminalCallbacks() { + override fun onTransportReady(session: TerminalSession) { ready.countDown() } + }) + try { + session.start() + session.setVisible(true) + assertTrue(ready.await(10, TimeUnit.SECONDS)) + // octal prevents the marker appearing solely because shell input was echoed. + assertTrue(session.sendText("printf '\\107\\110\\117\\123\\124\\124\\131_OK\\n'\r")) + await { session.frame?.text()?.contains("GHOSTTY_OK") == true } + assertNull(session.failure) + } finally { session.finishIfRunning() } + } + + @Test fun terminalBackgroundCannotEraseSiblingChrome() { + val instrumentation = InstrumentationRegistry.getInstrumentation() + val target = instrumentation.targetContext + val ready = CountDownLatch(1) + val terminal = TerminalSession(LocalPtyTransport(target.filesDir.absolutePath), object : TerminalCallbacks() { + override fun onTransportReady(session: TerminalSession) { ready.countDown() } + }) + try { + terminal.start() + terminal.setVisible(true) + assertTrue(ready.await(8, TimeUnit.SECONDS)) + await { terminal.frame != null } + instrumentation.runOnMainSync { + val bitmap = android.graphics.Bitmap.createBitmap(240, 240, android.graphics.Bitmap.Config.ARGB_8888) + bitmap.eraseColor(android.graphics.Color.MAGENTA) + val canvas = android.graphics.Canvas(bitmap) + val view = GhosttyView(target) + view.layout(0, 0, 180, 100) + view.session = terminal + canvas.translate(20f, 80f) + view.draw(canvas) + assertEquals(android.graphics.Color.MAGENTA, bitmap.getPixel(100, 20)) + assertEquals(android.graphics.Color.MAGENTA, bitmap.getPixel(100, 210)) + assertNotEquals(android.graphics.Color.MAGENTA, bitmap.getPixel(100, 130)) + view.session = null + bitmap.recycle() + } + } finally { terminal.finishIfRunning() } + } + + @Test fun rejectedOutputEndsSessionAndRejectsFutureInput() { + val ready = CountDownLatch(1) + val finished = CountDownLatch(1) + val exits = AtomicInteger() + val transport = BlockingTransport(rejectOutput = true) + val session = TerminalSession(transport, object : TerminalCallbacks() { + override fun onTransportReady(session: TerminalSession) { ready.countDown() } + override fun onSessionFinished(session: TerminalSession) { exits.incrementAndGet(); finished.countDown() } + }) + try { + session.start() + assertTrue(ready.await(8, TimeUnit.SECONDS)) + assertTrue(session.sendText("not-retried")) + assertTrue(finished.await(8, TimeUnit.SECONDS)) + assertFalse(session.sendText("later")) + assertEquals(1, exits.get()) + assertNotNull(session.failure) + } finally { session.finishIfRunning() } + } + + @Test fun boundedInputAndCloseRejectFurtherWrites() { + val ready = CountDownLatch(1) + val transport = BlockingTransport(rejectOutput = false) + val session = TerminalSession(transport, object : TerminalCallbacks() { + override fun onTransportReady(session: TerminalSession) { ready.countDown() } + }) + try { + assertFalse(session.sendText("before-connect")) + session.start() + assertTrue(ready.await(8, TimeUnit.SECONDS)) + val block = ByteArray(32768) + assertTrue(session.tryWrite(block, 0, block.size)) + assertTrue(transport.writing.await(8, TimeUnit.SECONDS)) + var rejected = false + repeat(8) { if (!session.tryWrite(block, 0, block.size)) rejected = true } + assertTrue(rejected) + session.finishIfRunning() + assertFalse(session.sendText("closed")) + assertTrue(transport.closed.await(8, TimeUnit.SECONDS)) + } finally { session.finishIfRunning() } + } + + @Test fun imePreeditIsLocalAndDetachedInputCannotReachSession() { + val instrumentation = InstrumentationRegistry.getInstrumentation() + val target = instrumentation.targetContext + val ready = CountDownLatch(1) + val closed = CountDownLatch(1) + val output = ByteArrayOutputStream() + val transport = object : SessionTransport { + override fun open(columns: Int, rows: Int, cellWidth: Int, cellHeight: Int) {} + override fun resize(columns: Int, rows: Int, cellWidth: Int, cellHeight: Int) {} + override fun input() = object : InputStream() { override fun read(): Int { closed.await(); return -1 } } + override fun output(): OutputStream = output + override fun awaitExit() = 0 + override fun close() { closed.countDown() } + } + val session = TerminalSession(transport, object : TerminalCallbacks() { + override fun onTransportReady(session: TerminalSession) { ready.countDown() } + }) + val activity = instrumentation.startActivitySync(Intent(target, MainActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) + try { + session.start() + assertTrue(ready.await(8, TimeUnit.SECONDS)) + lateinit var view: GhosttyView + lateinit var connection: android.view.inputmethod.InputConnection + instrumentation.runOnMainSync { + view = GhosttyView(activity).apply { this.session = session; directInput = true } + activity.setContentView(view) + } + instrumentation.waitForIdleSync() + instrumentation.runOnMainSync { + connection = requireNotNull(view.onCreateInputConnection(EditorInfo())) + assertTrue(connection.setComposingText("zhong", 1)) + assertTrue(connection.setComposingText("中文", 1)) + assertEquals(0, output.size()) + assertTrue(connection.commitText("中文", 1)) + } + await { output.toString("UTF-8") == "中文" } + instrumentation.runOnMainSync { + activity.setContentView(android.view.View(activity)) + assertFalse(connection.commitText("must-not-send", 1)) + } + assertEquals("中文", output.toString("UTF-8")) + } finally { + instrumentation.runOnMainSync { activity.finish() } + session.finishIfRunning() + } + } + + @Test fun closingDuringConnectDoesNotPublishLateReady() { + val opening = CountDownLatch(1) + val closed = CountDownLatch(1) + val ready = AtomicInteger() + val transport = object : SessionTransport { + override fun open(columns: Int, rows: Int, cellWidth: Int, cellHeight: Int) { opening.countDown(); closed.await() } + override fun resize(columns: Int, rows: Int, cellWidth: Int, cellHeight: Int) {} + override fun input(): InputStream = error("Closed connect must not read") + override fun output(): OutputStream = error("Closed connect must not write") + override fun awaitExit() = 0 + override fun close() { closed.countDown() } + } + val session = TerminalSession(transport, object : TerminalCallbacks() { + override fun onTransportReady(session: TerminalSession) { ready.incrementAndGet() } + }) + session.start() + assertTrue(opening.await(8, TimeUnit.SECONDS)) + session.finishIfRunning() + assertTrue(closed.await(8, TimeUnit.SECONDS)) + InstrumentationRegistry.getInstrumentation().runOnMainSync { assertEquals(0, ready.get()) } + assertFalse(session.sendText("late")) + } + + private fun await(condition: () -> Boolean) { + val deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(8) + while (!condition() && System.nanoTime() < deadline) Thread.sleep(25) + assertTrue(condition()) + } + + private class BlockingTransport(private val rejectOutput: Boolean) : SessionTransport { + val closed = CountDownLatch(1) + val writing = CountDownLatch(1) + override fun open(columns: Int, rows: Int, cellWidth: Int, cellHeight: Int) {} + override fun resize(columns: Int, rows: Int, cellWidth: Int, cellHeight: Int) {} + override fun input() = object : InputStream() { override fun read(): Int { closed.await(); return -1 } } + override fun output() = object : OutputStream() { + override fun write(value: Int) { writing.countDown(); if (rejectOutput) throw IOException("rejected"); closed.await() } + override fun write(bytes: ByteArray, offset: Int, length: Int) = write(0) + } + override fun awaitExit() = 0 + override fun close() { closed.countDown() } + } +} diff --git a/mobile/android/app/src/androidTest/java/io/github/kuddev/pebrel/mobile/PrototypeVisualTest.kt b/mobile/android/app/src/androidTest/java/io/github/kuddev/pebrel/mobile/PrototypeVisualTest.kt new file mode 100644 index 0000000..d96914d --- /dev/null +++ b/mobile/android/app/src/androidTest/java/io/github/kuddev/pebrel/mobile/PrototypeVisualTest.kt @@ -0,0 +1,182 @@ +package io.github.kuddev.pebrel.mobile + +import android.content.Intent +import android.os.Environment +import android.os.ParcelFileDescriptor +import android.os.SystemClock +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiObject2 +import androidx.test.uiautomator.Until +import java.io.File +import org.junit.Test +import org.junit.runner.RunWith + +/** Real preview APK interaction, without seeding demo hosts or terminal output. */ +@RunWith(AndroidJUnit4::class) +class PrototypeVisualTest { + private val instrumentation = InstrumentationRegistry.getInstrumentation() + private val target = instrumentation.targetContext + private val device = UiDevice.getInstance(instrumentation) + private val waitMs = 8_000L + + // CI clears the target before instrumentation starts. Calling pm clear from + // inside the test kills the process hosting the instrumentation itself. + @Test + fun prototypePagesAndDraftIsolation() { + val launch = requireNotNull(target.packageManager.getLaunchIntentForPackage(target.packageName)) + target.startActivity(launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)) + check(device.wait(Until.hasObject(By.pkg(target.packageName)), waitMs)) + try { + waitLabel(R.string.sessions) + waitLabel(R.string.ssh_hosts) + waitLabel(R.string.computers) + capture("01-home-empty") + + waitLabel(R.string.add_ssh).click() + check(!scrollTo(target.getString(R.string.save)).isEnabled) + capture("02-host-form") + device.pressBack() + waitLabel(R.string.sessions) + waitLabel(R.string.add_ssh).click() + val fields = edits(4) + check(kotlin.math.abs(fields[1].visibleBounds.top - fields[2].visibleBounds.top) < 8) { + "Host address and port must share the connection row" + } + fields[0].text = "Visual Test Host" + fields[1].text = "192.0.2.10" + fields[2].text = "22" + fields[3].text = "tester" + // The prototype places Save after the form, so scroll it into view. + scrollTo(target.getString(R.string.save)).click() + waitLabel("Visual Test Host") + check(!hasLabel(target.getString(R.string.password))) + capture("03-home-saved-host") + + waitLabel(R.string.settings).click() + waitLabel(R.string.theme_settings) + capture("04-settings") + waitLabel(R.string.theme_settings).click() + scrollTo("Nord").click() + capture("05-theme-nord") + waitLabel(R.string.back).click() + waitLabel(R.string.back).click() + waitLabel(R.string.sessions) + capture("06-home-nord") + + scrollTo(target.getString(R.string.local_terminal)).click() + waitLabel(R.string.local_compose).click() + val input = edits(1).last() + input.click() + input.text = "git" + waitLabel("git status").click() + check(edits(1).last().text == "git status") + capture("07-terminal-candidate") + edits(1).last().text = "printf '\\033[32mGhostty 中文 😀 é\\033[0m\\n'" + waitLabel(R.string.send).click() + val sendDeadline = SystemClock.uptimeMillis() + waitMs + while (edits(1).last().text.isNotEmpty() && SystemClock.uptimeMillis() < sendDeadline) SystemClock.sleep(100) + check(edits(1).last().text.isEmpty()) + // Record actual ANSI/CJK/emoji rendering as well as the interface chrome. + SystemClock.sleep(500) + capture("07b-terminal-output") + edits(1).last().text = "git status" + // Back button in header, rather than the IME Back key. + waitLabel(R.string.back).click() + waitLabel(R.string.sessions) + capture("08-session-thumbnail") + + scrollTo(target.getString(R.string.local_terminal)).click() + waitLabel(R.string.local_compose).click() + check(edits(1).last().text.isEmpty()) { "A new session inherited another session draft" } + edits(1).last().text = "second session draft" + waitLabel(R.string.long_editor).click() + check(edits(1).last().text == "second session draft") + capture("09-long-editor") + waitLabel(R.string.editor_done).click() + waitLabel(R.string.back).click() + + // 360 dp width, not 360 physical pixels at the default 420 dpi. + shell("wm size 720x1600") + shell("wm density 320") + waitLabel(R.string.sessions) + capture("10-narrow-home") + waitLabel(R.string.settings).click() + waitLabel(R.string.theme_settings) + capture("11-narrow-settings") + waitLabel(R.string.back).click() + waitLabel(R.string.add_ssh).click() + val sshFields = edits(4) + sshFields[0].text = "OpenSSH UI Test" + sshFields[1].text = "10.0.2.2" + sshFields[2].text = "2222" + sshFields[3].text = "pebreltest" + // The inline password is directly editable, not an authentication tile. + repeat(8) { + if (device.findObject(By.desc(target.getString(R.string.auth_password))) == null) { + device.swipe(device.displayWidth / 2, device.displayHeight * 3 / 4, + device.displayWidth / 2, device.displayHeight / 3, 25) + } + } + val password = requireNotNull(device.findObject(By.desc(target.getString(R.string.auth_password)))) + password.text = "pebrel-test-only" + capture("12-inline-password-pills") + scrollTo(target.getString(R.string.save_connect)).click() + waitLabel(R.string.trust_connect) + capture("13-ssh-fingerprint") + waitLabel(R.string.trust_connect).click() + waitLabel(R.string.toggle_keyboard) + capture("14-russh-terminal-frame") + } finally { + capture("last-state") + shell("wm size reset") + shell("wm density reset") + } + } + + private fun waitLabel(id: Int): UiObject2 = waitLabel(target.getString(id)) + private fun waitLabel(value: String): UiObject2 { + val deadline = SystemClock.uptimeMillis() + waitMs + while (SystemClock.uptimeMillis() < deadline) { + device.findObject(By.text(value))?.let { return it } + device.findObject(By.desc(value))?.let { return it } + SystemClock.sleep(100) + } + error("Missing text or content description: $value") + } + + private fun edits(count: Int): List { + val deadline = SystemClock.uptimeMillis() + waitMs + while (SystemClock.uptimeMillis() < deadline) { + val found = device.findObjects(By.clazz("android.widget.EditText")) + if (found.size >= count) return found + SystemClock.sleep(100) + } + error("Expected $count editable fields") + } + + private fun hasLabel(value: String) = device.hasObject(By.text(value)) || device.hasObject(By.desc(value)) + + private fun scrollTo(value: String): UiObject2 { + repeat(10) { + device.findObject(By.text(value))?.let { return it } + device.swipe(device.displayWidth / 2, device.displayHeight * 3 / 4, + device.displayWidth / 2, device.displayHeight / 3, 30) + device.waitForIdle() + } + return waitLabel(value) + } + + private fun capture(name: String) { + device.waitForIdle() + val root = requireNotNull(target.getExternalFilesDir(Environment.DIRECTORY_PICTURES)).resolve("visual") + check(root.exists() || root.mkdirs()) + check(device.takeScreenshot(File(root, "$name.png"))) + } + + private fun shell(command: String) { + ParcelFileDescriptor.AutoCloseInputStream(instrumentation.uiAutomation.executeShellCommand(command)).use { it.readBytes() } + } +} diff --git a/mobile/android/app/src/androidTest/java/io/github/kuddev/pebrel/mobile/SshIntegrationTest.kt b/mobile/android/app/src/androidTest/java/io/github/kuddev/pebrel/mobile/SshIntegrationTest.kt new file mode 100644 index 0000000..64364dd --- /dev/null +++ b/mobile/android/app/src/androidTest/java/io/github/kuddev/pebrel/mobile/SshIntegrationTest.kt @@ -0,0 +1,279 @@ +package io.github.kuddev.pebrel.mobile + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.terminal.* +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import java.util.Collections +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.Executors +import io.github.kuddev.pebrel.ssh.NativeSshException +import kotlinx.coroutines.* +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.security.MessageDigest + +/** The optimized APK talks to a real OpenSSH fixture on the emulator host. */ +@RunWith(AndroidJUnit4::class) +class SshIntegrationTest { + private val arguments get() = InstrumentationRegistry.getArguments() + private val host = HostProfile("ci-ssh", "CI OpenSSH", arguments.getString("sshHost") ?: "10.0.2.2", + arguments.getString("sshPort")?.toInt() ?: 2222, arguments.getString("sshUser") ?: "pebreltest") + private val expectedFingerprint get() = requireNotNull( + InstrumentationRegistry.getArguments().getString("sshFingerprint")) + + @Test fun tmuxReattachesTheSameRemoteShellAndKeepsSftpAvailable() = runBlocking { + persistentSession(SshSessionMode.TMUX) + } + + @Test fun herdrReattachesTheSameRemoteShellAndKeepsSftpAvailable() = runBlocking { + persistentSession(SshSessionMode.HERDR) + } + + private suspend fun persistentSession(mode: SshSessionMode) { + val name = "pebrel-qa-${System.nanoTime()}" + val target = host.copy(fingerprint = expectedFingerprint, sessionMode = mode, sessionName = name) + fun connection(profile: HostProfile = target) = SshConnection(profile, "pebrel-test-only".toCharArray(), + { _, _ -> error("The fixture fingerprint must already match") }) + suspend fun opened(): Pair { + val ready = CompletableDeferred() + val channel = connection() + val terminal = TerminalSession(SshTerminalTransport(channel), object : TerminalCallbacks() { + override fun onTransportReady(session: TerminalSession) { ready.complete(Unit) } + override fun onSessionFinished(session: TerminalSession) { ready.complete(Unit) } + }) + terminal.setVisible(true) + terminal.start() + try { + withTimeout(25_000) { ready.await() } + terminal.failureCause?.let { throw AssertionError("Persistent SSH failed", it) } + try { + withTimeout(15_000) { + while (terminal.frame?.cursorVisible != true || terminal.frame?.text().isNullOrBlank()) delay(50) + } + } catch (error: TimeoutCancellationException) { + throw AssertionError("${mode.id} initial frame: ${terminal.frame?.meta?.contentToString()}\n${terminal.frame?.text()?.take(2400)}", error) + } + return terminal to channel + } catch (error: Throwable) { terminal.finishIfRunning(); throw error } + } + suspend fun output(terminal: TerminalSession, pattern: Regex): String = withTimeout(12_000) { + while (true) { + terminal.failureCause?.let { throw AssertionError("Persistent session stopped", it) } + pattern.find(terminal.frame?.text().orEmpty())?.let { return@withTimeout it.value } + delay(50) + } + @Suppress("UNREACHABLE_CODE") "" + } + try { + val (first, ssh) = opened() + val pid = try { + // 标记首字符使用八进制,避免把命令回显误当成远端执行结果。 + assertTrue(first.sendText("PEBREL_QA_TOKEN=$name; export PEBREL_QA_TOKEN; printf '\\115UX_FIRST:%s:%s\\n' \"\$PEBREL_QA_TOKEN\" \"\$\$\"\r")) + val marker = output(first, Regex("MUX_FIRST:$name:[0-9]+")) + assertTrue(SftpClient(ssh::sftp).list(".").path.startsWith('/')) + marker.substringAfterLast(':') + } finally { first.finishIfRunning() } + val (second, _) = opened() + try { + assertTrue(second.sendText("printf '\\115UX_SECOND:%s:%s\\n' \"\$PEBREL_QA_TOKEN\" \"\$\$\"\r")) + assertEquals("MUX_SECOND:$name:$pid", output(second, Regex("MUX_SECOND:$name:[0-9]+"))) + } finally { second.finishIfRunning() } + } finally { + withContext(Dispatchers.IO + NonCancellable) { + connection(host.copy(fingerprint = expectedFingerprint)).use { cleanup -> + cleanup.connect() + cleanup.openExec(if (mode == SshSessionMode.TMUX) "tmux kill-session -t '$name'" else "herdr session stop '$name'") + cleanup.input().readBytes() + cleanup.awaitExit() + } + } + } + } + + @Test fun sftpRoundTripPagingAndCancellationKeepTheRealShellUsable() = runBlocking { + val ready = CountDownLatch(1) + val connection = SshConnection(host.copy(fingerprint = expectedFingerprint), "pebrel-test-only".toCharArray(), { _, _ -> error("Already trusted") }) + val terminal = TerminalSession(SshTerminalTransport(connection), object : TerminalCallbacks() { + override fun onTransportReady(session: TerminalSession) { ready.countDown() } + override fun onSessionFinished(session: TerminalSession) { ready.countDown() } + }) + val files = SftpClient(connection::sftp) + try { + terminal.setVisible(true); terminal.start() + assertTrue(ready.await(25, TimeUnit.SECONDS)) + terminal.failureCause?.let { throw AssertionError("SSH failed", it) } + val first = files.list(".") + val root = first.path + val entries = first.entries.toMutableList() + var cursor = first.cursor + while (cursor != null) { + val more = files.list(root, cursor) + assertTrue(more.entries.size <= 256) + entries += more.entries; cursor = more.cursor + } + val fixture = arguments.getString("sftpFixtureDirectory") + if (fixture != null) { + var page = files.list(sftpChild(root, fixture)) + var count = page.entries.size + assertNotNull("Fixture must exercise directory paging", page.cursor) + while (page.cursor != null) { page = files.list(page.path, page.cursor); count += page.entries.size } + assertTrue(count > 256) + } + val testDirectory = sftpChild(root, "pebrel-test-${System.nanoTime()}") + files.mkdir(testDirectory) + val readme = sftpChild(testDirectory, "README.md") + val text = "# SFTP\n\n中文与 emoji 🌌\n".toByteArray(Charsets.UTF_8) + files.upload(readme, ByteArrayInputStream(text), text.size.toLong()) + val sample = files.preview(readme) + assertTrue(sample.bytes.toString(Charsets.UTF_8).contains("SFTP")) + assertArrayEquals(text, sample.bytes) + val bytes = ByteArray(1024 * 1024 + 19) { (it % 251).toByte() } + val destination = sftpChild(testDirectory, "roundtrip.bin") + var terminalChecked = false + files.upload(destination, ByteArrayInputStream(bytes), bytes.size.toLong()) { count, _ -> + if (!terminalChecked && count >= 65536) { + assertTrue(terminal.sendText("printf '\\123FTP_PARALLEL_OK\\n'\r")) + val deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5) + while (terminal.frame?.rows?.any { it?.text?.contains("SFTP_PARALLEL_OK") == true } != true && System.nanoTime() < deadline) Thread.sleep(25) + assertTrue("SFTP held the shell output loop", terminal.frame?.rows?.any { it?.text?.contains("SFTP_PARALLEL_OK") == true } == true) + terminalChecked = true + } + } + assertTrue(terminalChecked) + val copy = ByteArrayOutputStream() + val uploaded = files.download(destination, copy) + assertArrayEquals(MessageDigest.getInstance("SHA-256").digest(bytes), MessageDigest.getInstance("SHA-256").digest(copy.toByteArray())) + assertEquals("SFTP_EXISTS", (runCatching { files.upload(destination, ByteArrayInputStream(byteArrayOf(1)), 1) }.exceptionOrNull() as NativeSshException).code) + val renamed = "$destination.renamed" + files.rename(uploaded, renamed) + files.remove(files.stat(renamed)) + val folder = sftpChild(testDirectory, "directory") + files.mkdir(folder) + files.upload(sftpChild(folder, "keep.txt"), ByteArrayInputStream(byteArrayOf(1)), 1) + assertTrue(runCatching { files.remove(files.stat(folder)) }.isFailure) + files.remove(files.stat(sftpChild(folder, "keep.txt"))) + files.remove(files.stat(folder)) + val started = CompletableDeferred() + val cancelPath = sftpChild(testDirectory, "cancel.bin") + val upload = launch(Dispatchers.IO) { + files.upload(cancelPath, ByteArrayInputStream(bytes), bytes.size.toLong()) { _, _ -> + started.complete(Unit) + Thread.sleep(50) + } + } + started.await(); upload.cancelAndJoin() + assertTrue(runCatching { files.stat(cancelPath) }.isFailure) + assertFalse(files.list(testDirectory).entries.any { it.name.startsWith(".pebrel-upload-") }) + assertTrue(terminal.sendText("printf '\\123FTP_AFTER_CANCEL_OK\\n'\r")) + withTimeout(5000) { while (terminal.frame?.rows?.any { it?.text?.contains("SFTP_AFTER_CANCEL_OK") == true } != true) delay(25) } + files.remove(files.stat(readme)) + files.remove(files.stat(testDirectory)) + } finally { terminal.finishIfRunning() } + } + + @Test fun realPasswordShellStreamsOutputThroughGhostty() { + val ready = CountDownLatch(1) + val finished = CountDownLatch(1) + val stages = Collections.synchronizedList(mutableListOf()) + val secret = "pebrel-test-only".toCharArray() + val connection = SshConnection(host, secret, { _, fingerprint -> fingerprint == expectedFingerprint }, { stages.add(it) }) + val terminal = TerminalSession(SshTerminalTransport(connection), object : TerminalCallbacks() { + override fun onTransportReady(session: TerminalSession) { ready.countDown() } + override fun onSessionFinished(session: TerminalSession) { finished.countDown(); ready.countDown() } + }) + try { + terminal.setVisible(true) + terminal.start() + assertTrue("SSH did not settle", ready.await(25, TimeUnit.SECONDS)) + terminal.failureCause?.let { throw AssertionError("Real OpenSSH connection failed", it) } + assertNull(terminal.failure) + assertEquals(listOf(SshStage.NETWORK, SshStage.VERIFYING, SshStage.AUTHENTICATING, SshStage.OPENING_SHELL), stages.toList()) + assertTrue(secret.all { it == '\u0000' }) + assertTrue(terminal.sendText("printf '\\123\\123\\110_GHOSTTY_OK\\n'\r")) + val deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(10) + while (terminal.frame?.rows?.any { it?.text?.contains("SSH_GHOSTTY_OK") == true } != true && System.nanoTime() < deadline) Thread.sleep(50) + assertTrue(terminal.frame?.rows?.any { it?.text?.contains("SSH_GHOSTTY_OK") == true } == true) + assertTrue(terminal.sendText("exit\r")) + assertTrue(finished.await(10, TimeUnit.SECONDS)) + assertFalse(terminal.sendText("must-not-send")) + } finally { terminal.finishIfRunning() } + } + + @Test fun wrongPasswordReportsAuthenticationAndWipesSecret() { + val secret = "wrong-test-password".toCharArray() + SshConnection(host.copy(fingerprint = expectedFingerprint), secret, { _, _ -> error("Already trusted") }).use { connection -> + val error = runCatching { connection.connect() }.exceptionOrNull() + assertTrue(error is SshFailure) + assertEquals(error?.stackTraceToString(), SshFailureKind.AUTH, (error as SshFailure).kind) + assertTrue(secret.all { it == '\u0000' }) + } + } + + @Test fun changedHostKeyNeverReachesAuthentication() { + val stages = Collections.synchronizedList(mutableListOf()) + SshConnection(host.copy(fingerprint = "SHA256:wrong-fixture-key"), "pebrel-test-only".toCharArray(), + { _, _ -> error("Changed identity must not prompt as new") }, { stages.add(it) }).use { connection -> + val error = runCatching { connection.connect() }.exceptionOrNull() + assertTrue(error is SshFailure) + assertEquals(error?.stackTraceToString(), SshFailureKind.HOST_KEY_CHANGED, (error as SshFailure).kind) + assertFalse(stages.contains(SshStage.AUTHENTICATING)) + } + } + + @Test fun declinedTrustNeverAuthenticates() { + val stages = Collections.synchronizedList(mutableListOf()) + SshConnection(host, "pebrel-test-only".toCharArray(), { _, _ -> false }, { stages.add(it) }).use { connection -> + val error = runCatching { connection.connect() }.exceptionOrNull() + assertTrue(error is SshFailure) + assertEquals(error?.stackTraceToString(), SshFailureKind.TRUST_REJECTED, (error as SshFailure).kind) + assertFalse(stages.contains(SshStage.AUTHENTICATING)) + } + } + + @Test fun execKeepsStderrOutOfRpcStdoutAndPreservesExitCode() { + SshConnection(host.copy(fingerprint = expectedFingerprint), "pebrel-test-only".toCharArray(), + { _, _ -> error("Already trusted") }).use { connection -> + connection.connect() + connection.openExec("printf 'rpc-output'; printf 'diagnostic' >&2; exit 7") + assertEquals("rpc-output", connection.input().readBytes().toString(Charsets.UTF_8)) + assertEquals("diagnostic", connection.input(stderr = true).readBytes().toString(Charsets.UTF_8)) + assertEquals(7, connection.awaitExit()) + } + } + + @Test fun closingPendingNativeReadUnblocksAndNextConnectionStillWorks() { + val executor = Executors.newSingleThreadExecutor() + try { + repeat(2) { + SshConnection(host.copy(fingerprint = expectedFingerprint), "pebrel-test-only".toCharArray(), + { _, _ -> error("Already trusted") }).use { connection -> + connection.connect() + connection.openExec("printf ready; sleep 60") + val input = connection.input() + val ready = ByteArray(5) + var read = 0 + while (read < ready.size) { + val count = input.read(ready, read, ready.size - read) + assertTrue(count > 0) + read += count + } + assertEquals("ready", ready.toString(Charsets.UTF_8)) + val reading = CountDownLatch(1) + val result = executor.submit { + reading.countDown() + runCatching { input.read() }.isFailure + } + assertTrue(reading.await(2, TimeUnit.SECONDS)) + connection.close() + assertTrue(result.get(3, TimeUnit.SECONDS)) + assertTrue(runCatching { connection.output().write(1) }.isFailure) + } + } + } finally { executor.shutdownNow() } + } +} diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..df66bff --- /dev/null +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/app/src/main/assets/reader/extensions-vendor.json b/mobile/android/app/src/main/assets/reader/extensions-vendor.json new file mode 100644 index 0000000..b27b1f6 --- /dev/null +++ b/mobile/android/app/src/main/assets/reader/extensions-vendor.json @@ -0,0 +1,68 @@ +[ + { + "package": "katex", + "version": "0.18.9", + "source": "https://registry.npmjs.org/katex/-/katex-0.18.9.tgz", + "integrity": "sha512-8ad9RyoKsb/g8/yLFE+KAlP+DhbCTRUNi/V9XGsxn0R+trJJltNwzcDNo0q/DEkOy5fQUQTAQyCXCYSE+OakTQ==", + "license": "MIT", + "files": { + "katex/katex.min.css": "b9ce0e8ce93f0c18c4986fe1f1c3c269d921b56a69e6c97f83a507916b38aab5", + "katex/katex.min.js": "155f6c2d673c5912e3b48f45d8830eaad18ae1953915939ceb648ea8b9c3e7e2", + "katex/LICENSE": "766ccc1f306c885aa45542a9846bbd0a505b27a0374f146778171c2254ce18e3", + "katex/fonts/KaTeX_AMS-Regular.woff2": "0cdd387c9590a1a9f9794560022dbb59654a7d86f187aa0c81495ad42d3a7308", + "katex/fonts/KaTeX_Caligraphic-Bold.woff2": "de7701e42cf1f4cf0b766c03fb27977207eee2f4fd5d76fa82188406da43ea4c", + "katex/fonts/KaTeX_Caligraphic-Regular.woff2": "5d53e70ad607c2352162dec9e0923fb54ecdafaccbf604cd8dcf7d00facb989b", + "katex/fonts/KaTeX_Fraktur-Bold.woff2": "74444efd593c005e3f4573b44524704c0af0a937fe911cca9e94068d0d140d3f", + "katex/fonts/KaTeX_Fraktur-Regular.woff2": "51814d270d06ff0255dba0799994fa4d8c84d11f09951d47595f4abb1f3602dc", + "katex/fonts/KaTeX_Main-Bold.woff2": "0f60d1b897938ec918c8ce073092411baf9438f6739465693ff18b0f9d20b021", + "katex/fonts/KaTeX_Main-BoldItalic.woff2": "99cd42a3c072d918f2f44984a807cf7aa16e13545fd0875fc07c6c65f99e715b", + "katex/fonts/KaTeX_Main-Italic.woff2": "97479ca6cce906abc961ecac96faa5f9ca2e61b8e7670d475826bcdee9a7c267", + "katex/fonts/KaTeX_Main-Regular.woff2": "c2342cd8b869e01752a9321dc17213fc40d4d04c79688c1d43f2cf316abd7866", + "katex/fonts/KaTeX_Math-BoldItalic.woff2": "dc47344dbb6cb5b655c8460d561f4df5f501b90c804ad3c6cec65fe322351ab1", + "katex/fonts/KaTeX_Math-Italic.woff2": "7af58c5ec8f132a2ddde9027c6d7814decce4d3b822a11192a42a20e2e973264", + "katex/fonts/KaTeX_SansSerif-Bold.woff2": "e99ae51144bf1232efcc1bfe5add36262c6866b0faab24fa75740e1b98577a62", + "katex/fonts/KaTeX_SansSerif-Italic.woff2": "00b26ac825e2095056396e0553b8ac26d3f8ad158c3826e28b4c45b385c4714a", + "katex/fonts/KaTeX_SansSerif-Regular.woff2": "68e8c73ef42afd3ccec58bf0fba302cce448938e7fc020a5e31f8a952eee1342", + "katex/fonts/KaTeX_Script-Regular.woff2": "036d4e95149b69ff9bcc0cd55771efeb25ffa3947293e69acd78d5ac328c684b", + "katex/fonts/KaTeX_Size1-Regular.woff2": "6b47c40166b6dbe21a5dfca7718413f2147fd2399be1ba605d8ad39cedf25dfe", + "katex/fonts/KaTeX_Size2-Regular.woff2": "d04c54219f9eaec6d4d4fd42dfb28785975a4794d6b2fc71e566b9cd6db842dd", + "katex/fonts/KaTeX_Size3-Regular.woff2": "73d591271b1604960cb10bb90fee021670af7297017e0e98480b332d11f51995", + "katex/fonts/KaTeX_Size4-Regular.woff2": "a4af7d414440a1c1790825cfb700cf9cf43b0f2c4b04f0ebc523011ad9853ec0", + "katex/fonts/KaTeX_Typewriter-Regular.woff2": "71d517d67827787cfabdf186914cc3358eda539e37931941f2b2fd4a21f68c0b" + } + }, + { + "package": "mermaid", + "version": "11.17.2", + "source": "https://registry.npmjs.org/mermaid/-/mermaid-11.17.2.tgz", + "integrity": "sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg==", + "license": "MIT", + "files": { + "mermaid/LICENSE": "ec9fb67dcb25eccc416ed56e1aab819222c805a2a4bfe4cb19e7556bf2ffde80", + "mermaid/mermaid.min.js": "ece61b09fae51a54ff47c6e53b0a6c1ac3e70bd3ed3087aef33db36d37df08ba" + }, + "transform": { + "esbuild": "0.28.2", + "target": "chrome91", + "supported": { + "class-static-blocks": false + }, + "upstream_sha256": "581ed7d74bd9048d0e3a91363927d72ef22942d7722546b27f7cc29e35390eb8" + } + }, + { + "package": "core-js", + "version": "3.50.0", + "license": "MIT", + "source": "https://registry.npmjs.org/core-js/-/core-js-3.50.0.tgz", + "modules": [ + "object.has-own", + "array.at", + "string.at", + "structured-clone" + ], + "files": { + "reader-compat.js": "ce41f2f55c4deb67c4d309399b4ccde6f390222032e9edf9602a0a2c44ffa42b" + } + } +] diff --git a/mobile/android/app/src/main/assets/reader/extensions.js b/mobile/android/app/src/main/assets/reader/extensions.js new file mode 100644 index 0000000..7542291 --- /dev/null +++ b/mobile/android/app/src/main/assets/reader/extensions.js @@ -0,0 +1,92 @@ +(function () { + var generation = 0; + var labels = { error: '', loading: '' }; + var diagrams = new Map(); + var cacheBytes = 0; + var nextDiagram = 0; + var queue = Promise.resolve(); + + window.pebrelSetReaderLabels = function (error, loading) { + labels = { error: error, loading: loading }; + document.querySelectorAll('[data-render-error]').forEach(function (node) { node.textContent = error; }); + document.querySelectorAll('[data-render-loading]').forEach(function (node) { node.textContent = loading; }); + }; + + function renderMath() { + document.querySelectorAll('.math-render:not([data-rendered])').forEach(function (node) { + node.dataset.rendered = 'true'; + var source = node.textContent; + try { + if (source.length > 32768) throw new Error('formula_size'); + katex.render(source, node, { displayMode: node.dataset.display === 'true', throwOnError: true, + trust: false, strict: 'warn', maxExpand: 1000, maxSize: 20, output: 'htmlAndMathml' }); + } catch (_) { + node.textContent = source; + node.classList.add('render-error'); + } + }); + } + + function renderDiagram(node, current) { + if (current !== generation || !node.isConnected) return Promise.resolve(); + var figure = node.closest('.diagram'); + var source = figure.querySelector('code').textContent; + var key = figure.id + ':' + source; + var cached = diagrams.get(key); + if (cached) { delete node.dataset.renderError; node.innerHTML = cached; return Promise.resolve(); } + delete node.dataset.renderError; + node.dataset.renderLoading = 'true'; + node.textContent = labels.loading; + return new Promise(function (resolve) { setTimeout(resolve, 0); }).then(function () { + if (current !== generation || !node.isConnected) return null; + if (source.length > 50000) throw new Error('diagram_size'); + return mermaid.render('pebrel-diagram-' + (++nextDiagram), source); + }).then(function (result) { + if (!result || current !== generation || !node.isConnected) return; + delete node.dataset.renderLoading; + // strict 模式清洗 SVG;不绑定图中的脚本或外链回调。 + node.innerHTML = result.svg; + node.querySelectorAll('a').forEach(function (link) { link.removeAttribute('href'); link.removeAttribute('xlink:href'); }); + var svg = node.innerHTML; + if (svg.length <= 512000) { + diagrams.set(key, svg); cacheBytes += key.length + svg.length; + while (diagrams.size > 24 || cacheBytes > 2 * 1024 * 1024) { + var first = diagrams.keys().next().value; + cacheBytes -= first.length + diagrams.get(first).length; + diagrams.delete(first); + } + } + }).catch(function () { + if (current !== generation || !node.isConnected) return; + delete node.dataset.renderLoading; + node.dataset.renderError = 'true'; + node.textContent = labels.error; + figure.querySelector('details').open = true; + }); + } + + window.pebrelEnhance = function () { + var current = ++generation; + renderMath(); + var style = getComputedStyle(document.documentElement); + if (window.mermaid) mermaid.initialize({ startOnLoad: false, securityLevel: 'strict', + suppressErrorRendering: true, maxTextSize: 50000, maxEdges: 500, + secure: ['securityLevel', 'startOnLoad', 'maxTextSize', 'maxEdges', 'suppressErrorRendering'], + theme: 'base', htmlLabels: false, flowchart: { htmlLabels: false }, + themeVariables: { background: style.getPropertyValue('--background').trim(), + primaryColor: style.getPropertyValue('--surface').trim(), + primaryTextColor: style.getPropertyValue('--foreground').trim(), + primaryBorderColor: style.getPropertyValue('--border').trim(), + lineColor: style.getPropertyValue('--muted').trim(), + secondaryColor: style.getPropertyValue('--surface').trim(), + tertiaryColor: style.getPropertyValue('--background').trim(), fontFamily: 'sans-serif' } }); + var nodes = Array.from(document.querySelectorAll('[data-diagram]')); + // Mermaid 内部有全局布局状态,串行渲染;换页后不让旧 Promise 改写新文档。 + queue = queue.catch(function () {}).then(function () { + return nodes.reduce(function (previous, node) { + return previous.then(function () { return renderDiagram(node, current); }); + }, Promise.resolve()); + }); + return queue; + }; +})(); diff --git a/mobile/android/app/src/main/assets/reader/highlight.min.js b/mobile/android/app/src/main/assets/reader/highlight.min.js new file mode 100644 index 0000000..6e1a09e --- /dev/null +++ b/mobile/android/app/src/main/assets/reader/highlight.min.js @@ -0,0 +1,1244 @@ +/*! + Highlight.js v11.11.1 (git: 08cb242e7d) + (c) 2006-2024 Josh Goebel and other contributors + License: BSD-3-Clause + */ +var hljs=function(){"use strict";function e(n){ +return n instanceof Map?n.clear=n.delete=n.set=()=>{ +throw Error("map is read-only")}:n instanceof Set&&(n.add=n.clear=n.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(n),Object.getOwnPropertyNames(n).forEach((t=>{ +const a=n[t],i=typeof a;"object"!==i&&"function"!==i||Object.isFrozen(a)||e(a) +})),n}class n{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function t(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function a(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n] +;return n.forEach((e=>{for(const n in e)t[n]=e[n]})),t}const i=e=>!!e.scope +;class r{constructor(e,n){ +this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){ +this.buffer+=t(e)}openNode(e){if(!i(e))return;const n=((e,{prefix:n})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const t=e.split(".") +;return[`${n}${t.shift()}`,...t.map(((e,n)=>`${e}${"_".repeat(n+1)}`))].join(" ") +}return`${n}${e}`})(e.scope,{prefix:this.classPrefix});this.span(n)} +closeNode(e){i(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const s=(e={})=>{const n={children:[]} +;return Object.assign(n,e),n};class o{constructor(){ +this.rootNode=s(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const n=s({scope:e}) +;this.add(n),this.stack.push(n)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){ +return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n), +n.children.forEach((n=>this._walk(e,n))),e.closeNode(n)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +o._collapse(e)})))}}class l extends o{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,n){const t=e.root +;n&&(t.scope="language:"+n),this.add(t)}toHTML(){ +return new r(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function c(e){ +return e?"string"==typeof e?e:e.source:null}function d(e){return b("(?=",e,")")} +function g(e){return b("(?:",e,")*")}function u(e){return b("(?:",e,")?")} +function b(...e){return e.map((e=>c(e))).join("")}function m(...e){const n=(e=>{ +const n=e[e.length-1] +;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} +})(e);return"("+(n.capture?"":"?:")+e.map((e=>c(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const _=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function h(e,{joinWith:n}){let t=0;return e.map((e=>{t+=1;const n=t +;let a=c(e),i="";for(;a.length>0;){const e=_.exec(a);if(!e){i+=a;break} +i+=a.substring(0,e.index), +a=a.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+(Number(e[1])+n):(i+=e[0], +"("===e[0]&&t++)}return i})).map((e=>`(${e})`)).join(n)} +const f="[a-zA-Z]\\w*",E="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",w="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",v="\\b(0b[01]+)",N={ +begin:"\\\\[\\s\\S]",relevance:0},k={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[N]},x={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[N]},O=(e,n,t={})=>{const i=a({scope:"comment",begin:e,end:n, +contains:[]},t);i.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const r=m("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return i.contains.push({begin:b(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i +},M=O("//","$"),A=O("/\\*","\\*/"),S=O("#","$");var C=Object.freeze({ +__proto__:null,APOS_STRING_MODE:k,BACKSLASH_ESCAPE:N,BINARY_NUMBER_MODE:{ +scope:"number",begin:v,relevance:0},BINARY_NUMBER_RE:v,COMMENT:O, +C_BLOCK_COMMENT_MODE:A,C_LINE_COMMENT_MODE:M,C_NUMBER_MODE:{scope:"number", +begin:w,relevance:0},C_NUMBER_RE:w,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}}),HASH_COMMENT_MODE:S,IDENT_RE:f, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+E,relevance:0}, +NUMBER_MODE:{scope:"number",begin:y,relevance:0},NUMBER_RE:y, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:x,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[N,{begin:/\[/,end:/\]/,relevance:0,contains:[N]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const n=/^#![ ]*\// +;return e.binary&&(e.begin=b(n,/.*\b/,e.binary,/\b.*/)),a({scope:"meta",begin:n, +end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:f,relevance:0},UNDERSCORE_IDENT_RE:E, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:E,relevance:0}});function T(e,n){ +"."===e.input[e.index-1]&&n.ignoreMatch()}function R(e,n){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function D(e,n){ +n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=T,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function I(e,n){ +Array.isArray(e.illegal)&&(e.illegal=m(...e.illegal))}function L(e,n){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function B(e,n){ +void 0===e.relevance&&(e.relevance=1)}const $=(e,n)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const t=Object.assign({},e);Object.keys(e).forEach((n=>{delete e[n] +})),e.keywords=t.keywords,e.begin=b(t.beforeMatch,d(t.begin)),e.starts={ +relevance:0,contains:[Object.assign(t,{endsParent:!0})] +},e.relevance=0,delete t.beforeMatch +},F=["of","and","for","in","not","or","if","then","parent","list","value"] +;function z(e,n,t="keyword"){const a=Object.create(null) +;return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach((t=>{ +Object.assign(a,z(e[t],n,t))})),a;function i(e,t){ +n&&(t=t.map((e=>e.toLowerCase()))),t.forEach((n=>{const t=n.split("|") +;a[t[0]]=[e,j(t[0],t[1])]}))}}function j(e,n){ +return n?Number(n):(e=>F.includes(e.toLowerCase()))(e)?0:1}const U={},P=e=>{ +console.error(e)},K=(e,...n)=>{console.log("WARN: "+e,...n)},q=(e,n)=>{ +U[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),U[`${e}/${n}`]=!0) +},H=Error();function G(e,n,{key:t}){let a=0;const i=e[t],r={},s={} +;for(let e=1;e<=n.length;e++)s[e+a]=i[e],r[e+a]=!0,a+=p(n[e-1]) +;e[t]=s,e[t]._emit=r,e[t]._multi=!0}function Z(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw P("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +H +;if("object"!=typeof e.beginScope||null===e.beginScope)throw P("beginScope must be object"), +H;G(e,e.begin,{key:"beginScope"}),e.begin=h(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw P("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +H +;if("object"!=typeof e.endScope||null===e.endScope)throw P("endScope must be object"), +H;G(e,e.end,{key:"endScope"}),e.end=h(e.end,{joinWith:""})}})(e)}function W(e){ +function n(n,t){ +return RegExp(c(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":"")) +}class t{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,n){ +n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(h(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const n=this.matcherRe.exec(e);if(!n)return null +;const t=n.findIndex(((e,n)=>n>0&&void 0!==e)),a=this.matchIndexes[t] +;return n.splice(0,t),Object.assign(n,a)}}class i{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t +;return this.rules.slice(e).forEach((([e,t])=>n.addRule(e,t))), +n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){ +this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){ +const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex +;let t=n.exec(e) +;if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{ +const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)} +return t&&(this.regexIndex+=t.position+1, +this.regexIndex===this.count&&this.considerAll()),t}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=a(e.classNameAliases||{}),function t(r,s){const o=r +;if(r.isCompiled)return o +;[R,L,Z,$].forEach((e=>e(r,s))),e.compilerExtensions.forEach((e=>e(r,s))), +r.__beforeBegin=null,[D,I,B].forEach((e=>e(r,s))),r.isCompiled=!0;let l=null +;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), +l=r.keywords.$pattern, +delete r.keywords.$pattern),l=l||/\w+/,r.keywords&&(r.keywords=z(r.keywords,e.case_insensitive)), +o.keywordPatternRe=n(l,!0), +s&&(r.begin||(r.begin=/\B|\b/),o.beginRe=n(o.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), +r.end&&(o.endRe=n(o.end)), +o.terminatorEnd=c(o.end)||"",r.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(r.end?"|":"")+s.terminatorEnd)), +r.illegal&&(o.illegalRe=n(r.illegal)), +r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((n=>a(e,{ +variants:null},n)))),e.cachedVariants?e.cachedVariants:Q(e)?a(e,{ +starts:e.starts?a(e.starts):null +}):Object.isFrozen(e)?a(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{t(e,o) +})),r.starts&&t(r.starts,s),o.matcher=(e=>{const n=new i +;return e.contains.forEach((e=>n.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n})(o),o}(e)}function Q(e){ +return!!e&&(e.endsWithParent||Q(e.starts))}class X extends Error{ +constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}} +const V=t,J=a,Y=Symbol("nomatch"),ee=t=>{ +const a=Object.create(null),i=Object.create(null),r=[];let s=!0 +;const o="Could not find the language '{}', did you forget to load/include a language module?",c={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:l};function _(e){ +return p.noHighlightRe.test(e)}function h(e,n,t){let a="",i="" +;"object"==typeof n?(a=e, +t=n.ignoreIllegals,i=n.language):(q("10.7.0","highlight(lang, code, ...args) has been deprecated."), +q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +i=e,a=n),void 0===t&&(t=!0);const r={code:a,language:i};O("before:highlight",r) +;const s=r.result?r.result:f(r.language,r.code,t) +;return s.code=r.code,O("after:highlight",s),s}function f(e,t,i,r){ +const l=Object.create(null);function c(){if(!O.keywords)return void A.addText(S) +;let e=0;O.keywordPatternRe.lastIndex=0;let n=O.keywordPatternRe.exec(S),t="" +;for(;n;){t+=S.substring(e,n.index) +;const i=v.case_insensitive?n[0].toLowerCase():n[0],r=(a=i,O.keywords[a]);if(r){ +const[e,a]=r +;if(A.addText(t),t="",l[i]=(l[i]||0)+1,l[i]<=7&&(C+=a),e.startsWith("_"))t+=n[0];else{ +const t=v.classNameAliases[e]||e;g(n[0],t)}}else t+=n[0] +;e=O.keywordPatternRe.lastIndex,n=O.keywordPatternRe.exec(S)}var a +;t+=S.substring(e),A.addText(t)}function d(){null!=O.subLanguage?(()=>{ +if(""===S)return;let e=null;if("string"==typeof O.subLanguage){ +if(!a[O.subLanguage])return void A.addText(S) +;e=f(O.subLanguage,S,!0,M[O.subLanguage]),M[O.subLanguage]=e._top +}else e=E(S,O.subLanguage.length?O.subLanguage:null) +;O.relevance>0&&(C+=e.relevance),A.__addSublanguage(e._emitter,e.language) +})():c(),S=""}function g(e,n){ +""!==e&&(A.startScope(n),A.addText(e),A.endScope())}function u(e,n){let t=1 +;const a=n.length-1;for(;t<=a;){if(!e._emit[t]){t++;continue} +const a=v.classNameAliases[e[t]]||e[t],i=n[t];a?g(i,a):(S=i,c(),S=""),t++}} +function b(e,n){ +return e.scope&&"string"==typeof e.scope&&A.openNode(v.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(g(S,v.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +S=""):e.beginScope._multi&&(u(e.beginScope,n),S="")),O=Object.create(e,{parent:{ +value:O}}),O}function m(e,t,a){let i=((e,n)=>{const t=e&&e.exec(n) +;return t&&0===t.index})(e.endRe,a);if(i){if(e["on:end"]){const a=new n(e) +;e["on:end"](t,a),a.isMatchIgnored&&(i=!1)}if(i){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return m(e.parent,t,a)}function _(e){ +return 0===O.matcher.regexIndex?(S+=e[0],1):(D=!0,0)}function h(e){ +const n=e[0],a=t.substring(e.index),i=m(O,e,a);if(!i)return Y;const r=O +;O.endScope&&O.endScope._wrap?(d(), +g(n,O.endScope._wrap)):O.endScope&&O.endScope._multi?(d(), +u(O.endScope,e)):r.skip?S+=n:(r.returnEnd||r.excludeEnd||(S+=n), +d(),r.excludeEnd&&(S=n));do{ +O.scope&&A.closeNode(),O.skip||O.subLanguage||(C+=O.relevance),O=O.parent +}while(O!==i.parent);return i.starts&&b(i.starts,e),r.returnEnd?0:n.length} +let y={};function w(a,r){const o=r&&r[0];if(S+=a,null==o)return d(),0 +;if("begin"===y.type&&"end"===r.type&&y.index===r.index&&""===o){ +if(S+=t.slice(r.index,r.index+1),!s){const n=Error(`0 width match regex (${e})`) +;throw n.languageName=e,n.badRule=y.rule,n}return 1} +if(y=r,"begin"===r.type)return(e=>{ +const t=e[0],a=e.rule,i=new n(a),r=[a.__beforeBegin,a["on:begin"]] +;for(const n of r)if(n&&(n(e,i),i.isMatchIgnored))return _(t) +;return a.skip?S+=t:(a.excludeBegin&&(S+=t), +d(),a.returnBegin||a.excludeBegin||(S=t)),b(a,e),a.returnBegin?0:t.length})(r) +;if("illegal"===r.type&&!i){ +const e=Error('Illegal lexeme "'+o+'" for mode "'+(O.scope||"")+'"') +;throw e.mode=O,e}if("end"===r.type){const e=h(r);if(e!==Y)return e} +if("illegal"===r.type&&""===o)return S+="\n",1 +;if(R>1e5&&R>3*r.index)throw Error("potential infinite loop, way more iterations than matches") +;return S+=o,o.length}const v=N(e) +;if(!v)throw P(o.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const k=W(v);let x="",O=r||k;const M={},A=new p.__emitter(p);(()=>{const e=[] +;for(let n=O;n!==v;n=n.parent)n.scope&&e.unshift(n.scope) +;e.forEach((e=>A.openNode(e)))})();let S="",C=0,T=0,R=0,D=!1;try{ +if(v.__emitTokens)v.__emitTokens(t,A);else{for(O.matcher.considerAll();;){ +R++,D?D=!1:O.matcher.considerAll(),O.matcher.lastIndex=T +;const e=O.matcher.exec(t);if(!e)break;const n=w(t.substring(T,e.index),e) +;T=e.index+n}w(t.substring(T))}return A.finalize(),x=A.toHTML(),{language:e, +value:x,relevance:C,illegal:!1,_emitter:A,_top:O}}catch(n){ +if(n.message&&n.message.includes("Illegal"))return{language:e,value:V(t), +illegal:!0,relevance:0,_illegalBy:{message:n.message,index:T, +context:t.slice(T-100,T+100),mode:n.mode,resultSoFar:x},_emitter:A};if(s)return{ +language:e,value:V(t),illegal:!1,relevance:0,errorRaised:n,_emitter:A,_top:O} +;throw n}}function E(e,n){n=n||p.languages||Object.keys(a);const t=(e=>{ +const n={value:V(e),illegal:!1,relevance:0,_top:c,_emitter:new p.__emitter(p)} +;return n._emitter.addText(e),n})(e),i=n.filter(N).filter(x).map((n=>f(n,e,!1))) +;i.unshift(t);const r=i.sort(((e,n)=>{ +if(e.relevance!==n.relevance)return n.relevance-e.relevance +;if(e.language&&n.language){if(N(e.language).supersetOf===n.language)return 1 +;if(N(n.language).supersetOf===e.language)return-1}return 0})),[s,o]=r,l=s +;return l.secondBest=o,l}function y(e){let n=null;const t=(e=>{ +let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"" +;const t=p.languageDetectRe.exec(n);if(t){const n=N(t[1]) +;return n||(K(o.replace("{}",t[1])), +K("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"} +return n.split(/\s+/).find((e=>_(e)||N(e)))})(e);if(_(t))return +;if(O("before:highlightElement",{el:e,language:t +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new X("One of your code blocks includes unescaped HTML.",e.innerHTML) +;n=e;const a=n.textContent,r=t?h(a,{language:t,ignoreIllegals:!0}):E(a) +;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,n,t)=>{const a=n&&i[n]||t +;e.classList.add("hljs"),e.classList.add("language-"+a) +})(e,t,r.language),e.result={language:r.language,re:r.relevance, +relevance:r.relevance},r.secondBest&&(e.secondBest={ +language:r.secondBest.language,relevance:r.secondBest.relevance +}),O("after:highlightElement",{el:e,result:r,text:a})}let w=!1;function v(){ +if("loading"===document.readyState)return w||window.addEventListener("DOMContentLoaded",(()=>{ +v()}),!1),void(w=!0);document.querySelectorAll(p.cssSelector).forEach(y)} +function N(e){return e=(e||"").toLowerCase(),a[e]||a[i[e]]} +function k(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +i[e.toLowerCase()]=n}))}function x(e){const n=N(e) +;return n&&!n.disableAutodetect}function O(e,n){const t=e;r.forEach((e=>{ +e[t]&&e[t](n)}))}Object.assign(t,{highlight:h,highlightAuto:E,highlightAll:v, +highlightElement:y, +highlightBlock:e=>(q("10.7.0","highlightBlock will be removed entirely in v12.0"), +q("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{p=J(p,e)}, +initHighlighting:()=>{ +v(),q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +v(),q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,n)=>{let i=null;try{i=n(t)}catch(n){ +if(P("Language definition for '{}' could not be registered.".replace("{}",e)), +!s)throw n;P(n),i=c} +i.name||(i.name=e),a[e]=i,i.rawDefinition=n.bind(null,t),i.aliases&&k(i.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete a[e] +;for(const n of Object.keys(i))i[n]===e&&delete i[n]}, +listLanguages:()=>Object.keys(a),getLanguage:N,registerAliases:k, +autoDetection:x,inherit:J,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{ +e["before:highlightBlock"](Object.assign({block:n.el},n)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{ +e["after:highlightBlock"](Object.assign({block:n.el},n))})})(e),r.push(e)}, +removePlugin:e=>{const n=r.indexOf(e);-1!==n&&r.splice(n,1)}}),t.debugMode=()=>{ +s=!1},t.safeMode=()=>{s=!0},t.versionString="11.11.1",t.regex={concat:b, +lookahead:d,either:m,optional:u,anyNumberOfTimes:g} +;for(const n in C)"object"==typeof C[n]&&e(C[n]);return Object.assign(t,C),t +},ne=ee({});ne.newInstance=()=>ee({});const te=e=>({IMPORTANT:{scope:"meta", +begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{ +scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/}, +FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/}, +ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} +}),ae=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","optgroup","option","p","picture","q","quote","samp","section","select","source","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video","defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],ie=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),re=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),se=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),oe=["accent-color","align-content","align-items","align-self","alignment-baseline","all","anchor-name","animation","animation-composition","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-range","animation-range-end","animation-range-start","animation-timeline","animation-timing-function","appearance","aspect-ratio","backdrop-filter","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-position-x","background-position-y","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-end-end-radius","border-end-start-radius","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-start-end-radius","border-start-start-radius","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-align","box-decoration-break","box-direction","box-flex","box-flex-group","box-lines","box-ordinal-group","box-orient","box-pack","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","color-scheme","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","contain-intrinsic-block-size","contain-intrinsic-height","contain-intrinsic-inline-size","contain-intrinsic-size","contain-intrinsic-width","container","container-name","container-type","content","content-visibility","counter-increment","counter-reset","counter-set","cue","cue-after","cue-before","cursor","cx","cy","direction","display","dominant-baseline","empty-cells","enable-background","field-sizing","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flood-color","flood-opacity","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-optical-sizing","font-palette","font-size","font-size-adjust","font-smooth","font-smoothing","font-stretch","font-style","font-synthesis","font-synthesis-position","font-synthesis-small-caps","font-synthesis-style","font-synthesis-weight","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-emoji","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","forced-color-adjust","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphenate-character","hyphenate-limit-chars","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","initial-letter","initial-letter-align","inline-size","inset","inset-area","inset-block","inset-block-end","inset-block-start","inset-inline","inset-inline-end","inset-inline-start","isolation","justify-content","justify-items","justify-self","kerning","left","letter-spacing","lighting-color","line-break","line-height","line-height-step","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","margin-trim","marker","marker-end","marker-mid","marker-start","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","masonry-auto-flow","math-depth","math-shift","math-style","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","offset","offset-anchor","offset-distance","offset-path","offset-position","offset-rotate","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-anchor","overflow-block","overflow-clip-margin","overflow-inline","overflow-wrap","overflow-x","overflow-y","overlay","overscroll-behavior","overscroll-behavior-block","overscroll-behavior-inline","overscroll-behavior-x","overscroll-behavior-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","paint-order","pause","pause-after","pause-before","perspective","perspective-origin","place-content","place-items","place-self","pointer-events","position","position-anchor","position-visibility","print-color-adjust","quotes","r","resize","rest","rest-after","rest-before","right","rotate","row-gap","ruby-align","ruby-position","scale","scroll-behavior","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scroll-timeline","scroll-timeline-axis","scroll-timeline-name","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","speak","speak-as","src","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","tab-size","table-layout","text-align","text-align-all","text-align-last","text-anchor","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-skip-ink","text-decoration-style","text-decoration-thickness","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-size-adjust","text-transform","text-underline-offset","text-underline-position","text-wrap","text-wrap-mode","text-wrap-style","timeline-scope","top","touch-action","transform","transform-box","transform-origin","transform-style","transition","transition-behavior","transition-delay","transition-duration","transition-property","transition-timing-function","translate","unicode-bidi","user-modify","user-select","vector-effect","vertical-align","view-timeline","view-timeline-axis","view-timeline-inset","view-timeline-name","view-transition-name","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","white-space-collapse","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index","zoom"].sort().reverse(),le=re.concat(se).sort().reverse() +;var ce="[0-9](_*[0-9])*",de=`\\.(${ce})`,ge="[0-9a-fA-F](_*[0-9a-fA-F])*",ue={ +className:"number",variants:[{ +begin:`(\\b(${ce})((${de})|\\.)?|(${de}))[eE][+-]?(${ce})[fFdD]?\\b`},{ +begin:`\\b(${ce})((${de})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ +begin:`(${de})[fFdD]?\\b`},{begin:`\\b(${ce})[fFdD]\\b`},{ +begin:`\\b0[xX]((${ge})\\.?|(${ge})?\\.(${ge}))[pP][+-]?(${ce})[fFdD]?\\b`},{ +begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${ge})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};function be(e,n,t){return-1===t?"":e.replace(n,(a=>be(e,n,t-1)))} +const me="[A-Za-z$_][0-9A-Za-z$_]*",pe=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends","using"],_e=["true","false","null","undefined","NaN","Infinity"],he=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],fe=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],Ee=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],ye=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],we=[].concat(Ee,he,fe) +;function ve(e){const n=e.regex,t=me,a={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const t=e[0].length+e.index,a=e.input[t] +;if("<"===a||","===a)return void n.ignoreMatch();let i +;">"===a&&(((e,{after:n})=>{const t="e+"\\s*\\(")), +n.concat("(?!",N.join("|"),")")),t,n.lookahead(/\s*\(/)), +className:"title.function",relevance:0};var N;const k={ +begin:n.concat(/\./,n.lookahead(n.concat(t,/(?![0-9A-Za-z$_(])/))),end:t, +excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},x={ +match:[/get|set/,/\s+/,t,/(?=\()/],className:{1:"keyword",3:"title.function"}, +contains:[{begin:/\(\)/},f] +},O="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+e.UNDERSCORE_IDENT_RE+")\\s*=>",M={ +match:[/const|var|let/,/\s+/,t,/\s*/,/=\s*/,/(async\s*)?/,n.lookahead(O)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[f]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{ +PARAMS_CONTAINS:h,CLASS_REFERENCE:y},illegal:/#(?![$_A-z])/, +contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,d,g,u,b,m,{match:/\$\d+/},l,y,{ +scope:"attr",match:t+n.lookahead(":"),relevance:0},M,{ +begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[m,e.REGEXP_MODE,{ +className:"function",begin:O,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/, +excludeBegin:!0,excludeEnd:!0,keywords:i,contains:h}]}]},{begin:/,/,relevance:0 +},{match:/\s+/,relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:a.begin, +"on:begin":a.isTrulyOpeningTag,end:a.end}],subLanguage:"xml",contains:[{ +begin:a.begin,end:a.end,skip:!0,contains:["self"]}]}]},w,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[f,e.inherit(e.TITLE_MODE,{begin:t, +className:"title.function"})]},{match:/\.\.\./,relevance:0},k,{match:"\\$"+t, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[f]},v,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},E,x,{match:/\$[(.]/}]}} +const Ne=e=>b(/\b/,e,/\w$/.test(e)?/\b/:/\B/),ke=["Protocol","Type"].map(Ne),xe=["init","self"].map(Ne),Oe=["Any","Self"],Me=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","borrowing","break","case","catch","class","consume","consuming","continue","convenience","copy","default","defer","deinit","didSet","distributed","do","dynamic","each","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","macro","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","package","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Ae=["false","nil","true"],Se=["assignment","associativity","higherThan","left","lowerThan","none","right"],Ce=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],Te=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],Re=m(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),De=m(Re,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),Ie=b(Re,De,"*"),Le=m(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),Be=m(Le,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),$e=b(Le,Be,"*"),Fe=b(/[A-Z]/,Be,"*"),ze=["attached","autoclosure",b(/convention\(/,m("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",b(/objc\(/,$e,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],je=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] +;var Ue=Object.freeze({__proto__:null,grmr_bash:e=>{const n=e.regex,t={},a={ +begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]} +;Object.assign(t,{className:"variable",variants:[{ +begin:n.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const i={ +className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE] +},r=e.inherit(e.COMMENT(),{match:[/(^|\s)/,/#.*$/],scope:{2:"comment"}}),s={ +begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/, +end:/(\w+)/,className:"string"})]}},o={className:"string",begin:/"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,t,i]};i.contains.push(o);const l={begin:/\$?\(\(/, +end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t] +},c=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 +}),d={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, +contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ +name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/, +keyword:["if","then","else","elif","fi","time","for","while","until","in","do","done","case","esac","coproc","function","select"], +literal:["true","false"], +built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","sudo","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"] +},contains:[c,e.SHEBANG(),d,l,r,s,{match:/(\/[a-z._-]+)+/},o,{match:/\\"/},{ +className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}},grmr_c:e=>{ +const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ +match:/\batomic_[a-z]{3,6}\b/}]},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{match:/\b(0b[01']+)/},{ +match:/(-?)\b([\d']+(\.[\d']*)?|\.[\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)/ +},{ +match:/(-?)\b(0[xX][a-fA-F0-9]+(?:'[a-fA-F0-9]+)*(?:\.[a-fA-F0-9]*(?:'[a-fA-F0-9]*)*)?(?:[pP][-+]?[0-9]+)?(l|L)?(u|U)?)/ +},{match:/(-?)\b\d+(?:'\d+)*(?:\.\d*(?:'\d*)*)?(?:[eE][-+]?\d+)?/}],relevance:0 +},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef elifdef elifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","typeof","typeof_unqual","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], +type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_BitInt","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal96","_Decimal128","_Decimal64x","_Decimal128x","_Float16","_Float32","_Float64","_Float128","_Float32x","_Float64x","_Float128x","const","static","constexpr","complex","bool","imaginary"], +literal:"true false NULL", +built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" +},b=[c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],m={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:b.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:b.concat(["self"]),relevance:0}]),relevance:0},p={ +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})], +relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/, +keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/, +end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s] +}]},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"=]/,contains:[{ +beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c, +strings:o,keywords:u}}},grmr_cpp:e=>{const n=e.regex,t=e.COMMENT("//","$",{ +contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="(?!struct)("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",begin:"\\b[a-z\\d_]*_t\\b"},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{ +begin:"[+-]?(?:(?:[0-9](?:'?[0-9])*\\.(?:[0-9](?:'?[0-9])*)?|\\.[0-9](?:'?[0-9])*)(?:[Ee][+-]?[0-9](?:'?[0-9])*)?|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*|0[Xx](?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?|\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)[Pp][+-]?[0-9](?:'?[0-9])*)(?:[Ff](?:16|32|64|128)?|(BF|bf)16|[Ll]|)" +},{ +begin:"[+-]?\\b(?:0[Bb][01](?:'?[01])*|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*|0(?:'?[0-7])*|[1-9](?:'?[0-9])*)(?:[Uu](?:LL?|ll?)|[Uu][Zz]?|(?:LL?|ll?)[Uu]?|[Zz][Uu]|)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"], +keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"], +literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"], +_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","flat_map","flat_set","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"] +},b={className:"function.dispatch",relevance:0,keywords:{ +_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"] +}, +begin:n.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,n.lookahead(/(<[^<>]+>|)\s*\(/)) +},m=[b,c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],p={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:m.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:m.concat(["self"]),relevance:0}]),relevance:0},_={className:"function", +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[d],relevance:0},{begin:/::/,relevance:0},{ +begin:/:/,endsWithParent:!0,contains:[o,l]},{relevance:0,match:/,/},{ +className:"params",begin:/\(/,end:/\)/,keywords:u,relevance:0, +contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/,end:/\)/,keywords:u, +relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s]}] +},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C++", +aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:u,illegal:"",keywords:u,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:u},{ +match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/], +className:{1:"keyword",3:"title.class"}}])}},grmr_csharp:e=>{const n={ +keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","args","async","await","by","descending","dynamic","equals","file","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","record","remove","required","scoped","select","set","unmanaged","value|0","var","when","where","with","yield"]), +built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"], +literal:["default","false","null","true"]},t=e.inherit(e.TITLE_MODE,{ +begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{ +begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},i={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] +},r=e.inherit(i,{illegal:/\n/}),s={className:"subst",begin:/\{/,end:/\}/, +keywords:n},o=e.inherit(s,{illegal:/\n/}),l={className:"string",begin:/\$"/, +end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ +},e.BACKSLASH_ESCAPE,o]},c={className:"string",begin:/\$@"/,end:'"',contains:[{ +begin:/\{\{/},{begin:/\}\}/},{begin:'""'},s]},d=e.inherit(c,{illegal:/\n/, +contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},o]}) +;s.contains=[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE], +o.contains=[d,l,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{ +illegal:/\n/})];const g={variants:[{className:"string", +begin:/"""("*)(?!")(.|\n)*?"""\1/,relevance:1 +},c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},u={begin:"<",end:">", +contains:[{beginKeywords:"in out"},t] +},b=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",m={ +begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], +keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, +contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ +begin:"\x3c!--|--\x3e"},{begin:""}]}] +}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", +end:"$",keywords:{ +keyword:"if else elif endif define undef warning error line region endregion pragma checksum" +}},g,a,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, +illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" +},t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", +relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", +begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ +className:"string",begin:/"/,end:/"/}]},{ +beginKeywords:"new return throw await else",relevance:0},{className:"function", +begin:"("+b+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ +beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", +relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +contains:[e.TITLE_MODE,u],relevance:0},{match:/\(\)/},{className:"params", +begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, +contains:[g,a,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},m]}},grmr_css:e=>{ +const n=e.regex,t=te(e),a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{ +name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{ +keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"}, +contains:[t.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/ +},t.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0 +},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+re.join("|")+")"},{begin:":(:)?("+se.join("|")+")"}] +},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+oe.join("|")+")\\b"},{ +begin:/:/,end:/[;}{]/, +contains:[t.BLOCK_COMMENT,t.HEXCOLOR,t.IMPORTANT,t.CSS_NUMBER_MODE,...a,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[...a,{className:"string",begin:/[^)]/,endsWithParent:!0, +excludeEnd:!0}]},t.FUNCTION_DISPATCH]},{begin:n.lookahead(/@/),end:"[{;]", +relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ +},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ +$pattern:/[a-z-]+/,keyword:"and or not only",attribute:ie.join(" ")},contains:[{ +begin:/[a-z-]+(?=:)/,className:"attribute"},...a,t.CSS_NUMBER_MODE]}]},{ +className:"selector-tag",begin:"\\b("+ae.join("|")+")\\b"}]}},grmr_diff:e=>{ +const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{ +className:"meta",relevance:10, +match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) +},{className:"comment",variants:[{ +begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), +end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]}},grmr_go:e=>{const n={ +keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"], +type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], +literal:["true","false","iota","nil"], +built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] +};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{const n=e.regex +;return{name:"GraphQL",aliases:["gql"],case_insensitive:!0,disableAutodetect:!1, +keywords:{ +keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"], +literal:["true","false","null"]}, +contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ +scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation", +begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/, +end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{ +scope:"symbol",begin:n.concat(/[_A-Za-z][_0-9A-Za-z]*/,n.lookahead(/\s*:/)), +relevance:0}],illegal:[/[;<']/,/BEGIN/]}},grmr_ini:e=>{const n=e.regex,t={ +className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{ +begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/, +end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{ +begin:/\$\{(.*?)\}/}]},r={className:"literal", +begin:/\bon|off|true|false|yes|no\b/},s={className:"string", +contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{ +begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}] +},o={begin:/\[/,end:/\]/,contains:[a,r,i,s,t,"self"],relevance:0 +},l=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{ +name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, +contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{ +begin:n.concat(l,"(\\s*\\.\\s*",l,")*",n.lookahead(/\s*=\s*[^#\s]/)), +className:"attr",starts:{end:/$/,contains:[a,o,r,i,s,t]}}]}},grmr_java:e=>{ +const n=e.regex,t="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",a=t+be("(?:<"+t+"~~~(?:\\s*,\\s*"+t+"~~~)*>)?",/~~~/g,2),i={ +keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits","goto","when"], +literal:["false","true","null"], +type:["char","boolean","long","float","int","byte","short","double"], +built_in:["super","this"]},r={className:"meta",begin:"@"+t,contains:[{ +begin:/\(/,end:/\)/,contains:["self"]}]},s={className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} +;return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, +relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ +begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, +className:"string",contains:[e.BACKSLASH_ESCAPE] +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ +match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,t],className:{ +1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ +begin:[n.concat(/(?!else)/,t),/\s+/,t,/\s+/,/=(?!=)/],className:{1:"type", +3:"variable",5:"operator"}},{begin:[/record/,/\s+/,t],className:{1:"keyword", +3:"title.class"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"new throw return else",relevance:0},{ +begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ +2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0, +contains:[r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,ue,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},ue,r]}},grmr_javascript:ve, +grmr_json:e=>{const n=["true","false","null"],t={scope:"literal", +beginKeywords:n.join(" ")};return{name:"JSON",aliases:["jsonc"],keywords:{ +literal:n},contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/, +relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,t,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}},grmr_kotlin:e=>{const n={ +keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", +built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", +literal:"true false null"},t={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" +},a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},i={ +className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", +variants:[{begin:'"""',end:'"""(?=[^"])',contains:[i,a]},{begin:"'",end:"'", +illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, +contains:[e.BACKSLASH_ESCAPE,i,a]}]};a.contains.push(r);const s={ +className:"meta", +begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" +},o={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, +end:/\)/,contains:[e.inherit(r,{className:"string"}),"self"]}] +},l=ue,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={ +variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, +contains:[]}]},g=d;return g.variants[1].contains=[d],d.variants[1].contains=[g], +{name:"Kotlin",aliases:["kt","kts"],keywords:n, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", +begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword", +begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", +begin:/@\w+/}]}},t,s,o,{className:"function",beginKeywords:"fun",end:"[(]|$", +returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ +begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, +keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, +endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, +endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0 +},e.C_LINE_COMMENT_MODE,c,s,o,r,e.C_NUMBER_MODE]},c]},{ +begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{ +3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, +illegal:"extends implements",contains:[{ +beginKeywords:"public protected internal private constructor" +},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, +excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/, +excludeBegin:!0,returnEnd:!0},s,o]},r,{className:"meta",begin:"^#!/usr/bin/env", +end:"$",illegal:"\n"},l]}},grmr_less:e=>{ +const n=te(e),t=le,a="[\\w-]+",i="("+a+"|@\\{"+a+"\\})",r=[],s=[],o=e=>({ +className:"string",begin:"~?"+e+".*?"+e}),l=(e,n,t)=>({className:e,begin:n, +relevance:t}),c={$pattern:/[a-z-]+/,keyword:"and or not only", +attribute:ie.join(" ")},d={begin:"\\(",end:"\\)",contains:s,keywords:c, +relevance:0} +;s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,o("'"),o('"'),n.CSS_NUMBER_MODE,{ +begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", +excludeEnd:!0} +},n.HEXCOLOR,d,l("variable","@@?"+a,10),l("variable","@\\{"+a+"\\}"),l("built_in","~?`[^`]*?`"),{ +className:"attribute",begin:a+"\\s*:",end:":",returnBegin:!0,excludeEnd:!0 +},n.IMPORTANT,{beginKeywords:"and not"},n.FUNCTION_DISPATCH);const g=s.concat({ +begin:/\{/,end:/\}/,contains:r}),u={beginKeywords:"when",endsWithParent:!0, +contains:[{beginKeywords:"and not"}].concat(s)},b={begin:i+"\\s*:", +returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/ +},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+oe.join("|")+")\\b", +end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}] +},m={className:"keyword", +begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", +starts:{end:"[;{}]",keywords:c,returnEnd:!0,contains:s,relevance:0}},p={ +className:"variable",variants:[{begin:"@"+a+"\\s*:",relevance:15},{begin:"@"+a +}],starts:{end:"[;}]",returnEnd:!0,contains:g}},_={variants:[{ +begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:i,end:/\{/}],returnBegin:!0, +returnEnd:!0,illegal:"[<='$\"]",relevance:0, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,u,l("keyword","all\\b"),l("variable","@\\{"+a+"\\}"),{ +begin:"\\b("+ae.join("|")+")\\b",className:"selector-tag" +},n.CSS_NUMBER_MODE,l("selector-tag",i,0),l("selector-id","#"+i),l("selector-class","\\."+i,0),l("selector-tag","&",0),n.ATTRIBUTE_SELECTOR_MODE,{ +className:"selector-pseudo",begin:":("+re.join("|")+")"},{ +className:"selector-pseudo",begin:":(:)?("+se.join("|")+")"},{begin:/\(/, +end:/\)/,relevance:0,contains:g},{begin:"!important"},n.FUNCTION_DISPATCH]},h={ +begin:a+":(:)?"+`(${t.join("|")})`,returnBegin:!0,contains:[_]} +;return r.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,m,p,h,b,_,u,n.FUNCTION_DISPATCH), +{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:r}}, +grmr_lua:e=>{const n="\\[=*\\[",t="\\]=*\\]",a={begin:n,end:t,contains:["self"] +},i=[e.COMMENT("--(?!"+n+")","$"),e.COMMENT("--"+n,t,{contains:[a],relevance:10 +})];return{name:"Lua",aliases:["pluto"],keywords:{ +$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:i}].concat(i) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:n,end:t,contains:[a],relevance:5}])}},grmr_makefile:e=>{const n={ +className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{ +const n={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},t={ +variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},a={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},i={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},r=e.inherit(a,{contains:[] +}),s=e.inherit(i,{contains:[]});a.contains.push(s),i.contains.push(r) +;let o=[n,t];return[a,i,r,s].forEach((e=>{e.contains=e.contains.concat(o) +})),o=o.concat(a,i),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:o}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},a,i,{className:"quote",begin:"^>\\s+",contains:o, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},t,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},{scope:"literal", +match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}}, +grmr_objectivec:e=>{const n=/[a-zA-Z@][a-zA-Z0-9_]*/,t={$pattern:n, +keyword:["@interface","@class","@protocol","@implementation"]};return{ +name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], +keywords:{"variable.language":["this","super"],$pattern:n, +keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"], +literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"], +built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"], +type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"] +},illegal:"/,end:/$/,illegal:"\\n" +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class", +begin:"("+t.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:t, +contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE, +relevance:0}]}},grmr_perl:e=>{const n=e.regex,t=/[dualxmsipngr]{0,12}/,a={ +$pattern:/[\w.]+/, +keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot class close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl field fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map method mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0" +},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},r={begin:/->\{/, +end:/\}/},s={scope:"attr",match:/\s+:\s*\w+(\s*\(.*?\))?/},o={scope:"variable", +variants:[{begin:/\$\d/},{ +begin:n.concat(/[$%@](?!")(\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") +},{begin:/[$%@](?!")[^\s\w{=]|\$=/,relevance:0}],contains:[s]},l={ +className:"number",variants:[{match:/0?\.[0-9][0-9_]+\b/},{ +match:/\bv?(0|[1-9][0-9_]*(\.[0-9_]+)?|[1-9][0-9_]*)\b/},{ +match:/\b0[0-7][0-7_]*\b/},{match:/\b0x[0-9a-fA-F][0-9a-fA-F_]*\b/},{ +match:/\b0b[0-1][0-1_]*\b/}],relevance:0 +},c=[e.BACKSLASH_ESCAPE,i,o],d=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],g=(e,a,i="\\1")=>{ +const r="\\1"===i?i:n.concat(i,a) +;return n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,r,/(?:\\.|[^\\\/])*?/,i,t) +},u=(e,a,i)=>n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,i,t),b=[o,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{ +endsWithParent:!0}),r,{className:"string",contains:c,variants:[{ +begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", +end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ +begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", +relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", +contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{ +begin:"-?\\w+\\s*=>",relevance:0}]},l,{ +begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", +keywords:"split return print reverse grep",relevance:0, +contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{ +begin:g("s|tr|y",n.either(...d,{capture:!0}))},{begin:g("s|tr|y","\\(","\\)")},{ +begin:g("s|tr|y","\\[","\\]")},{begin:g("s|tr|y","\\{","\\}")}],relevance:2},{ +className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{ +begin:u("(?:m|qr)?",/\//,/\//)},{begin:u("m|qr",n.either(...d,{capture:!0 +}),/\1/)},{begin:u("m|qr",/\(/,/\)/)},{begin:u("m|qr",/\[/,/\]/)},{ +begin:u("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub method", +end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE,s] +},{className:"class",beginKeywords:"class",end:"[;{]",excludeEnd:!0,relevance:5, +contains:[e.TITLE_MODE,s,l]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$", +end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$", +className:"comment"}]}];return i.contains=b,r.contains=b,{name:"Perl", +aliases:["pl","pm"],keywords:a,contains:b}},grmr_php:e=>{ +const n=e.regex,t=/(?![A-Za-z0-9])(?![$])/,a=n.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,t),i=n.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,t),r=n.concat(/[A-Z]+/,t),s={ +scope:"variable",match:"\\$+"+a},o={scope:"subst",variants:[{begin:/\$\w+/},{ +begin:/\{\$/,end:/\}/}]},l=e.inherit(e.APOS_STRING_MODE,{illegal:null +}),c="[ \t\n]",d={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{ +illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(o)}),l,{ +begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/, +contains:e.QUOTE_STRING_MODE.contains.concat(o),"on:begin":(e,n)=>{ +n.data._beginMatch=e[1]||e[2]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}},e.END_SAME_AS_BEGIN({ +begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/})]},g={scope:"number",variants:[{ +begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{ +begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{ +begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?" +}],relevance:0 +},u=["false","null","true"],b=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],m=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],p={ +keyword:b,literal:(e=>{const n=[];return e.forEach((e=>{ +n.push(e),e.toLowerCase()===e?n.push(e.toUpperCase()):n.push(e.toLowerCase()) +})),n})(u),built_in:m},_=e=>e.map((e=>e.replace(/\|\d+$/,""))),h={variants:[{ +match:[/new/,n.concat(c,"+"),n.concat("(?!",_(m).join("\\b|"),"\\b)"),i],scope:{ +1:"keyword",4:"title.class"}}]},f=n.concat(a,"\\b(?!\\()"),E={variants:[{ +match:[n.concat(/::/,n.lookahead(/(?!class\b)/)),f],scope:{2:"variable.constant" +}},{match:[/::/,/class/],scope:{2:"variable.language"}},{ +match:[i,n.concat(/::/,n.lookahead(/(?!class\b)/)),f],scope:{1:"title.class", +3:"variable.constant"}},{match:[i,n.concat("::",n.lookahead(/(?!class\b)/))], +scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class", +3:"variable.language"}}]},y={scope:"attr", +match:n.concat(a,n.lookahead(":"),n.lookahead(/(?!::)/))},w={relevance:0, +begin:/\(/,end:/\)/,keywords:p,contains:[y,s,E,e.C_BLOCK_COMMENT_MODE,d,g,h] +},v={relevance:0, +match:[/\b/,n.concat("(?!fn\\b|function\\b|",_(b).join("\\b|"),"|",_(m).join("\\b|"),"\\b)"),a,n.concat(c,"*"),n.lookahead(/(?=\()/)], +scope:{3:"title.function.invoke"},contains:[w]};w.contains.push(v) +;const N=[y,E,e.C_BLOCK_COMMENT_MODE,d,g,h],k={ +begin:n.concat(/#\[\s*\\?/,n.either(i,r)),beginScope:"meta",end:/]/, +endScope:"meta",keywords:{literal:u,keyword:["new","array"]},contains:[{ +begin:/\[/,end:/]/,keywords:{literal:u,keyword:["new","array"]}, +contains:["self",...N]},...N,{scope:"meta",variants:[{match:i},{match:r}]}]} +;return{case_insensitive:!1,keywords:p, +contains:[k,e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{ +contains:[{scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/, +keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE, +contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{ +begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{ +begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},s,v,E,{ +match:[/const/,/\s/,a],scope:{1:"keyword",3:"variable.constant"}},h,{ +scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/, +excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use" +},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params", +begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:p, +contains:["self",k,s,E,e.C_BLOCK_COMMENT_MODE,d,g]}]},{scope:"class",variants:[{ +beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait", +illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{ +beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ +beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/, +contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{ +beginKeywords:"use",relevance:0,end:";",contains:[{ +match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},d,g]} +},grmr_php_template:e=>({name:"PHP template",subLanguage:"xml",contains:[{ +begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*", +end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0 +},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null, +skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null, +contains:null,skip:!0})]}]}),grmr_plaintext:e=>({name:"Plain text", +aliases:["text","txt"],disableAutodetect:!0}),grmr_python:e=>{ +const n=e.regex,t=/[\p{XID_Start}_]\p{XID_Continue}*/u,a=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={ +$pattern:/[A-Za-z]\w+|__\w+__/,keyword:a, +built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], +literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], +type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] +},r={className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/, +end:/\}/,keywords:i,illegal:/#/},o={begin:/\{\{/,relevance:0},l={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, +end:/"""/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([uU]|[rR])'/,end:/'/, +relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ +begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, +end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, +contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},c="[0-9](_?[0-9])*",d=`(\\b(${c}))?\\.(${c})|\\b(${c})\\.`,g="\\b|"+a.join("|"),u={ +className:"number",relevance:0,variants:[{ +begin:`(\\b(${c})|(${d}))[eE][+-]?(${c})[jJ]?(?=${g})`},{begin:`(${d})[jJ]?`},{ +begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${g})`},{ +begin:`\\b0[bB](_?[01])+[lL]?(?=${g})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${g})` +},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${g})`},{begin:`\\b(${c})[jJ](?=${g})` +}]},b={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i, +contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={ +className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/, +end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i, +contains:["self",r,u,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,u,r],{ +name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i, +illegal:/(<\/|\?)|=>/,contains:[r,u,{scope:"variable.language",match:/\bself\b/ +},{beginKeywords:"if",relevance:0},{match:/\bor\b/,scope:"keyword" +},l,b,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,t],scope:{1:"keyword", +3:"title.function"},contains:[m]},{variants:[{ +match:[/\bclass/,/\s+/,t,/\s*/,/\(\s*/,t,/\s*\)/]},{match:[/\bclass/,/\s+/,t]}], +scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{ +className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[u,m,l]}]}}, +grmr_python_repl:e=>({aliases:["pycon"],contains:[{className:"meta.prompt", +starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{ +begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}),grmr_r:e=>{ +const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,a=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,r=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/) +;return{name:"R",keywords:{$pattern:t, +keyword:"function if in break next repeat else for while", +literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", +built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" +},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/, +starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)), +endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{ +scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0 +}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}] +}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE], +variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', +relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{ +1:"operator",2:"number"},match:[i,a]},{scope:{1:"operator",2:"number"}, +match:[/%[^%]*%/,a]},{scope:{1:"punctuation",2:"number"},match:[r,a]},{scope:{ +2:"number"},match:[/[^a-zA-Z0-9._]|^/,a]}]},{scope:{3:"operator"}, +match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{ +match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:r},{begin:"`",end:"`", +contains:[{begin:/\\./}]}]}},grmr_ruby:e=>{ +const n=e.regex,t="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",a=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(a,/(::\w+)*/),r={ +"variable.constant":["__FILE__","__LINE__","__ENCODING__"], +"variable.language":["self","super"], +keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield","include","extend","prepend","public","private","protected","raise","throw"], +built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"], +literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},o={ +begin:"#<",end:">"},l=[e.COMMENT("#","$",{contains:[s] +}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10 +}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/, +end:/\}/,keywords:r},d={className:"string",contains:[e.BACKSLASH_ESCAPE,c], +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{ +begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{ +begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//, +end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{ +begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{ +begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ +begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ +begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ +begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)), +contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, +contains:[e.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",u={className:"number", +relevance:0,variants:[{ +begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ +begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" +},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ +begin:"\\b0(_?[0-7])+r?i?\\b"}]},b={variants:[{match:/\(\)/},{ +className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0, +keywords:r}]},m=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{ +match:[/\b(class|module)\s+/,i]}],scope:{2:"title.class", +4:"title.class.inherited"},keywords:r},{match:[/(include|extend)\s+/,i],scope:{ +2:"title.class"},keywords:r},{relevance:0,match:[i,/\.new[. (]/],scope:{ +1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},{relevance:0,match:a,scope:"title.class"},{ +match:[/def/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[b]},{ +begin:e.IDENT_RE+"::"},{className:"symbol", +begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", +begin:":(?!\\s)",contains:[d,{begin:t}],relevance:0},u,{className:"variable", +begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ +className:"params",begin:/\|(?!=)/,end:/\|/,excludeBegin:!0,excludeEnd:!0, +relevance:0,keywords:r},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*", +keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c], +illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{ +begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[", +end:"\\][a-z]*"}]}].concat(o,l),relevance:0}].concat(o,l) +;c.contains=m,b.contains=m;const p=[{begin:/^\s*=>/,starts:{end:"$",contains:m} +},{className:"meta.prompt", +begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", +starts:{end:"$",keywords:r,contains:m}}];return l.unshift(o),{name:"Ruby", +aliases:["rb","gemspec","podspec","thor","irb"],keywords:r,illegal:/\/\*/, +contains:[e.SHEBANG({binary:"ruby"})].concat(p).concat(l).concat(m)}}, +grmr_rust:e=>{ +const n=e.regex,t=/(r#)?/,a=n.concat(t,e.UNDERSCORE_IDENT_RE),i=n.concat(t,e.IDENT_RE),r={ +className:"title.function.invoke",relevance:0, +begin:n.concat(/\b/,/(?!let|for|while|if|else|match\b)/,i,n.lookahead(/\s*\(/)) +},s="([ui](8|16|32|64|128|size)|f(32|64))?",o=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],l=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"] +;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:l, +keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","union","unsafe","unsized","use","virtual","where","while","yield"], +literal:["true","false","Some","None","Ok","Err"],built_in:o},illegal:""},r]}}, +grmr_scss:e=>{const n=te(e),t=se,a=re,i="@[a-z-]+",r={className:"variable", +begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS", +case_insensitive:!0,illegal:"[=/|']", +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,n.CSS_NUMBER_MODE,{ +className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ +className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 +},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", +begin:"\\b("+ae.join("|")+")\\b",relevance:0},{className:"selector-pseudo", +begin:":("+a.join("|")+")"},{className:"selector-pseudo", +begin:":(:)?("+t.join("|")+")"},r,{begin:/\(/,end:/\)/, +contains:[n.CSS_NUMBER_MODE]},n.CSS_VARIABLE,{className:"attribute", +begin:"\\b("+oe.join("|")+")\\b"},{ +begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" +},{begin:/:/,end:/[;}{]/,relevance:0, +contains:[n.BLOCK_COMMENT,r,n.HEXCOLOR,n.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.IMPORTANT,n.FUNCTION_DISPATCH] +},{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{ +begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, +keyword:"and or not only",attribute:ie.join(" ")},contains:[{begin:i, +className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" +},r,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE] +},n.FUNCTION_DISPATCH]}},grmr_shell:e=>({name:"Shell Session", +aliases:["console","shellsession"],contains:[{className:"meta.prompt", +begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, +subLanguage:"bash"}}]}),grmr_sql:e=>{ +const n=e.regex,t=e.COMMENT("--","$"),a=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],i=a,r=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!a.includes(e))),s={ +match:n.concat(/\b/,n.either(...i),/\s*\(/),relevance:0,keywords:{built_in:i}} +;function o(e){ +return n.concat(/\b/,n.either(...e.map((e=>e.replace(/\s+/,"\\s+")))),/\b/)} +const l={scope:"keyword", +match:o(["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"]), +relevance:0};return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ +$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:n,when:t}={})=>{const a=t +;return n=n||[],e.map((e=>e.match(/\|\d+$/)||n.includes(e)?e:a(e)?e+"|0":e)) +})(r,{when:e=>e.length<3}),literal:["true","false","unknown"], +type:["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"], +built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] +},contains:[{scope:"type", +match:o(["double precision","large object","with timezone","without timezone"]) +},l,s,{scope:"variable",match:/@[a-z0-9][a-z0-9_]*/},{scope:"string",variants:[{ +begin:/'/,end:/'/,contains:[{match:/''/}]}]},{begin:/"/,end:/"/,contains:[{ +match:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{scope:"operator", +match:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0}]}}, +grmr_swift:e=>{const n={match:/\s+/,relevance:0},t=e.COMMENT("/\\*","\\*/",{ +contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],i={match:[/\./,m(...ke,...xe)], +className:{2:"keyword"}},r={match:b(/\./,m(...Me)),relevance:0 +},s=Me.filter((e=>"string"==typeof e)).concat(["_|0"]),o={variants:[{ +className:"keyword", +match:m(...Me.filter((e=>"string"!=typeof e)).concat(Oe).map(Ne),...xe)}]},l={ +$pattern:m(/\b\w+/,/#\w+/),keyword:s.concat(Ce),literal:Ae},c=[i,r,o],g=[{ +match:b(/\./,m(...Te)),relevance:0},{className:"built_in", +match:b(/\b/,m(...Te),/(?=\()/)}],u={match:/->/,relevance:0},p=[u,{ +className:"operator",relevance:0,variants:[{match:Ie},{match:`\\.(\\.|${De})+`}] +}],_="([0-9]_*)+",h="([0-9a-fA-F]_*)+",f={className:"number",relevance:0, +variants:[{match:`\\b(${_})(\\.(${_}))?([eE][+-]?(${_}))?\\b`},{ +match:`\\b0x(${h})(\\.(${h}))?([pP][+-]?(${_}))?\\b`},{match:/\b0o([0-7]_*)+\b/ +},{match:/\b0b([01]_*)+\b/}]},E=(e="")=>({className:"subst",variants:[{ +match:b(/\\/,e,/[0\\tnr"']/)},{match:b(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}] +}),y=(e="")=>({className:"subst",match:b(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/) +}),w=(e="")=>({className:"subst",label:"interpol",begin:b(/\\/,e,/\(/),end:/\)/ +}),v=(e="")=>({begin:b(e,/"""/),end:b(/"""/,e),contains:[E(e),y(e),w(e)] +}),N=(e="")=>({begin:b(e,/"/),end:b(/"/,e),contains:[E(e),w(e)]}),k={ +className:"string", +variants:[v(),v("#"),v("##"),v("###"),N(),N("#"),N("##"),N("###")] +},x=[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0, +contains:[e.BACKSLASH_ESCAPE]}],O={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//, +contains:x},M=e=>{const n=b(e,/\//),t=b(/\//,e);return{begin:n,end:t, +contains:[...x,{scope:"comment",begin:`#(?!.*${t})`,end:/$/}]}},A={ +scope:"regexp",variants:[M("###"),M("##"),M("#"),O]},S={match:b(/`/,$e,/`/) +},C=[S,{className:"variable",match:/\$\d+/},{className:"variable", +match:`\\$${Be}+`}],T=[{match:/(@|#(un)?)available/,scope:"keyword",starts:{ +contains:[{begin:/\(/,end:/\)/,keywords:je,contains:[...p,f,k]}]}},{ +scope:"keyword",match:b(/@/,m(...ze),d(m(/\(/,/\s+/)))},{scope:"meta", +match:b(/@/,$e)}],R={match:d(/\b[A-Z]/),relevance:0,contains:[{className:"type", +match:b(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,Be,"+") +},{className:"type",match:Fe,relevance:0},{match:/[?!]+/,relevance:0},{ +match:/\.\.\./,relevance:0},{match:b(/\s+&\s+/,d(Fe)),relevance:0}]},D={ +begin://,keywords:l,contains:[...a,...c,...T,u,R]};R.contains.push(D) +;const I={begin:/\(/,end:/\)/,relevance:0,keywords:l,contains:["self",{ +match:b($e,/\s*:/),keywords:"_|0",relevance:0 +},...a,A,...c,...g,...p,f,k,...C,...T,R]},L={begin://, +keywords:"repeat each",contains:[...a,R]},B={begin:/\(/,end:/\)/,keywords:l, +contains:[{begin:m(d(b($e,/\s*:/)),d(b($e,/\s+/,$e,/\s*:/))),end:/:/, +relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params", +match:$e}]},...a,...c,...p,f,k,...T,R,I],endsParent:!0,illegal:/["']/},$={ +match:[/(func|macro)/,/\s+/,m(S.match,$e,Ie)],className:{1:"keyword", +3:"title.function"},contains:[L,B,n],illegal:[/\[/,/%/]},F={ +match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"}, +contains:[L,B,n],illegal:/\[|%/},z={match:[/operator/,/\s+/,Ie],className:{ +1:"keyword",3:"title"}},j={begin:[/precedencegroup/,/\s+/,Fe],className:{ +1:"keyword",3:"title"},contains:[R],keywords:[...Se,...Ae],end:/}/},U={ +begin:[/(struct|protocol|class|extension|enum|actor)/,/\s+/,$e,/\s*/], +beginScope:{1:"keyword",3:"title.class"},keywords:l,contains:[L,...c,{begin:/:/, +end:/\{/,keywords:l,contains:[{scope:"title.class.inherited",match:Fe},...c], +relevance:0}]};for(const e of k.variants){ +const n=e.contains.find((e=>"interpol"===e.label));n.keywords=l +;const t=[...c,...g,...p,f,k,...C];n.contains=[...t,{begin:/\(/,end:/\)/, +contains:["self",...t]}]}return{name:"Swift",keywords:l,contains:[...a,$,F,{ +match:[/class\b/,/\s+/,/func\b/,/\s+/,/\b[A-Za-z_][A-Za-z0-9_]*\b/],scope:{ +1:"keyword",3:"keyword",5:"title.function"}},{match:[/class\b/,/\s+/,/var\b/], +scope:{1:"keyword",3:"keyword"}},U,z,j,{beginKeywords:"import",end:/$/, +contains:[...a],relevance:0},A,...c,...g,...p,f,k,...C,...T,R,I]}}, +grmr_typescript:e=>{ +const n=e.regex,t=ve(e),a=me,i=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],r={ +begin:[/namespace/,/\s+/,e.IDENT_RE],beginScope:{1:"keyword",3:"title.class"} +},s={beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:{ +keyword:"interface extends",built_in:i},contains:[t.exports.CLASS_REFERENCE] +},o={$pattern:me, +keyword:pe.concat(["type","interface","public","private","protected","implements","declare","abstract","readonly","enum","override","satisfies"]), +literal:_e,built_in:we.concat(i),"variable.language":ye},l={className:"meta", +begin:"@"+a},c=(e,n,t)=>{const a=e.contains.findIndex((e=>e.label===n)) +;if(-1===a)throw Error("can not find mode to replace");e.contains.splice(a,1,t)} +;Object.assign(t.keywords,o),t.exports.PARAMS_CONTAINS.push(l) +;const d=t.contains.find((e=>"attr"===e.scope)),g=Object.assign({},d,{ +match:n.concat(a,n.lookahead(/\s*\?:/))}) +;return t.exports.PARAMS_CONTAINS.push([t.exports.CLASS_REFERENCE,d,g]), +t.contains=t.contains.concat([l,r,s,g]), +c(t,"shebang",e.SHEBANG()),c(t,"use_strict",{className:"meta",relevance:10, +begin:/^\s*['"]use strict['"]/ +}),t.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(t,{ +name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),t},grmr_vbnet:e=>{ +const n=e.regex,t=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,i=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,s={ +className:"literal",variants:[{begin:n.concat(/# */,n.either(a,t),/ *#/)},{ +begin:n.concat(/# */,r,/ *#/)},{begin:n.concat(/# */,i,/ *#/)},{ +begin:n.concat(/# */,n.either(a,t),/ +/,n.either(i,r),/ *#/)}] +},o=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}] +}),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]}) +;return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0, +classNameAliases:{label:"symbol"},keywords:{ +keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", +built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", +type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", +literal:"true false nothing"}, +illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ +className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, +end:/"/,illegal:/\n/,contains:[{begin:/""/}]},s,{className:"number",relevance:0, +variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ +},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ +begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ +className:"label",begin:/^\w+:/},o,l,{className:"meta", +begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, +end:/$/,keywords:{ +keyword:"const disable else elseif enable end externalsource if region then"}, +contains:[l]}]}},grmr_wasm:e=>{e.regex;const n=e.COMMENT(/\(;/,/;\)/) +;return n.contains.push("self"),{name:"WebAssembly",keywords:{$pattern:/[\w.]+/, +keyword:["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"] +},contains:[e.COMMENT(/;;/,/$/),n,{match:[/(?:offset|align)/,/\s*/,/=/], +className:{1:"keyword",3:"operator"}},{className:"variable",begin:/\$[\w_]+/},{ +match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},{ +begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword", +3:"title.function"}},e.QUOTE_STRING_MODE,{match:/(i32|i64|f32|f64)(?!\.)/, +className:"type"},{className:"keyword", +match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/ +},{className:"number",relevance:0, +match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/ +}]}},grmr_xml:e=>{ +const n=e.regex,t=n.concat(/[\p{L}_]/u,n.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),a={ +className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/, +contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] +},r=e.inherit(i,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{ +className:"string"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),l={ +endsWithParent:!0,illegal:/`]+/}]}]}]};return{ +name:"HTML, XML", +aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], +case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,o,s,r,{begin:/\[/,end:/\]/,contains:[{ +className:"meta",begin://,contains:[i,r,o,s]}]}] +},e.COMMENT(//,{relevance:10}),{begin://, +relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, +relevance:10,contains:[o]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"style"},contains:[l],starts:{ +end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"script"},contains:[l],starts:{ +end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ +className:"tag",begin:/<>|<\/>/},{className:"tag", +begin:n.concat(//,/>/,/\s/)))), +end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:l}]},{ +className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(t,/>/))),contains:[{ +className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]} +},grmr_yaml:e=>{ +const n="true false yes no null",t="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={ +className:"string",relevance:0,variants:[{begin:/"/,end:/"/},{begin:/\S+/}], +contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{ +begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(a,{variants:[{ +begin:/'/,end:/'/,contains:[{begin:/''/,relevance:0}]},{begin:/"/,end:/"/},{ +begin:/[^\s,{}[\]]+/}]}),r={end:",",endsWithParent:!0,excludeEnd:!0,keywords:n, +relevance:0},s={begin:/\{/,end:/\}/,contains:[r],illegal:"\\n",relevance:0},o={ +begin:"\\[",end:"\\]",contains:[r],illegal:"\\n",relevance:0},l=[{ +className:"attr",variants:[{begin:/[\w*@][\w*@ :()\./-]*:(?=[ \t]|$)/},{ +begin:/"[\w*@][\w*@ :()\./-]*":(?=[ \t]|$)/},{ +begin:/'[\w*@][\w*@ :()\./-]*':(?=[ \t]|$)/}]},{className:"meta", +begin:"^---\\s*$",relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+t},{className:"type", +begin:"!<"+t+">"},{className:"type",begin:"!"+t},{className:"type",begin:"!!"+t +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},s,o,{ +className:"string",relevance:0,begin:/'/,end:/'/,contains:[{match:/''/, +scope:"char.escape",relevance:0}]},a],c=[...l] +;return c.pop(),c.push(i),r.contains=c,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:l}}});const Pe=ne;for(const e of Object.keys(Ue)){ +const n=e.replace("grmr_","").replace("_","-");Pe.registerLanguage(n,Ue[e])} +return Pe}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); \ No newline at end of file diff --git a/mobile/android/app/src/main/assets/reader/katex/LICENSE b/mobile/android/app/src/main/assets/reader/katex/LICENSE new file mode 100644 index 0000000..37c6433 --- /dev/null +++ b/mobile/android/app/src/main/assets/reader/katex/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2020 Khan Academy and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_AMS-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_AMS-Regular.woff2 new file mode 100644 index 0000000..0acaaff Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_AMS-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Caligraphic-Bold.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Caligraphic-Bold.woff2 new file mode 100644 index 0000000..f390922 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Caligraphic-Bold.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Caligraphic-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Caligraphic-Regular.woff2 new file mode 100644 index 0000000..75344a1 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Caligraphic-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Fraktur-Bold.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Fraktur-Bold.woff2 new file mode 100644 index 0000000..395f28b Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Fraktur-Bold.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Fraktur-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Fraktur-Regular.woff2 new file mode 100644 index 0000000..735f694 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Fraktur-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-Bold.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-Bold.woff2 new file mode 100644 index 0000000..ab2ad21 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-Bold.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-BoldItalic.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-BoldItalic.woff2 new file mode 100644 index 0000000..5931794 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-BoldItalic.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-Italic.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-Italic.woff2 new file mode 100644 index 0000000..b50920e Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-Italic.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-Regular.woff2 new file mode 100644 index 0000000..eb24a7b Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Main-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Math-BoldItalic.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Math-BoldItalic.woff2 new file mode 100644 index 0000000..2965702 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Math-BoldItalic.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Math-Italic.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Math-Italic.woff2 new file mode 100644 index 0000000..215c143 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Math-Italic.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_SansSerif-Bold.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_SansSerif-Bold.woff2 new file mode 100644 index 0000000..cfaa3bd Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_SansSerif-Bold.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_SansSerif-Italic.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_SansSerif-Italic.woff2 new file mode 100644 index 0000000..349c06d Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_SansSerif-Italic.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_SansSerif-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_SansSerif-Regular.woff2 new file mode 100644 index 0000000..a90eea8 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_SansSerif-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Script-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Script-Regular.woff2 new file mode 100644 index 0000000..b3048fc Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Script-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size1-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size1-Regular.woff2 new file mode 100644 index 0000000..c5a8462 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size1-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size2-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size2-Regular.woff2 new file mode 100644 index 0000000..e1bccfe Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size2-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size3-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size3-Regular.woff2 new file mode 100644 index 0000000..249a286 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size3-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size4-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size4-Regular.woff2 new file mode 100644 index 0000000..680c130 Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Size4-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Typewriter-Regular.woff2 b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Typewriter-Regular.woff2 new file mode 100644 index 0000000..771f1af Binary files /dev/null and b/mobile/android/app/src/main/assets/reader/katex/fonts/KaTeX_Typewriter-Regular.woff2 differ diff --git a/mobile/android/app/src/main/assets/reader/katex/katex.min.css b/mobile/android/app/src/main/assets/reader/katex/katex.min.css new file mode 100644 index 0000000..68f4b22 --- /dev/null +++ b/mobile/android/app/src/main/assets/reader/katex/katex.min.css @@ -0,0 +1 @@ +@font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;position:relative;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.18.9"}.katex .katex-mathml{border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.katex-newline{display:block}.katex .katex-base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .katex-base,.katex .katex-strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .katex-vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .katex-thinbox{display:inline-flex;flex-direction:row;max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline,.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-rule,.katex .katex-underline .underline-line,.katex .mfrac .frac-line{min-height:1px}.katex .mspace{display:inline-block}.katex .katex-smash{display:inline;line-height:0}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.katex-inner,.katex .llap>.katex-inner,.katex .rlap>.katex-inner{position:absolute}.katex .clap>.katex-fix,.katex .llap>.katex-fix,.katex .rlap>.katex-fix{display:inline-block}.katex .llap>.katex-inner{right:0}.katex .clap>.katex-inner,.katex .rlap>.katex-inner{left:0}.katex .clap>.katex-inner>span{margin-left:-50%;margin-right:50%}.katex .katex-rule{border:0 solid;display:inline-block;position:relative}.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.katex-root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .katex-sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .katex-sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .katex-sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .katex-sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .katex-sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .katex-sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .katex-sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .katex-sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .katex-sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .katex-sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .katex-sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .katex-sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .katex-sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .katex-sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .katex-sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .katex-sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .katex-sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .katex-sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .katex-sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .katex-sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .katex-sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .katex-sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .katex-sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .katex-sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .katex-sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .katex-sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .katex-sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .katex-sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .katex-sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .katex-sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .katex-sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .katex-sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .katex-sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .katex-sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .katex-sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .katex-sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .katex-sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .katex-sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .katex-sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .katex-sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .katex-sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .katex-sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .katex-sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .katex-sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .katex-sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .katex-sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .katex-sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .katex-sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .katex-sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .katex-sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .katex-sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .katex-sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .katex-sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .katex-sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .katex-sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .katex-sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .katex-sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .katex-sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .katex-sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .katex-sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .katex-sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .katex-sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .katex-sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .katex-sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .katex-sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .katex-sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .katex-sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .katex-sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .katex-sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .katex-sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .katex-sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .katex-sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .katex-sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .katex-sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .katex-sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .katex-sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .katex-sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .katex-sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .katex-sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .katex-sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .katex-sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .katex-sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .katex-sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .katex-sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .katex-sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .katex-sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .katex-sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .katex-sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .katex-sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .katex-sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .katex-sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .katex-sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .katex-sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .katex-sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .katex-sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .katex-sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .katex-sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .katex-sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .katex-sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .katex-sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .katex-sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .katex-sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .katex-sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .katex-sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .katex-sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .katex-sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .katex-sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .katex-sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .katex-sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .katex-sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .katex-sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .katex-sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .katex-sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .katex-sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .katex-sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .katex-sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .katex-sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .katex-sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .katex-sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .katex-sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .katex-sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .katex-accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .katex-accent .accent-body{position:relative}.katex .katex-accent .accent-body:not(.accent-full){width:0}.katex .katex-overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .katex-stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .katex-stretchy:after,.katex .katex-stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .katex-sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .reflectbox{display:inline-block;transform:scaleX(-1)}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.katex-tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.katex-tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo} diff --git a/mobile/android/app/src/main/assets/reader/katex/katex.min.js b/mobile/android/app/src/main/assets/reader/katex/katex.min.js new file mode 100644 index 0000000..42649a7 --- /dev/null +++ b/mobile/android/app/src/main/assets/reader/katex/katex.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.katex=t():e.katex=t()}("undefined"!=typeof self?self:this,function(){return function(){"use strict";var e={d:function(t,r){for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}},t={};e.d(t,{default:function(){return po}});class r extends Error{constructor(e,t){let n,o,s="KaTeX parse error: "+e;const i=t&&t.loc;if(i&&i.start<=i.end){const e=i.lexer.input;n=i.start,o=i.end,n===e.length?s+=" at end of input: ":s+=" at position "+(n+1)+": ";const t=e.slice(n,o).replace(/[^]/g,"$&\u0332");let r,a;r=n>15?"\u2026"+e.slice(n-15,n):e.slice(0,n),a=o+15e.replace(o,"-$1").toLowerCase(),i={"&":"&",">":">","<":"<",'"':""","'":"'"},a=/[&><"']/g,l=e=>String(e).replace(a,e=>i[e]),c=e=>"ordgroup"===e.type||"color"===e.type?1===e.body.length?c(e.body[0]):e:"font"===e.type?c(e.body):e,h=new Set(["mathord","textord","atom"]),m=e=>h.has(c(e).type),u={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:e=>"#"+e},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(e,t)=>(t.push(e),t)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:e=>Math.max(0,e),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:e=>Math.max(0,e),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:e=>Math.max(0,e),cli:"-e, --max-expand ",cliProcessor:e=>"Infinity"===e?1/0:parseInt(e)},globalGroup:{type:"boolean",cli:!1}};function p(e){if(Object.prototype.hasOwnProperty.call(e,"default")&&void 0!==e.default)return e.default;return function(e){if("string"!=typeof e)return e.enum[0];switch(e){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{};default:throw new Error("Unexpected schema type; settings must declare an explicit default.")}}(Array.isArray(e.type)?e.type[0]:e.type)}function d(e,t,r,n){const o=Object.prototype.hasOwnProperty.call(r,t)?r[t]:void 0,s=Object.prototype.hasOwnProperty.call(n,"processor")?n.processor:void 0;e[t]=void 0!==o?s?s(o):o:p(n)}class g{constructor(e){void 0===e&&(e={}),this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(const t of Object.keys(u)){const r=u[t];r&&d(this,t,e,r)}}reportNonstrict(e,t,r){let o=this.strict;if("function"==typeof o&&(o=o(e,t,r)),o&&"ignore"!==o){if(!0===o||"error"===o)throw new n("LaTeX-incompatible input and strict mode is set to 'error': "+t+" ["+e+"]",r);"warn"===o?"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+t+" ["+e+"]"):"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+o+"': "+t+" ["+e+"]")}}useStrictBehavior(e,t,r){let n=this.strict;if("function"==typeof n)try{n=n(e,t,r)}catch(e){n="error"}return!(!n||"ignore"===n)&&(!0===n||"error"===n||("warn"===n?("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+t+" ["+e+"]"),!1):("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+n+"': "+t+" ["+e+"]"),!1)))}isTrusted(e){if("url"in e&&e.url&&!e.protocol){const t=(e=>{const t=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(e);return t?":"!==t[2]?null:/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(t[1])?t[1].toLowerCase():null:"_relative"})(e.url);if(null==t)return!1;e.protocol=t}const t="function"==typeof this.trust?this.trust(e):this.trust;return Boolean(t)}}class f{constructor(e,t,r){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=t,this.cramped=r}sup(){return b[y[this.id]]}sub(){return b[x[this.id]]}fracNum(){return b[w[this.id]]}fracDen(){return b[v[this.id]]}cramp(){return b[k[this.id]]}text(){return b[z[this.id]]}isTight(){return this.size>=2}}const b=[new f(0,0,!1),new f(1,0,!0),new f(2,1,!1),new f(3,1,!0),new f(4,2,!1),new f(5,2,!0),new f(6,3,!1),new f(7,3,!0)],y=[4,5,4,5,6,7,6,7],x=[5,5,5,5,7,7,7,7],w=[2,3,4,5,6,7,6,7],v=[3,3,5,5,7,7,7,7],k=[1,1,3,3,5,5,7,7],z=[0,1,2,3,2,3,2,3];var S={DISPLAY:b[0],TEXT:b[2],SCRIPT:b[4],SCRIPTSCRIPT:b[6]};const M=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];const A=[];function T(e){for(let t=0;t=A[t]&&e<=A[t+1])return!0;return!1}M.forEach(e=>e.blocks.forEach(e=>A.push(...e)));const C=e=>e+" "+e,B=80,q={doubleleftarrow:"M262 157\nl10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3\n 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28\n 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5\nc2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5\n 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87\n-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7\n-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z\nm8 0v40h399730v-40zm0 194v40h399730v-40z",doublerightarrow:"M399738 392l\n-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5\n 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88\n-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68\n-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18\n-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782\nc-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3\n-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z",leftarrow:"M400000 241H110l3-3c68.7-52.7 113.7-120\n 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8\n-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247\nc-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208\n 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3\n 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202\n l-3-3h399890zM100 241v40h399900v-40z",leftbrace:"M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117\n-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7\n 5-6 9-10 13-.7 1-7.3 1-20 1H6z",leftbraceunder:"M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13\n 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688\n 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7\n-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z",leftgroup:"M400000 80\nH435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0\n 435 0h399565z",leftgroupunder:"M400000 262\nH435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219\n 435 219h399565z",leftharpoon:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3\n-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5\n-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7\n-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z",leftharpoonplus:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5\n 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3\n-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7\n-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z\nm0 0v40h400000v-40z",leftharpoondown:"M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333\n 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5\n 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667\n-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z",leftharpoondownplus:"M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12\n 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7\n-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0\nv40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z",lefthook:"M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5\n-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3\n-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21\n 71.5 23h399859zM103 281v-40h399897v40z",leftlinesegment:C("M40 281 V428 H0 V94 H40 V241 H400000 v40z"),leftbracketunder:C("M0 0 h120 V290 H399995 v120 H0z"),leftbracketover:C("M0 440 h120 V150 H399995 v-120 H0z"),leftmapsto:C("M40 281 V448H0V74H40V241H400000v40z"),leftToFrom:"M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23\n-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8\nc28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3\n 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z",longequal:C("M0 50 h400000 v40H0z m0 194h40000v40H0z"),midbrace:"M200428 334\nc-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14\n-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7\n 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11\n 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z",midbraceunder:"M199572 214\nc100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14\n 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3\n 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0\n-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z",oiintSize1:"M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6\n-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z\nm368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8\n60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z",oiintSize2:"M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8\n-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z\nm502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2\nc0 110 84 276 504 276s502.4-166 502.4-276z",oiiintSize1:"M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6\n-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z\nm525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0\n85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z",oiiintSize2:"M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8\n-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z\nm770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1\nc0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z",rightarrow:"M0 241v40h399891c-47.3 35.3-84 78-110 128\n-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20\n 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7\n 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85\n-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n 151.7 139 205zm0 0v40h399900v-40z",rightbrace:"M400000 542l\n-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5\ns-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1\nc124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z",rightbraceunder:"M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3\n 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237\n-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z",rightgroup:"M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0\n 3-1 3-3v-38c-76-158-257-219-435-219H0z",rightgroupunder:"M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18\n 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z",rightharpoon:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3\n-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2\n-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58\n 69.2 92 94.5zm0 0v40h399900v-40z",rightharpoonplus:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11\n-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7\n 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z\nm0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z",rightharpoondown:"M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8\n 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5\n-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95\n-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z",rightharpoondownplus:"M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8\n 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3\n 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3\n-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z\nm0-194v40h400000v-40zm0 0v40h400000v-40z",righthook:"M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3\n 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0\n-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21\n 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z",rightlinesegment:C("M399960 241 V94 h40 V428 h-40 V281 H0 v-40z"),rightbracketunder:C("M399995 0 h-120 V290 H0 v120 H400000z"),rightbracketover:C("M399995 440 h-120 V150 H0 v-120 H399995z"),rightToFrom:"M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23\n 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32\n-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142\n-167z M100 147v40h399900v-40zM0 341v40h399900v-40z",twoheadleftarrow:"M0 167c68 40\n 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69\n-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3\n-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19\n-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101\n 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z",twoheadrightarrow:"M400000 167\nc-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3\n 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42\n 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333\n-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70\n 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z",tilde1:"M200 55.538c-77 0-168 73.953-177 73.953-3 0-7\n-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0\n 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0\n 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128\n-68.267.847-113-73.952-191-73.952z",tilde2:"M344 55.266c-142 0-300.638 81.316-311.5 86.418\n-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9\n 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114\nc1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751\n 181.476 676 181.476c-149 0-189-126.21-332-126.21z",tilde3:"M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457\n-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0\n 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697\n 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696\n -338 0-409-156.573-744-156.573z",tilde4:"M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345\n-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409\n 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9\n 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409\n -175.236-744-175.236z",vec:"M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5\n3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11\n10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63\n-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1\n-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59\nH213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359\nc-16-25.333-24-45-24-59z",widehat1:"M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22\nc-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z",widehat2:"M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat3:"M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat4:"M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widecheck1:"M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1,\n-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z",widecheck2:"M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck3:"M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck4:"M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",baraboveleftarrow:"M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202\nc4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5\nc-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130\ns-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47\n121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6\ns2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11\nc0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z\nM100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z",rightarrowabovebar:"M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32\n-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0\n13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39\n-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5\n-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z",baraboveshortleftharpoon:"M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17\nc2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21\nc-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40\nc-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z\nM0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z",rightharpoonaboveshortbar:"M0,241 l0,40c399126,0,399993,0,399993,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z",shortbaraboveleftharpoon:"M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9,\n1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7,\n-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z\nM93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z",shortrightharpoonabovebar:"M53,241l0,40c398570,0,399437,0,399437,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z"};class I{constructor(e){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=e,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}hasClass(e){return this.classes.includes(e)}toNode(){const e=document.createDocumentFragment();for(let t=0;t{if("toText"in e)return e.toText();throw new Error("Expected MathDomNode with toText, got "+e.constructor.name)}).join("")}}const R={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:1.00375,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:1.00375},O={ex:!0,em:!0,mu:!0},E=function(e){return"string"!=typeof e&&(e=e.unit),e in R||e in O||"ex"===e},H=function(e,t){let r;if(e.unit in R)r=R[e.unit]/t.fontMetrics().ptPerEm/t.sizeMultiplier;else if("mu"===e.unit)r=t.fontMetrics().cssEmPerMu;else{let o;if(o=t.style.isTight()?t.havingStyle(t.style.text()):t,"ex"===e.unit)r=o.fontMetrics().xHeight;else{if("em"!==e.unit)throw new n("Invalid unit: '"+e.unit+"'");r=o.fontMetrics().quad}o!==t&&(r*=o.sizeMultiplier/t.sizeMultiplier)}return Math.min(e.number*r,t.maxSize)},N=function(e){return+e.toFixed(4)+"em"},D=function(e){return e.filter(e=>e).join(" ")},L=function(e){let t="";for(const r of Object.keys(e)){const n=e[r];void 0!==n&&(t+=s(r)+":"+n+";")}return t},F=function(e,t,r){if(this.classes=e||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=r||{},t){t.style.isTight()&&this.classes.push("mtight");const e=t.getColor();e&&(this.style.color=e)}},P=function(e){const t=document.createElement(e);t.className=D(this.classes),Object.assign(t.style,this.style);for(const e of Object.keys(this.attributes))t.setAttribute(e,this.attributes[e]);for(let e=0;e/=\x00-\x1f]/,G=function(e){let t="<"+e;this.classes.length&&(t+=' class="'+l(D(this.classes))+'"');const r=L(this.style);r&&(t+=' style="'+l(r)+'"');for(const e of Object.keys(this.attributes)){if(V.test(e))throw new n("Invalid attribute name '"+e+"'");t+=" "+e+'="'+l(this.attributes[e])+'"'}t+=">";for(let e=0;e",t};class j{constructor(e,t,r,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,this.italic=void 0,F.call(this,e,r,n),this.children=t||[]}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return this.classes.includes(e)}toNode(){return P.call(this,"span")}toMarkup(){return G.call(this,"span")}}class U{constructor(e,t,r,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,F.call(this,t,n),this.children=r||[],this.setAttribute("href",e)}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return this.classes.includes(e)}toNode(){return P.call(this,"a")}toMarkup(){return G.call(this,"a")}}class X{constructor(e,t,r){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=t,this.src=e,this.classes=["mord"],this.height=0,this.depth=0,this.maxFontSize=0,this.style=r}hasClass(e){return this.classes.includes(e)}toNode(){const e=document.createElement("img");return e.src=this.src,e.alt=this.alt,e.className="mord",Object.assign(e.style,this.style),e}toMarkup(){let e=''+l(this.alt)+'=n[0]&&e<=n[1])return r.name}}return null}(this.text.charCodeAt(0));l&&this.classes.push(l+"_fallback"),/[\xee\xef\xed\xec]/.test(this.text)&&(this.text=Y[this.text])}hasClass(e){return this.classes.includes(e)}toNode(){const e=document.createTextNode(this.text);let t=null;return this.italic>0&&(t=document.createElement("span"),t.style.marginRight=N(this.italic)),this.classes.length>0&&(t=t||document.createElement("span"),t.className=D(this.classes)),Object.keys(this.style).length>0&&(t=t||document.createElement("span"),Object.assign(t.style,this.style)),t?(t.appendChild(e),t):e}toMarkup(){let e=!1,t="0&&(r+="margin-right:"+N(this.italic)+";"),r+=L(this.style),r&&(e=!0,t+=' style="'+l(r)+'"');const n=l(this.text);return e?(t+=">",t+=n,t+="",t):n}}class _{constructor(e,t){this.children=void 0,this.attributes=void 0,this.children=e||[],this.attributes=t||{}}toNode(){const e=document.createElementNS("http://www.w3.org/2000/svg","svg");for(const t of Object.keys(this.attributes))e.setAttribute(t,this.attributes[t]);for(let t=0;t':''}}class Z{constructor(e){this.attributes=void 0,this.attributes=e||{}}toNode(){const e=document.createElementNS("http://www.w3.org/2000/svg","line");for(const t of Object.keys(this.attributes))e.setAttribute(t,this.attributes[t]);return e}toMarkup(){let e="","\\gt",!0),oe(se,ae,be,"\u2208","\\in",!0),oe(se,ae,be,"\ue020","\\@not"),oe(se,ae,be,"\u2282","\\subset",!0),oe(se,ae,be,"\u2283","\\supset",!0),oe(se,ae,be,"\u2286","\\subseteq",!0),oe(se,ae,be,"\u2287","\\supseteq",!0),oe(se,le,be,"\u2288","\\nsubseteq",!0),oe(se,le,be,"\u2289","\\nsupseteq",!0),oe(se,ae,be,"\u22a8","\\models"),oe(se,ae,be,"\u2190","\\leftarrow",!0),oe(se,ae,be,"\u2264","\\le"),oe(se,ae,be,"\u2264","\\leq",!0),oe(se,ae,be,"<","\\lt",!0),oe(se,ae,be,"\u2192","\\rightarrow",!0),oe(se,ae,be,"\u2192","\\to"),oe(se,le,be,"\u2271","\\ngeq",!0),oe(se,le,be,"\u2270","\\nleq",!0),oe(se,ae,ye,"\xa0","\\ "),oe(se,ae,ye,"\xa0","\\space"),oe(se,ae,ye,"\xa0","\\nobreakspace"),oe(ie,ae,ye,"\xa0","\\ "),oe(ie,ae,ye,"\xa0"," "),oe(ie,ae,ye,"\xa0","\\space"),oe(ie,ae,ye,"\xa0","\\nobreakspace"),oe(se,ae,ye,"","\\nobreak"),oe(se,ae,ye,"","\\allowbreak"),oe(se,ae,fe,",",","),oe(se,ae,fe,";",";"),oe(se,le,he,"\u22bc","\\barwedge",!0),oe(se,le,he,"\u22bb","\\veebar",!0),oe(se,ae,he,"\u2299","\\odot",!0),oe(se,ae,he,"\u2295","\\oplus",!0),oe(se,ae,he,"\u2297","\\otimes",!0),oe(se,ae,xe,"\u2202","\\partial",!0),oe(se,ae,he,"\u2298","\\oslash",!0),oe(se,le,he,"\u229a","\\circledcirc",!0),oe(se,le,he,"\u22a1","\\boxdot",!0),oe(se,ae,he,"\u25b3","\\bigtriangleup"),oe(se,ae,he,"\u25bd","\\bigtriangledown"),oe(se,ae,he,"\u2020","\\dagger"),oe(se,ae,he,"\u22c4","\\diamond"),oe(se,ae,he,"\u22c6","\\star"),oe(se,ae,he,"\u25c3","\\triangleleft"),oe(se,ae,he,"\u25b9","\\triangleright"),oe(se,ae,ge,"{","\\{"),oe(ie,ae,xe,"{","\\{"),oe(ie,ae,xe,"{","\\textbraceleft"),oe(se,ae,me,"}","\\}"),oe(ie,ae,xe,"}","\\}"),oe(ie,ae,xe,"}","\\textbraceright"),oe(se,ae,ge,"{","\\lbrace"),oe(se,ae,me,"}","\\rbrace"),oe(se,ae,ge,"[","\\lbrack",!0),oe(ie,ae,xe,"[","\\lbrack",!0),oe(se,ae,me,"]","\\rbrack",!0),oe(ie,ae,xe,"]","\\rbrack",!0),oe(se,ae,ge,"(","\\lparen",!0),oe(se,ae,me,")","\\rparen",!0),oe(ie,ae,xe,"<","\\textless",!0),oe(ie,ae,xe,">","\\textgreater",!0),oe(se,ae,ge,"\u230a","\\lfloor",!0),oe(se,ae,me,"\u230b","\\rfloor",!0),oe(se,ae,ge,"\u2308","\\lceil",!0),oe(se,ae,me,"\u2309","\\rceil",!0),oe(se,ae,xe,"\\","\\backslash"),oe(se,ae,xe,"\u2223","|"),oe(se,ae,xe,"\u2223","\\vert"),oe(ie,ae,xe,"|","\\textbar",!0),oe(se,ae,xe,"\u2225","\\|"),oe(se,ae,xe,"\u2225","\\Vert"),oe(ie,ae,xe,"\u2225","\\textbardbl"),oe(ie,ae,xe,"~","\\textasciitilde"),oe(ie,ae,xe,"\\","\\textbackslash"),oe(ie,ae,xe,"^","\\textasciicircum"),oe(se,ae,be,"\u2191","\\uparrow",!0),oe(se,ae,be,"\u21d1","\\Uparrow",!0),oe(se,ae,be,"\u2193","\\downarrow",!0),oe(se,ae,be,"\u21d3","\\Downarrow",!0),oe(se,ae,be,"\u2195","\\updownarrow",!0),oe(se,ae,be,"\u21d5","\\Updownarrow",!0),oe(se,ae,de,"\u2210","\\coprod"),oe(se,ae,de,"\u22c1","\\bigvee"),oe(se,ae,de,"\u22c0","\\bigwedge"),oe(se,ae,de,"\u2a04","\\biguplus"),oe(se,ae,de,"\u22c2","\\bigcap"),oe(se,ae,de,"\u22c3","\\bigcup"),oe(se,ae,de,"\u222b","\\int"),oe(se,ae,de,"\u222b","\\intop"),oe(se,ae,de,"\u222c","\\iint"),oe(se,ae,de,"\u222d","\\iiint"),oe(se,ae,de,"\u220f","\\prod"),oe(se,ae,de,"\u2211","\\sum"),oe(se,ae,de,"\u2a02","\\bigotimes"),oe(se,ae,de,"\u2a01","\\bigoplus"),oe(se,ae,de,"\u2a00","\\bigodot"),oe(se,ae,de,"\u222e","\\oint"),oe(se,ae,de,"\u222f","\\oiint"),oe(se,ae,de,"\u2230","\\oiiint"),oe(se,ae,de,"\u2a06","\\bigsqcup"),oe(se,ae,de,"\u222b","\\smallint"),oe(ie,ae,ue,"\u2026","\\textellipsis"),oe(se,ae,ue,"\u2026","\\mathellipsis"),oe(ie,ae,ue,"\u2026","\\ldots",!0),oe(se,ae,ue,"\u2026","\\ldots",!0),oe(se,ae,ue,"\u22ef","\\@cdots",!0),oe(se,ae,ue,"\u22f1","\\ddots",!0),oe(se,ae,xe,"\u22ee","\\varvdots"),oe(ie,ae,xe,"\u22ee","\\varvdots"),oe(se,ae,ce,"\u02ca","\\acute"),oe(se,ae,ce,"\u02cb","\\grave"),oe(se,ae,ce,"\xa8","\\ddot"),oe(se,ae,ce,"~","\\tilde"),oe(se,ae,ce,"\u02c9","\\bar"),oe(se,ae,ce,"\u02d8","\\breve"),oe(se,ae,ce,"\u02c7","\\check"),oe(se,ae,ce,"^","\\hat"),oe(se,ae,ce,"\u20d7","\\vec"),oe(se,ae,ce,"\u02d9","\\dot"),oe(se,ae,ce,"\u02da","\\mathring"),oe(se,ae,pe,"\ue131","\\@imath"),oe(se,ae,pe,"\ue237","\\@jmath"),oe(se,ae,xe,"\u0131","\u0131"),oe(se,ae,xe,"\u0237","\u0237"),oe(ie,ae,xe,"\u0131","\\i",!0),oe(ie,ae,xe,"\u0237","\\j",!0),oe(ie,ae,xe,"\xdf","\\ss",!0),oe(ie,ae,xe,"\xe6","\\ae",!0),oe(ie,ae,xe,"\u0153","\\oe",!0),oe(ie,ae,xe,"\xf8","\\o",!0),oe(ie,ae,xe,"\xc6","\\AE",!0),oe(ie,ae,xe,"\u0152","\\OE",!0),oe(ie,ae,xe,"\xd8","\\O",!0),oe(ie,ae,ce,"\u02ca","\\'"),oe(ie,ae,ce,"\u02cb","\\`"),oe(ie,ae,ce,"\u02c6","\\^"),oe(ie,ae,ce,"\u02dc","\\~"),oe(ie,ae,ce,"\u02c9","\\="),oe(ie,ae,ce,"\u02d8","\\u"),oe(ie,ae,ce,"\u02d9","\\."),oe(ie,ae,ce,"\xb8","\\c"),oe(ie,ae,ce,"\u02da","\\r"),oe(ie,ae,ce,"\u02c7","\\v"),oe(ie,ae,ce,"\xa8",'\\"'),oe(ie,ae,ce,"\u02dd","\\H"),oe(ie,ae,ce,"\u25ef","\\textcircled");const we={"--":!0,"---":!0,"``":!0,"''":!0};oe(ie,ae,xe,"\u2013","--",!0),oe(ie,ae,xe,"\u2013","\\textendash"),oe(ie,ae,xe,"\u2014","---",!0),oe(ie,ae,xe,"\u2014","\\textemdash"),oe(ie,ae,xe,"\u2018","`",!0),oe(ie,ae,xe,"\u2018","\\textquoteleft"),oe(ie,ae,xe,"\u2019","'",!0),oe(ie,ae,xe,"\u2019","\\textquoteright"),oe(ie,ae,xe,"\u201c","``",!0),oe(ie,ae,xe,"\u201c","\\textquotedblleft"),oe(ie,ae,xe,"\u201d","''",!0),oe(ie,ae,xe,"\u201d","\\textquotedblright"),oe(se,ae,xe,"\xb0","\\degree",!0),oe(ie,ae,xe,"\xb0","\\degree"),oe(ie,ae,xe,"\xb0","\\textdegree",!0),oe(se,ae,xe,"\xa3","\\pounds"),oe(se,ae,xe,"\xa3","\\mathsterling",!0),oe(ie,ae,xe,"\xa3","\\pounds"),oe(ie,ae,xe,"\xa3","\\textsterling",!0),oe(se,le,xe,"\u2720","\\maltese"),oe(ie,le,xe,"\u2720","\\maltese");const ve='0123456789/@."';for(let e=0;e<14;e++){const t=ve.charAt(e);oe(se,ae,xe,t,t)}const ke='0123456789!@*()-=+";:?/.,';for(let e=0;e<25;e++){const t=ke.charAt(e);oe(ie,ae,xe,t,t)}const ze="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";for(let e=0;e<52;e++){const t=ze.charAt(e);oe(se,ae,pe,t,t),oe(ie,ae,xe,t,t)}let Se;oe(se,le,xe,"C","\u2102"),oe(ie,le,xe,"C","\u2102"),oe(se,le,xe,"H","\u210d"),oe(ie,le,xe,"H","\u210d"),oe(se,le,xe,"N","\u2115"),oe(ie,le,xe,"N","\u2115"),oe(se,le,xe,"P","\u2119"),oe(ie,le,xe,"P","\u2119"),oe(se,le,xe,"Q","\u211a"),oe(ie,le,xe,"Q","\u211a"),oe(se,le,xe,"R","\u211d"),oe(ie,le,xe,"R","\u211d"),oe(se,le,xe,"Z","\u2124"),oe(ie,le,xe,"Z","\u2124"),oe(se,ae,pe,"h","\u210e"),oe(ie,ae,pe,"h","\u210e");for(let e=0;e<52;e++){const t=ze.charAt(e);Se=String.fromCharCode(55349,56320+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,56372+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,56424+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,56580+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,56684+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,56736+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,56788+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,56840+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,56944+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),e<26&&(Se=String.fromCharCode(55349,56632+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,56476+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se))}Se=String.fromCharCode(55349,56668),oe(se,ae,pe,"k",Se),oe(ie,ae,xe,"k",Se);for(let e=0;e<10;e++){const t=e.toString();Se=String.fromCharCode(55349,57294+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,57314+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,57324+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se),Se=String.fromCharCode(55349,57334+e),oe(se,ae,pe,t,Se),oe(ie,ae,xe,t,Se)}const Me="\xd0\xde\xfe";for(let e=0;e<3;e++){const t=Me.charAt(e);oe(se,ae,pe,t,t),oe(ie,ae,xe,t,t)}const Ae={mathClass:"mathbf",textClass:"textbf",font:"Main-Bold"},Te={mathClass:"mathnormal",textClass:"textit",font:"Math-Italic"},Ce={mathClass:"boldsymbol",textClass:"boldsymbol",font:"Main-BoldItalic"},Be={mathClass:"",textClass:"",font:""},qe={mathClass:"mathfrak",textClass:"textfrak",font:"Fraktur-Regular"},Ie={mathClass:"mathbb",textClass:"textbb",font:"AMS-Regular"},Re={mathClass:"mathboldfrak",textClass:"textboldfrak",font:"Fraktur-Regular"},Oe={mathClass:"mathsf",textClass:"textsf",font:"SansSerif-Regular"},Ee={mathClass:"mathboldsf",textClass:"textboldsf",font:"SansSerif-Bold"},He={mathClass:"mathitsf",textClass:"textitsf",font:"SansSerif-Italic"},Ne={mathClass:"mathtt",textClass:"texttt",font:"Typewriter-Regular"},De=[Ae,Ae,Te,Te,Ce,Ce,{mathClass:"mathscr",textClass:"textscr",font:"Script-Regular"},Be,Be,Be,qe,qe,Ie,Ie,Re,Re,Oe,Oe,Ee,Ee,He,He,Be,Be,Ne,Ne],Le=[Ae,Be,Oe,Ee,Ne],Fe=function(e,t,r){if(ne[r][e]){const t=ne[r][e].replace;t&&(e=t)}return{value:e,metrics:ee(e,t,r)}},Pe=function(e,t,r,n,o){const s=Fe(e,t,r),i=s.metrics;let a;if(e=s.value,i){let t=i.italic;("text"===r||n&&"mathit"===n.font)&&(t=0),a=new W(e,i.height,i.depth,t,i.skew,i.width,o)}else"undefined"!=typeof console&&console.warn("No character metrics for '"+e+"' in style '"+t+"' and mode '"+r+"'"),a=new W(e,0,0,0,0,0,o);if(n){a.maxFontSize=n.sizeMultiplier,n.style.isTight()&&a.classes.push("mtight");const e=n.getColor();e&&(a.style.color=e)}return a},Ve=function(e,t,r,n){return void 0===n&&(n=[]),"boldsymbol"===r.font&&Fe(e,"Main-Bold",t).metrics?Pe(e,"Main-Bold",t,r,n.concat(["mathbf"])):"\\"===e||"main"===ne[t][e].font?Pe(e,"Main-Regular",t,r,n):Pe(e,"AMS-Regular",t,r,n.concat(["amsrm"]))},Ge=function(e,t){const r="mathord"===e.type?"mathord":"textord",o=e.mode,s=e.text,i=["mord"],a=t.font,l=t.fontFamily,c=t.fontWeight,h=t.fontShape,m="math"===o||"text"===o&&!!a,u=m?a:l;let p="",d="";if(55349===s.charCodeAt(0)){const e=(e=>{const t=1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536;if(119808<=t&&t<120484){const e=Math.floor((t-119808)/26);return De[e]}if(120782<=t&&t<=120831){const e=Math.floor((t-120782)/10);return Le[e]}if(120485===t||120486===t)return De[0];if(120486{if(D(e.classes)!==D(t.classes)||e.skew!==t.skew||e.maxFontSize!==t.maxFontSize||0!==e.italic&&e.hasClass("mathnormal"))return!1;if(1===e.classes.length){const t=e.classes[0];if("mbin"===t||"mord"===t)return!1}for(const r of Object.keys(e.style))if(e.style[r]!==t.style[r])return!1;for(const r of Object.keys(t.style))if(e.style[r]!==t.style[r])return!1;return!0},Ue=e=>{for(let t=0;tt&&(t=s.height),s.depth>r&&(r=s.depth),s.maxFontSize>n&&(n=s.maxFontSize)}e.height=t,e.depth=r,e.maxFontSize=n},Ye=function(e,t,r,n){const o=new j(e,t,r,n);return Xe(o),o},We=(e,t,r,n)=>new j(e,t,r,n),_e=function(e,t,r){const n=Ye([e],[],t);return n.height=Math.max(r||t.fontMetrics().defaultRuleThickness,t.minRuleThickness),n.style.borderBottomWidth=N(n.height),n.maxFontSize=1,n},$e=function(e){const t=new I(e);return Xe(t),t},Ze=function(e,t){return e instanceof I?Ye([],[e],t):e},Ke=function(e,t){const r=function(e){if("individualShift"===e.positionType){const t=e.children,r=[t[0]],n=-t[0].shift-t[0].elem.depth;let o=n;for(let e=1;e{const r=Ye(["mspace"],[],t),n=H(e,t);return r.style.marginRight=N(n),r},Qe=(e,t,r)=>{let n,o;switch(e){case"amsrm":n="AMS";break;case"textrm":n="Main";break;case"textsf":n="SansSerif";break;case"texttt":n="Typewriter";break;default:n=e}return o="textbf"===t&&"textit"===r?"BoldItalic":"textbf"===t?"Bold":"textit"===r?"Italic":"Regular",n+"-"+o},et={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathnormal:{variant:"italic",fontName:"Math-Italic"},mathsfit:{variant:"sans-serif-italic",fontName:"SansSerif-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},tt={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]},rt=function(e,t){const r=tt[e],n=r[0],o=r[1],s=r[2],i=new $(n),a=new _([i],{width:N(o),height:N(s),style:"width:"+N(o),viewBox:"0 0 "+1e3*o+" "+1e3*s,preserveAspectRatio:"xMinYMin"}),l=We(["katex-overlay"],[a],t);return l.height=s,l.style.height=N(s),l.style.width=N(o),l},nt={number:3,unit:"mu"},ot={number:4,unit:"mu"},st={number:5,unit:"mu"},it={mord:{mop:nt,mbin:ot,mrel:st,minner:nt},mop:{mord:nt,mop:nt,mrel:st,minner:nt},mbin:{mord:ot,mop:ot,mopen:ot,minner:ot},mrel:{mord:st,mop:st,mopen:st,minner:st},mopen:{},mclose:{mop:nt,mbin:ot,mrel:st,minner:nt},mpunct:{mord:nt,mop:nt,mrel:st,mopen:nt,mclose:nt,mpunct:nt,minner:nt},minner:{mord:nt,mop:nt,mbin:ot,mrel:st,mopen:nt,mpunct:nt,minner:nt}},at={mord:{mop:nt},mop:{mord:nt,mop:nt},mbin:{},mrel:{},mopen:{},mclose:{mop:nt},mpunct:{},minner:{mop:nt}},lt={},ct={},ht={};function mt(e){const t=e.type,r=e.names,n=e.htmlBuilder,o=e.mathmlBuilder;for(let t=0;t{const r=t.classes[0],n=e.classes[0];"mbin"===r&&ft.has(n)?t.classes[0]="mord":"mbin"===n&>.has(r)&&(e.classes[0]="mord")},{node:i},a,l),wt(o,(e,t)=>{var r,n;const o=zt(t),i=zt(e),a=o&&i?e.hasClass("mtight")?null==(r=at[o])?void 0:r[i]:null==(n=it[o])?void 0:n[i]:null;if(a)return Je(a,s)},{node:i},a,l),o},wt=function(e,t,r,n,o){n&&e.push(n);let s=0;for(;sr=>{e.splice(t+1,0,r),s++})(s)}n&&e.pop()},vt=function(e){return e instanceof I||e instanceof U||e instanceof j&&e.hasClass("enclosing")?e:null},kt=function(e,t){const r=vt(e);if(r){const e=r.children;if(e.length){if("right"===t)return kt(e[e.length-1],"right");if("left"===t)return kt(e[0],"left")}}return e},zt=function(e,t){if(!e)return null;t&&(e=kt(e,t));const r=e.classes[0];return yt[r]||null},St=function(e,t){const r=["nulldelimiter"].concat(e.baseSizingClasses());return Ye(t.concat(r))},Mt=function(e,t,r){if(!e)return Ye();if(ct[e.type]){let n=ct[e.type](e,t);if(r&&t.size!==r.size){n=Ye(t.sizingClasses(r),[n],t);const e=t.sizeMultiplier/r.sizeMultiplier;n.height*=e,n.depth*=e}return n}throw new n("Got group of unknown type: '"+e.type+"'")};function At(e,t){const r=Ye(["katex-base"],e,t),n=Ye(["katex-strut"]);return n.style.height=N(r.height+r.depth),r.depth&&(n.style.verticalAlign=N(-r.depth)),r.children.unshift(n),r}function Tt(e,t){let r=null;1===e.length&&"tag"===e[0].type&&(r=e[0].tag,e=e[0].body);const n=xt(e,t,"root");let o;2===n.length&&n[1].hasClass("katex-tag")&&(o=n.pop());const s=[];let i,a=[];for(let e=0;e0&&(s.push(At(a,t)),a=[]),s.push(n[e]));a.length>0&&s.push(At(a,t)),r?(i=At(xt(r,t,!0),t),i.classes=["katex-tag"],s.push(i)):o&&s.push(o);const l=Ye(["katex-html"],s);if(l.setAttribute("aria-hidden","true"),i){const e=i.children[0];e.style.height=N(l.height+l.depth),l.depth&&(e.style.verticalAlign=N(-l.depth))}return l}function Ct(e){return new I(e)}class Bt{constructor(e,t,r){this.type=void 0,this.attributes=void 0,this.children=void 0,this.classes=void 0,this.type=e,this.attributes={},this.children=t||[],this.classes=r||[]}setAttribute(e,t){this.attributes[e]=t}getAttribute(e){return this.attributes[e]}toNode(){const e=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(const t of Object.entries(this.attributes)){const r=t[0],n=t[1];e.setAttribute(r,n)}this.classes.length>0&&(e.className=D(this.classes));for(let t=0;t0&&(e+=' class ="'+l(D(this.classes))+'"'),e+=">";for(let t=0;t",e}toText(){return this.children.map(e=>e.toText()).join("")}}class qt{constructor(e){this.text=void 0,this.text=e}toNode(){return document.createTextNode(this.text)}toMarkup(){return l(this.toText())}toText(){return this.text}}class It{constructor(e){this.width=void 0,this.character=void 0,this.width=e,this.character=e>=.05555&&e<=.05556?"\u200a":e>=.1666&&e<=.1667?"\u2009":e>=.2222&&e<=.2223?"\u2005":e>=.2777&&e<=.2778?"\u2005\u200a":e>=-.05556&&e<=-.05555?"\u200a\u2063":e>=-.1667&&e<=-.1666?"\u2009\u2063":e>=-.2223&&e<=-.2222?"\u205f\u2063":e>=-.2778&&e<=-.2777?"\u2005\u2063":null}toNode(){if(this.character)return document.createTextNode(this.character);{const e=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return e.setAttribute("width",N(this.width)),e}}toMarkup(){return this.character?""+this.character+"":''}toText(){return this.character?this.character:" "}}const Rt=new Set(["\\imath","\\jmath"]),Ot=new Set(["mrow","mtable"]),Et=function(e,t,r){var n,o;return ne[t][e]&&ne[t][e].replace&&55349!==e.charCodeAt(0)&&(!Object.prototype.hasOwnProperty.call(we,e)||"tt"!==(null==r||null==(n=r.fontFamily)?void 0:n.slice(4,6))&&"tt"!==(null==r||null==(o=r.font)?void 0:o.slice(4,6)))&&(e=ne[t][e].replace),new qt(e)},Ht=function(e){return 1===e.length?e[0]:new Bt("mrow",e)},Nt={mathit:"italic",boldsymbol:e=>"textord"===e.type?"bold":"bold-italic",mathbf:"bold",mathbb:"double-struck",mathsfit:"sans-serif-italic",mathfrak:"fraktur",mathscr:"script",mathcal:"script",mathsf:"sans-serif",mathtt:"monospace"},Dt=(e,t)=>{if("text"===e.mode){if("texttt"===t.fontFamily)return"monospace";if("textsf"===t.fontFamily)return"textit"===t.fontShape&&"textbf"===t.fontWeight?"sans-serif-bold-italic":"textit"===t.fontShape?"sans-serif-italic":"textbf"===t.fontWeight?"bold-sans-serif":"sans-serif";if("textit"===t.fontShape&&"textbf"===t.fontWeight)return"bold-italic";if("textit"===t.fontShape)return"italic";if("textbf"===t.fontWeight)return"bold"}const r=t.font;if(!r||"mathnormal"===r)return null;const n=e.mode,o=Nt[r];if(o)return"function"==typeof o?o(e):o;let s=e.text;if(Rt.has(s))return null;if(ne[n][s]){const e=ne[n][s].replace;e&&(s=e)}return ee(s,et[r].fontName,n)?et[r].variant:null};function Lt(e){if(!e)return!1;if("mi"===e.type&&1===e.children.length){const t=e.children[0];return t instanceof qt&&"."===t.text}if("mo"===e.type&&1===e.children.length&&"true"===e.getAttribute("separator")&&"0em"===e.getAttribute("lspace")&&"0em"===e.getAttribute("rspace")){const t=e.children[0];return t instanceof qt&&","===t.text}return!1}const Ft=function(e,t,r){if(1===e.length){const n=Vt(e[0],t);return r&&n instanceof Bt&&"mo"===n.type&&(n.setAttribute("lspace","0em"),n.setAttribute("rspace","0em")),[n]}const n=[];let o;for(let r=0;r=1&&("mn"===o.type||Lt(o))){const e=s.children[0];e instanceof Bt&&"mn"===e.type&&(e.children=[...o.children,...e.children],n.pop())}else if("mi"===o.type&&1===o.children.length){const e=o.children[0];if(e instanceof qt&&"\u0338"===e.text&&("mo"===s.type||"mi"===s.type||"mn"===s.type)){const e=s.children[0];e instanceof qt&&e.text.length>0&&(e.text=e.text.slice(0,1)+"\u0338"+e.text.slice(1),n.pop())}}}n.push(s),o=s}return n},Pt=function(e,t,r){return Ht(Ft(e,t,r))},Vt=function(e,t){if(!e)return new Bt("mrow");if(ht[e.type])return ht[e.type](e,t);throw new n("Got group of unknown type: '"+e.type+"'")};function Gt(e,t,r,n,o){const s=Ft(e,r);let i;i=1===s.length&&s[0]instanceof Bt&&Ot.has(s[0].type)?s[0]:new Bt("mrow",s);const a=new Bt("annotation",[new qt(t)]);a.setAttribute("encoding","application/x-tex");const l=new Bt("semantics",[i,a]),c=new Bt("math",[l]);c.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),n&&c.setAttribute("display","block");return Ye([o?"katex":"katex-mathml"],[c])}const jt=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],Ut=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],Xt=function(e,t){return t.size<2?e:jt[e-1][t.size-1]};class Yt{constructor(e){this.style=void 0,this.color=void 0,this.size=void 0,this.textSize=void 0,this.phantom=void 0,this.font=void 0,this.fontFamily=void 0,this.fontWeight=void 0,this.fontShape=void 0,this.sizeMultiplier=void 0,this.maxSize=void 0,this.minRuleThickness=void 0,this._fontMetrics=void 0,this.style=e.style,this.color=e.color,this.size=e.size||Yt.BASESIZE,this.textSize=e.textSize||this.size,this.phantom=!!e.phantom,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.sizeMultiplier=Ut[this.size-1],this.maxSize=e.maxSize,this.minRuleThickness=e.minRuleThickness,this._fontMetrics=void 0}extend(e){const t={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};return Object.assign(t,e),new Yt(t)}havingStyle(e){return this.style===e?this:this.extend({style:e,size:Xt(this.textSize,e)})}havingCrampedStyle(){return this.havingStyle(this.style.cramp())}havingSize(e){return this.size===e&&this.textSize===e?this:this.extend({style:this.style.text(),size:e,textSize:e,sizeMultiplier:Ut[e-1]})}havingBaseStyle(e){e=e||this.style.text();const t=Xt(Yt.BASESIZE,e);return this.size===t&&this.textSize===Yt.BASESIZE&&this.style===e?this:this.extend({style:e,size:t})}havingBaseSizing(){let e;switch(this.style.id){case 4:case 5:e=3;break;case 6:case 7:e=1;break;default:e=6}return this.extend({style:this.style.text(),size:e})}withColor(e){return this.extend({color:e})}withPhantom(){return this.extend({phantom:!0})}withFont(e){return this.extend({font:e})}withTextFontFamily(e){return this.extend({fontFamily:e,font:""})}withTextFontWeight(e){return this.extend({fontWeight:e,font:""})}withTextFontShape(e){return this.extend({fontShape:e,font:""})}sizingClasses(e){return e.size!==this.size?["katex-sizing","reset-size"+e.size,"size"+this.size]:[]}baseSizingClasses(){return this.size!==Yt.BASESIZE?["katex-sizing","reset-size"+this.size,"size"+Yt.BASESIZE]:[]}fontMetrics(){return this._fontMetrics||(this._fontMetrics=function(e){let t;if(t=e>=5?0:e>=3?1:2,!te[t]){const e=te[t]={cssEmPerMu:J.quad[t]/18};for(const r of Object.keys(J))e[r]=J[r][t]}return te[t]}(this.size)),this._fontMetrics}getColor(){return this.phantom?"transparent":this.color}}Yt.BASESIZE=6;var Wt=Yt;const _t=function(e){return new Wt({style:e.displayMode?S.DISPLAY:S.TEXT,maxSize:e.maxSize,minRuleThickness:e.minRuleThickness})},$t=function(e,t){if(t.displayMode){const r=["katex-display"];t.leqno&&r.push("leqno"),t.fleqn&&r.push("fleqn"),e=Ye(r,[e])}return e},Zt=function(e,t,r){const n=_t(r);let o;if("mathml"===r.output)return Gt(e,t,n,r.displayMode,!0);if("html"===r.output){const t=Tt(e,n);o=Ye(["katex"],[t])}else{const s=Gt(e,t,n,r.displayMode,!1),i=Tt(e,n);o=Ye(["katex"],[s,i])}return $t(o,r)};const Kt={widehat:"^",widecheck:"\u02c7",widetilde:"~",utilde:"~",overleftarrow:"\u2190",underleftarrow:"\u2190",xleftarrow:"\u2190",overrightarrow:"\u2192",underrightarrow:"\u2192",xrightarrow:"\u2192",underbrace:"\u23df",overbrace:"\u23de",underbracket:"\u23b5",overbracket:"\u23b4",overgroup:"\u23e0",undergroup:"\u23e1",overleftrightarrow:"\u2194",underleftrightarrow:"\u2194",xleftrightarrow:"\u2194",Overrightarrow:"\u21d2",xRightarrow:"\u21d2",overleftharpoon:"\u21bc",xleftharpoonup:"\u21bc",overrightharpoon:"\u21c0",xrightharpoonup:"\u21c0",xLeftarrow:"\u21d0",xLeftrightarrow:"\u21d4",xhookleftarrow:"\u21a9",xhookrightarrow:"\u21aa",xmapsto:"\u21a6",xrightharpoondown:"\u21c1",xleftharpoondown:"\u21bd",xrightleftharpoons:"\u21cc",xleftrightharpoons:"\u21cb",xtwoheadleftarrow:"\u219e",xtwoheadrightarrow:"\u21a0",xlongequal:"=",xtofrom:"\u21c4",xrightleftarrows:"\u21c4",xrightequilibrium:"\u21cc",xleftequilibrium:"\u21cb","\\cdrightarrow":"\u2192","\\cdleftarrow":"\u2190","\\cdlongequal":"="},Jt=function(e){const t=new Bt("mo",[new qt(Kt[e.replace(/^\\/,"")])]);return t.setAttribute("stretchy","true"),t},Qt={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],"\\cdrightarrow":[["rightarrow"],3,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],"\\cdleftarrow":[["leftarrow"],3,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],"\\cdlongequal":[["longequal"],3,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overbracket:[["leftbracketover","rightbracketover"],1.6,440],underbracket:[["leftbracketunder","rightbracketunder"],1.6,410],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]},er=new Set(["widehat","widecheck","widetilde","utilde"]),tr=function(e,t){const r=function(){let r=4e5;const n=e.label.slice(1);if(er.has(n)&&"base"in e){const o="ordgroup"===e.base.type?e.base.body.length:1;let s,i,a;if(o>5)"widehat"===n||"widecheck"===n?(s=420,r=2364,a=.42,i=n+"4"):(s=312,r=2340,a=.34,i="tilde4");else{const e=[1,1,2,2,3,3][o];"widehat"===n||"widecheck"===n?(r=[0,1062,2364,2364,2364][e],s=[0,239,300,360,420][e],a=[0,.24,.3,.3,.36,.42][e],i=n+e):(r=[0,600,1033,2339,2340][e],s=[0,260,286,306,312][e],a=[0,.26,.286,.3,.306,.34][e],i="tilde"+e)}const l=new $(i),c=new _([l],{width:"100%",height:N(a),viewBox:"0 0 "+r+" "+s,preserveAspectRatio:"none"});return{span:We([],[c],t),minWidth:0,height:a}}{const e=[],o=Qt[n];if(!o)throw new Error('No SVG data for "'+n+'".');const s=o[0],i=o[1],a=o[2],l=a/1e3,c=s.length;let h,m;if(1===c){if(4!==o.length)throw new Error('Expected 4-tuple for single-path SVG data "'+n+'".');h=["hide-tail"],m=[o[3]]}else if(2===c)h=["halfarrow-left","halfarrow-right"],m=["xMinYMin","xMaxYMin"];else{if(3!==c)throw new Error("Correct katexImagesData or update code here to support\n "+c+" children.");h=["brace-left","brace-center","brace-right"],m=["xMinYMin","xMidYMin","xMaxYMin"]}for(let n=0;n0&&(n.style.minWidth=N(o)),n},rr=new Set(["bin","close","inner","open","punct","rel"]),nr=new Set(["accent-token","mathord","op-token","spacing","textord"]);function or(e,t){if(!e||e.type!==t)throw new Error("Expected node of type "+t+", but got "+(e?"node of type "+e.type:String(e)));return e}function sr(e){const t=ir(e);if(!t)throw new Error("Expected node of symbol group type, but got "+(e?"node of type "+e.type:String(e)));return t}function ir(e){return"atom"===e.type||nr.has(e.type)?e:null}function ar(e,t,r){let o="";for(const s of e.body)if("textord"===s.type)o+=s.text;else{if(!r||"spacing"!==s.type||" "!==s.text)throw new n(t,e);o+=" "}return o}const lr=e=>{return e instanceof W?e:((t=e)instanceof j||t instanceof U||t instanceof I)&&1===e.children.length?lr(e.children[0]):void 0;var t},cr=(e,t)=>{let r,n,o;e&&"supsub"===e.type?(n=or(e.base,"accent"),r=n.base,e.base=r,o=function(e){if(e instanceof j)return e;throw new Error("Expected span but got "+String(e)+".")}(Mt(e,t)),e.base=n):(n=or(e,"accent"),r=n.base);const s=Mt(r,t.havingCrampedStyle());let i=0;var a,l;n.isShifty&&m(r)&&(i=null!=(a=null==(l=lr(s))?void 0:l.skew)?a:0);const c="\\c"===n.label;let h,u=c?s.height+s.depth:Math.min(s.height,t.fontMetrics().xHeight);if(n.isStretchy)h=tr(n,t),h=Ke({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"elem",elem:h,wrapperClasses:["svg-align"],wrapperStyle:i>0?{width:"calc(100% - "+N(2*i)+")",marginLeft:N(2*i)}:void 0}]});else{let e,r;"\\vec"===n.label?(e=rt("vec",t),r=tt.vec[1]):(e=Ge({type:"textord",mode:n.mode,text:n.label},t),e=function(e){if(e instanceof W)return e;throw new Error("Expected symbolNode but got "+String(e)+".")}(e),e.italic=0,r=e.width,c&&(u+=e.depth)),h=Ye(["accent-body"],[e]);const o="\\textcircled"===n.label;o&&(h.classes.push("accent-full"),u=s.height);let a=i;o||(a-=r/2),h.style.left=N(a),"\\textcircled"===n.label&&(h.style.top=".2em"),h=Ke({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:-u},{type:"elem",elem:h}]})}const p=Ye(["mord","katex-accent"],[h],t);return o?(o.children[0]=p,o.height=Math.max(p.height,o.height),o.classes[0]="mord",o):p},hr=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map(e=>"\\"+e).join("|"));mt({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],numArgs:1,handler:(e,t)=>{const r=pt(t[0]),n=!hr.test(e.funcName),o=!n||"\\widehat"===e.funcName||"\\widetilde"===e.funcName||"\\widecheck"===e.funcName;return{type:"accent",mode:e.parser.mode,label:e.funcName,isStretchy:n,isShifty:o,base:r}},htmlBuilder:cr,mathmlBuilder:(e,t)=>{const r=e.isStretchy?Jt(e.label):new Bt("mo",[Et(e.label,e.mode)]),n=new Bt("mover",[Vt(e.base,t),r]);return n.setAttribute("accent","true"),n}}),mt({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\c","\\r","\\H","\\v","\\textcircled"],numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"],handler:(e,t)=>{const r=t[0];let n=e.parser.mode;return"math"===n&&(e.parser.settings.reportNonstrict("mathVsTextAccents","LaTeX's accent "+e.funcName+" works only in text mode"),n="text"),{type:"accent",mode:n,label:e.funcName,isStretchy:!1,isShifty:!0,base:r}}}),mt({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],numArgs:1,handler:(e,t)=>{let r=e.parser,n=e.funcName;const o=t[0];return{type:"accentUnder",mode:r.mode,label:n,base:o}},htmlBuilder:(e,t)=>{const r=Mt(e.base,t),n=tr(e,t),o="\\utilde"===e.label?.12:0,s=Ke({positionType:"top",positionData:r.height,children:[{type:"elem",elem:n,wrapperClasses:["svg-align"]},{type:"kern",size:o},{type:"elem",elem:r}]});return Ye(["mord","accentunder"],[s],t)},mathmlBuilder:(e,t)=>{const r=Jt(e.label),n=new Bt("munder",[Vt(e.base,t),r]);return n.setAttribute("accentunder","true"),n}});const mr=e=>{const t=new Bt("mpadded",e?[e]:[]);return t.setAttribute("width","+0.6em"),t.setAttribute("lspace","0.3em"),t};mt({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium","\\\\cdrightarrow","\\\\cdleftarrow","\\\\cdlongequal"],numArgs:1,numOptionalArgs:1,handler(e,t,r){let n=e.parser,o=e.funcName;return{type:"xArrow",mode:n.mode,label:o,body:t[0],below:r[0]}},htmlBuilder(e,t){const r=t.style;let n=t.havingStyle(r.sup());const o=Ze(Mt(e.body,n,t),t),s="\\x"===e.label.slice(0,2)?"x":"cd";let i;o.classes.push(s+"-arrow-pad"),e.below&&(n=t.havingStyle(r.sub()),i=Ze(Mt(e.below,n,t),t),i.classes.push(s+"-arrow-pad"));const a=tr(e,t),l=-t.fontMetrics().axisHeight+.5*a.height;let c,h=-t.fontMetrics().axisHeight-.5*a.height-.111;if((o.depth>.25||"\\xleftequilibrium"===e.label)&&(h-=o.depth),i){const e=-t.fontMetrics().axisHeight+i.height+.5*a.height+.111;c=Ke({positionType:"individualShift",children:[{type:"elem",elem:o,shift:h},{type:"elem",elem:a,shift:l,wrapperClasses:["svg-align"]},{type:"elem",elem:i,shift:e}]})}else c=Ke({positionType:"individualShift",children:[{type:"elem",elem:o,shift:h},{type:"elem",elem:a,shift:l,wrapperClasses:["svg-align"]}]});return Ye(["mrel","x-arrow"],[c],t)},mathmlBuilder(e,t){const r=Jt(e.label);let n;if(r.setAttribute("minsize","x"===e.label.charAt(0)?"1.75em":"3.0em"),e.body){const o=mr(Vt(e.body,t));if(e.below){const s=mr(Vt(e.below,t));n=new Bt("munderover",[r,s,o])}else n=new Bt("mover",[r,o])}else if(e.below){const o=mr(Vt(e.below,t));n=new Bt("munder",[r,o])}else n=mr(),n=new Bt("mover",[r,n]);return n}}),mt({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],numArgs:1,primitive:!0,handler(e,t){let r=e.parser,n=e.funcName;const o=t[0];return{type:"mclass",mode:r.mode,mclass:"m"+n.slice(5),body:dt(o),isCharacterBox:m(o)}},htmlBuilder:function(e,t){const r=xt(e.body,t,!0);return Ye([e.mclass],r,t)},mathmlBuilder:function(e,t){let r;const n=Ft(e.body,t);return"minner"===e.mclass?r=new Bt("mpadded",n):"mord"===e.mclass?e.isCharacterBox?(r=n[0],r.type="mi"):r=new Bt("mi",n):(e.isCharacterBox?(r=n[0],r.type="mo"):r=new Bt("mo",n),"mbin"===e.mclass?(r.attributes.lspace="0.22em",r.attributes.rspace="0.22em"):"mpunct"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0.17em"):"mopen"!==e.mclass&&"mclose"!==e.mclass||(r.attributes.lspace="0em",r.attributes.rspace="0em")),r}});const ur=e=>{const t="ordgroup"===e.type&&e.body.length?e.body[0]:e;return"atom"!==t.type||"bin"!==t.family&&"rel"!==t.family?"mord":"m"+t.family};mt({type:"mclass",names:["\\@binrel"],numArgs:2,handler(e,t){return{type:"mclass",mode:e.parser.mode,mclass:ur(t[0]),body:dt(t[1]),isCharacterBox:m(t[1])}}}),mt({type:"mclass",names:["\\stackrel","\\overset","\\underset"],numArgs:2,handler(e,t){let r=e.parser,n=e.funcName;const o=t[1],s=t[0];let i;i="\\stackrel"!==n?ur(o):"mrel";const a={type:"op",mode:o.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:"\\stackrel"!==n,body:dt(o)},l="\\underset"===n?{type:"supsub",mode:s.mode,base:a,sub:s}:{type:"supsub",mode:s.mode,base:a,sup:s};return{type:"mclass",mode:r.mode,mclass:i,body:[l],isCharacterBox:m(l)}}}),mt({type:"pmb",names:["\\pmb"],numArgs:1,allowedInText:!0,handler(e,t){return{type:"pmb",mode:e.parser.mode,mclass:ur(t[0]),body:dt(t[0])}},htmlBuilder(e,t){const r=xt(e.body,t,!0),n=Ye([e.mclass],r,t);return n.style.textShadow="0.02em 0.01em 0.04px",n},mathmlBuilder(e,t){const r=Ft(e.body,t),n=new Bt("mstyle",r);return n.setAttribute("style","text-shadow: 0.02em 0.01em 0.04px"),n}});const pr={">":"\\\\cdrightarrow","<":"\\\\cdleftarrow","=":"\\\\cdlongequal",A:"\\uparrow",V:"\\downarrow","|":"\\Vert",".":"no arrow"},dr=()=>({type:"styling",body:[],mode:"math",style:"display",resetFont:!0}),gr=e=>"textord"===e.type&&"@"===e.text,fr=(e,t)=>("mathord"===e.type||"atom"===e.type)&&e.text===t;function br(e,t,r){const n=pr[e];switch(n){case"\\\\cdrightarrow":case"\\\\cdleftarrow":return r.callFunction(n,[t[0]],[t[1]]);case"\\uparrow":case"\\downarrow":{const e={type:"atom",text:n,mode:"math",family:"rel"},o={type:"ordgroup",mode:"math",body:[r.callFunction("\\\\cdleft",[t[0]],[]),r.callFunction("\\Big",[e],[]),r.callFunction("\\\\cdright",[t[1]],[])]};return r.callFunction("\\\\cdparent",[o],[])}case"\\\\cdlongequal":return r.callFunction("\\\\cdlongequal",[],[]);case"\\Vert":{const e={type:"textord",text:"\\Vert",mode:"math"};return r.callFunction("\\Big",[e],[])}default:return{type:"textord",text:" ",mode:"math"}}}mt({type:"cdlabel",names:["\\\\cdleft","\\\\cdright"],numArgs:1,handler(e,t){let r=e.parser,n=e.funcName;return{type:"cdlabel",mode:r.mode,side:n.slice(4),label:t[0]}},htmlBuilder(e,t){const r=t.havingStyle(t.style.sup()),n=Ze(Mt(e.label,r,t),t);return n.classes.push("cd-label-"+e.side),n.style.bottom=N(.8-n.depth),n.height=0,n.depth=0,n},mathmlBuilder(e,t){let r=new Bt("mrow",[Vt(e.label,t)]);return r=new Bt("mpadded",[r]),r.setAttribute("width","0"),"left"===e.side&&r.setAttribute("lspace","-1width"),r.setAttribute("voffset","0.7em"),r=new Bt("mstyle",[r]),r.setAttribute("displaystyle","false"),r.setAttribute("scriptlevel","1"),r}}),mt({type:"cdlabelparent",names:["\\\\cdparent"],numArgs:1,handler(e,t){return{type:"cdlabelparent",mode:e.parser.mode,fragment:t[0]}},htmlBuilder(e,t){const r=Ze(Mt(e.fragment,t),t);return r.classes.push("cd-vert-arrow"),r},mathmlBuilder(e,t){return new Bt("mrow",[Vt(e.fragment,t)])}}),mt({type:"textord",names:["\\@char"],numArgs:1,allowedInText:!0,handler(e,t){let r=e.parser;const o=ar(or(t[0],"ordgroup"),"\\@char has non-numeric argument");let s,i=parseInt(o);if(isNaN(i))throw new n("\\@char has non-numeric argument "+o);if(i<0||i>1114111)throw new n("\\@char with invalid code point "+o);return i<=65535?s=String.fromCharCode(i):(i-=65536,s=String.fromCharCode(55296+(i>>10),56320+(1023&i))),{type:"textord",mode:r.mode,text:s}}});mt({type:"color",names:["\\textcolor"],numArgs:2,allowedInText:!0,argTypes:["color","original"],handler(e,t){let r=e.parser;const n=or(t[0],"color-token").color,o=t[1];return{type:"color",mode:r.mode,color:n,body:dt(o)}},htmlBuilder:(e,t)=>{const r=xt(e.body,t.withColor(e.color),!1);return $e(r)},mathmlBuilder:(e,t)=>{const r=Ft(e.body,t.withColor(e.color)),n=new Bt("mstyle",r);return n.setAttribute("mathcolor",e.color),n}}),mt({type:"color",names:["\\color"],numArgs:1,allowedInText:!0,argTypes:["color"],handler(e,t){let r=e.parser,n=e.breakOnTokenText;const o=or(t[0],"color-token").color;r.gullet.macros.set("\\current@color",o);const s=r.parseExpression(!0,n);return{type:"color",mode:r.mode,color:o,body:s}}}),mt({type:"cr",names:["\\\\"],numArgs:0,numOptionalArgs:0,allowedInText:!0,handler(e,t,r){let n=e.parser;const o="["===n.gullet.future().text?n.parseSizeGroup(!0):null,s=!n.settings.displayMode||!n.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode");return{type:"cr",mode:n.mode,newLine:s,size:o&&or(o,"size").value}},htmlBuilder(e,t){const r=Ye(["mspace"],[],t);return e.newLine&&(r.classes.push("katex-newline"),e.size&&(r.style.marginTop=N(H(e.size,t)))),r},mathmlBuilder(e,t){const r=new Bt("mspace");return e.newLine&&(r.setAttribute("linebreak","newline"),e.size&&r.setAttribute("height",N(H(e.size,t)))),r}});const yr={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},xr=e=>{const t=e.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(t))throw new n("Expected a control sequence",e);return t},wr=(e,t,r,n)=>{let o=e.gullet.macros.get(r.text);null==o&&(r.noexpand=!0,o={tokens:[r],numArgs:0,unexpandable:!e.gullet.isExpandable(r.text)}),e.gullet.macros.set(t,o,n)};mt({type:"internal",names:["\\global","\\long","\\\\globallong"],numArgs:0,allowedInText:!0,handler(e){let t=e.parser,r=e.funcName;t.consumeSpaces();const o=t.fetch();if(yr[o.text])return"\\global"!==r&&"\\\\globallong"!==r||(o.text=yr[o.text]),or(t.parseFunction(),"internal");throw new n("Invalid token after macro prefix",o)}}),mt({type:"internal",names:["\\def","\\gdef","\\edef","\\xdef"],numArgs:0,allowedInText:!0,primitive:!0,handler(e){let t=e.parser,r=e.funcName,o=t.gullet.popToken();const s=o.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(s))throw new n("Expected a control sequence",o);let i,a=0;const l=[[]];for(;"{"!==t.gullet.future().text;)if(o=t.gullet.popToken(),"#"===o.text){if("{"===t.gullet.future().text){i=t.gullet.future(),l[a].push("{");break}if(o=t.gullet.popToken(),!/^[1-9]$/.test(o.text))throw new n('Invalid argument number "'+o.text+'"');if(parseInt(o.text)!==a+1)throw new n('Argument number "'+o.text+'" out of order');a++,l.push([])}else{if("EOF"===o.text)throw new n("Expected a macro definition");l[a].push(o.text)}let c=t.gullet.consumeArg().tokens;return i&&c.unshift(i),"\\edef"!==r&&"\\xdef"!==r||(c=t.gullet.expandTokens(c),c.reverse()),t.gullet.macros.set(s,{tokens:c,numArgs:a,delimiters:l},r===yr[r]),{type:"internal",mode:t.mode}}}),mt({type:"internal",names:["\\let","\\\\globallet"],numArgs:0,allowedInText:!0,primitive:!0,handler(e){let t=e.parser,r=e.funcName;const n=xr(t.gullet.popToken());t.gullet.consumeSpaces();const o=(e=>{let t=e.gullet.popToken();return"="===t.text&&(t=e.gullet.popToken()," "===t.text&&(t=e.gullet.popToken())),t})(t);return wr(t,n,o,"\\\\globallet"===r),{type:"internal",mode:t.mode}}}),mt({type:"internal",names:["\\futurelet","\\\\globalfuture"],numArgs:0,allowedInText:!0,primitive:!0,handler(e){let t=e.parser,r=e.funcName;const n=xr(t.gullet.popToken()),o=t.gullet.popToken(),s=t.gullet.popToken();return wr(t,n,s,"\\\\globalfuture"===r),t.gullet.pushToken(s),t.gullet.pushToken(o),{type:"internal",mode:t.mode}}});const vr=function(e,t,r){const n=ee(ne.math[e]&&ne.math[e].replace||e,t,r);if(!n)throw new Error("Unsupported symbol "+e+" and font size "+t+".");return n},kr=function(e,t,r,n){const o=r.havingBaseStyle(t),s=Ye(n.concat(o.sizingClasses(r)),[e],r),i=o.sizeMultiplier/r.sizeMultiplier;return s.height*=i,s.depth*=i,s.maxFontSize=o.sizeMultiplier,s},zr=function(e,t,r){const n=t.havingBaseStyle(r),o=(1-t.sizeMultiplier/n.sizeMultiplier)*t.fontMetrics().axisHeight;e.classes.push("delimcenter"),e.style.top=N(o),e.height-=o,e.depth+=o},Sr=function(e,t,r,n,o,s){const i=function(e,t,r,n){return Pe(e,"Size"+t+"-Regular",r,n)}(e,t,o,n),a=kr(Ye(["delimsizing","size"+t],[i],n),S.TEXT,n,s);return r&&zr(a,n,S.TEXT),a},Mr=function(e,t,r){let n;n="Size1-Regular"===t?"delim-size1":"delim-size4";return{type:"elem",elem:Ye(["delimsizinginner",n],[Ye([],[Pe(e,t,r)])])}},Ar=function(e,t,r){const n=K["Size4-Regular"][e.charCodeAt(0)]?K["Size4-Regular"][e.charCodeAt(0)][4]:K["Size1-Regular"][e.charCodeAt(0)][4],o=new $("inner",function(e,t){switch(e){case"\u239c":return C("M291 0 H417 V"+t+" H291z");case"\u2223":return C("M145 0 H188 V"+t+" H145z");case"\u2225":return C("M145 0 H188 V"+t+" H145z")+C("M367 0 H410 V"+t+" H367z");case"\u239f":return C("M457 0 H583 V"+t+" H457z");case"\u23a2":return C("M319 0 H403 V"+t+" H319z");case"\u23a5":return C("M263 0 H347 V"+t+" H263z");case"\u23aa":return C("M384 0 H504 V"+t+" H384z");case"\u23d0":return C("M312 0 H355 V"+t+" H312z");case"\u2016":return C("M257 0 H300 V"+t+" H257z")+C("M478 0 H521 V"+t+" H478z");default:return""}}(e,Math.round(1e3*t))),s=new _([o],{width:N(n),height:N(t),style:"width:"+N(n),viewBox:"0 0 "+1e3*n+" "+Math.round(1e3*t),preserveAspectRatio:"xMinYMin"}),i=We([],[s],r);return i.height=t,i.style.height=N(t),i.style.width=N(n),{type:"elem",elem:i}},Tr={type:"kern",size:-.008},Cr=new Set(["|","\\lvert","\\rvert","\\vert"]),Br=new Set(["\\|","\\lVert","\\rVert","\\Vert"]),qr=function(e,t,r,n,o,s){let i,a,l,c,h="",m=0;i=l=c=e,a=null;let u="Size1-Regular";"\\uparrow"===e?l=c="\u23d0":"\\Uparrow"===e?l=c="\u2016":"\\downarrow"===e?i=l="\u23d0":"\\Downarrow"===e?i=l="\u2016":"\\updownarrow"===e?(i="\\uparrow",l="\u23d0",c="\\downarrow"):"\\Updownarrow"===e?(i="\\Uparrow",l="\u2016",c="\\Downarrow"):Cr.has(e)?(l="\u2223",h="vert",m=333):Br.has(e)?(l="\u2225",h="doublevert",m=556):"["===e||"\\lbrack"===e?(i="\u23a1",l="\u23a2",c="\u23a3",u="Size4-Regular",h="lbrack",m=667):"]"===e||"\\rbrack"===e?(i="\u23a4",l="\u23a5",c="\u23a6",u="Size4-Regular",h="rbrack",m=667):"\\lfloor"===e||"\u230a"===e?(l=i="\u23a2",c="\u23a3",u="Size4-Regular",h="lfloor",m=667):"\\lceil"===e||"\u2308"===e?(i="\u23a1",l=c="\u23a2",u="Size4-Regular",h="lceil",m=667):"\\rfloor"===e||"\u230b"===e?(l=i="\u23a5",c="\u23a6",u="Size4-Regular",h="rfloor",m=667):"\\rceil"===e||"\u2309"===e?(i="\u23a4",l=c="\u23a5",u="Size4-Regular",h="rceil",m=667):"("===e||"\\lparen"===e?(i="\u239b",l="\u239c",c="\u239d",u="Size4-Regular",h="lparen",m=875):")"===e||"\\rparen"===e?(i="\u239e",l="\u239f",c="\u23a0",u="Size4-Regular",h="rparen",m=875):"\\{"===e||"\\lbrace"===e?(i="\u23a7",a="\u23a8",c="\u23a9",l="\u23aa",u="Size4-Regular"):"\\}"===e||"\\rbrace"===e?(i="\u23ab",a="\u23ac",c="\u23ad",l="\u23aa",u="Size4-Regular"):"\\lgroup"===e||"\u27ee"===e?(i="\u23a7",c="\u23a9",l="\u23aa",u="Size4-Regular"):"\\rgroup"===e||"\u27ef"===e?(i="\u23ab",c="\u23ad",l="\u23aa",u="Size4-Regular"):"\\lmoustache"===e||"\u23b0"===e?(i="\u23a7",c="\u23ad",l="\u23aa",u="Size4-Regular"):"\\rmoustache"!==e&&"\u23b1"!==e||(i="\u23ab",c="\u23a9",l="\u23aa",u="Size4-Regular");const p=vr(i,u,o),d=p.height+p.depth,g=vr(l,u,o),f=g.height+g.depth,b=vr(c,u,o),y=b.height+b.depth;let x=0,w=1;if(null!==a){const e=vr(a,u,o);x=e.height+e.depth,w=2}const v=d+y+x,k=v+Math.max(0,Math.ceil((t-v)/(w*f)))*w*f;let z=n.fontMetrics().axisHeight;r&&(z*=n.sizeMultiplier);const M=k/2-z,A=[];if(h.length>0){const e=k-d-y,t=Math.round(1e3*k),r=function(e,t){switch(e){case"lbrack":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+" v1759 v84 h347 v-84\nH403z M403 1759 V0 H319 V1759 v"+t+" v1759 v84 h84z";case"rbrack":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+" v1759 H0 v84 H347z\nM347 1759 V0 H263 V1759 v"+t+" v1759 h84z";case"vert":return"M145 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M188 15 H145 v585 v"+t+" v585 h43z";case"doublevert":return"M145 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M188 15 H145 v585 v"+t+" v585 h43z\nM367 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M410 15 H367 v585 v"+t+" v585 h43z";case"lfloor":return"M319 602 V0 H403 V602 v"+t+" v1715 h263 v84 H319z\nMM319 602 V0 H403 V602 v"+t+" v1715 H319z";case"rfloor":return"M319 602 V0 H403 V602 v"+t+" v1799 H0 v-84 H319z\nMM319 602 V0 H403 V602 v"+t+" v1715 H319z";case"lceil":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+" v602 h84z\nM403 1759 V0 H319 V1759 v"+t+" v602 h84z";case"rceil":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+" v602 h84z\nM347 1759 V0 h-84 V1759 v"+t+" v602 h84z";case"lparen":return"M863,9c0,-2,-2,-5,-6,-9c0,0,-17,0,-17,0c-12.7,0,-19.3,0.3,-20,1\nc-5.3,5.3,-10.3,11,-15,17c-242.7,294.7,-395.3,682,-458,1162c-21.3,163.3,-33.3,349,\n-36,557 l0,"+(t+84)+"c0.2,6,0,26,0,60c2,159.3,10,310.7,24,454c53.3,528,210,\n949.7,470,1265c4.7,6,9.7,11.7,15,17c0.7,0.7,7,1,19,1c0,0,18,0,18,0c4,-4,6,-7,6,-9\nc0,-2.7,-3.3,-8.7,-10,-18c-135.3,-192.7,-235.5,-414.3,-300.5,-665c-65,-250.7,-102.5,\n-544.7,-112.5,-882c-2,-104,-3,-167,-3,-189\nl0,-"+(t+92)+"c0,-162.7,5.7,-314,17,-454c20.7,-272,63.7,-513,129,-723c65.3,\n-210,155.3,-396.3,270,-559c6.7,-9.3,10,-15.3,10,-18z";case"rparen":return"M76,0c-16.7,0,-25,3,-25,9c0,2,2,6.3,6,13c21.3,28.7,42.3,60.3,\n63,95c96.7,156.7,172.8,332.5,228.5,527.5c55.7,195,92.8,416.5,111.5,664.5\nc11.3,139.3,17,290.7,17,454c0,28,1.7,43,3.3,45l0,"+(t+9)+"\nc-3,4,-3.3,16.7,-3.3,38c0,162,-5.7,313.7,-17,455c-18.7,248,-55.8,469.3,-111.5,664\nc-55.7,194.7,-131.8,370.3,-228.5,527c-20.7,34.7,-41.7,66.3,-63,95c-2,3.3,-4,7,-6,11\nc0,7.3,5.7,11,17,11c0,0,11,0,11,0c9.3,0,14.3,-0.3,15,-1c5.3,-5.3,10.3,-11,15,-17\nc242.7,-294.7,395.3,-681.7,458,-1161c21.3,-164.7,33.3,-350.7,36,-558\nl0,-"+(t+144)+"c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7,\n-470,-1265c-4.7,-6,-9.7,-11.7,-15,-17c-0.7,-0.7,-6.7,-1,-18,-1z";default:throw new Error("Unknown stretchy delimiter.")}}(h,Math.round(1e3*e)),o=new $(h,r),s=N(m/1e3),i=N(t/1e3),a=new _([o],{width:s,height:i,viewBox:"0 0 "+m+" "+t}),l=We([],[a],n);l.height=t/1e3,l.style.width=s,l.style.height=i,A.push({type:"elem",elem:l})}else{if(A.push(Mr(c,u,o)),A.push(Tr),null===a){const e=k-d-y+.016;A.push(Ar(l,e,n))}else{const e=(k-d-y-x)/2+.016;A.push(Ar(l,e,n)),A.push(Tr),A.push(Mr(a,u,o)),A.push(Tr),A.push(Ar(l,e,n))}A.push(Tr),A.push(Mr(i,u,o))}const T=n.havingBaseStyle(S.TEXT),C=Ke({positionType:"bottom",positionData:M,children:A});return kr(Ye(["delimsizing","mult"],[C],T),S.TEXT,n,s)},Ir=.08,Rr=function(e,t,r,n,o){const s=function(e,t,r){t*=1e3;let n="";switch(e){case"sqrtMain":n=function(e,t){return"M95,"+(622+e+t)+"\nc-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14\nc0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54\nc44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10\ns173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429\nc69,-144,104.5,-217.7,106.5,-221\nl"+e/2.075+" -"+e+"\nc5.3,-9.3,12,-14,20,-14\nH400000v"+(40+e)+"H845.2724\ns-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7\nc-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z\nM"+(834+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,B);break;case"sqrtSize1":n=function(e,t){return"M263,"+(601+e+t)+"c0.7,0,18,39.7,52,119\nc34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120\nc340,-704.7,510.7,-1060.3,512,-1067\nl"+e/2.084+" -"+e+"\nc4.7,-7.3,11,-11,19,-11\nH40000v"+(40+e)+"H1012.3\ns-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232\nc-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1\ns-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26\nc-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z\nM"+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,B);break;case"sqrtSize2":n=function(e,t){return"M983 "+(10+e+t)+"\nl"+e/3.13+" -"+e+"\nc4,-6.7,10,-10,18,-10 H400000v"+(40+e)+"\nH1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7\ns-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744\nc-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30\nc26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722\nc56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5\nc53.7,-170.3,84.5,-266.8,92.5,-289.5z\nM"+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,B);break;case"sqrtSize3":n=function(e,t){return"M424,"+(2398+e+t)+"\nc-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514\nc0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20\ns-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121\ns209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081\nl"+e/4.223+" -"+e+"c4,-6.7,10,-10,18,-10 H400000\nv"+(40+e)+"H1014.6\ns-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185\nc-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2z M"+(1001+e)+" "+t+"\nh400000v"+(40+e)+"h-400000z"}(t,B);break;case"sqrtSize4":n=function(e,t){return"M473,"+(2713+e+t)+"\nc339.3,-1799.3,509.3,-2700,510,-2702 l"+e/5.298+" -"+e+"\nc3.3,-7.3,9.3,-11,18,-11 H400000v"+(40+e)+"H1017.7\ns-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200\nc0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26\ns76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,\n606zM"+(1001+e)+" "+t+"h400000v"+(40+e)+"H1017.7z"}(t,B);break;case"sqrtTall":n=function(e,t,r){return"M702 "+(e+t)+"H400000"+(40+e)+"\nH742v"+(r-54-t-e)+"l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1\nh-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170\nc-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667\n219 661 l218 661zM702 "+t+"H400000v"+(40+e)+"H742z"}(t,B,r)}return n}(e,n,r),i=new $(e,s),a=new _([i],{width:"400em",height:N(t),viewBox:"0 0 400000 "+r,preserveAspectRatio:"xMinYMin slice"});return We(["hide-tail"],[a],o)},Or=new Set(["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","\\surd"]),Er=new Set(["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1"]),Hr=new Set(["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"]),Nr=[0,1.2,1.8,2.4,3],Dr=function(e,t,r,o,s){if("<"===e||"\\lt"===e||"\u27e8"===e?e="\\langle":">"!==e&&"\\gt"!==e&&"\u27e9"!==e||(e="\\rangle"),Or.has(e)||Hr.has(e))return Sr(e,t,!1,r,o,s);if(Er.has(e))return qr(e,Nr[t],!1,r,o,s);throw new n("Illegal delimiter: '"+e+"'")},Lr=[{type:"small",style:S.SCRIPTSCRIPT},{type:"small",style:S.SCRIPT},{type:"small",style:S.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],Fr=[{type:"small",style:S.SCRIPTSCRIPT},{type:"small",style:S.SCRIPT},{type:"small",style:S.TEXT},{type:"stack"}],Pr=[{type:"small",style:S.SCRIPTSCRIPT},{type:"small",style:S.SCRIPT},{type:"small",style:S.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],Vr=function(e){if("small"===e.type)return"Main-Regular";if("large"===e.type)return"Size"+e.size+"-Regular";if("stack"===e.type)return"Size4-Regular";{const t=e.type;throw new Error("Add support for delim type '"+t+"' here.")}},Gr=function(e,t,r,n){for(let o=Math.min(2,3-n.style.size);ot)return s}return r[r.length-1]},jr=function(e,t,r,n,o,s){let i;"<"===e||"\\lt"===e||"\u27e8"===e?e="\\langle":">"!==e&&"\\gt"!==e&&"\u27e9"!==e||(e="\\rangle"),i=Hr.has(e)?Lr:Or.has(e)?Pr:Fr;const a=Gr(e,t,i,n);return"small"===a.type?function(e,t,r,n,o,s){const i=Pe(e,"Main-Regular",o,n),a=kr(i,t,n,s);return r&&zr(a,n,t),a}(e,a.style,r,n,o,s):"large"===a.type?Sr(e,a.size,r,n,o,s):qr(e,t,r,n,o,s)},Ur=function(e,t,r,n,o,s){const i=n.fontMetrics().axisHeight*n.sizeMultiplier,a=5/n.fontMetrics().ptPerEm,l=Math.max(t-i,r+i),c=Math.max(l/500*901,2*l-a);return jr(e,c,!0,n,o,s)},Xr={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},Yr=new Set(["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","<",">","\\langle","\u27e8","\\rangle","\u27e9","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."]);function Wr(e){return"isMiddle"in e}function _r(e,t){const r=ir(e);if(r&&Yr.has(r.text))return r;throw new n(r?"Invalid delimiter '"+r.text+"' after '"+t.funcName+"'":"Invalid delimiter type '"+e.type+"'",e)}function $r(e){if(!e.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}mt({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],numArgs:1,argTypes:["primitive"],handler:(e,t)=>{const r=_r(pt(t[0]),e);return{type:"delimsizing",mode:e.parser.mode,size:Xr[e.funcName].size,mclass:Xr[e.funcName].mclass,delim:r.text}},htmlBuilder:(e,t)=>"."===e.delim?Ye([e.mclass]):Dr(e.delim,e.size,t,e.mode,[e.mclass]),mathmlBuilder:e=>{const t=[];"."!==e.delim&&t.push(Et(e.delim,e.mode));const r=new Bt("mo",t);"mopen"===e.mclass||"mclose"===e.mclass?r.setAttribute("fence","true"):r.setAttribute("fence","false"),r.setAttribute("stretchy","true");const n=N(Nr[e.size]);return r.setAttribute("minsize",n),r.setAttribute("maxsize",n),r}}),mt({type:"leftright-right",names:["\\right"],numArgs:1,primitive:!0,handler:(e,t)=>{const r=e.parser.gullet.macros.get("\\current@color");if(r&&"string"!=typeof r)throw new n("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:e.parser.mode,delim:_r(t[0],e).text,color:r}}}),mt({type:"leftright",names:["\\left"],numArgs:1,primitive:!0,handler:(e,t)=>{const r=_r(t[0],e),n=e.parser;++n.leftrightDepth;const o=n.parseExpression(!1);--n.leftrightDepth,n.expect("\\right",!1);const s=or(n.parseFunction(),"leftright-right");return{type:"leftright",mode:n.mode,body:o,left:r.text,right:s.delim,rightColor:s.color}},htmlBuilder:(e,t)=>{$r(e);const r=xt(e.body,t,!0,["mopen","mclose"]);let n,o,s=0,i=0,a=!1;for(let e=0;e{$r(e);const r=Ft(e.body,t);if("."!==e.left){const t=new Bt("mo",[Et(e.left,e.mode)]);t.setAttribute("fence","true"),r.unshift(t)}if("."!==e.right){const t=new Bt("mo",[Et(e.right,e.mode)]);t.setAttribute("fence","true"),e.rightColor&&t.setAttribute("mathcolor",e.rightColor),r.push(t)}return Ht(r)}}),mt({type:"middle",names:["\\middle"],numArgs:1,primitive:!0,handler:(e,t)=>{const r=_r(t[0],e);if(!e.parser.leftrightDepth)throw new n("\\middle without preceding \\left",r);return{type:"middle",mode:e.parser.mode,delim:r.text}},htmlBuilder:(e,t)=>{let r;return"."===e.delim?r=St(t,[]):(r=Dr(e.delim,1,t,e.mode,[]),r.isMiddle={delim:e.delim,options:t}),r},mathmlBuilder:(e,t)=>{const r="\\vert"===e.delim||"|"===e.delim?Et("|","text"):Et(e.delim,e.mode),n=new Bt("mo",[r]);return n.setAttribute("fence","true"),n.setAttribute("lspace","0.05em"),n.setAttribute("rspace","0.05em"),n}});mt({type:"enclose",names:["\\colorbox"],numArgs:2,allowedInText:!0,argTypes:["color","hbox"],handler(e,t,r){let n=e.parser,o=e.funcName;const s=or(t[0],"color-token").color,i=t[1];return{type:"enclose",mode:n.mode,label:o,backgroundColor:s,body:i}},htmlBuilder:(e,t)=>{const r=Ze(Mt(e.body,t),t),n=e.label.slice(1);let o,s,i=t.sizeMultiplier;const a=m(e.body);if("sout"===n)o=Ye(["katex-stretchy","katex-sout"]),o.height=t.fontMetrics().defaultRuleThickness/i,s=-.5*t.fontMetrics().xHeight;else if("phase"===n){const e=H({number:.6,unit:"pt"},t),n=H({number:.35,unit:"ex"},t);i/=t.havingBaseSizing().sizeMultiplier;const a=r.height+r.depth+e+n;r.style.paddingLeft=N(a/2+e);const c=Math.floor(1e3*a*i),h="M400000 "+(l=c)+" H0 L"+l/2+" 0 l65 45 L145 "+(l-80)+" H400000z",m=new _([new $("phase",h)],{width:"400em",height:N(c/1e3),viewBox:"0 0 400000 "+c,preserveAspectRatio:"xMinYMin slice"});o=We(["hide-tail"],[m],t),o.style.height=N(a),s=r.depth+e+n}else{let i,l;/cancel/.test(n)?a||r.classes.push("cancel-pad"):"angl"===n?r.classes.push("anglpad"):r.classes.push("boxpad");let c=0;/box/.test(n)?(c=Math.max(t.fontMetrics().fboxrule,t.minRuleThickness),i=t.fontMetrics().fboxsep+("colorbox"===n?0:c),l=i):"angl"===n?(c=Math.max(t.fontMetrics().defaultRuleThickness,t.minRuleThickness),i=4*c,l=Math.max(0,.25-r.depth)):(i=a?.2:0,l=i),o=function(e,t,r,n,o){let s;const i=e.height+e.depth+r+n;if(/fbox|color|angl/.test(t)){if(s=Ye(["katex-stretchy",t],[],o),"fbox"===t){const e=o.color&&o.getColor();e&&(s.style.borderColor=e)}}else{const e=[];/^[bx]cancel$/.test(t)&&e.push(new Z({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(t)&&e.push(new Z({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));const r=new _(e,{width:"100%",height:N(i)});s=We([],[r],o)}return s.height=i,s.style.height=N(i),s}(r,n,i,l,t),/fbox|boxed|fcolorbox/.test(n)?(o.style.borderStyle="solid",o.style.borderWidth=N(c)):"angl"===n&&.049!==c&&(o.style.borderTopWidth=N(c),o.style.borderRightWidth=N(c)),s=r.depth+l,e.backgroundColor&&(o.style.backgroundColor=e.backgroundColor,e.borderColor&&(o.style.borderColor=e.borderColor))}var l;let c;if(e.backgroundColor)c=Ke({positionType:"individualShift",children:[{type:"elem",elem:o,shift:s},{type:"elem",elem:r,shift:0}]});else{const e=/cancel|phase/.test(n)?["svg-align"]:[];c=Ke({positionType:"individualShift",children:[{type:"elem",elem:r,shift:0},{type:"elem",elem:o,shift:s,wrapperClasses:e}]})}return/cancel/.test(n)&&(c.height=r.height,c.depth=r.depth),/cancel/.test(n)&&!a?Ye(["mord","cancel-lap"],[c],t):Ye(["mord"],[c],t)},mathmlBuilder:(e,t)=>{let r;const n=new Bt(e.label.includes("colorbox")?"mpadded":"menclose",[Vt(e.body,t)]);switch(e.label){case"\\cancel":n.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":n.setAttribute("notation","downdiagonalstrike");break;case"\\phase":n.setAttribute("notation","phasorangle");break;case"\\sout":n.setAttribute("notation","horizontalstrike");break;case"\\fbox":n.setAttribute("notation","box");break;case"\\angl":n.setAttribute("notation","actuarial");break;case"\\fcolorbox":case"\\colorbox":if(r=t.fontMetrics().fboxsep*t.fontMetrics().ptPerEm,n.setAttribute("width","+"+2*r+"pt"),n.setAttribute("height","+"+2*r+"pt"),n.setAttribute("lspace",r+"pt"),n.setAttribute("voffset",r+"pt"),"\\fcolorbox"===e.label){const r=Math.max(t.fontMetrics().fboxrule,t.minRuleThickness);n.setAttribute("style","border: "+N(r)+" solid "+e.borderColor)}break;case"\\xcancel":n.setAttribute("notation","updiagonalstrike downdiagonalstrike")}return e.backgroundColor&&n.setAttribute("mathbackground",e.backgroundColor),n}}),mt({type:"enclose",names:["\\fcolorbox"],numArgs:3,allowedInText:!0,argTypes:["color","color","hbox"],handler(e,t,r){let n=e.parser,o=e.funcName;const s=or(t[0],"color-token").color,i=or(t[1],"color-token").color,a=t[2];return{type:"enclose",mode:n.mode,label:o,backgroundColor:i,borderColor:s,body:a}}}),mt({type:"enclose",names:["\\fbox"],numArgs:1,argTypes:["hbox"],allowedInText:!0,handler(e,t){return{type:"enclose",mode:e.parser.mode,label:"\\fbox",body:t[0]}}}),mt({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\phase"],numArgs:1,handler(e,t){let r=e.parser,n=e.funcName;const o=t[0];return{type:"enclose",mode:r.mode,label:n,body:o}}}),mt({type:"enclose",names:["\\sout"],numArgs:1,allowedInText:!0,handler(e,t){let r=e.parser,n=e.funcName;"math"===r.mode&&r.settings.reportNonstrict("mathVsSout","LaTeX's \\sout works only in text mode");const o=t[0];return{type:"enclose",mode:r.mode,label:n,body:o}}}),mt({type:"enclose",names:["\\angl"],numArgs:1,argTypes:["hbox"],allowedInText:!1,handler(e,t){return{type:"enclose",mode:e.parser.mode,label:"\\angl",body:t[0]}}});const Zr={};function Kr(e){let t=e.type,r=e.names,n=e.props,o=e.handler,s=e.htmlBuilder,i=e.mathmlBuilder;const a={type:t,numArgs:n.numArgs||0,allowedInText:!1,numOptionalArgs:0,handler:o};for(let e=0;e{if(!e.parser.settings.displayMode)throw new n("{"+e.envName+"} can be used only in display mode.")},on=new Set(["gather","gather*"]);function sn(e){if(!e.includes("ed"))return!e.includes("*")}function an(e,t,r){let o=t.hskipBeforeAndAfter,s=t.addJot,i=t.cols,a=t.arraystretch,l=t.colSeparationType,c=t.autoTag,h=t.singleRow,m=t.emptySingleRow,u=t.maxNumCols,p=t.leqno;if(e.gullet.beginGroup(),h||e.gullet.macros.set("\\cr","\\\\\\relax"),!a){const t=e.gullet.expandMacroAsText("\\arraystretch");if(null==t)a=1;else if(a=parseFloat(t),!a||a<0)throw new n("Invalid \\arraystretch: "+t)}e.gullet.beginGroup();let d=[];const g=[d],f=[],b=[],y=null!=c?[]:void 0;function x(){c&&e.gullet.macros.set("\\@eqnsw","1",!0)}function w(){y&&(e.gullet.macros.get("\\df@tag")?(y.push(e.subparse([new tn("\\df@tag")])),e.gullet.macros.set("\\df@tag",void 0,!0)):y.push(Boolean(c)&&"1"===e.gullet.macros.get("\\@eqnsw")))}for(x(),b.push(rn(e));;){const t=e.parseExpression(!1,h?"\\end":"\\\\");e.gullet.endGroup(),e.gullet.beginGroup();let o={type:"ordgroup",mode:e.mode,body:t};r&&(o={type:"styling",mode:e.mode,style:r,resetFont:!0,body:[o]}),d.push(o);const s=e.fetch().text;if("&"===s){if(u&&d.length===u){if(h||l)throw new n("Too many tab characters: &",e.nextToken);e.settings.reportNonstrict("textEnv","Too few columns specified in the {array} column argument.")}e.consume()}else{if("\\end"===s){w(),1!==d.length||"styling"!==o.type||1!==o.body.length||"ordgroup"!==o.body[0].type||0!==o.body[0].body.length||!(g.length>1)&&m||Array.isArray(null==y?void 0:y[y.length-1])||c&&!0===(null==y?void 0:y[y.length-1])||g.pop(),b.length0&&(y+=.25),c.push({pos:y,isDashed:e[t]})}for(x(i[0]),r=0;r0&&(u+=b,ce))for(r=0;r=a)continue;var B,q;if(o>0||e.hskipBeforeAndAfter)i=null!=(B=null==(q=c)?void 0:q.pregap)?B:u,0!==i&&(z=Ye(["arraycolsep"],[]),z.style.width=N(i),k.push(z));const p=[];for(r=0;r0){const e=_e("katex-hline",t,h),r=_e("katex-hdashline",t,h),n=[{type:"elem",elem:O,shift:0}];for(;c.length>0;){const t=c.pop(),o=t.pos-w;t.isDashed?n.push({type:"elem",elem:r,shift:o}):n.push({type:"elem",elem:e,shift:o})}O=Ke({positionType:"individualShift",children:n})}if(0===A.length)return Ye(["mord"],[O],t);{const e=Ke({positionType:"individualShift",children:A}),r=Ye(["katex-tag"],[e],t);return $e([O,r])}},hn={c:"center ",l:"left ",r:"right "},mn=function(e,t){const r=[],n=new Bt("mtd",[],["mtr-glue"]),o=new Bt("mtd",[],["mml-eqn-num"]);for(let s=0;s0){const t=e.cols;let r="",n=!1,o=0,i=t.length;"separator"===t[0].type&&(a+="top ",o=1),"separator"===t[t.length-1].type&&(a+="bottom ",i-=1);for(let e=o;e0?"left ":"",a+=h[h.length-1].length>0?"right ":"";for(let e=1;e0&&c&&(n=1),r[e]={type:"align",align:t,pregap:n,postgap:0}}return s.colSeparationType=c?"align":"alignat",s};Kr({type:"array",names:["array","darray"],props:{numArgs:1},handler(e,t){const r=(ir(t[0])?[t[0]]:or(t[0],"ordgroup").body).map(function(e){const t=sr(e).text;if("lcr".includes(t))return{type:"align",align:t};if("|"===t)return{type:"separator",separator:"|"};if(":"===t)return{type:"separator",separator:":"};throw new n("Unknown column alignment: "+t,e)}),o={cols:r,hskipBeforeAndAfter:!0,maxNumCols:r.length};return an(e.parser,o,ln(e.envName))},htmlBuilder:cn,mathmlBuilder:mn}),Kr({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix","matrix*","pmatrix*","bmatrix*","Bmatrix*","vmatrix*","Vmatrix*"],props:{numArgs:0},handler(e){const t={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[e.envName.replace("*","")];let r="c";const o={hskipBeforeAndAfter:!1,cols:[{type:"align",align:r}]};if("*"===e.envName.charAt(e.envName.length-1)){const t=e.parser;if(t.consumeSpaces(),"["===t.fetch().text){if(t.consume(),t.consumeSpaces(),r=t.fetch().text,!"lcr".includes(r))throw new n("Expected l or c or r",t.nextToken);t.consume(),t.consumeSpaces(),t.expect("]"),t.consume(),o.cols=[{type:"align",align:r}]}}const s=an(e.parser,o,ln(e.envName)),i=Math.max(0,...s.body.map(e=>e.length));return s.cols=new Array(i).fill({type:"align",align:r}),t?{type:"leftright",mode:e.mode,body:[s],left:t[0],right:t[1],rightColor:void 0}:s},htmlBuilder:cn,mathmlBuilder:mn}),Kr({type:"array",names:["smallmatrix"],props:{numArgs:0},handler(e){const t=an(e.parser,{arraystretch:.5},"script");return t.colSeparationType="small",t},htmlBuilder:cn,mathmlBuilder:mn}),Kr({type:"array",names:["subarray"],props:{numArgs:1},handler(e,t){const r=(ir(t[0])?[t[0]]:or(t[0],"ordgroup").body).map(function(e){const t=sr(e).text;if("lc".includes(t))return{type:"align",align:t};throw new n("Unknown column alignment: "+t,e)});if(r.length>1)throw new n("{subarray} can contain only one column");const o={cols:r,hskipBeforeAndAfter:!1,arraystretch:.5},s=an(e.parser,o,"script");if(s.body.length>0&&s.body[0].length>1)throw new n("{subarray} can contain only one column");return s},htmlBuilder:cn,mathmlBuilder:mn}),Kr({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(e){const t=an(e.parser,{arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},ln(e.envName));return{type:"leftright",mode:e.mode,body:[t],left:e.envName.includes("r")?".":"\\{",right:e.envName.includes("r")?"\\}":".",rightColor:void 0}},htmlBuilder:cn,mathmlBuilder:mn}),Kr({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:un,htmlBuilder:cn,mathmlBuilder:mn}),Kr({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(e){on.has(e.envName)&&nn(e);const t={cols:[{type:"align",align:"c"}],addJot:!0,colSeparationType:"gather",autoTag:sn(e.envName),emptySingleRow:!0,leqno:e.parser.settings.leqno};return an(e.parser,t,"display")},htmlBuilder:cn,mathmlBuilder:mn}),Kr({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:un,htmlBuilder:cn,mathmlBuilder:mn}),Kr({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(e){nn(e);const t={autoTag:sn(e.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,leqno:e.parser.settings.leqno};return an(e.parser,t,"display")},htmlBuilder:cn,mathmlBuilder:mn}),Kr({type:"array",names:["CD"],props:{numArgs:0},handler(e){return nn(e),function(e){const t=[];for(e.gullet.beginGroup(),e.gullet.macros.set("\\cr","\\\\\\relax"),e.gullet.beginGroup();;){t.push(e.parseExpression(!1,"\\\\")),e.gullet.endGroup(),e.gullet.beginGroup();const r=e.fetch().text;if("&"!==r&&"\\\\"!==r){if("\\end"===r){0===t[t.length-1].length&&t.pop();break}throw new n("Expected \\\\ or \\cr or \\end",e.nextToken)}e.consume()}let r=[];const o=[r];for(let s=0;sAV".includes(o))throw new n('Expected one of "<>AV=|." after @',i[t]);for(let e=0;e<2;e++){let r=!0;for(let a=t+1;a{let r=e.parser,n=e.funcName;const o=pt(t[0]),s=n in dn?dn[n]:n;return{type:"font",mode:r.mode,font:s.slice(1),body:o}},htmlBuilder:(e,t)=>{const r=e.font,n=t.withFont(r);return Mt(e.body,n)},mathmlBuilder:(e,t)=>{const r=e.font,n=t.withFont(r);return Vt(e.body,n)}}),mt({type:"mclass",names:["\\boldsymbol","\\bm"],numArgs:1,handler:(e,t)=>{let r=e.parser;const n=t[0];return{type:"mclass",mode:r.mode,mclass:ur(n),body:[{type:"font",mode:r.mode,font:"boldsymbol",body:n}],isCharacterBox:m(n)}}}),mt({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it","\\cal"],numArgs:0,allowedInText:!0,handler:(e,t)=>{let r=e.parser,n=e.funcName,o=e.breakOnTokenText;const s=r.mode,i=r.parseExpression(!0,o);return{type:"font",mode:s,font:"math"+n.slice(1),body:{type:"ordgroup",mode:r.mode,body:i}}}});const gn=(e,t)=>{if(!t)return e;return{type:"styling",mode:e.mode,style:t,body:[e]}};mt({type:"genfrac",names:["\\cfrac","\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],numArgs:2,allowedInArgument:!0,handler:(e,t)=>{let r=e.parser,n=e.funcName;const o=t[0],s=t[1];let i,a=null,l=null;switch(n){case"\\cfrac":case"\\dfrac":case"\\frac":case"\\tfrac":i=!0;break;case"\\\\atopfrac":i=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":i=!1,a="(",l=")";break;case"\\\\bracefrac":i=!1,a="\\{",l="\\}";break;case"\\\\brackfrac":i=!1,a="[",l="]";break;default:throw new Error("Unrecognized genfrac command")}const c="\\cfrac"===n;let h=null;return c||n.startsWith("\\d")?h="display":n.startsWith("\\t")&&(h="text"),gn({type:"genfrac",mode:r.mode,numer:o,denom:s,continued:c,hasBarLine:i,leftDelim:a,rightDelim:l,barSize:null},h)},htmlBuilder:(e,t)=>{const r=t.style,n=r.fracNum(),o=r.fracDen();let s;s=t.havingStyle(n);const i=Mt(e.numer,s,t);if(e.continued){const e=8.5/t.fontMetrics().ptPerEm,r=3.5/t.fontMetrics().ptPerEm;i.height=i.height0?3*h:7*h,p=t.fontMetrics().denom1):(c>0?(m=t.fontMetrics().num2,u=h):(m=t.fontMetrics().num3,u=3*h),p=t.fontMetrics().denom2),l){const e=t.fontMetrics().axisHeight;m-i.depth-(e+.5*c){const r=new Bt("mfrac",[Vt(e.numer,t),Vt(e.denom,t)]);if(e.hasBarLine){if(e.barSize){const n=H(e.barSize,t);r.setAttribute("linethickness",N(n))}}else r.setAttribute("linethickness","0px");if(null!=e.leftDelim||null!=e.rightDelim){const t=[];if(null!=e.leftDelim){const r=new Bt("mo",[new qt(e.leftDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}if(t.push(r),null!=e.rightDelim){const r=new Bt("mo",[new qt(e.rightDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}return Ht(t)}return r}}),mt({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],numArgs:0,infix:!0,handler(e){let t,r=e.parser,n=e.funcName,o=e.token;switch(n){case"\\over":t="\\frac";break;case"\\choose":t="\\binom";break;case"\\atop":t="\\\\atopfrac";break;case"\\brace":t="\\\\bracefrac";break;case"\\brack":t="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:r.mode,replaceWith:t,token:o}}});const fn=["display","text","script","scriptscript"],bn=function(e){let t=null;return e.length>0&&(t=e,t="."===t?null:t),t};mt({type:"genfrac",names:["\\genfrac"],numArgs:6,allowedInArgument:!0,argTypes:["math","math","size","text","math","math"],handler(e,t){let r=e.parser;const n=t[4],o=t[5],s=pt(t[0]),i="atom"===s.type&&"open"===s.family?bn(s.text):null,a=pt(t[1]),l="atom"===a.type&&"close"===a.family?bn(a.text):null,c=or(t[2],"size");let h,m=null;c.isBlank?h=!0:(m=c.value,h=m.number>0);let u=null,p=t[3];if("ordgroup"===p.type){if(p.body.length>0){const e=or(p.body[0],"textord");u=fn[Number(e.text)]}}else p=or(p,"textord"),u=fn[Number(p.text)];return gn({type:"genfrac",mode:r.mode,numer:n,denom:o,continued:!1,hasBarLine:h,barSize:m,leftDelim:i,rightDelim:l},u)}}),mt({type:"infix",names:["\\above"],numArgs:1,argTypes:["size"],infix:!0,handler(e,t){let r=e.parser,n=(e.funcName,e.token);return{type:"infix",mode:r.mode,replaceWith:"\\\\abovefrac",size:or(t[0],"size").value,token:n}}}),mt({type:"genfrac",names:["\\\\abovefrac"],numArgs:3,argTypes:["math","size","math"],handler:(e,t)=>{let r=e.parser;e.funcName;const n=t[0],o=or(t[1],"infix").size;if(!o)throw new Error("\\\\abovefrac expected size, but got "+String(o));const s=t[2],i=o.number>0;return{type:"genfrac",mode:r.mode,numer:n,denom:s,continued:!1,hasBarLine:i,barSize:o,leftDelim:null,rightDelim:null}}});const yn=(e,t)=>{const r=t.style;let n,o;"supsub"===e.type?(n=e.sup?Mt(e.sup,t.havingStyle(r.sup()),t):Mt(e.sub,t.havingStyle(r.sub()),t),o=or(e.base,"horizBrace")):o=or(e,"horizBrace");const s=Mt(o.base,t.havingBaseStyle(S.DISPLAY)),i=tr(o,t);let a;if(a=o.isOver?Ke({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:i,wrapperClasses:["svg-align"]}]}):Ke({positionType:"bottom",positionData:s.depth+.1+i.height,children:[{type:"elem",elem:i,wrapperClasses:["svg-align"]},{type:"kern",size:.1},{type:"elem",elem:s}]}),n){const e=Ye(["minner",o.isOver?"mover":"munder"],[a],t);a=o.isOver?Ke({positionType:"firstBaseline",children:[{type:"elem",elem:e},{type:"kern",size:.2},{type:"elem",elem:n}]}):Ke({positionType:"bottom",positionData:e.depth+.2+n.height+n.depth,children:[{type:"elem",elem:n},{type:"kern",size:.2},{type:"elem",elem:e}]})}return Ye(["minner",o.isOver?"mover":"munder"],[a],t)};mt({type:"horizBrace",names:["\\overbrace","\\underbrace","\\overbracket","\\underbracket"],numArgs:1,handler(e,t){let r=e.parser,n=e.funcName;return{type:"horizBrace",mode:r.mode,label:n,isOver:n.includes("\\over"),base:t[0]}},htmlBuilder:yn,mathmlBuilder:(e,t)=>{const r=Jt(e.label);return new Bt(e.isOver?"mover":"munder",[Vt(e.base,t),r])}}),mt({type:"href",names:["\\href"],numArgs:2,argTypes:["url","original"],allowedInText:!0,handler:(e,t)=>{let r=e.parser;const n=t[1],o=or(t[0],"url").url;return r.settings.isTrusted({command:"\\href",url:o})?{type:"href",mode:r.mode,href:o,body:dt(n)}:r.formatUnsupportedCmd("\\href")},htmlBuilder:(e,t)=>{const r=xt(e.body,t,!1);return function(e,t,r,n){const o=new U(e,t,r,n);return Xe(o),o}(e.href,[],r,t)},mathmlBuilder:(e,t)=>{let r=Pt(e.body,t);return r instanceof Bt||(r=new Bt("mrow",[r])),r.setAttribute("href",e.href),r}}),mt({type:"href",names:["\\url"],numArgs:1,argTypes:["url"],allowedInText:!0,handler:(e,t)=>{let r=e.parser;const n=or(t[0],"url").url;if(!r.settings.isTrusted({command:"\\url",url:n}))return r.formatUnsupportedCmd("\\url");const o=[];for(let e=0;e{let r=e.parser,o=e.funcName;e.token;const s=or(t[0],"raw").string,i=t[1];let a;r.settings.strict&&r.settings.reportNonstrict("htmlExtension","HTML extension is disabled on strict mode");const l={};switch(o){case"\\htmlClass":l.class=s,a={command:"\\htmlClass",class:s};break;case"\\htmlId":l.id=s,a={command:"\\htmlId",id:s};break;case"\\htmlStyle":l.style=s,a={command:"\\htmlStyle",style:s};break;case"\\htmlData":{const e="{,}",t=[];let r="";for(let n=0;n{const r=xt(e.body,t,!1),n=["enclosing"];e.attributes.class&&n.push(...e.attributes.class.trim().split(/\s+/));const o=Ye(n,r,t);for(const t of Object.entries(e.attributes)){const e=t[0],r=t[1];"class"!==e&&o.setAttribute(e,r)}return o},mathmlBuilder:(e,t)=>Pt(e.body,t)}),mt({type:"htmlmathml",names:["\\html@mathml"],numArgs:2,allowedInArgument:!0,allowedInText:!0,handler:(e,t)=>({type:"htmlmathml",mode:e.parser.mode,html:dt(t[0]),mathml:dt(t[1])}),htmlBuilder:(e,t)=>{const r=xt(e.html,t,!1);return $e(r)},mathmlBuilder:(e,t)=>Pt(e.mathml,t)});const xn=function(e){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(e))return{number:+e,unit:"bp"};{const t=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e);if(!t)throw new n("Invalid size: '"+e+"' in \\includegraphics");const r={number:+(t[1]+t[2]),unit:t[3]};if(!E(r))throw new n("Invalid unit: '"+r.unit+"' in \\includegraphics.");return r}};mt({type:"includegraphics",names:["\\includegraphics"],numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1,handler:(e,t,r)=>{let o=e.parser,s={number:0,unit:"em"},i={number:.9,unit:"em"},a={number:0,unit:"em"},l="";if(r[0]){const e=or(r[0],"raw").string.split(",");for(let t=0;t{const r=H(e.height,t);let n=0;e.totalheight.number>0&&(n=H(e.totalheight,t)-r);let o=0;e.width.number>0&&(o=H(e.width,t));const s={height:N(r+n)};o>0&&(s.width=N(o)),n>0&&(s.verticalAlign=N(-n));const i=new X(e.src,e.alt,s);return i.height=r,i.depth=n,i},mathmlBuilder:(e,t)=>{const r=new Bt("mglyph",[]);r.setAttribute("alt",e.alt);const n=H(e.height,t);let o=0;if(e.totalheight.number>0&&(o=H(e.totalheight,t)-n,r.setAttribute("valign",N(-o))),r.setAttribute("height",N(n+o)),e.width.number>0){const n=H(e.width,t);r.setAttribute("width",N(n))}return r.setAttribute("src",e.src),r}}),mt({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],numArgs:1,argTypes:["size"],primitive:!0,allowedInText:!0,handler(e,t){let r=e.parser,n=e.funcName;const o=or(t[0],"size");if(r.settings.strict){const e="m"===n[1],t="mu"===o.value.unit;e?(t||r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" supports only mu units, not "+o.value.unit+" units"),"math"!==r.mode&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" works only in math mode")):t&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" doesn't support mu units")}return{type:"kern",mode:r.mode,dimension:o.value}},htmlBuilder(e,t){return Je(e.dimension,t)},mathmlBuilder(e,t){const r=H(e.dimension,t);return new It(r)}}),mt({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],numArgs:1,allowedInText:!0,handler:(e,t)=>{let r=e.parser,n=e.funcName;const o=t[0];return{type:"lap",mode:r.mode,alignment:n.slice(5),body:o}},htmlBuilder:(e,t)=>{let r;"clap"===e.alignment?(r=Ye([],[Mt(e.body,t)]),r=Ye(["katex-inner"],[r],t)):r=Ye(["katex-inner"],[Mt(e.body,t)]);const n=Ye(["katex-fix"],[]);let o=Ye([e.alignment],[r,n],t);const s=Ye(["katex-strut"]);return s.style.height=N(o.height+o.depth),o.depth&&(s.style.verticalAlign=N(-o.depth)),o.children.unshift(s),o=Ye(["katex-thinbox"],[o],t),Ye(["mord","katex-vbox"],[o],t)},mathmlBuilder:(e,t)=>{const r=new Bt("mpadded",[Vt(e.body,t)]);if("rlap"!==e.alignment){const t="llap"===e.alignment?"-1":"-0.5";r.setAttribute("lspace",t+"width")}return r.setAttribute("width","0px"),r}}),mt({type:"styling",names:["\\(","$"],numArgs:0,allowedInText:!0,allowedInMath:!1,handler(e,t){let r=e.funcName,n=e.parser;const o=n.mode;n.switchMode("math");const s="\\("===r?"\\)":"$",i=n.parseExpression(!1,s);return n.expect(s),n.switchMode(o),{type:"styling",mode:n.mode,style:"text",resetFont:!0,body:i}}}),mt({type:"text",names:["\\)","\\]"],numArgs:0,allowedInText:!0,allowedInMath:!1,handler(e,t){throw new n("Mismatched "+e.funcName)}});const wn=(e,t)=>{switch(t.style.size){case S.DISPLAY.size:return e.display;case S.TEXT.size:return e.text;case S.SCRIPT.size:return e.script;case S.SCRIPTSCRIPT.size:return e.scriptscript;default:return e.text}};mt({type:"mathchoice",names:["\\mathchoice"],numArgs:4,primitive:!0,handler:(e,t)=>({type:"mathchoice",mode:e.parser.mode,display:dt(t[0]),text:dt(t[1]),script:dt(t[2]),scriptscript:dt(t[3])}),htmlBuilder:(e,t)=>{const r=wn(e,t),n=xt(r,t,!1);return $e(n)},mathmlBuilder:(e,t)=>{const r=wn(e,t);return Pt(r,t)}});const vn=(e,t,r,n,o,s,i)=>{e=Ye([],[e]);const a=r&&m(r);let l,c,h;if(t){const e=Mt(t,n.havingStyle(o.sup()),n);c={elem:e,kern:Math.max(n.fontMetrics().bigOpSpacing1,n.fontMetrics().bigOpSpacing3-e.depth)}}if(r){const e=Mt(r,n.havingStyle(o.sub()),n);l={elem:e,kern:Math.max(n.fontMetrics().bigOpSpacing2,n.fontMetrics().bigOpSpacing4-e.height)}}if(c&&l){const t=n.fontMetrics().bigOpSpacing5+l.elem.height+l.elem.depth+l.kern+e.depth+i;h=Ke({positionType:"bottom",positionData:t,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:l.elem,marginLeft:N(-s)},{type:"kern",size:l.kern},{type:"elem",elem:e},{type:"kern",size:c.kern},{type:"elem",elem:c.elem,marginLeft:N(s)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]})}else if(l){const t=e.height-i;h=Ke({positionType:"top",positionData:t,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:l.elem,marginLeft:N(-s)},{type:"kern",size:l.kern},{type:"elem",elem:e}]})}else{if(!c)return e;{const t=e.depth+i;h=Ke({positionType:"bottom",positionData:t,children:[{type:"elem",elem:e},{type:"kern",size:c.kern},{type:"elem",elem:c.elem,marginLeft:N(s)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]})}}const u=[h];if(l&&0!==s&&!a){const e=Ye(["mspace"],[],n);e.style.marginRight=N(s),u.unshift(e)}return Ye(["mop","op-limits"],u,n)},kn=new Set(["\\smallint"]),zn=(e,t)=>{let r,n,o,s=!1;"supsub"===e.type?(r=e.sup,n=e.sub,o=or(e.base,"op"),s=!0):o=or(e,"op");const i=t.style;let a,l,c=!1;if(i.size===S.DISPLAY.size&&o.symbol&&!kn.has(o.name)&&(c=!0),o.symbol){const e=c?"Size2-Regular":"Size1-Regular";let r="";if("\\oiint"!==o.name&&"\\oiiint"!==o.name||(r=o.name.slice(1),o.name="oiint"===r?"\\iint":"\\iiint"),a=Pe(o.name,e,"math",t,["mop","op-symbol",c?"large-op":"small-op"]),l=a.italic,r.length>0){const e=rt(r+"Size"+(c?"2":"1"),t);a=Ke({positionType:"individualShift",children:[{type:"elem",elem:a,shift:0},{type:"elem",elem:e,shift:c?.08:0}]}),o.name="\\"+r,a.classes.unshift("mop"),a.italic=l}}else if(o.body){const e=xt(o.body,t,!0);1===e.length&&e[0]instanceof W?(a=e[0],a.classes[0]="mop"):a=Ye(["mop"],e,t)}else{const e=[];for(let r=1;r{let r=e.parser,n=e.funcName;return 1===n.length&&(n=Sn[n]),{type:"op",mode:r.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:n}},htmlBuilder:zn,mathmlBuilder:(e,t)=>{let r;if(e.symbol)r=new Bt("mo",[Et(e.name,e.mode)]),kn.has(e.name)&&r.setAttribute("largeop","false");else if(e.body)r=new Bt("mo",Ft(e.body,t));else{r=new Bt("mi",[new qt(e.name.slice(1))]);const t=new Bt("mo",[Et("\u2061","text")]);r=e.parentIsSupSub?new Bt("mrow",[r,t]):Ct([r,t])}return r}}),mt({type:"op",names:["\\mathop"],numArgs:1,primitive:!0,handler:(e,t)=>{let r=e.parser;const n=t[0];return{type:"op",mode:r.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:dt(n)}}});const Mn={"\u222b":"\\int","\u222c":"\\iint","\u222d":"\\iiint","\u222e":"\\oint","\u222f":"\\oiint","\u2230":"\\oiiint"};mt({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],numArgs:0,handler(e){let t=e.parser,r=e.funcName;return{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:r}}}),mt({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],numArgs:0,handler(e){let t=e.parser,r=e.funcName;return{type:"op",mode:t.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:r}}}),mt({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","\u222b","\u222c","\u222d","\u222e","\u222f","\u2230"],numArgs:0,allowedInArgument:!0,handler(e){let t=e.parser,r=e.funcName;return 1===r.length&&(r=Mn[r]),{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:r}}});const An=(e,t)=>{let r,n,o,s,i=!1;if("supsub"===e.type?(r=e.sup,n=e.sub,o=or(e.base,"operatorname"),i=!0):o=or(e,"operatorname"),o.body.length>0){const e=o.body.map(e=>{const t="text"in e?e.text:void 0;return"string"==typeof t?{type:"textord",mode:e.mode,text:t}:e}),r=xt(e,t.withFont("mathrm"),!0);for(let e=0;e{let r=e.parser,n=e.funcName;const o=t[0];return{type:"operatorname",mode:r.mode,body:dt(o),alwaysHandleSupSub:"\\operatornamewithlimits"===n,limits:!1,parentIsSupSub:!1}},htmlBuilder:An,mathmlBuilder:(e,t)=>{let r=Ft(e.body,t.withFont("mathrm")),n=!0;for(let e=0;ee.toText()).join("");r=[new qt(e)]}const o=new Bt("mi",r);o.setAttribute("mathvariant","normal");const s=new Bt("mo",[Et("\u2061","text")]);return e.parentIsSupSub?new Bt("mrow",[o,s]):Ct([o,s])}}),Qr("\\operatorname","\\@ifstar\\operatornamewithlimits\\operatorname@"),ut({type:"ordgroup",htmlBuilder(e,t){return e.semisimple?$e(xt(e.body,t,!1)):Ye(["mord"],xt(e.body,t,!0),t)},mathmlBuilder(e,t){return Pt(e.body,t,!0)}}),mt({type:"overline",names:["\\overline"],numArgs:1,handler(e,t){let r=e.parser;const n=t[0];return{type:"overline",mode:r.mode,body:n}},htmlBuilder(e,t){const r=Mt(e.body,t.havingCrampedStyle()),n=_e("overline-line",t),o=t.fontMetrics().defaultRuleThickness,s=Ke({positionType:"firstBaseline",children:[{type:"elem",elem:r},{type:"kern",size:3*o},{type:"elem",elem:n},{type:"kern",size:o}]});return Ye(["mord","katex-overline"],[s],t)},mathmlBuilder(e,t){const r=new Bt("mo",[new qt("\u203e")]);r.setAttribute("stretchy","true");const n=new Bt("mover",[Vt(e.body,t),r]);return n.setAttribute("accent","true"),n}}),mt({type:"phantom",names:["\\phantom"],numArgs:1,allowedInText:!0,handler:(e,t)=>{let r=e.parser;const n=t[0];return{type:"phantom",mode:r.mode,body:dt(n)}},htmlBuilder:(e,t)=>{const r=xt(e.body,t.withPhantom(),!1);return $e(r)},mathmlBuilder:(e,t)=>{const r=Ft(e.body,t);return new Bt("mphantom",r)}}),Qr("\\hphantom","\\smash{\\phantom{#1}}"),mt({type:"vphantom",names:["\\vphantom"],numArgs:1,allowedInText:!0,handler:(e,t)=>{let r=e.parser;const n=t[0];return{type:"vphantom",mode:r.mode,body:n}},htmlBuilder:(e,t)=>{const r=Ye(["katex-inner"],[Mt(e.body,t.withPhantom())]),n=Ye(["katex-fix"],[]);return Ye(["mord","rlap"],[r,n],t)},mathmlBuilder:(e,t)=>{const r=Ft(dt(e.body),t),n=new Bt("mphantom",r),o=new Bt("mpadded",[n]);return o.setAttribute("width","0px"),o}}),mt({type:"raisebox",names:["\\raisebox"],numArgs:2,argTypes:["size","hbox"],allowedInText:!0,handler(e,t){let r=e.parser;const n=or(t[0],"size").value,o=t[1];return{type:"raisebox",mode:r.mode,dy:n,body:o}},htmlBuilder(e,t){const r=Mt(e.body,t),n=H(e.dy,t);return Ke({positionType:"shift",positionData:-n,children:[{type:"elem",elem:r}]})},mathmlBuilder(e,t){const r=new Bt("mpadded",[Vt(e.body,t)]),n=e.dy.number+e.dy.unit;return r.setAttribute("voffset",n),r}});const Tn=(e,t)=>({type:"reflectbox",mode:e.parser.mode,body:t[0]});function Cn(e,t,r){const n=xt(e,t,!1),o=t.sizeMultiplier/r.sizeMultiplier;for(let e=0;e=0?a.setAttribute("height",N(o)):(a.setAttribute("height",N(o)),a.setAttribute("depth",N(-o))),a.setAttribute("voffset",N(o)),a}});const Bn=["\\tiny","\\sixptsize","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"];mt({type:"sizing",names:Bn,numArgs:0,allowedInText:!0,handler:(e,t)=>{let r=e.breakOnTokenText,n=e.funcName,o=e.parser;const s=o.parseExpression(!1,r);return{type:"sizing",mode:o.mode,size:Bn.indexOf(n)+1,body:s}},htmlBuilder:(e,t)=>{const r=t.havingSize(e.size);return Cn(e.body,r,t)},mathmlBuilder:(e,t)=>{const r=t.havingSize(e.size),n=Ft(e.body,r),o=new Bt("mstyle",n);return o.setAttribute("mathsize",N(r.sizeMultiplier)),o}}),mt({type:"smash",names:["\\smash"],numArgs:1,numOptionalArgs:1,allowedInText:!0,handler:(e,t,r)=>{let n=e.parser,o=!1,s=!1;const i=r[0]&&or(r[0],"ordgroup");if(i){let e;for(let t=0;t{const r=Ye([],[Mt(e.body,t)]);if(!e.smashHeight&&!e.smashDepth)return r;if(e.smashHeight&&(r.height=0),e.smashDepth&&(r.depth=0),e.smashHeight&&e.smashDepth)return Ye(["mord","katex-smash"],[r],t);if(r.children)for(let t=0;t{const r=new Bt("mpadded",[Vt(e.body,t)]);return e.smashHeight&&r.setAttribute("height","0px"),e.smashDepth&&r.setAttribute("depth","0px"),r}}),mt({type:"sqrt",names:["\\sqrt"],numArgs:1,numOptionalArgs:1,handler(e,t,r){let n=e.parser;const o=r[0],s=t[0];return{type:"sqrt",mode:n.mode,body:s,index:o}},htmlBuilder(e,t){let r=Mt(e.body,t.havingCrampedStyle());0===r.height&&(r.height=t.fontMetrics().xHeight),r=Ze(r,t);const n=t.fontMetrics().defaultRuleThickness;let o=n;t.style.idr.height+r.depth+s&&(s=(s+h-r.height-r.depth)/2);const m=a.height-r.height-s-l;r.style.paddingLeft=N(c);const u=Ke({positionType:"firstBaseline",children:[{type:"elem",elem:r,wrapperClasses:["svg-align"]},{type:"kern",size:-(r.height+m)},{type:"elem",elem:a},{type:"kern",size:l}]});if(e.index){const r=t.havingStyle(S.SCRIPTSCRIPT),n=Mt(e.index,r,t),o=.6*(u.height-u.depth),s=Ke({positionType:"shift",positionData:-o,children:[{type:"elem",elem:n}]}),i=Ye(["katex-root"],[s]);return Ye(["mord","sqrt"],[i,u],t)}return Ye(["mord","sqrt"],[u],t)},mathmlBuilder(e,t){const r=e.body,n=e.index;return n?new Bt("mroot",[Vt(r,t),Vt(n,t)]):new Bt("msqrt",[Vt(r,t)])}});const qn={display:S.DISPLAY,text:S.TEXT,script:S.SCRIPT,scriptscript:S.SCRIPTSCRIPT};mt({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],numArgs:0,allowedInText:!0,primitive:!0,handler(e,t){let r=e.breakOnTokenText,n=e.funcName,o=e.parser;const s=o.parseExpression(!0,r),i=n.slice(1,n.length-5);if(!(i in qn))throw new Error("Unknown style: "+i);return{type:"styling",mode:o.mode,style:i,body:s}},htmlBuilder(e,t){const r=qn[e.style];let n=t.havingStyle(r);return e.resetFont&&(n=n.withFont("")),Cn(e.body,n,t)},mathmlBuilder(e,t){const r=qn[e.style];let n=t.havingStyle(r);e.resetFont&&(n=n.withFont(""));const o=Ft(e.body,n),s=new Bt("mstyle",o),i={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]}[e.style];return s.setAttribute("scriptlevel",i[0]),s.setAttribute("displaystyle",i[1]),s}});ut({type:"supsub",htmlBuilder(e,t){const r=function(e,t){const r=e.base;if(r)return"op"===r.type?r.limits&&(t.style.size===S.DISPLAY.size||r.alwaysHandleSupSub)?zn:null:"operatorname"===r.type?r.alwaysHandleSupSub&&(t.style.size===S.DISPLAY.size||r.limits)?An:null:"accent"===r.type?m(r.base)?cr:null:"horizBrace"===r.type&&!e.sub===r.isOver?yn:null;return null}(e,t);if(r)return r(e,t);const n=e.base,o=e.sup,s=e.sub,i=Mt(n,t);let a,l;const c=t.fontMetrics();let h=0,u=0;const p=n&&m(n);if(o){const e=t.havingStyle(t.style.sup());a=Mt(o,e,t),p||(h=i.height-e.fontMetrics().supDrop*e.sizeMultiplier/t.sizeMultiplier)}if(s){const e=t.havingStyle(t.style.sub());l=Mt(s,e,t),p||(u=i.depth+e.fontMetrics().subDrop*e.sizeMultiplier/t.sizeMultiplier)}let d;d=t.style===S.DISPLAY?c.sup1:t.style.cramped?c.sup3:c.sup2;const g=t.sizeMultiplier,f=N(.5/c.ptPerEm/g);let b,y=null;if(l){const t=e.base&&"op"===e.base.type&&e.base.name&&("\\oiint"===e.base.name||"\\oiiint"===e.base.name);var x;if(i instanceof W||t)y=N(-(null!=(x=i.italic)?x:0))}if(a&&l){h=Math.max(h,d,a.depth+.25*c.xHeight),u=Math.max(u,c.sub2);const e=4*c.defaultRuleThickness;if(h-a.depth-(l.height-u)0&&(h+=t,u-=t)}b=Ke({positionType:"individualShift",children:[{type:"elem",elem:l,shift:u,marginRight:f,marginLeft:y},{type:"elem",elem:a,shift:-h,marginRight:f}]})}else if(l){u=Math.max(u,c.sub1,l.height-.8*c.xHeight);b=Ke({positionType:"shift",positionData:u,children:[{type:"elem",elem:l,marginLeft:y,marginRight:f}]})}else{if(!a)throw new Error("supsub must have either sup or sub.");h=Math.max(h,d,a.depth+.25*c.xHeight),b=Ke({positionType:"shift",positionData:-h,children:[{type:"elem",elem:a,marginRight:f}]})}const w=zt(i,"right")||"mord";return Ye([w],[i,Ye(["msupsub"],[b])],t)},mathmlBuilder(e,t){let r,n,o=!1;e.base&&"horizBrace"===e.base.type&&(n=!!e.sup,n===e.base.isOver&&(o=!0,r=e.base.isOver)),!e.base||"op"!==e.base.type&&"operatorname"!==e.base.type||(e.base.parentIsSupSub=!0);const s=[Vt(e.base,t)];let i;if(e.sub&&s.push(Vt(e.sub,t)),e.sup&&s.push(Vt(e.sup,t)),o)i=r?"mover":"munder";else if(e.sub)if(e.sup){const r=e.base;i=r&&"op"===r.type&&r.limits&&t.style===S.DISPLAY||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(t.style===S.DISPLAY||r.limits)?"munderover":"msubsup"}else{const r=e.base;i=r&&"op"===r.type&&r.limits&&(t.style===S.DISPLAY||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.style===S.DISPLAY)?"munder":"msub"}else{const r=e.base;i=r&&"op"===r.type&&r.limits&&(t.style===S.DISPLAY||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.style===S.DISPLAY)?"mover":"msup"}return new Bt(i,s)}}),ut({type:"atom",htmlBuilder(e,t){return Ve(e.text,e.mode,t,["m"+e.family])},mathmlBuilder(e,t){const r=new Bt("mo",[Et(e.text,e.mode)]);if("bin"===e.family){const n=Dt(e,t);"bold-italic"===n&&r.setAttribute("mathvariant",n)}else"punct"===e.family?r.setAttribute("separator","true"):"open"!==e.family&&"close"!==e.family||r.setAttribute("stretchy","false");return r}});const In={mi:"italic",mn:"normal",mtext:"normal"};ut({type:"mathord",htmlBuilder(e,t){return Ge(e,t)},mathmlBuilder(e,t){const r=new Bt("mi",[Et(e.text,e.mode,t)]),n=Dt(e,t)||"italic";return n!==In[r.type]&&r.setAttribute("mathvariant",n),r}}),ut({type:"textord",htmlBuilder(e,t){return Ge(e,t)},mathmlBuilder(e,t){const r=Et(e.text,e.mode,t),n=Dt(e,t)||"normal";let o;return o="text"===e.mode?new Bt("mtext",[r]):/[0-9]/.test(e.text)?new Bt("mn",[r]):"\\prime"===e.text?new Bt("mo",[r]):new Bt("mi",[r]),n!==In[o.type]&&o.setAttribute("mathvariant",n),o}});const Rn=new Map([["\\nobreak","nobreak"],["\\allowbreak","allowbreak"]]),On=new Map([[" ",{}],["\\ ",{}],["~",{className:"nobreak"}],["\\space",{}],["\\nobreakspace",{className:"nobreak"}]]);ut({type:"spacing",htmlBuilder(e,t){const r=On.get(e.text),o=Rn.get(e.text);if(r){const n=r.className||"";if("text"===e.mode){const r=Ge(e,t);return r.classes.push(n),r}return Ye(["mspace",n],[Ve(e.text,e.mode,t)],t)}if(o)return Ye(["mspace",o],[],t);throw new n('Unknown type of space "'+e.text+'"')},mathmlBuilder(e,t){let r;if(!On.has(e.text)){if(Rn.has(e.text))return new Bt("mspace");throw new n('Unknown type of space "'+e.text+'"')}return r=new Bt("mtext",[new qt("\xa0")]),r}});const En=()=>{const e=new Bt("mtd",[]);return e.setAttribute("width","50%"),e};ut({type:"tag",mathmlBuilder(e,t){const r=new Bt("mtable",[new Bt("mtr",[En(),new Bt("mtd",[Pt(e.body,t)]),En(),new Bt("mtd",[Pt(e.tag,t)])])]);return r.setAttribute("width","100%"),r}});const Hn={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},Nn={"\\textbf":"textbf","\\textmd":"textmd"},Dn={"\\textit":"textit","\\textup":"textup"},Ln=(e,t)=>{const r=e.font;return r?Hn[r]?t.withTextFontFamily(Hn[r]):Nn[r]?t.withTextFontWeight(Nn[r]):"\\emph"===r?"textit"===t.fontShape?t.withTextFontShape("textup"):t.withTextFontShape("textit"):t.withTextFontShape(Dn[r]):t};mt({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup","\\emph"],numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0,handler(e,t){let r=e.parser,n=e.funcName;const o=t[0];return{type:"text",mode:r.mode,body:dt(o),font:n}},htmlBuilder(e,t){const r=Ln(e,t),n=xt(e.body,r,!0);return Ye(["mord","text"],n,r)},mathmlBuilder(e,t){const r=Ln(e,t);return Pt(e.body,r)}}),mt({type:"underline",names:["\\underline"],numArgs:1,allowedInText:!0,handler(e,t){return{type:"underline",mode:e.parser.mode,body:t[0]}},htmlBuilder(e,t){const r=Mt(e.body,t),n=_e("underline-line",t),o=t.fontMetrics().defaultRuleThickness,s=Ke({positionType:"top",positionData:r.height,children:[{type:"kern",size:o},{type:"elem",elem:n},{type:"kern",size:3*o},{type:"elem",elem:r}]});return Ye(["mord","katex-underline"],[s],t)},mathmlBuilder(e,t){const r=new Bt("mo",[new qt("\u203e")]);r.setAttribute("stretchy","true");const n=new Bt("munder",[Vt(e.body,t),r]);return n.setAttribute("accentunder","true"),n}}),mt({type:"vcenter",names:["\\vcenter"],numArgs:1,argTypes:["original"],allowedInText:!1,handler(e,t){return{type:"vcenter",mode:e.parser.mode,body:t[0]}},htmlBuilder(e,t){const r=Mt(e.body,t),n=t.fontMetrics().axisHeight,o=.5*(r.height-n-(r.depth+n));return Ke({positionType:"shift",positionData:o,children:[{type:"elem",elem:r}]})},mathmlBuilder(e,t){const r=new Bt("mpadded",[Vt(e.body,t)],["vcenter"]);return new Bt("mrow",[r])}}),mt({type:"verb",names:["\\verb"],numArgs:0,allowedInText:!0,handler(e,t,r){throw new n("\\verb ended by end of line instead of matching delimiter")},htmlBuilder(e,t){const r=Fn(e),n=[],o=t.havingStyle(t.style.text());for(let t=0;te.body.replace(/ /g,e.star?"\u2423":"\xa0");var Pn=lt;const Vn="[ \r\n\t]",Gn="(\\\\[a-zA-Z@]+)"+Vn+"*",jn="[\u0300-\u036f]",Un=new RegExp(jn+"+$"),Xn="("+Vn+"+)|\\\\(\n|[ \r\t]+\n?)[ \r\t]*|([!-\\[\\]-\u2027\u202a-\ud7ff\uf900-\uffff]"+jn+"*|[\ud800-\udbff][\udc00-\udfff]"+jn+"*|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5|"+Gn+"|\\\\[^\ud800-\udfff])";class Yn{constructor(e,t){this.input=void 0,this.settings=void 0,this.tokenRegex=void 0,this.catcodes=void 0,this.input=e,this.settings=t,this.tokenRegex=new RegExp(Xn,"g"),this.catcodes={"%":14,"~":13}}setCatcode(e,t){this.catcodes[e]=t}lex(){const e=this.input,t=this.tokenRegex.lastIndex;if(t===e.length)return new tn("EOF",new en(this,t,t));const r=this.tokenRegex.exec(e);if(null===r||r.index!==t)throw new n("Unexpected character: '"+e[t]+"'",new tn(e[t],new en(this,t,t+1)));const o=r[6]||r[3]||(r[2]?"\\ ":" ");if(14===this.catcodes[o]){const t=e.indexOf("\n",this.tokenRegex.lastIndex);return-1===t?(this.tokenRegex.lastIndex=e.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=t+1,this.lex()}return new tn(o,new en(this,t,this.tokenRegex.lastIndex))}}class Wn{constructor(e,t){void 0===e&&(e={}),void 0===t&&(t={}),this.current=void 0,this.builtins=void 0,this.undefStack=void 0,this.current=t,this.builtins=e,this.undefStack=[]}beginGroup(){this.undefStack.push({})}endGroup(){if(0===this.undefStack.length)throw new n("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");const e=this.undefStack.pop();for(const t of Object.keys(e))void 0===e[t]?delete this.current[t]:this.current[t]=e[t]}endGroups(){for(;this.undefStack.length>0;)this.endGroup()}has(e){return Object.prototype.hasOwnProperty.call(this.current,e)||Object.prototype.hasOwnProperty.call(this.builtins,e)}get(e){return Object.prototype.hasOwnProperty.call(this.current,e)?this.current[e]:Object.prototype.hasOwnProperty.call(this.builtins,e)?this.builtins[e]:void 0}set(e,t,r){if(void 0===r&&(r=!1),r){for(let t=0;t0&&(this.undefStack[this.undefStack.length-1][e]=t)}else{const t=this.undefStack[this.undefStack.length-1];t&&!Object.prototype.hasOwnProperty.call(t,e)&&(t[e]=Object.prototype.hasOwnProperty.call(this.current,e)?this.current[e]:void 0)}null==t?delete this.current[e]:this.current[e]=t}}var _n=Jr;Qr("\\noexpand",function(e){const t=e.popToken();return e.isExpandable(t.text)&&(t.noexpand=!0,t.treatAsRelax=!0),{tokens:[t],numArgs:0}}),Qr("\\expandafter",function(e){const t=e.popToken();return e.expandOnce(!0),{tokens:[t],numArgs:0}}),Qr("\\@firstoftwo",function(e){return{tokens:e.consumeArgs(2)[0],numArgs:0}}),Qr("\\@secondoftwo",function(e){return{tokens:e.consumeArgs(2)[1],numArgs:0}}),Qr("\\@ifnextchar",function(e){const t=e.consumeArgs(3);e.consumeSpaces();const r=e.future();return 1===t[0].length&&t[0][0].text===r.text?{tokens:t[1],numArgs:0}:{tokens:t[2],numArgs:0}}),Qr("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),Qr("\\TextOrMath",function(e){const t=e.consumeArgs(2);return"text"===e.mode?{tokens:t[0],numArgs:0}:{tokens:t[1],numArgs:0}});const $n={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};Qr("\\char",function(e){let t,r=e.popToken(),o=0;if("'"===r.text)t=8,r=e.popToken();else if('"'===r.text)t=16,r=e.popToken();else if("`"===r.text)if(r=e.popToken(),"\\"===r.text[0])o=r.text.charCodeAt(1);else{if("EOF"===r.text)throw new n("\\char` missing argument");o=r.text.charCodeAt(0)}else t=10;if(t){if(o=$n[r.text],null==o||o>=t)throw new n("Invalid base-"+t+" digit "+r.text);let s;for(;null!=(s=$n[e.future().text])&&s{let s=e.consumeArg().tokens;if(1!==s.length)throw new n("\\newcommand's first argument must be a macro name");const i=s[0].text,a=e.isDefined(i);if(a&&!t)throw new n("\\newcommand{"+i+"} attempting to redefine "+i+"; use \\renewcommand");if(!a&&!r)throw new n("\\renewcommand{"+i+"} when command "+i+" does not yet exist; use \\newcommand");let l=0;if(s=e.consumeArg().tokens,1===s.length&&"["===s[0].text){let t="",r=e.expandNextToken();for(;"]"!==r.text&&"EOF"!==r.text;)t+=r.text,r=e.expandNextToken();if(!t.match(/^\s*[0-9]+\s*$/))throw new n("Invalid number of arguments: "+t);l=parseInt(t),s=e.consumeArg().tokens}return a&&o||e.macros.set(i,{tokens:s,numArgs:l}),""};Qr("\\newcommand",e=>Zn(e,!1,!0,!1)),Qr("\\renewcommand",e=>Zn(e,!0,!1,!1)),Qr("\\providecommand",e=>Zn(e,!0,!0,!0)),Qr("\\message",e=>{const t=e.consumeArgs(1)[0];return console.log(t.reverse().map(e=>e.text).join("")),""}),Qr("\\errmessage",e=>{const t=e.consumeArgs(1)[0];return console.error(t.reverse().map(e=>e.text).join("")),""}),Qr("\\show",e=>{const t=e.popToken(),r=t.text;return console.log(t,e.macros.get(r),Pn[r],ne.math[r],ne.text[r]),""}),Qr("\\bgroup","{"),Qr("\\egroup","}"),Qr("~","\\nobreakspace"),Qr("\\lq","`"),Qr("\\rq","'"),Qr("\\aa","\\r a"),Qr("\\AA","\\r A"),Qr("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`\xa9}"),Qr("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"),Qr("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`\xae}"),Qr("\u212c","\\mathscr{B}"),Qr("\u2130","\\mathscr{E}"),Qr("\u2131","\\mathscr{F}"),Qr("\u210b","\\mathscr{H}"),Qr("\u2110","\\mathscr{I}"),Qr("\u2112","\\mathscr{L}"),Qr("\u2133","\\mathscr{M}"),Qr("\u211b","\\mathscr{R}"),Qr("\u212d","\\mathfrak{C}"),Qr("\u210c","\\mathfrak{H}"),Qr("\u2128","\\mathfrak{Z}"),Qr("\\Bbbk","\\Bbb{k}"),Qr("\\llap","\\mathllap{\\textrm{#1}}"),Qr("\\rlap","\\mathrlap{\\textrm{#1}}"),Qr("\\clap","\\mathclap{\\textrm{#1}}"),Qr("\\mathstrut","\\vphantom{(}"),Qr("\\underbar","\\underline{\\text{#1}}"),Qr("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}\\nobreak}{\\char"338}'),Qr("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`\u2260}}"),Qr("\\ne","\\neq"),Qr("\u2260","\\neq"),Qr("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`\u2209}}"),Qr("\u2209","\\notin"),Qr("\u2258","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`\u2258}}"),Qr("\u2259","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}"),Qr("\u225a","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225a}}"),Qr("\u225b","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`\u225b}}"),Qr("\u225d","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`\u225d}}"),Qr("\u225e","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`\u225e}}"),Qr("\u225f","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225f}}"),Qr("\u27c2","\\perp"),Qr("\u203c","\\mathclose{!\\mkern-0.8mu!}"),Qr("\u220c","\\notni"),Qr("\u231c","\\ulcorner"),Qr("\u231d","\\urcorner"),Qr("\u231e","\\llcorner"),Qr("\u231f","\\lrcorner"),Qr("\xa9","\\copyright"),Qr("\xae","\\textregistered"),Qr("\\ulcorner",'\\html@mathml{\\@ulcorner}{\\mathop{\\char"231c}}'),Qr("\\urcorner",'\\html@mathml{\\@urcorner}{\\mathop{\\char"231d}}'),Qr("\\llcorner",'\\html@mathml{\\@llcorner}{\\mathop{\\char"231e}}'),Qr("\\lrcorner",'\\html@mathml{\\@lrcorner}{\\mathop{\\char"231f}}'),Qr("\\vdots","{\\varvdots\\rule{0pt}{15pt}}"),Qr("\u22ee","\\vdots"),Qr("\\varGamma","\\mathit{\\Gamma}"),Qr("\\varDelta","\\mathit{\\Delta}"),Qr("\\varTheta","\\mathit{\\Theta}"),Qr("\\varLambda","\\mathit{\\Lambda}"),Qr("\\varXi","\\mathit{\\Xi}"),Qr("\\varPi","\\mathit{\\Pi}"),Qr("\\varSigma","\\mathit{\\Sigma}"),Qr("\\varUpsilon","\\mathit{\\Upsilon}"),Qr("\\varPhi","\\mathit{\\Phi}"),Qr("\\varPsi","\\mathit{\\Psi}"),Qr("\\varOmega","\\mathit{\\Omega}"),Qr("\\substack","\\begin{subarray}{c}#1\\end{subarray}"),Qr("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax"),Qr("\\boxed","\\fbox{$\\displaystyle{#1}$}"),Qr("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;"),Qr("\\implies","\\DOTSB\\;\\Longrightarrow\\;"),Qr("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;"),Qr("\\dddot","{\\overset{\\raisebox{-0.1ex}{\\normalsize ...}}{#1}}"),Qr("\\ddddot","{\\overset{\\raisebox{-0.1ex}{\\normalsize ....}}{#1}}");const Kn={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"},Jn=new Set(["bin","rel"]);Qr("\\dots",function(e){let t="\\dotso";const r=e.expandAfterFuture().text;return r in Kn?t=Kn[r]:("\\not"===r.slice(0,4)||r in ne.math&&Jn.has(ne.math[r].group))&&(t="\\dotsb"),t});const Qn={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};Qr("\\dotso",function(e){return e.future().text in Qn?"\\ldots\\,":"\\ldots"}),Qr("\\dotsc",function(e){const t=e.future().text;return t in Qn&&","!==t?"\\ldots\\,":"\\ldots"}),Qr("\\cdots",function(e){return e.future().text in Qn?"\\@cdots\\,":"\\@cdots"}),Qr("\\dotsb","\\cdots"),Qr("\\dotsm","\\cdots"),Qr("\\dotsi","\\!\\cdots"),Qr("\\dotsx","\\ldots\\,"),Qr("\\DOTSI","\\relax"),Qr("\\DOTSB","\\relax"),Qr("\\DOTSX","\\relax"),Qr("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),Qr("\\,","\\tmspace+{3mu}{.1667em}"),Qr("\\thinspace","\\,"),Qr("\\>","\\mskip{4mu}"),Qr("\\:","\\tmspace+{4mu}{.2222em}"),Qr("\\medspace","\\:"),Qr("\\;","\\tmspace+{5mu}{.2777em}"),Qr("\\thickspace","\\;"),Qr("\\!","\\tmspace-{3mu}{.1667em}"),Qr("\\negthinspace","\\!"),Qr("\\negmedspace","\\tmspace-{4mu}{.2222em}"),Qr("\\negthickspace","\\tmspace-{5mu}{.277em}"),Qr("\\enspace","\\kern.5em "),Qr("\\enskip","\\hskip.5em\\relax"),Qr("\\quad","\\hskip1em\\relax"),Qr("\\qquad","\\hskip2em\\relax"),Qr("\\tag","\\@ifstar\\tag@literal\\tag@paren"),Qr("\\tag@paren","\\tag@literal{({#1})}"),Qr("\\tag@literal",e=>{if(e.macros.get("\\df@tag"))throw new n("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"}),Qr("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),Qr("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),Qr("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),Qr("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),Qr("\\newline","\\\\\\relax"),Qr("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");const eo=N(K["Main-Regular"]["T".charCodeAt(0)][1]-.7*K["Main-Regular"]["A".charCodeAt(0)][1]);Qr("\\LaTeX","\\textrm{\\html@mathml{L\\kern-.36em\\raisebox{"+eo+"}{\\scriptstyle A}\\kern-.15em\\TeX}{LaTeX}}"),Qr("\\KaTeX","\\textrm{\\html@mathml{K\\kern-.17em\\raisebox{"+eo+"}{\\scriptstyle A}\\kern-.15em\\TeX}{KaTeX}}"),Qr("\\hspace","\\@ifstar\\@hspacer\\@hspace"),Qr("\\@hspace","\\hskip #1\\relax"),Qr("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),Qr("\\ordinarycolon",":"),Qr("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}"),Qr("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}'),Qr("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}'),Qr("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}'),Qr("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}'),Qr("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}'),Qr("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}'),Qr("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}'),Qr("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}'),Qr("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}'),Qr("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}'),Qr("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}'),Qr("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}'),Qr("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}'),Qr("\u2237","\\dblcolon"),Qr("\u2239","\\eqcolon"),Qr("\u2254","\\coloneqq"),Qr("\u2255","\\eqqcolon"),Qr("\u2a74","\\Coloneqq"),Qr("\\ratio","\\vcentcolon"),Qr("\\coloncolon","\\dblcolon"),Qr("\\colonequals","\\coloneqq"),Qr("\\coloncolonequals","\\Coloneqq"),Qr("\\equalscolon","\\eqqcolon"),Qr("\\equalscoloncolon","\\Eqqcolon"),Qr("\\colonminus","\\coloneq"),Qr("\\coloncolonminus","\\Coloneq"),Qr("\\minuscolon","\\eqcolon"),Qr("\\minuscoloncolon","\\Eqcolon"),Qr("\\coloncolonapprox","\\Colonapprox"),Qr("\\coloncolonsim","\\Colonsim"),Qr("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Qr("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Qr("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Qr("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Qr("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220c}}"),Qr("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),Qr("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),Qr("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}"),Qr("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}"),Qr("\\varlimsup","\\DOTSB\\operatorname*{\\overline{lim}}"),Qr("\\varliminf","\\DOTSB\\operatorname*{\\underline{lim}}"),Qr("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{lim}}"),Qr("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{lim}}"),Qr("\\gvertneqq","\\html@mathml{\\@gvertneqq}{\u2269}"),Qr("\\lvertneqq","\\html@mathml{\\@lvertneqq}{\u2268}"),Qr("\\ngeqq","\\html@mathml{\\@ngeqq}{\u2271}"),Qr("\\ngeqslant","\\html@mathml{\\@ngeqslant}{\u2271}"),Qr("\\nleqq","\\html@mathml{\\@nleqq}{\u2270}"),Qr("\\nleqslant","\\html@mathml{\\@nleqslant}{\u2270}"),Qr("\\nshortmid","\\html@mathml{\\@nshortmid}{\u2224}"),Qr("\\nshortparallel","\\html@mathml{\\@nshortparallel}{\u2226}"),Qr("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{\u2288}"),Qr("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{\u2289}"),Qr("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{\u228a}"),Qr("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{\u2acb}"),Qr("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{\u228b}"),Qr("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{\u2acc}"),Qr("\\imath","\\html@mathml{\\@imath}{\u0131}"),Qr("\\jmath","\\html@mathml{\\@jmath}{\u0237}"),Qr("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`\u27e6}}"),Qr("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`\u27e7}}"),Qr("\u27e6","\\llbracket"),Qr("\u27e7","\\rrbracket"),Qr("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`\u2983}}"),Qr("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`\u2984}}"),Qr("\u21a4","\\mapsfrom"),Qr("\\mapsfrom","\\html@mathml{\\mathrel{\\mathreflectbox{\\mapsto}}}{\\mathrel{\\char`\u21a4}}"),Qr("\u2983","\\lBrace"),Qr("\u2984","\\rBrace"),Qr("\\minuso","\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`\u29b5}}"),Qr("\u29b5","\\minuso"),Qr("\\darr","\\downarrow"),Qr("\\dArr","\\Downarrow"),Qr("\\Darr","\\Downarrow"),Qr("\\lang","\\langle"),Qr("\\rang","\\rangle"),Qr("\\uarr","\\uparrow"),Qr("\\uArr","\\Uparrow"),Qr("\\Uarr","\\Uparrow"),Qr("\\N","\\mathbb{N}"),Qr("\\R","\\mathbb{R}"),Qr("\\Z","\\mathbb{Z}"),Qr("\\alef","\\aleph"),Qr("\\alefsym","\\aleph"),Qr("\\Alpha","\\mathrm{A}"),Qr("\\Beta","\\mathrm{B}"),Qr("\\bull","\\bullet"),Qr("\\Chi","\\mathrm{X}"),Qr("\\clubs","\\clubsuit"),Qr("\\cnums","\\mathbb{C}"),Qr("\\Complex","\\mathbb{C}"),Qr("\\Dagger","\\ddagger"),Qr("\\diamonds","\\diamondsuit"),Qr("\\empty","\\emptyset"),Qr("\\Epsilon","\\mathrm{E}"),Qr("\\Eta","\\mathrm{H}"),Qr("\\exist","\\exists"),Qr("\\harr","\\leftrightarrow"),Qr("\\hArr","\\Leftrightarrow"),Qr("\\Harr","\\Leftrightarrow"),Qr("\\hearts","\\heartsuit"),Qr("\\image","\\Im"),Qr("\\infin","\\infty"),Qr("\\Iota","\\mathrm{I}"),Qr("\\isin","\\in"),Qr("\\Kappa","\\mathrm{K}"),Qr("\\larr","\\leftarrow"),Qr("\\lArr","\\Leftarrow"),Qr("\\Larr","\\Leftarrow"),Qr("\\lrarr","\\leftrightarrow"),Qr("\\lrArr","\\Leftrightarrow"),Qr("\\Lrarr","\\Leftrightarrow"),Qr("\\Mu","\\mathrm{M}"),Qr("\\natnums","\\mathbb{N}"),Qr("\\Nu","\\mathrm{N}"),Qr("\\Omicron","\\mathrm{O}"),Qr("\\plusmn","\\pm"),Qr("\\rarr","\\rightarrow"),Qr("\\rArr","\\Rightarrow"),Qr("\\Rarr","\\Rightarrow"),Qr("\\real","\\Re"),Qr("\\reals","\\mathbb{R}"),Qr("\\Reals","\\mathbb{R}"),Qr("\\Rho","\\mathrm{P}"),Qr("\\sdot","\\cdot"),Qr("\\sect","\\S"),Qr("\\spades","\\spadesuit"),Qr("\\sub","\\subset"),Qr("\\sube","\\subseteq"),Qr("\\supe","\\supseteq"),Qr("\\Tau","\\mathrm{T}"),Qr("\\thetasym","\\vartheta"),Qr("\\weierp","\\wp"),Qr("\\Zeta","\\mathrm{Z}"),Qr("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),Qr("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),Qr("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),Qr("\\bra","\\mathinner{\\langle{#1}|}"),Qr("\\ket","\\mathinner{|{#1}\\rangle}"),Qr("\\braket","\\mathinner{\\langle{#1}\\rangle}"),Qr("\\Bra","\\left\\langle#1\\right|"),Qr("\\Ket","\\left|#1\\right\\rangle");const to=e=>t=>{const r=t.consumeArg().tokens,n=t.consumeArg().tokens,o=t.consumeArg().tokens,s=t.consumeArg().tokens,i=t.macros.get("|"),a=t.macros.get("\\|");t.macros.beginGroup();const l=t=>r=>{e&&(r.macros.set("|",i),o.length&&r.macros.set("\\|",a));let s=t;if(!t&&o.length){"|"===r.future().text&&(r.popToken(),s=!0)}return{tokens:s?o:n,numArgs:0}};t.macros.set("|",l(!1)),o.length&&t.macros.set("\\|",l(!0));const c=t.consumeArg().tokens,h=t.expandTokens([...s,...c,...r]);return t.macros.endGroup(),{tokens:h.reverse(),numArgs:0}};Qr("\\bra@ket",to(!1)),Qr("\\bra@set",to(!0)),Qr("\\Braket","\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}"),Qr("\\Set","\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}"),Qr("\\set","\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}"),Qr("\\angln","{\\angl n}"),Qr("\\blue","\\textcolor{##6495ed}{#1}"),Qr("\\orange","\\textcolor{##ffa500}{#1}"),Qr("\\pink","\\textcolor{##ff00af}{#1}"),Qr("\\red","\\textcolor{##df0030}{#1}"),Qr("\\green","\\textcolor{##28ae7b}{#1}"),Qr("\\gray","\\textcolor{gray}{#1}"),Qr("\\purple","\\textcolor{##9d38bd}{#1}"),Qr("\\blueA","\\textcolor{##ccfaff}{#1}"),Qr("\\blueB","\\textcolor{##80f6ff}{#1}"),Qr("\\blueC","\\textcolor{##63d9ea}{#1}"),Qr("\\blueD","\\textcolor{##11accd}{#1}"),Qr("\\blueE","\\textcolor{##0c7f99}{#1}"),Qr("\\tealA","\\textcolor{##94fff5}{#1}"),Qr("\\tealB","\\textcolor{##26edd5}{#1}"),Qr("\\tealC","\\textcolor{##01d1c1}{#1}"),Qr("\\tealD","\\textcolor{##01a995}{#1}"),Qr("\\tealE","\\textcolor{##208170}{#1}"),Qr("\\greenA","\\textcolor{##b6ffb0}{#1}"),Qr("\\greenB","\\textcolor{##8af281}{#1}"),Qr("\\greenC","\\textcolor{##74cf70}{#1}"),Qr("\\greenD","\\textcolor{##1fab54}{#1}"),Qr("\\greenE","\\textcolor{##0d923f}{#1}"),Qr("\\goldA","\\textcolor{##ffd0a9}{#1}"),Qr("\\goldB","\\textcolor{##ffbb71}{#1}"),Qr("\\goldC","\\textcolor{##ff9c39}{#1}"),Qr("\\goldD","\\textcolor{##e07d10}{#1}"),Qr("\\goldE","\\textcolor{##a75a05}{#1}"),Qr("\\redA","\\textcolor{##fca9a9}{#1}"),Qr("\\redB","\\textcolor{##ff8482}{#1}"),Qr("\\redC","\\textcolor{##f9685d}{#1}"),Qr("\\redD","\\textcolor{##e84d39}{#1}"),Qr("\\redE","\\textcolor{##bc2612}{#1}"),Qr("\\maroonA","\\textcolor{##ffbde0}{#1}"),Qr("\\maroonB","\\textcolor{##ff92c6}{#1}"),Qr("\\maroonC","\\textcolor{##ed5fa6}{#1}"),Qr("\\maroonD","\\textcolor{##ca337c}{#1}"),Qr("\\maroonE","\\textcolor{##9e034e}{#1}"),Qr("\\purpleA","\\textcolor{##ddd7ff}{#1}"),Qr("\\purpleB","\\textcolor{##c6b9fc}{#1}"),Qr("\\purpleC","\\textcolor{##aa87ff}{#1}"),Qr("\\purpleD","\\textcolor{##7854ab}{#1}"),Qr("\\purpleE","\\textcolor{##543b78}{#1}"),Qr("\\mintA","\\textcolor{##f5f9e8}{#1}"),Qr("\\mintB","\\textcolor{##edf2df}{#1}"),Qr("\\mintC","\\textcolor{##e0e5cc}{#1}"),Qr("\\grayA","\\textcolor{##f6f7f7}{#1}"),Qr("\\grayB","\\textcolor{##f0f1f2}{#1}"),Qr("\\grayC","\\textcolor{##e3e5e6}{#1}"),Qr("\\grayD","\\textcolor{##d6d8da}{#1}"),Qr("\\grayE","\\textcolor{##babec2}{#1}"),Qr("\\grayF","\\textcolor{##888d93}{#1}"),Qr("\\grayG","\\textcolor{##626569}{#1}"),Qr("\\grayH","\\textcolor{##3b3e40}{#1}"),Qr("\\grayI","\\textcolor{##21242c}{#1}"),Qr("\\kaBlue","\\textcolor{##314453}{#1}"),Qr("\\kaGreen","\\textcolor{##71B307}{#1}");const ro={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0};class no{constructor(e,t,r){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=t,this.expansionCount=0,this.feed(e),this.macros=new Wn(_n,t.macros),this.mode=r,this.stack=[]}feed(e){this.lexer=new Yn(e,this.settings)}switchMode(e){this.mode=e}beginGroup(){this.macros.beginGroup()}endGroup(){this.macros.endGroup()}endGroups(){this.macros.endGroups()}future(){return 0===this.stack.length&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]}popToken(){return this.future(),this.stack.pop()}pushToken(e){this.stack.push(e)}pushTokens(e){this.stack.push(...e)}scanArgument(e){let t,r,n;if(e){if(this.consumeSpaces(),"["!==this.future().text)return null;t=this.popToken();var o=this.consumeArg(["]"]);n=o.tokens,r=o.end}else{var s=this.consumeArg();n=s.tokens,t=s.start,r=s.end}return this.pushToken(new tn("EOF",r.loc)),this.pushTokens(n),new tn("",en.range(t,r))}consumeSpaces(){for(;;){if(" "!==this.future().text)break;this.stack.pop()}}consumeArg(e){const t=[],r=e&&e.length>0;r||this.consumeSpaces();const o=this.future();let s,i=0,a=0;do{if(s=this.popToken(),t.push(s),"{"===s.text)++i;else if("}"===s.text){if(--i,-1===i)throw new n("Extra }",s)}else if("EOF"===s.text)throw new n("Unexpected end of input in a macro argument, expected '"+(e&&r?e[a]:"}")+"'",s);if(e&&r)if((0===i||1===i&&"{"===e[a])&&s.text===e[a]){if(++a,a===e.length){t.splice(-a,a);break}}else a=0}while(0!==i||r);return"{"===o.text&&"}"===t[t.length-1].text&&(t.pop(),t.shift()),t.reverse(),{tokens:t,start:o,end:s}}consumeArgs(e,t){if(t){if(t.length!==e+1)throw new n("The length of delimiters doesn't match the number of args!");const r=t[0];for(let e=0;ethis.settings.maxExpand)throw new n("Too many expansions: infinite loop or need to increase maxExpand setting")}expandOnce(e){const t=this.popToken(),r=t.text,o=t.noexpand?null:this._getExpansion(r);if(null==o||e&&o.unexpandable){if(e&&null==o&&"\\"===r[0]&&!this.isDefined(r))throw new n("Undefined control sequence: "+r);return this.pushToken(t),!1}this.countExpansion(1);let s=o.tokens;const i=this.consumeArgs(o.numArgs,o.delimiters);if(o.numArgs){s=s.slice();for(let e=s.length-1;e>=0;--e){let t=s[e];if("#"===t.text){if(0===e)throw new n("Incomplete placeholder at end of macro body",t);if(t=s[--e],"#"===t.text)s.splice(e+1,1);else{if(!/^[1-9]$/.test(t.text))throw new n("Not a valid argument number",t);s.splice(e,2,...i[+t.text-1])}}}}return this.pushTokens(s),s.length}expandAfterFuture(){return this.expandOnce(),this.future()}expandNextToken(){for(;;)if(!1===this.expandOnce()){const e=this.stack.pop();return e.treatAsRelax&&(e.text="\\relax"),e}}expandMacro(e){return this.macros.has(e)?this.expandTokens([new tn(e)]):void 0}expandTokens(e){const t=[],r=this.stack.length;for(this.pushTokens(e);this.stack.length>r;)if(!1===this.expandOnce(!0)){const e=this.stack.pop();e.treatAsRelax&&(e.noexpand=!1,e.treatAsRelax=!1),t.push(e)}return this.countExpansion(t.length),t}expandMacroAsText(e){const t=this.expandMacro(e);return t?t.map(e=>e.text).join(""):t}_getExpansion(e){const t=this.macros.get(e);if(null==t)return t;if(1===e.length){const t=this.lexer.catcodes[e];if(null!=t&&13!==t)return}const r="function"==typeof t?t(this):t;if("string"==typeof r){let e=0;if(r.includes("#")){const t=r.replace(/##/g,"");for(;t.includes("#"+(e+1));)++e}const t=new Yn(r,this.settings),n=[];let o=t.lex();for(;"EOF"!==o.text;)n.push(o),o=t.lex();n.reverse();return{tokens:n,numArgs:e}}return r}isDefined(e){return this.macros.has(e)||Object.prototype.hasOwnProperty.call(Pn,e)||Object.prototype.hasOwnProperty.call(ne.math,e)||Object.prototype.hasOwnProperty.call(ne.text,e)||Object.prototype.hasOwnProperty.call(ro,e)}isExpandable(e){const t=this.macros.get(e);return null!=t?"string"==typeof t||"function"==typeof t||!t.unexpandable:Object.prototype.hasOwnProperty.call(Pn,e)&&!Pn[e].primitive}}const oo=/^[\u208a\u208b\u208c\u208d\u208e\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2090\u2091\u2095\u1d62\u2c7c\u2096\u2097\u2098\u2099\u2092\u209a\u1d63\u209b\u209c\u1d64\u1d65\u2093\u1d66\u1d67\u1d68\u1d69\u1d6a]/,so=Object.freeze({"\u208a":"+","\u208b":"-","\u208c":"=","\u208d":"(","\u208e":")","\u2080":"0","\u2081":"1","\u2082":"2","\u2083":"3","\u2084":"4","\u2085":"5","\u2086":"6","\u2087":"7","\u2088":"8","\u2089":"9","\u2090":"a","\u2091":"e","\u2095":"h","\u1d62":"i","\u2c7c":"j","\u2096":"k","\u2097":"l","\u2098":"m","\u2099":"n","\u2092":"o","\u209a":"p","\u1d63":"r","\u209b":"s","\u209c":"t","\u1d64":"u","\u1d65":"v","\u2093":"x","\u1d66":"\u03b2","\u1d67":"\u03b3","\u1d68":"\u03c1","\u1d69":"\u03d5","\u1d6a":"\u03c7","\u207a":"+","\u207b":"-","\u207c":"=","\u207d":"(","\u207e":")","\u2070":"0","\xb9":"1","\xb2":"2","\xb3":"3","\u2074":"4","\u2075":"5","\u2076":"6","\u2077":"7","\u2078":"8","\u2079":"9","\u1d2c":"A","\u1d2e":"B","\u1d30":"D","\u1d31":"E","\u1d33":"G","\u1d34":"H","\u1d35":"I","\u1d36":"J","\u1d37":"K","\u1d38":"L","\u1d39":"M","\u1d3a":"N","\u1d3c":"O","\u1d3e":"P","\u1d3f":"R","\u1d40":"T","\u1d41":"U","\u2c7d":"V","\u1d42":"W","\u1d43":"a","\u1d47":"b","\u1d9c":"c","\u1d48":"d","\u1d49":"e","\u1da0":"f","\u1d4d":"g","\u02b0":"h","\u2071":"i","\u02b2":"j","\u1d4f":"k","\u02e1":"l","\u1d50":"m","\u207f":"n","\u1d52":"o","\u1d56":"p","\u02b3":"r","\u02e2":"s","\u1d57":"t","\u1d58":"u","\u1d5b":"v","\u02b7":"w","\u02e3":"x","\u02b8":"y","\u1dbb":"z","\u1d5d":"\u03b2","\u1d5e":"\u03b3","\u1d5f":"\u03b4","\u1d60":"\u03d5","\u1d61":"\u03c7","\u1dbf":"\u03b8"}),io={"\u0301":{text:"\\'",math:"\\acute"},"\u0300":{text:"\\`",math:"\\grave"},"\u0308":{text:'\\"',math:"\\ddot"},"\u0303":{text:"\\~",math:"\\tilde"},"\u0304":{text:"\\=",math:"\\bar"},"\u0306":{text:"\\u",math:"\\breve"},"\u030c":{text:"\\v",math:"\\check"},"\u0302":{text:"\\^",math:"\\hat"},"\u0307":{text:"\\.",math:"\\dot"},"\u030a":{text:"\\r",math:"\\mathring"},"\u030b":{text:"\\H"},"\u0327":{text:"\\c"}},ao={"\xe1":"a\u0301","\xe0":"a\u0300","\xe4":"a\u0308","\u01df":"a\u0308\u0304","\xe3":"a\u0303","\u0101":"a\u0304","\u0103":"a\u0306","\u1eaf":"a\u0306\u0301","\u1eb1":"a\u0306\u0300","\u1eb5":"a\u0306\u0303","\u01ce":"a\u030c","\xe2":"a\u0302","\u1ea5":"a\u0302\u0301","\u1ea7":"a\u0302\u0300","\u1eab":"a\u0302\u0303","\u0227":"a\u0307","\u01e1":"a\u0307\u0304","\xe5":"a\u030a","\u01fb":"a\u030a\u0301","\u1e03":"b\u0307","\u0107":"c\u0301","\u1e09":"c\u0327\u0301","\u010d":"c\u030c","\u0109":"c\u0302","\u010b":"c\u0307","\xe7":"c\u0327","\u010f":"d\u030c","\u1e0b":"d\u0307","\u1e11":"d\u0327","\xe9":"e\u0301","\xe8":"e\u0300","\xeb":"e\u0308","\u1ebd":"e\u0303","\u0113":"e\u0304","\u1e17":"e\u0304\u0301","\u1e15":"e\u0304\u0300","\u0115":"e\u0306","\u1e1d":"e\u0327\u0306","\u011b":"e\u030c","\xea":"e\u0302","\u1ebf":"e\u0302\u0301","\u1ec1":"e\u0302\u0300","\u1ec5":"e\u0302\u0303","\u0117":"e\u0307","\u0229":"e\u0327","\u1e1f":"f\u0307","\u01f5":"g\u0301","\u1e21":"g\u0304","\u011f":"g\u0306","\u01e7":"g\u030c","\u011d":"g\u0302","\u0121":"g\u0307","\u0123":"g\u0327","\u1e27":"h\u0308","\u021f":"h\u030c","\u0125":"h\u0302","\u1e23":"h\u0307","\u1e29":"h\u0327","\xed":"i\u0301","\xec":"i\u0300","\xef":"i\u0308","\u1e2f":"i\u0308\u0301","\u0129":"i\u0303","\u012b":"i\u0304","\u012d":"i\u0306","\u01d0":"i\u030c","\xee":"i\u0302","\u01f0":"j\u030c","\u0135":"j\u0302","\u1e31":"k\u0301","\u01e9":"k\u030c","\u0137":"k\u0327","\u013a":"l\u0301","\u013e":"l\u030c","\u013c":"l\u0327","\u1e3f":"m\u0301","\u1e41":"m\u0307","\u0144":"n\u0301","\u01f9":"n\u0300","\xf1":"n\u0303","\u0148":"n\u030c","\u1e45":"n\u0307","\u0146":"n\u0327","\xf3":"o\u0301","\xf2":"o\u0300","\xf6":"o\u0308","\u022b":"o\u0308\u0304","\xf5":"o\u0303","\u1e4d":"o\u0303\u0301","\u1e4f":"o\u0303\u0308","\u022d":"o\u0303\u0304","\u014d":"o\u0304","\u1e53":"o\u0304\u0301","\u1e51":"o\u0304\u0300","\u014f":"o\u0306","\u01d2":"o\u030c","\xf4":"o\u0302","\u1ed1":"o\u0302\u0301","\u1ed3":"o\u0302\u0300","\u1ed7":"o\u0302\u0303","\u022f":"o\u0307","\u0231":"o\u0307\u0304","\u0151":"o\u030b","\u1e55":"p\u0301","\u1e57":"p\u0307","\u0155":"r\u0301","\u0159":"r\u030c","\u1e59":"r\u0307","\u0157":"r\u0327","\u015b":"s\u0301","\u1e65":"s\u0301\u0307","\u0161":"s\u030c","\u1e67":"s\u030c\u0307","\u015d":"s\u0302","\u1e61":"s\u0307","\u015f":"s\u0327","\u1e97":"t\u0308","\u0165":"t\u030c","\u1e6b":"t\u0307","\u0163":"t\u0327","\xfa":"u\u0301","\xf9":"u\u0300","\xfc":"u\u0308","\u01d8":"u\u0308\u0301","\u01dc":"u\u0308\u0300","\u01d6":"u\u0308\u0304","\u01da":"u\u0308\u030c","\u0169":"u\u0303","\u1e79":"u\u0303\u0301","\u016b":"u\u0304","\u1e7b":"u\u0304\u0308","\u016d":"u\u0306","\u01d4":"u\u030c","\xfb":"u\u0302","\u016f":"u\u030a","\u0171":"u\u030b","\u1e7d":"v\u0303","\u1e83":"w\u0301","\u1e81":"w\u0300","\u1e85":"w\u0308","\u0175":"w\u0302","\u1e87":"w\u0307","\u1e98":"w\u030a","\u1e8d":"x\u0308","\u1e8b":"x\u0307","\xfd":"y\u0301","\u1ef3":"y\u0300","\xff":"y\u0308","\u1ef9":"y\u0303","\u0233":"y\u0304","\u0177":"y\u0302","\u1e8f":"y\u0307","\u1e99":"y\u030a","\u017a":"z\u0301","\u017e":"z\u030c","\u1e91":"z\u0302","\u017c":"z\u0307","\xc1":"A\u0301","\xc0":"A\u0300","\xc4":"A\u0308","\u01de":"A\u0308\u0304","\xc3":"A\u0303","\u0100":"A\u0304","\u0102":"A\u0306","\u1eae":"A\u0306\u0301","\u1eb0":"A\u0306\u0300","\u1eb4":"A\u0306\u0303","\u01cd":"A\u030c","\xc2":"A\u0302","\u1ea4":"A\u0302\u0301","\u1ea6":"A\u0302\u0300","\u1eaa":"A\u0302\u0303","\u0226":"A\u0307","\u01e0":"A\u0307\u0304","\xc5":"A\u030a","\u01fa":"A\u030a\u0301","\u1e02":"B\u0307","\u0106":"C\u0301","\u1e08":"C\u0327\u0301","\u010c":"C\u030c","\u0108":"C\u0302","\u010a":"C\u0307","\xc7":"C\u0327","\u010e":"D\u030c","\u1e0a":"D\u0307","\u1e10":"D\u0327","\xc9":"E\u0301","\xc8":"E\u0300","\xcb":"E\u0308","\u1ebc":"E\u0303","\u0112":"E\u0304","\u1e16":"E\u0304\u0301","\u1e14":"E\u0304\u0300","\u0114":"E\u0306","\u1e1c":"E\u0327\u0306","\u011a":"E\u030c","\xca":"E\u0302","\u1ebe":"E\u0302\u0301","\u1ec0":"E\u0302\u0300","\u1ec4":"E\u0302\u0303","\u0116":"E\u0307","\u0228":"E\u0327","\u1e1e":"F\u0307","\u01f4":"G\u0301","\u1e20":"G\u0304","\u011e":"G\u0306","\u01e6":"G\u030c","\u011c":"G\u0302","\u0120":"G\u0307","\u0122":"G\u0327","\u1e26":"H\u0308","\u021e":"H\u030c","\u0124":"H\u0302","\u1e22":"H\u0307","\u1e28":"H\u0327","\xcd":"I\u0301","\xcc":"I\u0300","\xcf":"I\u0308","\u1e2e":"I\u0308\u0301","\u0128":"I\u0303","\u012a":"I\u0304","\u012c":"I\u0306","\u01cf":"I\u030c","\xce":"I\u0302","\u0130":"I\u0307","\u0134":"J\u0302","\u1e30":"K\u0301","\u01e8":"K\u030c","\u0136":"K\u0327","\u0139":"L\u0301","\u013d":"L\u030c","\u013b":"L\u0327","\u1e3e":"M\u0301","\u1e40":"M\u0307","\u0143":"N\u0301","\u01f8":"N\u0300","\xd1":"N\u0303","\u0147":"N\u030c","\u1e44":"N\u0307","\u0145":"N\u0327","\xd3":"O\u0301","\xd2":"O\u0300","\xd6":"O\u0308","\u022a":"O\u0308\u0304","\xd5":"O\u0303","\u1e4c":"O\u0303\u0301","\u1e4e":"O\u0303\u0308","\u022c":"O\u0303\u0304","\u014c":"O\u0304","\u1e52":"O\u0304\u0301","\u1e50":"O\u0304\u0300","\u014e":"O\u0306","\u01d1":"O\u030c","\xd4":"O\u0302","\u1ed0":"O\u0302\u0301","\u1ed2":"O\u0302\u0300","\u1ed6":"O\u0302\u0303","\u022e":"O\u0307","\u0230":"O\u0307\u0304","\u0150":"O\u030b","\u1e54":"P\u0301","\u1e56":"P\u0307","\u0154":"R\u0301","\u0158":"R\u030c","\u1e58":"R\u0307","\u0156":"R\u0327","\u015a":"S\u0301","\u1e64":"S\u0301\u0307","\u0160":"S\u030c","\u1e66":"S\u030c\u0307","\u015c":"S\u0302","\u1e60":"S\u0307","\u015e":"S\u0327","\u0164":"T\u030c","\u1e6a":"T\u0307","\u0162":"T\u0327","\xda":"U\u0301","\xd9":"U\u0300","\xdc":"U\u0308","\u01d7":"U\u0308\u0301","\u01db":"U\u0308\u0300","\u01d5":"U\u0308\u0304","\u01d9":"U\u0308\u030c","\u0168":"U\u0303","\u1e78":"U\u0303\u0301","\u016a":"U\u0304","\u1e7a":"U\u0304\u0308","\u016c":"U\u0306","\u01d3":"U\u030c","\xdb":"U\u0302","\u016e":"U\u030a","\u0170":"U\u030b","\u1e7c":"V\u0303","\u1e82":"W\u0301","\u1e80":"W\u0300","\u1e84":"W\u0308","\u0174":"W\u0302","\u1e86":"W\u0307","\u1e8c":"X\u0308","\u1e8a":"X\u0307","\xdd":"Y\u0301","\u1ef2":"Y\u0300","\u0178":"Y\u0308","\u1ef8":"Y\u0303","\u0232":"Y\u0304","\u0176":"Y\u0302","\u1e8e":"Y\u0307","\u0179":"Z\u0301","\u017d":"Z\u030c","\u1e90":"Z\u0302","\u017b":"Z\u0307","\u03ac":"\u03b1\u0301","\u1f70":"\u03b1\u0300","\u1fb1":"\u03b1\u0304","\u1fb0":"\u03b1\u0306","\u03ad":"\u03b5\u0301","\u1f72":"\u03b5\u0300","\u03ae":"\u03b7\u0301","\u1f74":"\u03b7\u0300","\u03af":"\u03b9\u0301","\u1f76":"\u03b9\u0300","\u03ca":"\u03b9\u0308","\u0390":"\u03b9\u0308\u0301","\u1fd2":"\u03b9\u0308\u0300","\u1fd1":"\u03b9\u0304","\u1fd0":"\u03b9\u0306","\u03cc":"\u03bf\u0301","\u1f78":"\u03bf\u0300","\u03cd":"\u03c5\u0301","\u1f7a":"\u03c5\u0300","\u03cb":"\u03c5\u0308","\u03b0":"\u03c5\u0308\u0301","\u1fe2":"\u03c5\u0308\u0300","\u1fe1":"\u03c5\u0304","\u1fe0":"\u03c5\u0306","\u03ce":"\u03c9\u0301","\u1f7c":"\u03c9\u0300","\u038e":"\u03a5\u0301","\u1fea":"\u03a5\u0300","\u03ab":"\u03a5\u0308","\u1fe9":"\u03a5\u0304","\u1fe8":"\u03a5\u0306","\u038f":"\u03a9\u0301","\u1ffa":"\u03a9\u0300"};class lo{constructor(e,t){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new no(e,t,this.mode),this.settings=t,this.leftrightDepth=0,this.nextToken=null}expect(e,t){if(void 0===t&&(t=!0),this.fetch().text!==e)throw new n("Expected '"+e+"', got '"+this.fetch().text+"'",this.fetch());t&&this.consume()}consume(){this.nextToken=null}fetch(){return null==this.nextToken&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken}switchMode(e){this.mode=e,this.gullet.switchMode(e)}parse(){this.settings.globalGroup||this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");try{const e=this.parseExpression(!1);return this.expect("EOF"),this.settings.globalGroup||this.gullet.endGroup(),e}finally{this.gullet.endGroups()}}subparse(e){const t=this.nextToken;this.consume(),this.gullet.pushToken(new tn("}")),this.gullet.pushTokens(e);const r=this.parseExpression(!1);return this.expect("}"),this.nextToken=t,r}parseExpression(e,t){const r=[];for(;;){"math"===this.mode&&this.consumeSpaces();const n=this.fetch();if(lo.endOfExpression.has(n.text))break;if(t&&n.text===t)break;if(e&&Pn[n.text]&&Pn[n.text].infix)break;const o=this.parseAtom(t);if(!o)break;"internal"!==o.type&&r.push(o)}return"text"===this.mode&&this.formLigatures(r),this.handleInfixNodes(r)}handleInfixNodes(e){let t,r=-1;for(let o=0;o=128))return null;this.settings.strict&&(T(t.charCodeAt(0))?"math"===this.mode&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+t[0]+'" used in math mode',e):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+t[0]+'" ('+t.charCodeAt(0)+")",e)),o={type:"textord",mode:"text",loc:en.range(e),text:t}}var s;if(this.consume(),r)for(let t=0;tbl in O0?bDt(O0,bl,{enumerable:!0,configurable:!0,writable:!0,value:ry}):O0[bl]=ry;var n0e=(O0,bl,ry)=>xDt(O0,typeof bl!="symbol"?bl+"":bl,ry);var __esbuild_esm_mermaid_nm;(__esbuild_esm_mermaid_nm||={}).mermaid=(()=>{var X1;var O0=Object.create,bl=Object.defineProperty,ry=Object.getOwnPropertyDescriptor,U4t=Object.getOwnPropertyNames,Y4t=Object.getPrototypeOf,H4t=Object.prototype.hasOwnProperty,s=(e,t)=>bl(e,"name",{value:t,configurable:!0}),G=(e,t)=>()=>(e&&(t=e(e=0)),t),Ro=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Mr=(e,t)=>{for(var r in t)bl(e,r,{get:t[r],enumerable:!0})},a0e=(e,t,r,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of U4t(t))!H4t.call(e,n)&&n!==r&&bl(e,n,{get:()=>t[n],enumerable:!(i=ry(t,n))||i.enumerable});return e},$o=(e,t,r)=>(r=e!=null?O0(Y4t(e)):{},a0e(t||!e||!e.__esModule?bl(r,"default",{value:e,enumerable:!0}):r,e)),G4t=e=>a0e(bl({},"__esModule",{value:!0}),e),s0e,Rx,ZM,o0e,JM=G(()=>{"use strict";s0e=Object.freeze({left:0,top:0,width:16,height:16}),Rx=Object.freeze({rotate:0,vFlip:!1,hFlip:!1}),ZM=Object.freeze({...s0e,...Rx}),o0e=Object.freeze({...ZM,body:"",hidden:!1})}),l0e,c0e,X4t=G(()=>{"use strict";JM(),l0e=Object.freeze({width:null,height:null}),c0e=Object.freeze({...l0e,...Rx})}),u0e,hE,K4t=G(()=>{"use strict";u0e=s((e,t,r,i="")=>{let n=e.split(":");if(e.slice(0,1)==="@"){if(n.length<2||n.length>3)return null;i=n.shift().slice(1)}if(n.length>3||!n.length)return null;if(n.length>1){let l=n.pop(),u=n.pop(),h={provider:n.length>0?n[0]:i,prefix:u,name:l};return t&&!hE(h)?null:h}let a=n[0],o=a.split("-");if(o.length>1){let l={provider:i,prefix:o.shift(),name:o.join("-")};return t&&!hE(l)?null:l}if(r&&i===""){let l={provider:i,prefix:"",name:a};return t&&!hE(l,r)?null:l}return null},"stringToIcon"),hE=s((e,t)=>e?!!((t&&e.prefix===""||e.prefix)&&e.name):!1,"validateIconName")});function h0e(e,t){let r={};!e.hFlip!=!t.hFlip&&(r.hFlip=!0),!e.vFlip!=!t.vFlip&&(r.vFlip=!0);let i=((e.rotate||0)+(t.rotate||0))%4;return i&&(r.rotate=i),r}var Q4t=G(()=>{"use strict";s(h0e,"mergeIconTransformations")});function eD(e,t){let r=h0e(e,t);for(let i in o0e)i in Rx?i in e&&!(i in r)&&(r[i]=Rx[i]):i in t?r[i]=t[i]:i in e&&(r[i]=e[i]);return r}var Z4t=G(()=>{"use strict";JM(),Q4t(),s(eD,"mergeIconData")});function d0e(e,t){let r=e.icons,i=e.aliases||Object.create(null),n=Object.create(null);function a(o){if(r[o])return n[o]=[];if(!(o in n)){n[o]=null;let l=i[o]&&i[o].parent,u=l&&a(l);u&&(n[o]=[l].concat(u))}return n[o]}return s(a,"resolve"),(t||Object.keys(r).concat(Object.keys(i))).forEach(a),n}var J4t=G(()=>{"use strict";s(d0e,"getIconsTree")});function tD(e,t,r){let i=e.icons,n=e.aliases||Object.create(null),a={};function o(l){a=eD(i[l]||n[l],a)}return s(o,"parse"),o(t),r.forEach(o),eD(e,a)}function p0e(e,t){if(e.icons[t])return tD(e,t,[]);let r=d0e(e,[t])[t];return r?tD(e,t,r):null}var e3t=G(()=>{"use strict";Z4t(),J4t(),s(tD,"internalGetIconData"),s(p0e,"getIconData")});function rD(e,t,r){if(t===1)return e;if(r=r||100,typeof e=="number")return Math.ceil(e*t*r)/r;if(typeof e!="string")return e;let i=e.split(f0e);if(i===null||!i.length)return e;let n=[],a=i.shift(),o=g0e.test(a);for(;;){if(o){let l=parseFloat(a);isNaN(l)?n.push(a):n.push(Math.ceil(l*t*r)/r)}else n.push(a);if(a=i.shift(),a===void 0)return n.join("");o=!o}}var f0e,g0e,t3t=G(()=>{"use strict";f0e=/(-?[0-9.]*[0-9]+[0-9.]*)/g,g0e=/^-?[0-9.]*[0-9]+[0-9.]*$/g,s(rD,"calculateSize")});function m0e(e,t="defs"){let r="",i=e.indexOf("<"+t);for(;i>=0;){let n=e.indexOf(">",i),a=e.indexOf("",a);if(o===-1)break;r+=e.slice(n+1,a).trim(),e=e.slice(0,i).trim()+e.slice(o+1)}return{defs:r,content:e}}function y0e(e,t){return e?""+e+""+t:t}function v0e(e,t,r){let i=m0e(e);return y0e(i.defs,t+i.content+r)}var r3t=G(()=>{"use strict";s(m0e,"splitSVGDefs"),s(y0e,"mergeDefsAndContent"),s(v0e,"wrapSVGContent")});function b0e(e,t){let r={...ZM,...e},i={...c0e,...t},n={left:r.left,top:r.top,width:r.width,height:r.height},a=r.body;[r,i].forEach(y=>{let k=[],C=y.hFlip,S=y.vFlip,E=y.rotate;C?S?E+=2:(k.push("translate("+(n.width+n.left).toString()+" "+(0-n.top).toString()+")"),k.push("scale(-1 1)"),n.top=n.left=0):S&&(k.push("translate("+(0-n.left).toString()+" "+(n.height+n.top).toString()+")"),k.push("scale(1 -1)"),n.top=n.left=0);let _;switch(E<0&&(E-=Math.floor(E/4)*4),E=E%4,E){case 1:_=n.height/2+n.top,k.unshift("rotate(90 "+_.toString()+" "+_.toString()+")");break;case 2:k.unshift("rotate(180 "+(n.width/2+n.left).toString()+" "+(n.height/2+n.top).toString()+")");break;case 3:_=n.width/2+n.left,k.unshift("rotate(-90 "+_.toString()+" "+_.toString()+")");break}E%2===1&&(n.left!==n.top&&(_=n.left,n.left=n.top,n.top=_),n.width!==n.height&&(_=n.width,n.width=n.height,n.height=_)),k.length&&(a=v0e(a,'',""))});let o=i.width,l=i.height,u=n.width,h=n.height,d,p;o===null?(p=l===null?"1em":l==="auto"?h:l,d=rD(p,u/h)):(d=o==="auto"?u:o,p=l===null?rD(d,h/u):l==="auto"?h:l);let f={},g=s((y,k)=>{x0e(k)||(f[y]=k.toString())},"setAttr");g("width",d),g("height",p);let m=[n.left,n.top,u,h];return f.viewBox=m.join(" "),{attributes:f,viewBox:m,body:a}}var x0e,i3t=G(()=>{"use strict";JM(),X4t(),t3t(),r3t(),x0e=s(e=>e==="unset"||e==="undefined"||e==="none","isUnsetKeyword"),s(b0e,"iconToSVG")});function T0e(e,t=C0e){let r=[],i;for(;i=k0e.exec(e);)r.push(i[1]);if(!r.length)return e;let n="suffix"+(Math.random()*16777216|Date.now()).toString(16);return r.forEach(a=>{let o=typeof t=="function"?t(a):t+(w0e++).toString(),l=a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");e=e.replace(new RegExp('([#;"])('+l+')([")]|\\.[a-z])',"g"),"$1"+o+n+"$3")}),e=e.replace(new RegExp(n,"g"),""),e}var k0e,C0e,w0e,n3t=G(()=>{"use strict";k0e=/\sid="(\S+)"/g,C0e="IconifyId"+Date.now().toString(16)+(Math.random()*16777216|0).toString(16),w0e=0,s(T0e,"replaceIDs")});function E0e(e,t){let r=e.indexOf("xlink:")===-1?"":' xmlns:xlink="http://www.w3.org/1999/xlink"';for(let i in t)r+=" "+i+'="'+t[i]+'"';return'"+e+""}var a3t=G(()=>{"use strict";s(E0e,"iconToHTML")}),s3t=G(()=>{"use strict";K4t(),e3t(),i3t(),n3t(),a3t()}),dE,Sa,$x=G(()=>{"use strict";dE=s((e,t,{depth:r=2}={})=>{let i={depth:r};if(Array.isArray(t)&&!Array.isArray(e))return t.forEach(n=>dE(e,n,i)),e;if(Array.isArray(t)&&Array.isArray(e))return t.forEach(n=>{e.includes(n)||e.push(n)}),e;if(e==null||r<=0)return e!=null&&typeof e=="object"&&typeof t=="object"?Object.assign(e,t):t;if(t!=null&&typeof e=="object"&&typeof t=="object"){let n=e;Object.entries(t).forEach(([a,o])=>{if(typeof o=="object"){if(o===null)return;Object.hasOwn(e,a)||Object.defineProperty(e,a,{value:void 0,writable:!0,enumerable:!0,configurable:!0}),n[a]===void 0&&(n[a]=Array.isArray(o)?[]:{}),typeof n[a]=="object"&&(n[a]=dE(n[a],o,{depth:r-1}))}else typeof n[a]!="object"&&(Object.hasOwn(e,a)?n[a]=o:Object.defineProperty(e,a,{value:o,writable:!0,enumerable:!0,configurable:!0}))})}return e},"assignWithDepth"),Sa=dE}),iD=Ro((e,t)=>{"use strict";(function(r,i){typeof e=="object"&&typeof t<"u"?t.exports=i():typeof define=="function"&&define.amd?define(i):(r=typeof globalThis<"u"?globalThis:r||self).dayjs=i()})(e,(function(){"use strict";var r=1e3,i=6e4,n=36e5,a="millisecond",o="second",l="minute",u="hour",h="day",d="week",p="month",f="quarter",g="year",m="date",y="Invalid Date",k=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,C=/\[([^\]]+)]|YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,S={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:s(function(I){var z=["th","st","nd","rd"],F=I%100;return"["+I+(z[(F-20)%10]||z[F]||z[0])+"]"},"ordinal")},E=s(function(I,z,F){var O=String(I);return!O||O.length>=z?I:""+Array(z+1-O.length).join(F)+I},"m"),_={s:E,z:s(function(I){var z=-I.utcOffset(),F=Math.abs(z),O=Math.floor(F/60),B=F%60;return(z<=0?"+":"-")+E(O,2,"0")+":"+E(B,2,"0")},"z"),m:s(function I(z,F){if(z.date()1)return I(U[0])}else{var H=z.name;v[H]=z,B=H}return!O&&B&&(b=B),B||!O&&b},"t"),$=s(function(I,z){if(w(I))return I.clone();var F=typeof z=="object"?z:{};return F.date=I,F.args=arguments,new N(F)},"O"),M=_;M.l=R,M.i=w,M.w=function(I,z){return $(I,{locale:z.$L,utc:z.$u,x:z.$x,$offset:z.$offset})};var N=(function(){function I(F){this.$L=R(F.locale,null,!0),this.parse(F),this.$x=this.$x||F.x||{},this[x]=!0}s(I,"M");var z=I.prototype;return z.parse=function(F){this.$d=(function(O){var B=O.date,Y=O.utc;if(B===null)return new Date(NaN);if(M.u(B))return new Date;if(B instanceof Date)return new Date(B);if(typeof B=="string"&&!/Z$/i.test(B)){var U=B.match(k);if(U){var H=U[2]-1||0,J=(U[7]||"0").substring(0,3);return Y?new Date(Date.UTC(U[1],H,U[3]||1,U[4]||0,U[5]||0,U[6]||0,J)):new Date(U[1],H,U[3]||1,U[4]||0,U[5]||0,U[6]||0,J)}}return new Date(B)})(F),this.init()},z.init=function(){var F=this.$d;this.$y=F.getFullYear(),this.$M=F.getMonth(),this.$D=F.getDate(),this.$W=F.getDay(),this.$H=F.getHours(),this.$m=F.getMinutes(),this.$s=F.getSeconds(),this.$ms=F.getMilliseconds()},z.$utils=function(){return M},z.isValid=function(){return this.$d.toString()!==y},z.isSame=function(F,O){var B=$(F);return this.startOf(O)<=B&&B<=this.endOf(O)},z.isAfter=function(F,O){return $(F){"use strict";S0e=$o(iD(),1),xu={trace:0,debug:1,info:2,warn:3,error:4,fatal:5},Ee={trace:s((...e)=>{},"trace"),debug:s((...e)=>{},"debug"),info:s((...e)=>{},"info"),warn:s((...e)=>{},"warn"),error:s((...e)=>{},"error"),fatal:s((...e)=>{},"fatal")},pE=s(function(e="fatal"){let t=xu.fatal;typeof e=="string"?e.toLowerCase()in xu&&(t=xu[e]):typeof e=="number"&&(t=e),Ee.trace=()=>{},Ee.debug=()=>{},Ee.info=()=>{},Ee.warn=()=>{},Ee.error=()=>{},Ee.fatal=()=>{},t<=xu.fatal&&(Ee.fatal=console.error?console.error.bind(console,xl("FATAL"),"color: orange"):console.log.bind(console,"\x1B[35m",xl("FATAL"))),t<=xu.error&&(Ee.error=console.error?console.error.bind(console,xl("ERROR"),"color: orange"):console.log.bind(console,"\x1B[31m",xl("ERROR"))),t<=xu.warn&&(Ee.warn=console.warn?console.warn.bind(console,xl("WARN"),"color: orange"):console.log.bind(console,"\x1B[33m",xl("WARN"))),t<=xu.info&&(Ee.info=console.info?console.info.bind(console,xl("INFO"),"color: lightblue"):console.log.bind(console,"\x1B[34m",xl("INFO"))),t<=xu.debug&&(Ee.debug=console.debug?console.debug.bind(console,xl("DEBUG"),"color: lightgreen"):console.log.bind(console,"\x1B[32m",xl("DEBUG"))),t<=xu.trace&&(Ee.trace=console.debug?console.debug.bind(console,xl("TRACE"),"color: lightgreen"):console.log.bind(console,"\x1B[32m",xl("TRACE")))},"setLogLevel"),xl=s(e=>`%c${(0,S0e.default)().format("ss.SSS")} : ${e} : `,"format")}),Lx,_0e,o3t=G(()=>{"use strict";Lx={min:{r:0,g:0,b:0,s:0,l:0,a:0},max:{r:255,g:255,b:255,h:360,s:100,l:100,a:1},clamp:{r:s(e=>e>=255?255:e<0?0:e,"r"),g:s(e=>e>=255?255:e<0?0:e,"g"),b:s(e=>e>=255?255:e<0?0:e,"b"),h:s(e=>e%360,"h"),s:s(e=>e>=100?100:e<0?0:e,"s"),l:s(e=>e>=100?100:e<0?0:e,"l"),a:s(e=>e>=1?1:e<0?0:e,"a")},toLinear:s(e=>{let t=e/255;return e>.03928?Math.pow((t+.055)/1.055,2.4):t/12.92},"toLinear"),hue2rgb:s((e,t,r)=>(r<0&&(r+=1),r>1&&(r-=1),r<.16666666666666666?e+(t-e)*6*r:r<.5?t:r<.6666666666666666?e+(t-e)*(.6666666666666666-r)*6:e),"hue2rgb"),hsl2rgb:s(({h:e,s:t,l:r},i)=>{if(!t)return r*2.55;e/=360,t/=100,r/=100;let n=r<.5?r*(1+t):r+t-r*t,a=2*r-n;switch(i){case"r":return Lx.hue2rgb(a,n,e+.3333333333333333)*255;case"g":return Lx.hue2rgb(a,n,e)*255;case"b":return Lx.hue2rgb(a,n,e-.3333333333333333)*255}},"hsl2rgb"),rgb2hsl:s(({r:e,g:t,b:r},i)=>{e/=255,t/=255,r/=255;let n=Math.max(e,t,r),a=Math.min(e,t,r),o=(n+a)/2;if(i==="l")return o*100;if(n===a)return 0;let l=n-a,u=o>.5?l/(2-n-a):l/(n+a);if(i==="s")return u*100;switch(n){case e:return((t-r)/l+(t{"use strict";A0e={clamp:s((e,t,r)=>t>r?Math.min(t,Math.max(r,e)):Math.min(r,Math.max(t,e)),"clamp"),round:s(e=>Math.round(e*1e10)/1e10,"round")},R0e=A0e}),$0e,L0e,c3t=G(()=>{"use strict";$0e={dec2hex:s(e=>{let t=Math.round(e).toString(16);return t.length>1?t:`0${t}`},"dec2hex")},L0e=$0e}),I0e,ri,zh=G(()=>{"use strict";o3t(),l3t(),c3t(),I0e={channel:_0e,lang:R0e,unit:L0e},ri=I0e}),qh,Bs,fE=G(()=>{"use strict";zh(),qh={};for(let e=0;e<=255;e++)qh[e]=ri.unit.dec2hex(e);Bs={ALL:0,RGB:1,HSL:2}}),M0e,D0e,u3t=G(()=>{"use strict";var e;fE(),M0e=(e=class{constructor(){this.type=Bs.ALL}get(){return this.type}set(r){if(this.type&&this.type!==r)throw new Error("Cannot change both RGB and HSL channels at the same time");this.type=r}reset(){this.type=Bs.ALL}is(r){return this.type===r}},s(e,"Type"),e),D0e=M0e}),N0e,O0e,h3t=G(()=>{"use strict";var e;zh(),u3t(),fE(),N0e=(e=class{constructor(r,i){this.color=i,this.changed=!1,this.data=r,this.type=new D0e}set(r,i){return this.color=i,this.changed=!1,this.data=r,this.type.type=Bs.ALL,this}_ensureHSL(){let r=this.data,{h:i,s:n,l:a}=r;i===void 0&&(r.h=ri.channel.rgb2hsl(r,"h")),n===void 0&&(r.s=ri.channel.rgb2hsl(r,"s")),a===void 0&&(r.l=ri.channel.rgb2hsl(r,"l"))}_ensureRGB(){let r=this.data,{r:i,g:n,b:a}=r;i===void 0&&(r.r=ri.channel.hsl2rgb(r,"r")),n===void 0&&(r.g=ri.channel.hsl2rgb(r,"g")),a===void 0&&(r.b=ri.channel.hsl2rgb(r,"b"))}get r(){let r=this.data,i=r.r;return!this.type.is(Bs.HSL)&&i!==void 0?i:(this._ensureHSL(),ri.channel.hsl2rgb(r,"r"))}get g(){let r=this.data,i=r.g;return!this.type.is(Bs.HSL)&&i!==void 0?i:(this._ensureHSL(),ri.channel.hsl2rgb(r,"g"))}get b(){let r=this.data,i=r.b;return!this.type.is(Bs.HSL)&&i!==void 0?i:(this._ensureHSL(),ri.channel.hsl2rgb(r,"b"))}get h(){let r=this.data,i=r.h;return!this.type.is(Bs.RGB)&&i!==void 0?i:(this._ensureRGB(),ri.channel.rgb2hsl(r,"h"))}get s(){let r=this.data,i=r.s;return!this.type.is(Bs.RGB)&&i!==void 0?i:(this._ensureRGB(),ri.channel.rgb2hsl(r,"s"))}get l(){let r=this.data,i=r.l;return!this.type.is(Bs.RGB)&&i!==void 0?i:(this._ensureRGB(),ri.channel.rgb2hsl(r,"l"))}get a(){return this.data.a}set r(r){this.type.set(Bs.RGB),this.changed=!0,this.data.r=r}set g(r){this.type.set(Bs.RGB),this.changed=!0,this.data.g=r}set b(r){this.type.set(Bs.RGB),this.changed=!0,this.data.b=r}set h(r){this.type.set(Bs.HSL),this.changed=!0,this.data.h=r}set s(r){this.type.set(Bs.HSL),this.changed=!0,this.data.s=r}set l(r){this.type.set(Bs.HSL),this.changed=!0,this.data.l=r}set a(r){this.changed=!0,this.data.a=r}},s(e,"Channels"),e),O0e=N0e}),B0e,Ix,gE=G(()=>{"use strict";h3t(),B0e=new O0e({r:0,g:0,b:0,a:0},"transparent"),Ix=B0e}),nD,iy,P0e=G(()=>{"use strict";gE(),fE(),nD={re:/^#((?:[a-f0-9]{2}){2,4}|[a-f0-9]{3})$/i,parse:s(e=>{if(e.charCodeAt(0)!==35)return;let t=e.match(nD.re);if(!t)return;let r=t[1],i=parseInt(r,16),n=r.length,a=n%4===0,o=n>4,l=o?1:17,u=o?8:4,h=a?0:-1,d=o?255:15;return Ix.set({r:(i>>u*(h+3)&d)*l,g:(i>>u*(h+2)&d)*l,b:(i>>u*(h+1)&d)*l,a:a?(i&d)*l/255:1},e)},"parse"),stringify:s(e=>{let{r:t,g:r,b:i,a:n}=e;return n<1?`#${qh[Math.round(t)]}${qh[Math.round(r)]}${qh[Math.round(i)]}${qh[Math.round(n*255)]}`:`#${qh[Math.round(t)]}${qh[Math.round(r)]}${qh[Math.round(i)]}`},"stringify")},iy=nD}),Mx,Dx,d3t=G(()=>{"use strict";zh(),gE(),Mx={re:/^hsla?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(?:deg|grad|rad|turn)?)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(%)?))?\s*?\)$/i,hueRe:/^(.+?)(deg|grad|rad|turn)$/i,_hue2deg:s(e=>{let t=e.match(Mx.hueRe);if(t){let[,r,i]=t;switch(i){case"grad":return ri.channel.clamp.h(parseFloat(r)*.9);case"rad":return ri.channel.clamp.h(parseFloat(r)*180/Math.PI);case"turn":return ri.channel.clamp.h(parseFloat(r)*360)}}return ri.channel.clamp.h(parseFloat(e))},"_hue2deg"),parse:s(e=>{let t=e.charCodeAt(0);if(t!==104&&t!==72)return;let r=e.match(Mx.re);if(!r)return;let[,i,n,a,o,l]=r;return Ix.set({h:Mx._hue2deg(i),s:ri.channel.clamp.s(parseFloat(n)),l:ri.channel.clamp.l(parseFloat(a)),a:o?ri.channel.clamp.a(l?parseFloat(o)/100:parseFloat(o)):1},e)},"parse"),stringify:s(e=>{let{h:t,s:r,l:i,a:n}=e;return n<1?`hsla(${ri.lang.round(t)}, ${ri.lang.round(r)}%, ${ri.lang.round(i)}%, ${n})`:`hsl(${ri.lang.round(t)}, ${ri.lang.round(r)}%, ${ri.lang.round(i)}%)`},"stringify")},Dx=Mx}),Nx,aD,p3t=G(()=>{"use strict";P0e(),Nx={colors:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyanaqua:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",rebeccapurple:"#663399",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",transparent:"#00000000",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},parse:s(e=>{e=e.toLowerCase();let t=Nx.colors[e];if(t)return iy.parse(t)},"parse"),stringify:s(e=>{let t=iy.stringify(e);for(let r in Nx.colors)if(Nx.colors[r]===t)return r},"stringify")},aD=Nx}),sD,Ox,f3t=G(()=>{"use strict";zh(),gE(),sD={re:/^rgba?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?)))?\s*?\)$/i,parse:s(e=>{let t=e.charCodeAt(0);if(t!==114&&t!==82)return;let r=e.match(sD.re);if(!r)return;let[,i,n,a,o,l,u,h,d]=r;return Ix.set({r:ri.channel.clamp.r(n?parseFloat(i)*2.55:parseFloat(i)),g:ri.channel.clamp.g(o?parseFloat(a)*2.55:parseFloat(a)),b:ri.channel.clamp.b(u?parseFloat(l)*2.55:parseFloat(l)),a:h?ri.channel.clamp.a(d?parseFloat(h)/100:parseFloat(h)):1},e)},"parse"),stringify:s(e=>{let{r:t,g:r,b:i,a:n}=e;return n<1?`rgba(${ri.lang.round(t)}, ${ri.lang.round(r)}, ${ri.lang.round(i)}, ${ri.lang.round(n)})`:`rgb(${ri.lang.round(t)}, ${ri.lang.round(r)}, ${ri.lang.round(i)})`},"stringify")},Ox=sD}),F0e,Ul,mp=G(()=>{"use strict";P0e(),d3t(),p3t(),f3t(),fE(),F0e={format:{keyword:aD,hex:iy,rgb:Ox,rgba:Ox,hsl:Dx,hsla:Dx},parse:s(e=>{if(typeof e!="string")return e;let t=iy.parse(e)||Ox.parse(e)||Dx.parse(e)||aD.parse(e);if(t)return t;throw new Error(`Unsupported color format: "${e}"`)},"parse"),stringify:s(e=>!e.changed&&e.color?e.color:e.type.is(Bs.HSL)||e.data.r===void 0?Dx.stringify(e):e.a<1||!Number.isInteger(e.r)||!Number.isInteger(e.g)||!Number.isInteger(e.b)?Ox.stringify(e):iy.stringify(e),"stringify")},Ul=F0e}),z0e,oD,q0e=G(()=>{"use strict";zh(),mp(),z0e=s((e,t)=>{let r=Ul.parse(e);for(let i in t)r[i]=ri.channel.clamp[i](t[i]);return Ul.stringify(r)},"change"),oD=z0e}),W0e,Yl,j0e=G(()=>{"use strict";zh(),gE(),mp(),q0e(),W0e=s((e,t,r=0,i=1)=>{if(typeof e!="number")return oD(e,{a:t});let n=Ix.set({r:ri.channel.clamp.r(e),g:ri.channel.clamp.g(t),b:ri.channel.clamp.b(r),a:ri.channel.clamp.a(i)});return Ul.stringify(n)},"rgba"),Yl=W0e}),V0e,mE,g3t=G(()=>{"use strict";zh(),mp(),V0e=s((e,t)=>ri.lang.round(Ul.parse(e)[t]),"channel"),mE=V0e}),U0e,Y0e,m3t=G(()=>{"use strict";zh(),mp(),U0e=s(e=>{let{r:t,g:r,b:i}=Ul.parse(e),n=.2126*ri.channel.toLinear(t)+.7152*ri.channel.toLinear(r)+.0722*ri.channel.toLinear(i);return ri.lang.round(n)},"luminance"),Y0e=U0e}),H0e,G0e,y3t=G(()=>{"use strict";m3t(),H0e=s(e=>Y0e(e)>=.5,"isLight"),G0e=H0e}),X0e,Js,v3t=G(()=>{"use strict";y3t(),X0e=s(e=>!G0e(e),"isDark"),Js=X0e}),K0e,yE,lD=G(()=>{"use strict";zh(),mp(),K0e=s((e,t,r)=>{let i=Ul.parse(e),n=i[t],a=ri.channel.clamp[t](n+r);return n!==a&&(i[t]=a),Ul.stringify(i)},"adjustChannel"),yE=K0e}),Q0e,bt,b3t=G(()=>{"use strict";lD(),Q0e=s((e,t)=>yE(e,"l",t),"lighten"),bt=Q0e}),Z0e,xt,x3t=G(()=>{"use strict";lD(),Z0e=s((e,t)=>yE(e,"l",-t),"darken"),xt=Z0e}),J0e,cD,T3t=G(()=>{"use strict";lD(),J0e=s((e,t)=>yE(e,"a",-t),"transparentize"),cD=J0e}),ege,Se,k3t=G(()=>{"use strict";mp(),q0e(),ege=s((e,t)=>{let r=Ul.parse(e),i={};for(let n in t)t[n]&&(i[n]=r[n]+t[n]);return oD(e,i)},"adjust"),Se=ege}),tge,rge,C3t=G(()=>{"use strict";mp(),j0e(),tge=s((e,t,r=50)=>{let{r:i,g:n,b:a,a:o}=Ul.parse(e),{r:l,g:u,b:h,a:d}=Ul.parse(t),p=r/100,f=p*2-1,g=o-d,m=((f*g===-1?f:(f+g)/(1+f*g))+1)/2,y=1-m,k=i*m+l*y,C=n*m+u*y,S=a*m+h*y,E=o*p+d*(1-p);return Yl(k,C,S,E)},"mix"),rge=tge}),ige,lt,w3t=G(()=>{"use strict";mp(),C3t(),ige=s((e,t=100)=>{let r=Ul.parse(e);return r.r=255-r.r,r.g=255-r.g,r.b=255-r.b,rge(r,e,t)},"invert"),lt=ige}),E3t=G(()=>{"use strict";j0e(),g3t(),v3t(),b3t(),x3t(),T3t(),k3t(),w3t()}),Qa=G(()=>{"use strict";E3t()}),wc,Ec,Tu=G(()=>{"use strict";wc="#ffffff",Ec="#f2f2f2"}),Zr,Sc=G(()=>{"use strict";Qa(),Zr=s((e,t)=>t?Se(e,{s:-40,l:10}):Se(e,{s:-40,l:-10}),"mkBorder")}),nge,age,S3t=G(()=>{"use strict";var e;Qa(),Tu(),Sc(),nge=(e=class{constructor(){this.background="#f4f4f4",this.primaryColor="#fff4dd",this.noteBkgColor="#fff5ad",this.noteTextColor="#333",this.THEME_COLOR_LIMIT=12,this.radius=5,this.strokeWidth=1,this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.useGradient=!0,this.dropShadow="drop-shadow( 1px 2px 2px rgba(185,185,185,1))"}updateColors(){if(this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#eee":"#333"),this.secondaryColor=this.secondaryColor||Se(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||Se(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||Zr(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||Zr(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||Zr(this.noteBkgColor,this.darkMode),this.noteBkgColor=this.noteBkgColor||"#fff5ad",this.noteTextColor=this.noteTextColor||"#333",this.secondaryTextColor=this.secondaryTextColor||lt(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||lt(this.tertiaryColor),this.lineColor=this.lineColor||lt(this.background),this.arrowheadColor=this.arrowheadColor||lt(this.background),this.textColor=this.textColor||this.primaryTextColor,this.border2=this.border2||this.tertiaryBorderColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.primaryBorderColor,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?xt(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||this.actorBorder,this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||xt(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||lt(this.lineColor),this.rectBkgColor=this.rectBkgColor||this.tertiaryColor,this.sectionBkgColor=this.sectionBkgColor||this.tertiaryColor,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||this.secondaryColor,this.sectionBkgColor2=this.sectionBkgColor2||this.primaryColor,this.excludeBkgColor=this.excludeBkgColor||"#eeeeee",this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||this.primaryColor,this.activeTaskBorderColor=this.activeTaskBorderColor||this.primaryColor,this.activeTaskBkgColor=this.activeTaskBkgColor||bt(this.primaryColor,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.vertLineColor=this.vertLineColor||"navy",this.taskTextColor=this.taskTextColor||this.textColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.noteFontWeight=this.noteFontWeight||"normal",this.fontWeight=this.fontWeight||"normal",this.personBorder=this.personBorder||this.primaryBorderColor,this.personBkg=this.personBkg||this.mainBkg,this.darkMode?(this.rowOdd=this.rowOdd||xt(this.mainBkg,5)||"#ffffff",this.rowEven=this.rowEven||xt(this.mainBkg,10)):(this.rowOdd=this.rowOdd||bt(this.mainBkg,75)||"#ffffff",this.rowEven=this.rowEven||bt(this.mainBkg,5)),this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||this.tertiaryColor,this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.specialStateColor=this.lineColor,this.cScale0=this.cScale0||this.primaryColor,this.cScale1=this.cScale1||this.secondaryColor,this.cScale2=this.cScale2||this.tertiaryColor,this.cScale3=this.cScale3||Se(this.primaryColor,{h:30}),this.cScale4=this.cScale4||Se(this.primaryColor,{h:60}),this.cScale5=this.cScale5||Se(this.primaryColor,{h:90}),this.cScale6=this.cScale6||Se(this.primaryColor,{h:120}),this.cScale7=this.cScale7||Se(this.primaryColor,{h:150}),this.cScale8=this.cScale8||Se(this.primaryColor,{h:210,l:150}),this.cScale9=this.cScale9||Se(this.primaryColor,{h:270}),this.cScale10=this.cScale10||Se(this.primaryColor,{h:300}),this.cScale11=this.cScale11||Se(this.primaryColor,{h:330}),this.darkMode)for(let i=0;i{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),age=s(t=>{let r=new nge;return r.calculate(t),r},"getThemeVariables")}),sge,oge,_3t=G(()=>{"use strict";var e;Qa(),Sc(),sge=(e=class{constructor(){this.background="#333",this.primaryColor="#1f2020",this.secondaryColor=bt(this.primaryColor,16),this.tertiaryColor=Se(this.primaryColor,{h:-160}),this.primaryBorderColor=lt(this.background),this.secondaryBorderColor=Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Zr(this.tertiaryColor,this.darkMode),this.primaryTextColor=lt(this.primaryColor),this.secondaryTextColor=lt(this.secondaryColor),this.tertiaryTextColor=lt(this.tertiaryColor),this.lineColor=lt(this.background),this.textColor=lt(this.background),this.mainBkg="#1f2020",this.secondBkg="calculated",this.mainContrastColor="lightgrey",this.darkTextColor=bt(lt("#323D47"),10),this.lineColor="calculated",this.border1="#ccc",this.border2=Yl(255,255,255,.25),this.arrowheadColor="calculated",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="#181818",this.textColor="#ccc",this.THEME_COLOR_LIMIT=12,this.radius=5,this.strokeWidth=1,this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#F9FFFE",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="calculated",this.activationBkgColor="calculated",this.sequenceNumberColor="black",this.clusterBkg="#302F3D",this.sectionBkgColor=xt("#EAE8D9",30),this.altSectionBkgColor="calculated",this.sectionBkgColor2="#EAE8D9",this.excludeBkgColor=xt(this.sectionBkgColor,10),this.taskBorderColor=Yl(255,255,255,70),this.taskBkgColor="calculated",this.taskTextColor="calculated",this.taskTextLightColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor=Yl(255,255,255,50),this.activeTaskBkgColor="#81B1DB",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="grey",this.critBorderColor="#E83737",this.critBkgColor="#E83737",this.taskTextDarkColor="calculated",this.todayLineColor="#DB5757",this.vertLineColor="#00BFFF",this.personBorder=this.primaryBorderColor,this.personBkg=this.mainBkg,this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.rowOdd=this.rowOdd||bt(this.mainBkg,5)||"#ffffff",this.rowEven=this.rowEven||xt(this.mainBkg,10),this.labelColor="calculated",this.errorBkgColor="#a44141",this.errorTextColor="#ddd",this.useGradient=!0,this.gradientStart=this.primaryBorderColor,this.gradientStop=this.secondaryBorderColor,this.dropShadow="drop-shadow( 1px 2px 2px rgba(185,185,185,1))",this.noteFontWeight=this.noteFontWeight||"normal",this.fontWeight=this.fontWeight||"normal"}updateColors(){this.secondBkg=bt(this.mainBkg,16),this.lineColor=this.mainContrastColor,this.arrowheadColor=this.mainContrastColor,this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.edgeLabelBackground=bt(this.labelBackground,25),this.actorBorder=this.border1,this.actorBkg=this.mainBkg,this.actorTextColor=this.mainContrastColor,this.actorLineColor=this.actorBorder,this.signalColor=this.mainContrastColor,this.signalTextColor=this.mainContrastColor,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.mainContrastColor,this.loopTextColor=this.mainContrastColor,this.noteBorderColor=this.secondaryBorderColor,this.noteBkgColor=this.secondBkg,this.noteTextColor=this.secondaryTextColor,this.activationBorderColor=this.border1,this.activationBkgColor=this.secondBkg,this.rectBkgColor=this.rectBkgColor||this.tertiaryColor,this.altSectionBkgColor=this.background,this.taskBkgColor=bt(this.mainBkg,23),this.taskTextColor=this.darkTextColor,this.taskTextLightColor=this.mainContrastColor,this.taskTextOutsideColor=this.taskTextLightColor,this.gridColor=this.mainContrastColor,this.doneTaskBkgColor=this.mainContrastColor,this.taskTextDarkColor=lt(this.doneTaskBkgColor),this.archEdgeColor=this.lineColor,this.archEdgeArrowColor=this.lineColor,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#555",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.primaryBorderColor,this.specialStateColor="#f4f4f4",this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=Se(this.primaryColor,{h:64}),this.fillType3=Se(this.secondaryColor,{h:64}),this.fillType4=Se(this.primaryColor,{h:-64}),this.fillType5=Se(this.secondaryColor,{h:-64}),this.fillType6=Se(this.primaryColor,{h:128}),this.fillType7=Se(this.secondaryColor,{h:128}),this.cScale1=this.cScale1||"#0b0000",this.cScale2=this.cScale2||"#4d1037",this.cScale3=this.cScale3||"#3f5258",this.cScale4=this.cScale4||"#4f2f1b",this.cScale5=this.cScale5||"#6e0a0a",this.cScale6=this.cScale6||"#3b0048",this.cScale7=this.cScale7||"#995a01",this.cScale8=this.cScale8||"#154706",this.cScale9=this.cScale9||"#161722",this.cScale10=this.cScale10||"#00296f",this.cScale11=this.cScale11||"#01629c",this.cScale12=this.cScale12||"#010029",this.cScale0=this.cScale0||this.primaryColor,this.cScale1=this.cScale1||this.secondaryColor,this.cScale2=this.cScale2||this.tertiaryColor,this.cScale3=this.cScale3||Se(this.primaryColor,{h:30}),this.cScale4=this.cScale4||Se(this.primaryColor,{h:60}),this.cScale5=this.cScale5||Se(this.primaryColor,{h:90}),this.cScale6=this.cScale6||Se(this.primaryColor,{h:120}),this.cScale7=this.cScale7||Se(this.primaryColor,{h:150}),this.cScale8=this.cScale8||Se(this.primaryColor,{h:210}),this.cScale9=this.cScale9||Se(this.primaryColor,{h:270}),this.cScale10=this.cScale10||Se(this.primaryColor,{h:300}),this.cScale11=this.cScale11||Se(this.primaryColor,{h:330});for(let r=0;r{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),oge=s(t=>{let r=new sge;return r.calculate(t),r},"getThemeVariables")}),lge,ku,Wh=G(()=>{"use strict";var e;Qa(),Sc(),Tu(),lge=(e=class{constructor(){this.background="#f4f4f4",this.primaryColor="#ECECFF",this.secondaryColor=Se(this.primaryColor,{h:120}),this.secondaryColor="#ffffde",this.tertiaryColor=Se(this.primaryColor,{h:-160}),this.primaryBorderColor=Zr(this.primaryColor,this.darkMode),this.secondaryBorderColor=Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Zr(this.tertiaryColor,this.darkMode),this.primaryTextColor=lt(this.primaryColor),this.secondaryTextColor=lt(this.secondaryColor),this.tertiaryTextColor=lt(this.tertiaryColor),this.lineColor=lt(this.background),this.textColor=lt(this.background),this.background="white",this.mainBkg="#ECECFF",this.secondBkg="#ffffde",this.lineColor="#333333",this.border1="#9370DB",this.primaryBorderColor=Zr(this.primaryColor,this.darkMode),this.border2="#aaaa33",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="rgba(232,232,232, 0.8)",this.textColor="#333",this.THEME_COLOR_LIMIT=12,this.radius=5,this.strokeWidth=1,this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.clusterBkg="#FBFBFF",this.sectionBkgColor="calculated",this.altSectionBkgColor="calculated",this.sectionBkgColor2="calculated",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="calculated",this.taskTextColor=this.taskTextLightColor,this.taskTextDarkColor="calculated",this.taskTextOutsideColor=this.taskTextDarkColor,this.taskTextClickableColor="calculated",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBorderColor="calculated",this.critBkgColor="calculated",this.todayLineColor="calculated",this.vertLineColor="calculated",this.sectionBkgColor=Yl(102,102,255,.49),this.altSectionBkgColor="white",this.sectionBkgColor2="#fff400",this.taskBorderColor="#534fbc",this.taskBkgColor="#8a90dd",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="#534fbc",this.activeTaskBkgColor="#bfc7ff",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.vertLineColor="navy",this.noteFontWeight=this.noteFontWeight||"normal",this.fontWeight=this.fontWeight||"normal",this.personBorder=this.primaryBorderColor,this.personBkg=this.mainBkg,this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.rowOdd="calculated",this.rowEven="calculated",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222",this.useGradient=!1,this.gradientStart=this.primaryBorderColor,this.gradientStop=this.secondaryBorderColor,this.dropShadow="drop-shadow(1px 2px 2px rgba(185, 185, 185, 1))",this.updateColors()}updateColors(){this.cScale0=this.cScale0||this.primaryColor,this.cScale1=this.cScale1||this.secondaryColor,this.cScale2=this.cScale2||this.tertiaryColor,this.cScale3=this.cScale3||Se(this.primaryColor,{h:30}),this.cScale4=this.cScale4||Se(this.primaryColor,{h:60}),this.cScale5=this.cScale5||Se(this.primaryColor,{h:90}),this.cScale6=this.cScale6||Se(this.primaryColor,{h:120}),this.cScale7=this.cScale7||Se(this.primaryColor,{h:150}),this.cScale8=this.cScale8||Se(this.primaryColor,{h:210}),this.cScale9=this.cScale9||Se(this.primaryColor,{h:270}),this.cScale10=this.cScale10||Se(this.primaryColor,{h:300}),this.cScale11=this.cScale11||Se(this.primaryColor,{h:330}),this.cScalePeer1=this.cScalePeer1||xt(this.secondaryColor,45),this.cScalePeer2=this.cScalePeer2||xt(this.tertiaryColor,40);for(let r=0;r{this[n]==="calculated"&&(this[n]=void 0)}),typeof r!="object"){this.updateColors();return}let i=Object.keys(r);i.forEach(n=>{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),ku=s(t=>{let r=new lge;return r.calculate(t),r},"getThemeVariables")}),cge,uge,A3t=G(()=>{"use strict";var e;Qa(),Tu(),Sc(),cge=(e=class{constructor(){this.background="#f4f4f4",this.primaryColor="#cde498",this.secondaryColor="#cdffb2",this.background="white",this.mainBkg="#cde498",this.secondBkg="#cdffb2",this.lineColor="green",this.border1="#13540c",this.border2="#6eaa49",this.arrowheadColor="green",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.tertiaryColor=bt("#cde498",10),this.primaryBorderColor=Zr(this.primaryColor,this.darkMode),this.secondaryBorderColor=Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Zr(this.tertiaryColor,this.darkMode),this.primaryTextColor=lt(this.primaryColor),this.secondaryTextColor=lt(this.secondaryColor),this.tertiaryTextColor=lt(this.primaryColor),this.lineColor=lt(this.background),this.textColor=lt(this.background),this.THEME_COLOR_LIMIT=12,this.radius=5,this.strokeWidth=1,this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#333",this.edgeLabelBackground="#e8e8e8",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="calculated",this.signalColor="#333",this.signalTextColor="#333",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="#326932",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="#6eaa49",this.altSectionBkgColor="white",this.sectionBkgColor2="#6eaa49",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="#487e3a",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.vertLineColor="#00BFFF",this.personBorder=this.primaryBorderColor,this.personBkg=this.mainBkg,this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.noteFontWeight="normal",this.fontWeight="normal",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222",this.useGradient=!0,this.gradientStart=this.primaryBorderColor,this.gradientStop=this.secondaryBorderColor,this.dropShadow="drop-shadow( 1px 2px 2px rgba(185,185,185,0.5))"}updateColors(){this.actorBorder=xt(this.mainBkg,20),this.actorBkg=this.mainBkg,this.labelBoxBkgColor=this.actorBkg,this.labelTextColor=this.actorTextColor,this.loopTextColor=this.actorTextColor,this.noteBorderColor=this.border2,this.noteTextColor=this.actorTextColor,this.actorLineColor=this.actorBorder,this.rectBkgColor=this.rectBkgColor||this.tertiaryColor,this.cScale0=this.cScale0||this.primaryColor,this.cScale1=this.cScale1||this.secondaryColor,this.cScale2=this.cScale2||this.tertiaryColor,this.cScale3=this.cScale3||Se(this.primaryColor,{h:30}),this.cScale4=this.cScale4||Se(this.primaryColor,{h:60}),this.cScale5=this.cScale5||Se(this.primaryColor,{h:90}),this.cScale6=this.cScale6||Se(this.primaryColor,{h:120}),this.cScale7=this.cScale7||Se(this.primaryColor,{h:150}),this.cScale8=this.cScale8||Se(this.primaryColor,{h:210}),this.cScale9=this.cScale9||Se(this.primaryColor,{h:270}),this.cScale10=this.cScale10||Se(this.primaryColor,{h:300}),this.cScale11=this.cScale11||Se(this.primaryColor,{h:330}),this.cScalePeer1=this.cScalePeer1||xt(this.secondaryColor,45),this.cScalePeer2=this.cScalePeer2||xt(this.tertiaryColor,40);for(let r=0;r{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),uge=s(t=>{let r=new cge;return r.calculate(t),r},"getThemeVariables")}),hge,dge,R3t=G(()=>{"use strict";var e;Qa(),Sc(),Tu(),hge=(e=class{constructor(){this.primaryColor="#eee",this.contrast="#707070",this.secondaryColor=bt(this.contrast,55),this.background="#ffffff",this.tertiaryColor=Se(this.primaryColor,{h:-160}),this.primaryBorderColor=Zr(this.primaryColor,this.darkMode),this.secondaryBorderColor=Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Zr(this.tertiaryColor,this.darkMode),this.primaryTextColor=lt(this.primaryColor),this.secondaryTextColor=lt(this.secondaryColor),this.tertiaryTextColor=lt(this.tertiaryColor),this.lineColor=lt(this.background),this.textColor=lt(this.background),this.mainBkg="#eee",this.secondBkg="calculated",this.lineColor="#666",this.border1="#999",this.border2="calculated",this.note="#ffa",this.text="#333",this.critical="#d42",this.done="#bbb",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.THEME_COLOR_LIMIT=12,this.radius=5,this.strokeWidth=1,this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="white",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor=this.actorBorder,this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="calculated",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="white",this.sectionBkgColor2="calculated",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBkgColor="calculated",this.critBorderColor="calculated",this.todayLineColor="calculated",this.vertLineColor="calculated",this.personBorder=this.primaryBorderColor,this.personBkg=this.mainBkg,this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.noteFontWeight="normal",this.fontWeight="normal",this.rowOdd=this.rowOdd||bt(this.mainBkg,75)||"#ffffff",this.rowEven=this.rowEven||"#f4f4f4",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222",this.useGradient=!0,this.gradientStart=this.primaryBorderColor,this.gradientStop=this.secondaryBorderColor,this.dropShadow="drop-shadow( 1px 2px 2px rgba(185,185,185,1))"}updateColors(){this.secondBkg=bt(this.contrast,55),this.border2=this.contrast,this.actorBorder=bt(this.border1,23),this.actorBkg=this.mainBkg,this.actorTextColor=this.text,this.actorLineColor=this.actorBorder,this.rectBkgColor=this.rectBkgColor||this.tertiaryColor,this.signalColor=this.text,this.signalTextColor=this.text,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.text,this.loopTextColor=this.text,this.noteBorderColor="#999",this.noteBkgColor="#666",this.noteTextColor="#fff",this.cScale0=this.cScale0||"#555",this.cScale1=this.cScale1||"#F4F4F4",this.cScale2=this.cScale2||"#555",this.cScale3=this.cScale3||"#BBB",this.cScale4=this.cScale4||"#777",this.cScale5=this.cScale5||"#999",this.cScale6=this.cScale6||"#DDD",this.cScale7=this.cScale7||"#FFF",this.cScale8=this.cScale8||"#DDD",this.cScale9=this.cScale9||"#BBB",this.cScale10=this.cScale10||"#999",this.cScale11=this.cScale11||"#777";for(let r=0;r{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),dge=s(t=>{let r=new hge;return r.calculate(t),r},"getThemeVariables")}),pge,fge,$3t=G(()=>{"use strict";var e;Qa(),Sc(),Tu(),pge=(e=class{constructor(){this.background="#ffffff",this.primaryColor="#cccccc",this.mainBkg="#ffffff",this.noteBkgColor="#fff5ad",this.noteTextColor="#333",this.THEME_COLOR_LIMIT=12,this.radius=3,this.strokeWidth=2,this.primaryBorderColor=Zr(this.primaryColor,this.darkMode),this.fontFamily="arial, sans-serif",this.fontSize="14px",this.nodeBorder="#000000",this.stateBorder="#000000",this.useGradient=!0,this.gradientStart="#0042eb",this.gradientStop="#eb0042",this.dropShadow="drop-shadow( 0px 1px 2px rgba(0, 0, 0, 0.25));",this.tertiaryColor="#ffffff",this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.noteFontWeight="normal",this.fontWeight="normal"}updateColors(){this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#eee":"#333"),this.secondaryColor=this.secondaryColor||Se(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||Se(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||Zr(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||Zr(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||Zr(this.noteBkgColor,this.darkMode),this.noteBkgColor=this.noteBkgColor||"#fff5ad",this.noteTextColor=this.noteTextColor||"#333",this.secondaryTextColor=this.secondaryTextColor||lt(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||lt(this.tertiaryColor),this.lineColor=this.lineColor||lt(this.background),this.arrowheadColor=this.arrowheadColor||lt(this.background),this.textColor=this.textColor||this.primaryTextColor,this.border2=this.border2||this.tertiaryBorderColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.primaryBorderColor,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?xt(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||this.actorBorder,this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||xt(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||lt(this.lineColor),this.rectBkgColor=this.rectBkgColor||this.tertiaryColor;let r="#ECECFE",i="#E9E9F1",n=Se(r,{h:180,l:5});if(this.sectionBkgColor=this.sectionBkgColor||n,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||i,this.sectionBkgColor2=this.sectionBkgColor2||r,this.excludeBkgColor=this.excludeBkgColor||"#eeeeee",this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||r,this.activeTaskBorderColor=this.activeTaskBorderColor||r,this.activeTaskBkgColor=this.activeTaskBkgColor||bt(r,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.taskTextColor=this.taskTextColor||this.textColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.vertLineColor=this.vertLineColor||this.primaryBorderColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.archEdgeColor=this.lineColor,this.archEdgeArrowColor=this.lineColor,this.personBorder=this.personBorder||this.primaryBorderColor,this.personBkg=this.personBkg||this.mainBkg,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f0f0f0",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.specialStateColor=this.lineColor,this.cScale0=this.cScale0||r,this.cScale1=this.cScale1||i,this.cScale2=this.cScale2||n,this.cScale3=this.cScale3||Se(r,{h:30}),this.cScale4=this.cScale4||Se(r,{h:60}),this.cScale5=this.cScale5||Se(r,{h:90}),this.cScale6=this.cScale6||Se(r,{h:120}),this.cScale7=this.cScale7||Se(r,{h:150}),this.cScale8=this.cScale8||Se(r,{h:210,l:150}),this.cScale9=this.cScale9||Se(r,{h:270}),this.cScale10=this.cScale10||Se(r,{h:300}),this.cScale11=this.cScale11||Se(r,{h:330}),this.darkMode)for(let o=0;o{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),fge=s(t=>{let r=new pge;return r.calculate(t),r},"getThemeVariables")}),gge,mge,L3t=G(()=>{"use strict";var e;Qa(),Sc(),Tu(),gge=(e=class{constructor(){this.background="#333",this.primaryColor="#1f2020",this.secondaryColor=bt(this.primaryColor,16),this.tertiaryColor=Se(this.primaryColor,{h:-160}),this.primaryBorderColor=lt(this.background),this.secondaryBorderColor=Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Zr(this.tertiaryColor,this.darkMode),this.primaryTextColor=lt(this.primaryColor),this.secondaryTextColor=lt(this.secondaryColor),this.tertiaryTextColor=lt(this.tertiaryColor),this.mainBkg="#2a2020",this.secondBkg="calculated",this.mainContrastColor="lightgrey",this.darkTextColor=bt(lt("#323D47"),10),this.border1="#ccc",this.border2=Yl(255,255,255,.25),this.arrowheadColor=lt(this.background),this.fontFamily="arial, sans-serif",this.fontSize="14px",this.labelBackground="#181818",this.textColor="#ccc",this.THEME_COLOR_LIMIT=12,this.radius=3,this.strokeWidth=1,this.noteBkgColor="#fff5ad",this.noteTextColor="#333",this.THEME_COLOR_LIMIT=12,this.fontFamily="arial, sans-serif",this.fontSize="14px",this.useGradient=!0,this.gradientStart="#0042eb",this.gradientStop="#eb0042",this.dropShadow="drop-shadow( 1px 2px 2px rgba(185,185,185,0.2))",this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.noteFontWeight="normal",this.fontWeight="normal"}updateColors(){if(this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#eee":"#333"),this.secondaryColor=this.secondaryColor||Se(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||Se(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||Zr(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||Zr(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||Zr(this.noteBkgColor,this.darkMode),this.noteBkgColor=this.noteBkgColor||"#fff5ad",this.noteTextColor=this.noteTextColor||"#333",this.secondaryTextColor=this.secondaryTextColor||lt(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||lt(this.tertiaryColor),this.lineColor=this.lineColor||lt(this.background),this.arrowheadColor=this.arrowheadColor||lt(this.background),this.textColor=this.textColor||this.primaryTextColor,this.border2=this.border2||this.tertiaryBorderColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.border1,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?xt(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||this.actorBorder,this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||xt(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||lt(this.lineColor),this.rectBkgColor=this.rectBkgColor||this.tertiaryColor,this.sectionBkgColor=this.sectionBkgColor||this.tertiaryColor,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||this.secondaryColor,this.sectionBkgColor2=this.sectionBkgColor2||this.primaryColor,this.excludeBkgColor=this.excludeBkgColor||"#eeeeee",this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||this.primaryColor,this.activeTaskBorderColor=this.activeTaskBorderColor||this.primaryColor,this.activeTaskBkgColor=this.activeTaskBkgColor||bt(this.primaryColor,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.vertLineColor=this.vertLineColor||this.primaryBorderColor,this.taskTextColor=this.taskTextColor||this.textColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.archEdgeColor=this.lineColor,this.archEdgeArrowColor=this.lineColor,this.personBorder=this.personBorder||this.primaryBorderColor,this.personBkg=this.personBkg||this.mainBkg,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f0f0f0",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.specialStateColor=this.lineColor,this.cScale0=this.cScale0||this.primaryColor,this.cScale1=this.cScale1||this.secondaryColor,this.cScale2=this.cScale2||this.tertiaryColor,this.cScale3=this.cScale3||Se(this.primaryColor,{h:30}),this.cScale4=this.cScale4||Se(this.primaryColor,{h:60}),this.cScale5=this.cScale5||Se(this.primaryColor,{h:90}),this.cScale6=this.cScale6||Se(this.primaryColor,{h:120}),this.cScale7=this.cScale7||Se(this.primaryColor,{h:150}),this.cScale8=this.cScale8||Se(this.primaryColor,{h:210,l:150}),this.cScale9=this.cScale9||Se(this.primaryColor,{h:270}),this.cScale10=this.cScale10||Se(this.primaryColor,{h:300}),this.cScale11=this.cScale11||Se(this.primaryColor,{h:330}),this.darkMode)for(let i=0;i{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),mge=s(t=>{let r=new gge;return r.calculate(t),r},"getThemeVariables")}),yge,vge,I3t=G(()=>{"use strict";var e;Qa(),Sc(),Tu(),yge=(e=class{constructor(){this.background="#ffffff",this.primaryColor="#cccccc",this.mainBkg="#ffffff",this.noteBkgColor="#fff5ad",this.noteTextColor="#28253D",this.THEME_COLOR_LIMIT=12,this.radius=12,this.strokeWidth=2,this.primaryBorderColor=Zr("#28253D",this.darkMode),this.fontFamily='"Recursive Variable", arial, sans-serif',this.fontSize="14px",this.nodeBorder="#28253D",this.stateBorder="#28253D",this.useGradient=!1,this.gradientStart="#0042eb",this.gradientStop="#eb0042",this.dropShadow="url(#drop-shadow)",this.nodeShadow=!0,this.tertiaryColor="#ffffff",this.clusterBkg="#F9F9FB",this.clusterBorder="#BDBCCC",this.noteBorderColor="#FACC15",this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.actorBorder="#28253D",this.filterColor="#000000"}updateColors(){this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#eee":"#28253D"),this.secondaryColor=this.secondaryColor||Se(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||Se(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||Zr(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||Zr(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||Zr(this.noteBkgColor,this.darkMode),this.noteBkgColor=this.noteBkgColor||"#FEF9C3",this.noteTextColor=this.noteTextColor||"#28253D",this.secondaryTextColor=this.secondaryTextColor||lt(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||lt(this.tertiaryColor),this.lineColor=this.lineColor||lt(this.background),this.arrowheadColor=this.arrowheadColor||lt(this.background),this.textColor=this.textColor||this.primaryTextColor,this.border2=this.border2||this.tertiaryBorderColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.primaryBorderColor,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?xt(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.noteFontWeight=600,this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||this.actorBorder,this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||xt(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||lt(this.lineColor),this.rectBkgColor=this.rectBkgColor||this.tertiaryColor;let r="#ECECFE",i="#E9E9F1",n=Se(r,{h:180,l:5});this.sectionBkgColor=this.sectionBkgColor||n,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||i,this.sectionBkgColor2=this.sectionBkgColor2||r,this.excludeBkgColor=this.excludeBkgColor||"#eeeeee",this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||r,this.activeTaskBorderColor=this.activeTaskBorderColor||r,this.activeTaskBkgColor=this.activeTaskBkgColor||bt(r,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.taskTextColor=this.taskTextColor||this.textColor,this.vertLineColor=this.vertLineColor||this.primaryBorderColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.archEdgeColor=this.lineColor,this.archEdgeArrowColor=this.lineColor,this.personBorder=this.personBorder||this.primaryBorderColor,this.personBkg=this.personBkg||this.mainBkg,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.compositeTitleBackground="#F9F9FB",this.altBackground="#F9F9FB",this.stateEdgeLabelBackground="#FFFFFF",this.fontWeight=600,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f0f0f0",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.specialStateColor=this.lineColor;for(let o=0;o{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),vge=s(t=>{let r=new yge;return r.calculate(t),r},"getThemeVariables")}),bge,xge,M3t=G(()=>{"use strict";var e;Qa(),Sc(),Tu(),bge=(e=class{constructor(){this.background="#333",this.primaryColor="#1f2020",this.secondaryColor=bt(this.primaryColor,16),this.tertiaryColor=Se(this.primaryColor,{h:-160}),this.primaryBorderColor=lt(this.background),this.secondaryBorderColor=Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Zr(this.tertiaryColor,this.darkMode),this.primaryTextColor=lt(this.primaryColor),this.secondaryTextColor=lt(this.secondaryColor),this.tertiaryTextColor=lt(this.tertiaryColor),this.mainBkg="#111113",this.secondBkg="calculated",this.mainContrastColor="lightgrey",this.darkTextColor=bt(lt("#323D47"),10),this.border1="#ccc",this.border2=Yl(255,255,255,.25),this.arrowheadColor=lt(this.background),this.fontFamily='"Recursive Variable", arial, sans-serif',this.fontSize="14px",this.labelBackground="#111113",this.textColor="#ccc",this.THEME_COLOR_LIMIT=12,this.radius=12,this.strokeWidth=2,this.noteBkgColor=this.noteBkgColor??"#FEF9C3",this.noteTextColor=this.noteTextColor??"#28253D",this.THEME_COLOR_LIMIT=12,this.fontFamily='"Recursive Variable", arial, sans-serif',this.fontSize="14px",this.nodeBorder="#FFFFFF",this.stateBorder="#FFFFFF",this.useGradient=!1,this.gradientStart="#0042eb",this.gradientStop="#eb0042",this.dropShadow="url(#drop-shadow)",this.nodeShadow=!0,this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.clusterBkg="#1E1A2E",this.clusterBorder="#BDBCCC",this.noteBorderColor="#FACC15",this.noteFontWeight=600,this.filterColor="#FFFFFF"}updateColors(){if(this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#eee":"#FFFFFF"),this.secondaryColor=this.secondaryColor||Se(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||Se(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||Zr(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||Zr(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||Zr(this.noteBkgColor,this.darkMode),this.noteBkgColor=this.noteBkgColor||"#fff5ad",this.noteTextColor=this.noteTextColor||"#FFFFFF",this.secondaryTextColor=this.secondaryTextColor||lt(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||lt(this.tertiaryColor),this.lineColor=this.lineColor||lt(this.background),this.arrowheadColor=this.arrowheadColor||lt(this.background),this.textColor=this.textColor||this.primaryTextColor,this.border2=this.border2||this.tertiaryBorderColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.border1,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?xt(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder="#FFFFFF",this.signalColor="#FFFFFF",this.labelBoxBorderColor="#BDBCCC",this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||this.actorBorder,this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||xt(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||lt(this.lineColor),this.rectBkgColor=this.rectBkgColor||this.tertiaryColor,this.sectionBkgColor=this.sectionBkgColor||this.tertiaryColor,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||this.secondaryColor,this.sectionBkgColor2=this.sectionBkgColor2||this.primaryColor,this.excludeBkgColor=this.excludeBkgColor||"#eeeeee",this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||this.primaryColor,this.activeTaskBorderColor=this.activeTaskBorderColor||this.primaryColor,this.activeTaskBkgColor=this.activeTaskBkgColor||bt(this.primaryColor,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.taskTextColor=this.taskTextColor||this.textColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.archEdgeColor=this.lineColor,this.archEdgeArrowColor=this.lineColor,this.personBorder=this.personBorder||this.primaryBorderColor,this.personBkg=this.personBkg||this.mainBkg,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.vertLineColor=this.vertLineColor||this.primaryBorderColor,this.compositeBackground="#16141F",this.altBackground="#16141F",this.compositeTitleBackground="#16141F",this.stateEdgeLabelBackground="#16141F",this.fontWeight=600,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f0f0f0",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.specialStateColor=this.lineColor,this.cScale0=this.cScale0||this.primaryColor,this.cScale1=this.cScale1||this.secondaryColor,this.cScale2=this.cScale2||this.tertiaryColor,this.cScale3=this.cScale3||Se(this.primaryColor,{h:30}),this.cScale4=this.cScale4||Se(this.primaryColor,{h:60}),this.cScale5=this.cScale5||Se(this.primaryColor,{h:90}),this.cScale6=this.cScale6||Se(this.primaryColor,{h:120}),this.cScale7=this.cScale7||Se(this.primaryColor,{h:150}),this.cScale8=this.cScale8||Se(this.primaryColor,{h:210,l:150}),this.cScale9=this.cScale9||Se(this.primaryColor,{h:270}),this.cScale10=this.cScale10||Se(this.primaryColor,{h:300}),this.cScale11=this.cScale11||Se(this.primaryColor,{h:330}),this.darkMode)for(let i=0;i{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),xge=s(t=>{let r=new bge;return r.calculate(t),r},"getThemeVariables")}),Tge,kge,D3t=G(()=>{"use strict";var e;Qa(),Sc(),Tu(),Tge=(e=class{constructor(){this.background="#ffffff",this.primaryColor="#cccccc",this.mainBkg="#ffffff",this.noteBkgColor="#fff5ad",this.noteTextColor="#28253D",this.THEME_COLOR_LIMIT=12,this.radius=12,this.strokeWidth=2,this.primaryBorderColor=Zr(this.primaryColor,this.darkMode),this.fontFamily='"Recursive Variable", arial, sans-serif',this.fontSize="14px",this.nodeBorder="#28253D",this.stateBorder="#28253D",this.useGradient=!1,this.gradientStart="#0042eb",this.gradientStop="#eb0042",this.dropShadow="url(#drop-shadow)",this.nodeShadow=!0,this.tertiaryColor="#ffffff",this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.actorBorder="#28253D",this.noteBorderColor="#FACC15",this.noteFontWeight=600,this.borderColorArray=["#E879F9","#2DD4BF","#FB923C","#22D3EE","#4ADE80","#A78BFA","#F87171","#FACC15","#818CF8","#A3E635 ","#38BDF8","#FB7185"],this.bkgColorArray=["#FDF4FF","#F0FDFA","#FFF7ED","#ECFEFF","#F0FDF4","#F5F3FF","#FEF2F2","#FEFCE8","#EEF2FF","#F7FEE7","#F0F9FF","#FFF1F2"],this.filterColor="#000000"}updateColors(){this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#eee":"#28253D"),this.secondaryColor=this.secondaryColor||Se(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||Se(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||Zr(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||Zr(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||Zr(this.noteBkgColor,this.darkMode),this.noteBkgColor=this.noteBkgColor||"#fff5ad",this.noteTextColor=this.noteTextColor||"#28253D",this.secondaryTextColor=this.secondaryTextColor||lt(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||lt(this.tertiaryColor),this.lineColor=this.lineColor||lt(this.background),this.arrowheadColor=this.arrowheadColor||lt(this.background),this.textColor=this.textColor||this.primaryTextColor,this.border2=this.border2||this.tertiaryBorderColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.primaryBorderColor,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?xt(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||this.actorBorder,this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||xt(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||lt(this.lineColor),this.rectBkgColor=this.rectBkgColor||this.tertiaryColor;let r="#ECECFE",i="#E9E9F1",n=Se(r,{h:180,l:5});this.sectionBkgColor=this.sectionBkgColor||n,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||i,this.sectionBkgColor2=this.sectionBkgColor2||r,this.excludeBkgColor=this.excludeBkgColor||"#eeeeee",this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||r,this.activeTaskBorderColor=this.activeTaskBorderColor||r,this.activeTaskBkgColor=this.activeTaskBkgColor||bt(r,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.taskTextColor=this.taskTextColor||this.textColor,this.vertLineColor=this.vertLineColor||this.primaryBorderColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.archEdgeColor=this.lineColor,this.archEdgeArrowColor=this.lineColor,this.personBorder=this.personBorder||this.primaryBorderColor,this.personBkg=this.personBkg||this.mainBkg,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f0f0f0",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.specialStateColor=this.lineColor,this.cScale0=this.cScale0||"#f4a8ff",this.cScale1=this.cScale1||"#46ecd5",this.cScale2=this.cScale2||"#ffb86a",this.cScale3=this.cScale3||"#dab2ff",this.cScale4=this.cScale4||"#7bf1a8",this.cScale5=this.cScale5||"#c4b4ff",this.cScale6=this.cScale6||"#ffa2a2",this.cScale7=this.cScale7||"#ffdf20",this.cScale8=this.cScale8||"#a3b3ff",this.cScale9=this.cScale9||"#bbf451",this.cScale10=this.cScale10||"#74d4ff",this.cScale11=this.cScale11||"#ffa1ad";for(let o=0;o{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),kge=s(t=>{let r=new Tge;return r.calculate(t),r},"getThemeVariables")}),Cge,wge,N3t=G(()=>{"use strict";var e;Qa(),Sc(),Tu(),Cge=(e=class{constructor(){this.background="#333",this.primaryColor="#1f2020",this.secondaryColor=bt(this.primaryColor,16),this.tertiaryColor=Se(this.primaryColor,{h:-160}),this.primaryBorderColor=lt(this.background),this.secondaryBorderColor=Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Zr(this.tertiaryColor,this.darkMode),this.primaryTextColor=lt(this.primaryColor),this.secondaryTextColor=lt(this.secondaryColor),this.tertiaryTextColor=lt(this.tertiaryColor),this.mainBkg="#111113",this.secondBkg="calculated",this.mainContrastColor="lightgrey",this.darkTextColor=bt(lt("#323D47"),10),this.border1="#ccc",this.border2=Yl(255,255,255,.25),this.arrowheadColor=lt(this.background),this.fontFamily='"Recursive Variable", arial, sans-serif',this.fontSize="14px",this.labelBackground="#111113",this.textColor="#ccc",this.THEME_COLOR_LIMIT=12,this.radius=12,this.strokeWidth=2,this.noteBkgColor=this.noteBkgColor??"#FEF9C3",this.noteTextColor=this.noteTextColor??"#28253D",this.THEME_COLOR_LIMIT=12,this.fontFamily='"Recursive Variable", arial, sans-serif',this.fontSize="14px",this.nodeBorder="#FFFFFF",this.stateBorder="#FFFFFF",this.useGradient=!1,this.gradientStart="#0042eb",this.gradientStop="#eb0042",this.dropShadow="url(#drop-shadow)",this.nodeShadow=!0,this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.clusterBkg="#1E1A2E",this.clusterBorder="#BDBCCC",this.noteBorderColor="#FACC15",this.noteFontWeight=600,this.borderColorArray=["#E879F9","#2DD4BF","#FB923C","#22D3EE","#4ADE80","#A78BFA","#F87171","#FACC15","#818CF8","#A3E635 ","#38BDF8","#FB7185"],this.bkgColorArray=[],this.filterColor="#FFFFFF"}updateColors(){this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#eee":"#FFFFFF"),this.secondaryColor=this.secondaryColor||Se(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||Se(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||Zr(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||Zr(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||Zr(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||Zr(this.noteBkgColor,this.darkMode),this.noteBkgColor=this.noteBkgColor||"#fff5ad",this.noteTextColor=this.noteTextColor||"#FFFFFF",this.secondaryTextColor=this.secondaryTextColor||lt(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||lt(this.tertiaryColor),this.lineColor=this.lineColor||lt(this.background),this.arrowheadColor=this.arrowheadColor||lt(this.background),this.textColor=this.textColor||this.primaryTextColor,this.border2=this.border2||this.tertiaryBorderColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.border1,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?xt(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder="#FFFFFF",this.signalColor="#FFFFFF",this.labelBoxBorderColor="#BDBCCC",this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||this.actorBorder,this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||xt(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||lt(this.lineColor),this.rectBkgColor=this.rectBkgColor||this.tertiaryColor,this.rootLabelColor="#FFFFFF",this.sectionBkgColor=this.sectionBkgColor||this.tertiaryColor,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||this.secondaryColor,this.sectionBkgColor2=this.sectionBkgColor2||this.primaryColor,this.excludeBkgColor=this.excludeBkgColor||"#eeeeee",this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||this.primaryColor,this.activeTaskBorderColor=this.activeTaskBorderColor||this.primaryColor,this.activeTaskBkgColor=this.activeTaskBkgColor||bt(this.primaryColor,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.taskTextColor=this.taskTextColor||this.textColor,this.vertLineColor=this.vertLineColor||this.primaryBorderColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.archEdgeColor=this.lineColor,this.archEdgeArrowColor=this.lineColor,this.personBorder=this.personBorder||this.primaryBorderColor,this.personBkg=this.personBkg||this.mainBkg,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f0f0f0",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.specialStateColor=this.lineColor,this.cScale0=this.cScale0||"#f4a8ff",this.cScale1=this.cScale1||"#46ecd5",this.cScale2=this.cScale2||"#ffb86a",this.cScale3=this.cScale3||"#dab2ff",this.cScale4=this.cScale4||"#7bf1a8",this.cScale5=this.cScale5||"#c4b4ff",this.cScale6=this.cScale6||"#ffa2a2",this.cScale7=this.cScale7||"#ffdf20",this.cScale8=this.cScale8||"#a3b3ff",this.cScale9=this.cScale9||"#bbf451",this.cScale10=this.cScale10||"#74d4ff",this.cScale11=this.cScale11||"#ffa1ad";for(let i=0;i{this[n]=r[n]}),this.updateColors(),i.forEach(n=>{this[n]=r[n]})}},s(e,"Theme"),e),wge=s(t=>{let r=new Cge;return r.calculate(t),r},"getThemeVariables")}),Cu,uD=G(()=>{"use strict";S3t(),_3t(),Wh(),A3t(),R3t(),$3t(),L3t(),I3t(),M3t(),D3t(),N3t(),Cu={base:{getThemeVariables:age},dark:{getThemeVariables:oge},default:{getThemeVariables:ku},forest:{getThemeVariables:uge},neutral:{getThemeVariables:dge},neo:{getThemeVariables:fge},"neo-dark":{getThemeVariables:mge},redux:{getThemeVariables:vge},"redux-dark":{getThemeVariables:xge},"redux-color":{getThemeVariables:kge},"redux-dark-color":{getThemeVariables:wge}}}),xs,O3t=G(()=>{"use strict";xs={flowchart:{useMaxWidth:!0,titleTopMargin:25,subGraphTitleMargin:{top:0,bottom:0},diagramPadding:8,htmlLabels:null,nodeSpacing:50,rankSpacing:50,curve:"basis",padding:15,defaultRenderer:"dagre-wrapper",wrappingWidth:200,inheritDir:!1},swimlane:{useMaxWidth:!0,lineHops:"arc",ignoreCrossLaneEdges:!0,optimizeRanksByCrossings:!0,automaticLaneOrdering:!1},sequence:{useMaxWidth:!0,hideUnusedParticipants:!1,activationWidth:10,diagramMarginX:50,diagramMarginY:10,actorMargin:50,width:150,height:65,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",mirrorActors:!0,forceMenus:!1,bottomMarginAdj:1,rightAngles:!1,showSequenceNumbers:!1,actorFontSize:14,actorFontFamily:'"Open Sans", sans-serif',actorFontWeight:400,noteFontSize:14,noteFontFamily:'"trebuchet ms", verdana, arial, sans-serif',noteFontWeight:400,noteAlign:"center",messageFontSize:16,messageFontFamily:'"trebuchet ms", verdana, arial, sans-serif',messageFontWeight:400,wrap:!1,wrapPadding:10,labelBoxWidth:50,labelBoxHeight:20},gantt:{useMaxWidth:!0,titleTopMargin:25,barHeight:20,barGap:4,topPadding:50,rightPadding:75,leftPadding:75,gridLineStartPadding:35,fontSize:11,sectionFontSize:11,numberSectionStyles:4,axisFormat:"%Y-%m-%d",topAxis:!1,displayMode:"",weekday:"sunday"},journey:{useMaxWidth:!0,diagramMarginX:50,diagramMarginY:10,leftMargin:150,maxLabelWidth:360,width:150,height:50,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",bottomMarginAdj:1,rightAngles:!1,taskFontSize:14,taskFontFamily:'"Open Sans", sans-serif',taskMargin:50,activationWidth:10,textPlacement:"fo",actorColours:["#8FBC8F","#7CFC00","#00FFFF","#20B2AA","#B0E0E6","#FFFFE0"],sectionFills:["#191970","#8B008B","#4B0082","#2F4F4F","#800000","#8B4513","#00008B"],sectionColours:["#fff"],titleColor:"",titleFontFamily:'"trebuchet ms", verdana, arial, sans-serif',titleFontSize:"4ex"},class:{useMaxWidth:!0,titleTopMargin:25,arrowMarkerAbsolute:!1,dividerMargin:10,padding:5,textHeight:10,defaultRenderer:"dagre-wrapper",htmlLabels:!1,hideEmptyMembersBox:!1,hierarchicalNamespaces:!0},state:{useMaxWidth:!0,titleTopMargin:25,dividerMargin:10,sizeUnit:5,padding:8,textHeight:10,titleShift:-15,noteMargin:10,forkWidth:70,forkHeight:7,miniPadding:2,fontSizeFactor:5.02,fontSize:24,labelHeight:16,edgeLengthFactor:"20",compositTitleSize:35,radius:5,defaultRenderer:"dagre-wrapper"},er:{useMaxWidth:!0,titleTopMargin:25,diagramPadding:20,layoutDirection:"TB",minEntityWidth:100,minEntityHeight:75,entityPadding:15,nodeSpacing:140,rankSpacing:80,stroke:"gray",fill:"honeydew",fontSize:12},pie:{useMaxWidth:!0,textPosition:.75,donutHole:0,legendPosition:"right",highlightSlice:""},quadrantChart:{useMaxWidth:!0,chartWidth:500,chartHeight:500,titleFontSize:20,titlePadding:10,quadrantPadding:5,xAxisLabelPadding:5,yAxisLabelPadding:5,xAxisLabelFontSize:16,yAxisLabelFontSize:16,quadrantLabelFontSize:16,quadrantTextTopPadding:5,pointTextPadding:5,pointLabelFontSize:12,pointRadius:5,xAxisPosition:"top",yAxisPosition:"left",quadrantInternalBorderStrokeWidth:1,quadrantExternalBorderStrokeWidth:2},xyChart:{useMaxWidth:!0,width:700,height:500,titleFontSize:20,titlePadding:10,showDataLabel:!1,showDataLabelOutsideBar:!1,showTitle:!0,showLegend:!0,legendFontSize:14,legendPadding:10,xAxis:{$ref:"#/$defs/XYChartAxisConfig",showLabel:!0,labelFontSize:14,labelPadding:5,showTitle:!0,titleFontSize:16,titlePadding:5,showTick:!0,tickLength:5,tickWidth:2,showAxisLine:!0,axisLineWidth:2,labelRotation:0},yAxis:{$ref:"#/$defs/XYChartAxisConfig",showLabel:!0,labelFontSize:14,labelPadding:5,showTitle:!0,titleFontSize:16,titlePadding:5,showTick:!0,tickLength:5,tickWidth:2,showAxisLine:!0,axisLineWidth:2,labelRotation:0},chartOrientation:"vertical",plotReservedSpacePercent:50},requirement:{useMaxWidth:!0,rect_fill:"#f9f9f9",text_color:"#333",rect_border_size:"0.5px",rect_border_color:"#bbb",rect_min_width:200,rect_min_height:200,fontSize:14,rect_padding:10,line_height:20},mindmap:{useMaxWidth:!0,padding:10,maxNodeWidth:200,layoutAlgorithm:"cose-bilkent"},ishikawa:{useMaxWidth:!0,diagramPadding:20},kanban:{useMaxWidth:!0,padding:8,sectionWidth:200,ticketBaseUrl:""},timeline:{useMaxWidth:!0,diagramMarginX:50,diagramMarginY:10,leftMargin:150,width:150,height:50,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",bottomMarginAdj:1,rightAngles:!1,taskFontSize:14,taskFontFamily:'"Open Sans", sans-serif',taskMargin:50,activationWidth:10,textPlacement:"fo",actorColours:["#8FBC8F","#7CFC00","#00FFFF","#20B2AA","#B0E0E6","#FFFFE0"],sectionFills:["#191970","#8B008B","#4B0082","#2F4F4F","#800000","#8B4513","#00008B"],sectionColours:["#fff"],disableMulticolor:!1},gitGraph:{useMaxWidth:!0,titleTopMargin:25,diagramPadding:8,nodeLabel:{width:75,height:100,x:-25,y:0},mainBranchName:"main",mainBranchOrder:0,showCommitLabel:!0,showBranches:!0,rotateCommitLabel:!0,parallelCommits:!1,arrowMarkerAbsolute:!1},c4:{useMaxWidth:!0,diagramMarginX:50,diagramMarginY:10,c4ShapeMargin:50,c4ShapePadding:20,width:216,height:60,boxMargin:10,c4ShapeInRow:4,nextLinePaddingX:0,c4BoundaryInRow:2,personFontSize:14,personFontFamily:'"Open Sans", sans-serif',personFontWeight:"normal",external_personFontSize:14,external_personFontFamily:'"Open Sans", sans-serif',external_personFontWeight:"normal",systemFontSize:14,systemFontFamily:'"Open Sans", sans-serif',systemFontWeight:"normal",external_systemFontSize:14,external_systemFontFamily:'"Open Sans", sans-serif',external_systemFontWeight:"normal",system_dbFontSize:14,system_dbFontFamily:'"Open Sans", sans-serif',system_dbFontWeight:"normal",external_system_dbFontSize:14,external_system_dbFontFamily:'"Open Sans", sans-serif',external_system_dbFontWeight:"normal",system_queueFontSize:14,system_queueFontFamily:'"Open Sans", sans-serif',system_queueFontWeight:"normal",external_system_queueFontSize:14,external_system_queueFontFamily:'"Open Sans", sans-serif',external_system_queueFontWeight:"normal",boundaryFontSize:14,boundaryFontFamily:'"Open Sans", sans-serif',boundaryFontWeight:"normal",messageFontSize:12,messageFontFamily:'"Open Sans", sans-serif',messageFontWeight:"normal",containerFontSize:14,containerFontFamily:'"Open Sans", sans-serif',containerFontWeight:"normal",external_containerFontSize:14,external_containerFontFamily:'"Open Sans", sans-serif',external_containerFontWeight:"normal",container_dbFontSize:14,container_dbFontFamily:'"Open Sans", sans-serif',container_dbFontWeight:"normal",external_container_dbFontSize:14,external_container_dbFontFamily:'"Open Sans", sans-serif',external_container_dbFontWeight:"normal",container_queueFontSize:14,container_queueFontFamily:'"Open Sans", sans-serif',container_queueFontWeight:"normal",external_container_queueFontSize:14,external_container_queueFontFamily:'"Open Sans", sans-serif',external_container_queueFontWeight:"normal",componentFontSize:14,componentFontFamily:'"Open Sans", sans-serif',componentFontWeight:"normal",external_componentFontSize:14,external_componentFontFamily:'"Open Sans", sans-serif',external_componentFontWeight:"normal",component_dbFontSize:14,component_dbFontFamily:'"Open Sans", sans-serif',component_dbFontWeight:"normal",external_component_dbFontSize:14,external_component_dbFontFamily:'"Open Sans", sans-serif',external_component_dbFontWeight:"normal",component_queueFontSize:14,component_queueFontFamily:'"Open Sans", sans-serif',component_queueFontWeight:"normal",external_component_queueFontSize:14,external_component_queueFontFamily:'"Open Sans", sans-serif',external_component_queueFontWeight:"normal",wrap:!0,wrapPadding:10,person_bg_color:"#08427B",person_border_color:"#073B6F",external_person_bg_color:"#686868",external_person_border_color:"#8A8A8A",system_bg_color:"#1168BD",system_border_color:"#3C7FC0",system_db_bg_color:"#1168BD",system_db_border_color:"#3C7FC0",system_queue_bg_color:"#1168BD",system_queue_border_color:"#3C7FC0",external_system_bg_color:"#999999",external_system_border_color:"#8A8A8A",external_system_db_bg_color:"#999999",external_system_db_border_color:"#8A8A8A",external_system_queue_bg_color:"#999999",external_system_queue_border_color:"#8A8A8A",container_bg_color:"#438DD5",container_border_color:"#3C7FC0",container_db_bg_color:"#438DD5",container_db_border_color:"#3C7FC0",container_queue_bg_color:"#438DD5",container_queue_border_color:"#3C7FC0",external_container_bg_color:"#B3B3B3",external_container_border_color:"#A6A6A6",external_container_db_bg_color:"#B3B3B3",external_container_db_border_color:"#A6A6A6",external_container_queue_bg_color:"#B3B3B3",external_container_queue_border_color:"#A6A6A6",component_bg_color:"#85BBF0",component_border_color:"#78A8D8",component_db_bg_color:"#85BBF0",component_db_border_color:"#78A8D8",component_queue_bg_color:"#85BBF0",component_queue_border_color:"#78A8D8",external_component_bg_color:"#CCCCCC",external_component_border_color:"#BFBFBF",external_component_db_bg_color:"#CCCCCC",external_component_db_border_color:"#BFBFBF",external_component_queue_bg_color:"#CCCCCC",external_component_queue_border_color:"#BFBFBF"},sankey:{useMaxWidth:!0,width:600,height:400,linkColor:"gradient",nodeAlignment:"justify",showValues:!0,prefix:"",suffix:"",nodeWidth:10,nodePadding:12,labelStyle:"legacy"},block:{useMaxWidth:!0,padding:8},packet:{useMaxWidth:!0,rowHeight:32,bitWidth:32,bitsPerRow:32,showBits:!0,paddingX:5,paddingY:5},treeView:{useMaxWidth:!0,rowIndent:10,paddingX:5,paddingY:5,lineThickness:1,showIcons:!1,defaultIconPack:"",filenameIcons:{},extensionIcons:{}},architecture:{useMaxWidth:!0,padding:40,iconSize:80,fontSize:16,randomize:!1,nodeSeparation:75,idealEdgeLengthMultiplier:1.5,edgeElasticity:.45,numIter:2500,seed:1},eventmodeling:{useMaxWidth:!0,padding:30,rowHeight:32},radar:{useMaxWidth:!0,width:600,height:600,marginTop:50,marginRight:50,marginBottom:50,marginLeft:50,axisScaleFactor:1,axisLabelFactor:1.05,curveTension:.17},venn:{useMaxWidth:!0,width:800,height:450,padding:8,useDebugLayout:!1},cynefin:{useMaxWidth:!0,width:800,height:600,padding:40,showDomainDescriptions:!0,boundaryAmplitude:8,seed:0},theme:"default",look:"classic",handDrawnSeed:0,layout:"dagre",maxTextSize:5e4,maxEdges:500,darkMode:!1,fontFamily:'"trebuchet ms", verdana, arial, sans-serif;',logLevel:5,securityLevel:"strict",startOnLoad:!0,arrowMarkerAbsolute:!1,secure:["secure","securityLevel","startOnLoad","maxTextSize","suppressErrorRendering","maxEdges"],legacyMathML:!1,forceLegacyMathML:!1,deterministicIds:!1,fontSize:16,markdownAutoWrap:!0,suppressErrorRendering:!1}}),hD,dD,Ege,vi,Za=G(()=>{"use strict";uD(),O3t(),hD={...xs,deterministicIDSeed:void 0,elk:{mergeEdges:!1,nodePlacementStrategy:"BRANDES_KOEPF",nodePlacementAlignment:"NONE",forceNodeModelOrder:!1,considerModelOrder:"NODES_AND_EDGES",keepEntryNodeOnTop:!1},themeCSS:void 0,themeVariables:Cu.default.getThemeVariables(),sequence:{...xs.sequence,messageFont:s(function(){return{fontFamily:this.messageFontFamily,fontSize:this.messageFontSize,fontWeight:this.messageFontWeight}},"messageFont"),noteFont:s(function(){return{fontFamily:this.noteFontFamily,fontSize:this.noteFontSize,fontWeight:this.noteFontWeight}},"noteFont"),actorFont:s(function(){return{fontFamily:this.actorFontFamily,fontSize:this.actorFontSize,fontWeight:this.actorFontWeight}},"actorFont")},class:{defaultRenderer:"dagre-wrapper",hideEmptyMembersBox:!1,hierarchicalNamespaces:!0},gantt:{...xs.gantt,tickInterval:void 0,useWidth:void 0},c4:{...xs.c4,useWidth:void 0,personFont:s(function(){return{fontFamily:this.personFontFamily,fontSize:this.personFontSize,fontWeight:this.personFontWeight}},"personFont"),flowchart:{...xs.flowchart,inheritDir:!1},external_personFont:s(function(){return{fontFamily:this.external_personFontFamily,fontSize:this.external_personFontSize,fontWeight:this.external_personFontWeight}},"external_personFont"),systemFont:s(function(){return{fontFamily:this.systemFontFamily,fontSize:this.systemFontSize,fontWeight:this.systemFontWeight}},"systemFont"),external_systemFont:s(function(){return{fontFamily:this.external_systemFontFamily,fontSize:this.external_systemFontSize,fontWeight:this.external_systemFontWeight}},"external_systemFont"),system_dbFont:s(function(){return{fontFamily:this.system_dbFontFamily,fontSize:this.system_dbFontSize,fontWeight:this.system_dbFontWeight}},"system_dbFont"),external_system_dbFont:s(function(){return{fontFamily:this.external_system_dbFontFamily,fontSize:this.external_system_dbFontSize,fontWeight:this.external_system_dbFontWeight}},"external_system_dbFont"),system_queueFont:s(function(){return{fontFamily:this.system_queueFontFamily,fontSize:this.system_queueFontSize,fontWeight:this.system_queueFontWeight}},"system_queueFont"),external_system_queueFont:s(function(){return{fontFamily:this.external_system_queueFontFamily,fontSize:this.external_system_queueFontSize,fontWeight:this.external_system_queueFontWeight}},"external_system_queueFont"),containerFont:s(function(){return{fontFamily:this.containerFontFamily,fontSize:this.containerFontSize,fontWeight:this.containerFontWeight}},"containerFont"),external_containerFont:s(function(){return{fontFamily:this.external_containerFontFamily,fontSize:this.external_containerFontSize,fontWeight:this.external_containerFontWeight}},"external_containerFont"),container_dbFont:s(function(){return{fontFamily:this.container_dbFontFamily,fontSize:this.container_dbFontSize,fontWeight:this.container_dbFontWeight}},"container_dbFont"),external_container_dbFont:s(function(){return{fontFamily:this.external_container_dbFontFamily,fontSize:this.external_container_dbFontSize,fontWeight:this.external_container_dbFontWeight}},"external_container_dbFont"),container_queueFont:s(function(){return{fontFamily:this.container_queueFontFamily,fontSize:this.container_queueFontSize,fontWeight:this.container_queueFontWeight}},"container_queueFont"),external_container_queueFont:s(function(){return{fontFamily:this.external_container_queueFontFamily,fontSize:this.external_container_queueFontSize,fontWeight:this.external_container_queueFontWeight}},"external_container_queueFont"),componentFont:s(function(){return{fontFamily:this.componentFontFamily,fontSize:this.componentFontSize,fontWeight:this.componentFontWeight}},"componentFont"),external_componentFont:s(function(){return{fontFamily:this.external_componentFontFamily,fontSize:this.external_componentFontSize,fontWeight:this.external_componentFontWeight}},"external_componentFont"),component_dbFont:s(function(){return{fontFamily:this.component_dbFontFamily,fontSize:this.component_dbFontSize,fontWeight:this.component_dbFontWeight}},"component_dbFont"),external_component_dbFont:s(function(){return{fontFamily:this.external_component_dbFontFamily,fontSize:this.external_component_dbFontSize,fontWeight:this.external_component_dbFontWeight}},"external_component_dbFont"),component_queueFont:s(function(){return{fontFamily:this.component_queueFontFamily,fontSize:this.component_queueFontSize,fontWeight:this.component_queueFontWeight}},"component_queueFont"),external_component_queueFont:s(function(){return{fontFamily:this.external_component_queueFontFamily,fontSize:this.external_component_queueFontSize,fontWeight:this.external_component_queueFontWeight}},"external_component_queueFont"),boundaryFont:s(function(){return{fontFamily:this.boundaryFontFamily,fontSize:this.boundaryFontSize,fontWeight:this.boundaryFontWeight}},"boundaryFont"),messageFont:s(function(){return{fontFamily:this.messageFontFamily,fontSize:this.messageFontSize,fontWeight:this.messageFontWeight}},"messageFont")},pie:{...xs.pie,useWidth:984},xyChart:{...xs.xyChart,useWidth:void 0},requirement:{...xs.requirement,useWidth:void 0},packet:{...xs.packet},eventmodeling:{...xs.eventmodeling},treeView:{...xs.treeView,useWidth:void 0},radar:{...xs.radar},railroad:{...xs.railroad,fontSize:void 0,fontFamily:void 0,terminalFill:void 0,terminalStroke:void 0,terminalTextColor:void 0,nonTerminalFill:void 0,nonTerminalStroke:void 0,nonTerminalTextColor:void 0,lineColor:void 0,markerFill:void 0,commentFill:void 0,commentStroke:void 0,commentTextColor:void 0,specialFill:void 0,specialStroke:void 0,ruleNameColor:void 0},ishikawa:{...xs.ishikawa},sankey:{...xs.sankey,nodeColors:void 0},treemap:{useMaxWidth:!0,padding:10,diagramPadding:8,showValues:!0,nodeWidth:100,nodeHeight:40,borderWidth:1,valueFontSize:12,labelFontSize:14,valueFormat:","},venn:{...xs.venn},cynefin:{...xs.cynefin}},dD=s((e,t="")=>Object.keys(e).reduce((r,i)=>Array.isArray(e[i])?r:typeof e[i]=="object"&&e[i]!==null?[...r,t+i,...dD(e[i],"")]:[...r,t+i],[]),"keyify"),Ege=new Set(dD(hD,"")),vi=hD}),Sge,_ge,Bx,pD,fD=G(()=>{"use strict";Za(),Nt(),Sge={nodeColors:/^#[\da-f]{3,8}$|^rgb\([\d\s%,.]+\)$|^hsl\([\d\s%,.]+\)$|^[a-z]+$/i,filenameIcons:/^[\w-]+(?::[\w-]+)?$/,extensionIcons:/^[\w-]+(?::[\w-]+)?$/},_ge=s((e,t)=>{for(let r of Object.keys(e)){let i=e[r];(r.startsWith("__")||r.includes("proto")||r.includes("constr")||typeof i!="string"||!t.test(i))&&(Ee.debug("sanitize deleting dictionary entry:",r,i),delete e[r])}},"sanitizeDictionaryConfig"),Bx=s(e=>{if(Ee.debug("sanitizeDirective called with",e),!(typeof e!="object"||e==null)){if(Array.isArray(e)){e.forEach(t=>Bx(t));return}for(let t of Object.keys(e)){if(Ee.debug("Checking key",t),t.startsWith("__")||t.includes("proto")||t.includes("constr")||!Ege.has(t)||e[t]==null){Ee.debug("sanitize deleting key: ",t),delete e[t];continue}if(typeof e[t]=="object"){let i=Sge[t];i?_ge(e[t],i):(Ee.debug("sanitizing object",t),Bx(e[t]));continue}let r=["themeCSS","fontFamily","altFontFamily"];for(let i of r)t.includes(i)&&(Ee.debug("sanitizing css option",t),e[t]=pD(e[t]))}if(e.themeVariables)for(let t of Object.keys(e.themeVariables)){let r=e.themeVariables[t];r?.match&&!r.match(/^[\d "#%(),.;A-Za-z]+$/)&&(e.themeVariables[t]="")}Ee.debug("After sanitization",e)}},"sanitizeDirective"),pD=s(e=>{let t=0,r=0;for(let i of e){if(t{"use strict";$x(),Nt(),uD(),Za(),fD(),B0=Object.freeze(vi),_c=s(e=>!(e===!1||["false","null","0"].includes(String(e).trim().toLowerCase())),"evaluate"),Lo=Sa({},B0),yp=[],ny=Sa({},B0),ay=s((e,t)=>{let r=Sa({},e),i={};for(let n of t)yD(n),i=Sa(i,n);if(r=Sa(r,i),i.theme&&i.theme in Cu){let n=Sa({},vE),a=Sa(n.themeVariables||{},i.themeVariables);r.theme&&r.theme in Cu&&(r.themeVariables=Cu[r.theme].getThemeVariables(a))}return ny=r,Mge(ny),ny},"updateCurrentConfig"),Age=s(e=>(Lo=Sa({},B0),Lo=Sa(Lo,e),e.theme&&Cu[e.theme]&&(Lo.themeVariables=Cu[e.theme].getThemeVariables(e.themeVariables)),ay(Lo,yp),Lo),"setSiteConfig"),Rge=s(e=>{vE=Sa({},e)},"saveConfigFromInitialize"),$ge=s(e=>(Lo=Sa(Lo,e),ay(Lo,yp),Lo),"updateSiteConfig"),gD=s(()=>Sa({},Lo),"getSiteConfig"),mD=s(e=>(ay(ny,[e]),Rr()),"setConfig"),Rr=s(()=>Sa({},ny),"getConfig"),yD=s(e=>{e&&(["secure",...Lo.secure??[]].forEach(t=>{Object.hasOwn(e,t)&&(Ee.debug(`Denied attempt to modify a secure key ${t}`,e[t]),delete e[t])}),Object.keys(e).forEach(t=>{t.startsWith("__")&&delete e[t]}),Object.keys(e).forEach(t=>{typeof e[t]=="string"&&(e[t].includes("<")||e[t].includes(">")||e[t].includes("url(data:"))&&delete e[t],typeof e[t]=="object"&&yD(e[t])}))},"sanitize"),Lge=s(e=>{Bx(e),e.fontFamily&&!e.themeVariables?.fontFamily&&(e.themeVariables={...e.themeVariables,fontFamily:e.fontFamily}),yp.push(e),ay(Lo,yp)},"addDirective"),Px=s((e=Lo)=>{yp=[],ay(e,yp)},"reset"),Ige={LAZY_LOAD_DEPRECATED:"The configuration options lazyLoadedDiagrams and loadExternalDiagramsAtStartup are deprecated. Please use registerExternalDiagrams instead.",FLOWCHART_HTML_LABELS_DEPRECATED:"flowchart.htmlLabels is deprecated. Please use global htmlLabels instead."},vD={},bD=s(e=>{vD[e]||(Ee.warn(Ige[e]),vD[e]=!0)},"issueWarning"),Mge=s(e=>{e&&(e.lazyLoadedDiagrams||e.loadExternalDiagramsAtStartup)&&bD("LAZY_LOAD_DEPRECATED")},"checkConfig"),xD=s(()=>{let e={};vE&&(e=Sa(e,vE));for(let t of yp)e=Sa(e,t);return e},"getUserDefinedConfig"),Ps=s(e=>(e.flowchart?.htmlLabels!=null&&bD("FLOWCHART_HTML_LABELS_DEPRECATED"),_c(e.htmlLabels??e.flowchart?.htmlLabels??!0)),"getEffectiveHtmlLabels")});function TD(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,i=Array(t);r1?r-1:0),n=1;n2&&arguments[2]!==void 0?arguments[2]:sy;if(wD&&wD(e,null),!jh(t))return e;let i=t.length;for(;i--;){let n=t[i];if(typeof n=="string"){let a=r(n);a!==n&&(jge(t)||(t[i]=a),n=a)}e[n]=!0}return e}function zge(e){for(let t=0;t0&&arguments[0]!==void 0?arguments[0]:ume(),t=s(We=>kD(We),"DOMPurify");if(t.version="3.4.12",t.removed=[],!e||!e.document||e.document.nodeType!==el.document||!e.Element)return t.isSupported=!1,t;let r=e.document,i=r,n=i.currentScript;e.DocumentFragment;let a=e.HTMLTemplateElement,o=e.Node,l=e.Element,u=e.NodeFilter;e.NamedNodeMap===void 0&&(e.NamedNodeMap||e.MozNamedAttrMap),e.HTMLFormElement;let d=e.DOMParser,p=e.trustedTypes,f=l.prototype,g=Ac(f,"cloneNode"),m=Ac(f,"remove"),y=Ac(f,"nextSibling"),k=Ac(f,"childNodes"),C=Ac(f,"parentNode"),S=Ac(f,"shadowRoot"),E=Ac(f,"attributes"),_=o&&o.prototype?Ac(o.prototype,"nodeType"):null,b=o&&o.prototype?Ac(o.prototype,"nodeName"):null;if(typeof a=="function"){let We=r.createElement("template");We.content&&We.content.ownerDocument&&(r=We.content.ownerDocument)}let v,x="",w,R=!1,$=0,M=s(function(){if($>0)throw vp('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.')},"_assertNotInTrustedTypesPolicy"),N=s(function(We){M(),$++;try{return v.createHTML(We)}finally{$--}},"_createTrustedHTML"),j=s(function(We){M(),$++;try{return v.createScriptURL(We)}finally{$--}},"_createTrustedScriptURL"),I=s(function(){return R||(w=hme(p,n),R=!0),w},"_getDefaultTrustedTypesPolicy"),z=r,F=z.implementation,O=z.createNodeIterator,B=z.createDocumentFragment,Y=z.getElementsByTagName,U=i.importNode,H=PD();t.isSupported=typeof CD=="function"&&typeof C=="function"&&F&&F.createHTMLDocument!==void 0;let J=Jge,te=eme,ie=tme,ue=rme,se=ime,le=nme,Te=ame,oe=ome,fe=ND,ge=null,ke=Di({},[...LD,...kE,...CE,...wE,...ID]),Ne=null,xe=Di({},[...MD,...EE,...DD,...Fx]),$e=Object.seal(P0(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Me=null,He=null,Ge=Object.seal(P0(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}})),ze=!0,Ve=!0,et=!1,he=!0,ye=!1,be=!0,Ie=!1,pe=!1,Qe=null,W=null,Ae=!1,K=!1,de=!1,ee=!1,ve=!0,X=!1,me="user-content-",Ce=!0,Be=!1,Re={},Ke=null,De=Di({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","selectedcontent","style","svg","template","thead","title","video","xmp"]),Ze=null,Fe=Di({},["audio","video","img","source","image","track"]),Je=null,Le=Di({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),ct="http://www.w3.org/1998/Math/MathML",qe="http://www.w3.org/2000/svg",mt="http://www.w3.org/1999/xhtml",St=mt,Lt=!1,Bt=null,qt=Di({},[ct,qe,mt],TE),tr=Ts(["mi","mo","mn","ms","mtext"]),Et=Di({},tr),_t=Ts(["annotation-xml"]),Kt=Di({},_t),$t=Di({},["title","style","font","a","script"]),Ot=null,Jt=["application/xhtml+xml","text/html"],Br="text/html",At=null,jr=null,br=r.createElement("form"),Hr=s(function(We){return We instanceof RegExp||We instanceof Function},"isRegexOrFunction"),Ei=s(function(){let We=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(jr&&jr===We)return;(!We||typeof We!="object")&&(We={}),We=eo(We),Ot=Jt.indexOf(We.PARSER_MEDIA_TYPE)===-1?Br:We.PARSER_MEDIA_TYPE,At=Ot==="application/xhtml+xml"?TE:sy,ge=Vh(We,"ALLOWED_TAGS",ke,{transform:At}),Ne=Vh(We,"ALLOWED_ATTR",xe,{transform:At}),Bt=Vh(We,"ALLOWED_NAMESPACES",qt,{transform:TE}),Je=Vh(We,"ADD_URI_SAFE_ATTR",Le,{transform:At,base:Le}),Ze=Vh(We,"ADD_DATA_URI_TAGS",Fe,{transform:At,base:Fe}),Ke=Vh(We,"FORBID_CONTENTS",De,{transform:At}),Me=Vh(We,"FORBID_TAGS",eo({}),{transform:At}),He=Vh(We,"FORBID_ATTR",eo({}),{transform:At}),Re=Ja(We,"USE_PROFILES")?We.USE_PROFILES&&typeof We.USE_PROFILES=="object"?eo(We.USE_PROFILES):We.USE_PROFILES:!1,ze=We.ALLOW_ARIA_ATTR!==!1,Ve=We.ALLOW_DATA_ATTR!==!1,et=We.ALLOW_UNKNOWN_PROTOCOLS||!1,he=We.ALLOW_SELF_CLOSE_IN_ATTR!==!1,ye=We.SAFE_FOR_TEMPLATES||!1,be=We.SAFE_FOR_XML!==!1,Ie=We.WHOLE_DOCUMENT||!1,K=We.RETURN_DOM||!1,de=We.RETURN_DOM_FRAGMENT||!1,ee=We.RETURN_TRUSTED_TYPE||!1,Ae=We.FORCE_BODY||!1,ve=We.SANITIZE_DOM!==!1,X=We.SANITIZE_NAMED_PROPS||!1,Ce=We.KEEP_CONTENT!==!1,Be=We.IN_PLACE||!1,fe=Wge(We.ALLOWED_URI_REGEXP)?We.ALLOWED_URI_REGEXP:ND,St=typeof We.NAMESPACE=="string"?We.NAMESPACE:mt,Et=Ja(We,"MATHML_TEXT_INTEGRATION_POINTS")&&We.MATHML_TEXT_INTEGRATION_POINTS&&typeof We.MATHML_TEXT_INTEGRATION_POINTS=="object"?eo(We.MATHML_TEXT_INTEGRATION_POINTS):Di({},tr),Kt=Ja(We,"HTML_INTEGRATION_POINTS")&&We.HTML_INTEGRATION_POINTS&&typeof We.HTML_INTEGRATION_POINTS=="object"?eo(We.HTML_INTEGRATION_POINTS):Di({},_t);let q=Ja(We,"CUSTOM_ELEMENT_HANDLING")&&We.CUSTOM_ELEMENT_HANDLING&&typeof We.CUSTOM_ELEMENT_HANDLING=="object"?eo(We.CUSTOM_ELEMENT_HANDLING):P0(null);if($e=P0(null),Ja(q,"tagNameCheck")&&Hr(q.tagNameCheck)&&($e.tagNameCheck=q.tagNameCheck),Ja(q,"attributeNameCheck")&&Hr(q.attributeNameCheck)&&($e.attributeNameCheck=q.attributeNameCheck),Ja(q,"allowCustomizedBuiltInElements")&&typeof q.allowCustomizedBuiltInElements=="boolean"&&($e.allowCustomizedBuiltInElements=q.allowCustomizedBuiltInElements),ks($e),ye&&(Ve=!1),de&&(K=!0),Re&&(ge=Di({},ID),Ne=P0(null),Re.html===!0&&(Di(ge,LD),Di(Ne,MD)),Re.svg===!0&&(Di(ge,kE),Di(Ne,EE),Di(Ne,Fx)),Re.svgFilters===!0&&(Di(ge,CE),Di(Ne,EE),Di(Ne,Fx)),Re.mathMl===!0&&(Di(ge,wE),Di(Ne,DD),Di(Ne,Fx))),Ge.tagCheck=null,Ge.attributeCheck=null,Ja(We,"ADD_TAGS")&&(typeof We.ADD_TAGS=="function"?Ge.tagCheck=We.ADD_TAGS:jh(We.ADD_TAGS)&&(ge===ke&&(ge=eo(ge)),Di(ge,We.ADD_TAGS,At))),Ja(We,"ADD_ATTR")&&(typeof We.ADD_ATTR=="function"?Ge.attributeCheck=We.ADD_ATTR:jh(We.ADD_ATTR)&&(Ne===xe&&(Ne=eo(Ne)),Di(Ne,We.ADD_ATTR,At))),Ja(We,"ADD_URI_SAFE_ATTR")&&jh(We.ADD_URI_SAFE_ATTR)&&Di(Je,We.ADD_URI_SAFE_ATTR,At),Ja(We,"FORBID_CONTENTS")&&jh(We.FORBID_CONTENTS)&&(Ke===De&&(Ke=eo(Ke)),Di(Ke,We.FORBID_CONTENTS,At)),Ja(We,"ADD_FORBID_CONTENTS")&&jh(We.ADD_FORBID_CONTENTS)&&(Ke===De&&(Ke=eo(Ke)),Di(Ke,We.ADD_FORBID_CONTENTS,At)),Ce&&(ge["#text"]=!0),Ie&&Di(ge,["html","head","body"]),ge.table&&(Di(ge,["tbody"]),delete Me.tbody),We.TRUSTED_TYPES_POLICY){if(typeof We.TRUSTED_TYPES_POLICY.createHTML!="function")throw vp('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if(typeof We.TRUSTED_TYPES_POLICY.createScriptURL!="function")throw vp('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');let T=v;v=We.TRUSTED_TYPES_POLICY;try{x=N("")}catch(A){throw v=T,A}}else We.TRUSTED_TYPES_POLICY===null?(v=void 0,x=""):(v===void 0&&(v=I()),v&&typeof x=="string"&&(x=N("")));Ts&&Ts(We),jr=We},"_parseConfig"),Li=Di({},[...kE,...CE,...Qge]),Cr=Di({},[...wE,...Zge]),Ti=s(function(We,q,T){return q.namespaceURI===mt?We==="svg":q.namespaceURI===ct?We==="svg"&&(T==="annotation-xml"||Et[T]):!!Li[We]},"_checkSvgNamespace"),vt=s(function(We,q,T){return q.namespaceURI===mt?We==="math":q.namespaceURI===qe?We==="math"&&Kt[T]:!!Cr[We]},"_checkMathMlNamespace"),wt=s(function(We,q,T){return q.namespaceURI===qe&&!Kt[T]||q.namespaceURI===ct&&!Et[T]?!1:!Cr[We]&&($t[We]||!Li[We])},"_checkHtmlNamespace"),Wt=s(function(We){let q=C(We);(!q||!q.tagName)&&(q={namespaceURI:St,tagName:"template"});let T=sy(We.tagName),A=sy(q.tagName);return Bt[We.namespaceURI]?We.namespaceURI===qe?Ti(T,q,A):We.namespaceURI===ct?vt(T,q,A):We.namespaceURI===mt?wt(T,q,A):!!(Ot==="application/xhtml+xml"&&Bt[We.namespaceURI]):!1},"_checkValidNamespace"),kr=s(function(We){z0(t.removed,{element:We});try{C(We).removeChild(We)}catch{if(m(We),!C(We))throw vp("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place")}},"_forceRemove"),Ir=s(function(We){ki(We);let q=k(We);if(q){let A=[];F0(q,D=>{z0(A,D)}),F0(A,D=>{try{m(D)}catch{}})}let T=E(We);if(T)for(let A=T.length-1;A>=0;--A){let D=T[A],P=D&&D.name;if(typeof P=="string")try{We.removeAttribute(P)}catch{}}},"_neutralizeRoot"),Lr=s(function(We,q){try{z0(t.removed,{attribute:q.getAttributeNode(We),from:q})}catch{z0(t.removed,{attribute:null,from:q})}if(q.removeAttribute(We),We==="is")if(K||de)try{kr(q)}catch{}else try{q.setAttribute(We,"")}catch{}},"_removeAttribute"),di=s(function(We){let q=E(We);if(q)for(let T=q.length-1;T>=0;--T){let A=q[T],D=A&&A.name;if(!(typeof D!="string"||Ne[At(D)]))try{We.removeAttribute(D)}catch{}}},"_stripDisallowedAttributes"),ki=s(function(We){let q=[We];for(;q.length>0;){let T=q.pop();(_?_(T):T.nodeType)===el.element&&di(T);let A=k(T);if(A)for(let D=A.length-1;D>=0;--D)q.push(A[D])}},"_neutralizeSubtree"),qr=s(function(We){if(!be)return;let q=[We];for(;q.length>0;){let T=q.pop(),A=_?_(T):T.nodeType;if(A===el.processingInstruction||A===el.comment&&es(BD,T.data)){try{m(T)}catch{}continue}if(A===el.element){let P=T,V=At(b?b(T):T.nodeName);try{P.hasAttribute&&P.hasAttribute("patchsrc")&&P.removeAttribute("patchsrc"),P.hasAttribute&&P.hasAttribute("for")&&V!=="label"&&V!=="output"&&P.removeAttribute("for")}catch{}}let D=k(T);if(D)for(let P=D.length-1;P>=0;--P)q.push(D[P])}},"_neutralizePatchLinkage"),fi=s(function(We){let q=null,T=null;if(Ae)We=""+We;else{let P=_D(We,/^[\r\n\t ]+/);T=P&&P[0]}Ot==="application/xhtml+xml"&&St===mt&&(We=''+We+"");let A=v?N(We):We;if(St===mt)try{q=new d().parseFromString(A,Ot)}catch{}if(!q||!q.documentElement){q=F.createDocument(St,"template",null);try{q.documentElement.innerHTML=Lt?x:A}catch{}}let D=q.body||q.documentElement;return We&&T&&D.insertBefore(r.createTextNode(T),D.childNodes[0]||null),St===mt?Y.call(q,Ie?"html":"body")[0]:Ie?q.documentElement:D},"_initDocument"),gt=s(function(We){return O.call(We.ownerDocument||We,We,u.SHOW_ELEMENT|u.SHOW_COMMENT|u.SHOW_TEXT|u.SHOW_PROCESSING_INSTRUCTION|u.SHOW_CDATA_SECTION,null)},"_createNodeIterator"),Mt=s(function(We){return We=oy(We,J," "),We=oy(We,te," "),We=oy(We,ie," "),We},"_stripTemplateExpressions"),kt=s(function(We){var q;We.normalize();let T=O.call(We.ownerDocument||We,We,u.SHOW_TEXT|u.SHOW_COMMENT|u.SHOW_CDATA_SECTION|u.SHOW_PROCESSING_INSTRUCTION,null),A=T.nextNode();for(;A;)A.data=Mt(A.data),A=T.nextNode();let D=(q=We.querySelectorAll)===null||q===void 0?void 0:q.call(We,"template");D&&F0(D,P=>{Pe(P.content)&&kt(P.content)})},"_scrubTemplateExpressions"),Qt=s(function(We){let q=b?b(We):null;return typeof q!="string"||At(q)!=="form"?!1:typeof We.nodeName!="string"||typeof We.textContent!="string"||typeof We.removeChild!="function"||We.attributes!==E(We)||typeof We.removeAttribute!="function"||typeof We.setAttribute!="function"||typeof We.namespaceURI!="string"||typeof We.insertBefore!="function"||typeof We.hasChildNodes!="function"||We.nodeType!==_(We)||We.childNodes!==k(We)},"_isClobbered"),Pe=s(function(We){if(!_||typeof We!="object"||We===null)return!1;try{return _(We)===el.documentFragment}catch{return!1}},"_isDocumentFragment"),ii=s(function(We){if(!_||typeof We!="object"||We===null)return!1;try{return typeof _(We)=="number"}catch{return!1}},"_isNode");function Ye(We,q,T){We.length!==0&&F0(We,A=>{A.call(t,q,T,jr)})}s(Ye,"_executeHooks");let ti=s(function(We,q){return!!(be&&We.hasChildNodes()&&!ii(We.firstElementChild)&&es(OD,We.textContent)&&es(OD,We.innerHTML)||be&&We.namespaceURI===mt&&q==="style"&&ii(We.firstElementChild)||We.nodeType===el.processingInstruction||be&&We.nodeType===el.comment&&es(BD,We.data))},"_isUnsafeNode"),Na=s(function(We,q){if(!Me[q]&&Zo(q)&&($e.tagNameCheck instanceof RegExp&&es($e.tagNameCheck,q)||$e.tagNameCheck instanceof Function&&$e.tagNameCheck(q)))return!1;if(Ce&&!Ke[q]){let T=C(We),A=k(We);if(A&&T){let D=A.length;for(let P=D-1;P>=0;--P){let V=Be?A[P]:g(A[P],!0);T.insertBefore(V,y(We))}}}return kr(We),!0},"_sanitizeDisallowedNode"),ka=s(function(We,q){if(Ye(H.beforeSanitizeElements,We,null),We!==q&&C(We)===null)return!0;if(Qt(We))return kr(We),!0;let T=At(b?b(We):We.nodeName);if(Ye(H.uponSanitizeElement,We,{tagName:T,allowedTags:ge}),We!==q&&C(We)===null)return!0;if(ti(We,T))return kr(We),!0;if(Me[T]||!(Ge.tagCheck instanceof Function&&Ge.tagCheck(T))&&!ge[T]){let A=Na(We,T);return A===!1&&Ye(H.afterSanitizeElements,We,null),A}if((_?_(We):We.nodeType)===el.element&&!Wt(We)||(T==="noscript"||T==="noembed"||T==="noframes")&&es(lme,We.innerHTML))return kr(We),!0;if(ye&&We.nodeType===el.text){let A=Mt(We.textContent);We.textContent!==A&&(z0(t.removed,{element:We.cloneNode()}),We.textContent=A)}return Ye(H.afterSanitizeElements,We,null),!1},"_sanitizeElements"),Ao=s(function(We,q,T){if(He[q]||be&&q==="patchsrc"||be&&q==="for"&&We!=="label"&&We!=="output"||ve&&(q==="id"||q==="name")&&(T in r||T in br))return!1;let A=Ne[q]||Ge.attributeCheck instanceof Function&&Ge.attributeCheck(q,We);if(!(Ve&&es(ue,q))&&!(ze&&es(se,q))){if(A){if(!Je[q]&&!es(fe,oy(T,Te,""))&&!((q==="src"||q==="xlink:href"||q==="href")&&We!=="script"&&AD(T,"data:")===0&&Ze[We])&&!(et&&!es(le,oy(T,Te,"")))&&T)return!1}else if(!(Zo(We)&&($e.tagNameCheck instanceof RegExp&&es($e.tagNameCheck,We)||$e.tagNameCheck instanceof Function&&$e.tagNameCheck(We))&&($e.attributeNameCheck instanceof RegExp&&es($e.attributeNameCheck,q)||$e.attributeNameCheck instanceof Function&&$e.attributeNameCheck(q,We))||q==="is"&&$e.allowCustomizedBuiltInElements&&($e.tagNameCheck instanceof RegExp&&es($e.tagNameCheck,T)||$e.tagNameCheck instanceof Function&&$e.tagNameCheck(T))))return!1}return!0},"_isValidAttribute"),Qo=Di({},["annotation-xml","color-profile","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","missing-glyph"]),Zo=s(function(We){return!Qo[sy(We)]&&es(oe,We)},"_isBasicCustomElement"),nn=s(function(We,q,T,A){if(v&&typeof p=="object"&&typeof p.getAttributeType=="function"&&!T)switch(p.getAttributeType(We,q)){case"TrustedHTML":return N(A);case"TrustedScriptURL":return j(A)}return A},"_applyTrustedTypesToAttribute"),fs=s(function(We,q,T,A){try{T?We.setAttributeNS(T,q,A):We.setAttribute(q,A),Qt(We)?kr(We):SD(t.removed)}catch{Lr(q,We)}},"_setAttributeValue"),gs=s(function(We){Ye(H.beforeSanitizeAttributes,We,null);let q=We.attributes;if(!q||Qt(We))return;let T={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Ne,forceKeepAttr:void 0},A=q.length,D=At(We.nodeName);for(;A--;){let P=q[A],V=P.name,Z=P.namespaceURI,re=P.value,Q=At(V),ae=re,we=V==="value"?ae:Gge(ae);if(T.attrName=Q,T.attrValue=we,T.keepAttr=!0,T.forceKeepAttr=void 0,Ye(H.uponSanitizeAttribute,We,T),we=T.attrValue,X&&(Q==="id"||Q==="name")&&AD(we,me)!==0&&(Lr(V,We),we=me+we),be&&es(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,we)){Lr(V,We);continue}if(Q==="attributename"&&_D(we,"href")){Lr(V,We);continue}if(!T.forceKeepAttr){if(!T.keepAttr){Lr(V,We);continue}if(!he&&es(cme,we)){Lr(V,We);continue}if(ye&&(we=Mt(we)),!Ao(D,Q,we)){Lr(V,We);continue}we=nn(D,Q,Z,we),we!==ae&&fs(We,V,Z,we)}}Ye(H.afterSanitizeAttributes,We,null)},"_sanitizeAttributes"),ms=s(function(We){let q=null,T=gt(We);for(Ye(H.beforeSanitizeShadowDOM,We,null);q=T.nextNode();)if(Ye(H.uponSanitizeShadowNode,q,null),ka(q,We),gs(q),Pe(q.content)&&ms(q.content),(_?_(q):q.nodeType)===el.element){let A=S(q);Pe(A)&&(An(A),ms(A))}Ye(H.afterSanitizeShadowDOM,We,null)},"_sanitizeShadowDOM"),An=s(function(We){let q=[{node:We,shadow:null}];for(;q.length>0;){let T=q.pop();if(T.shadow){ms(T.shadow);continue}let A=T.node,D=(_?_(A):A.nodeType)===el.element,P=k(A);if(P)for(let V=P.length-1;V>=0;--V)q.push({node:P[V],shadow:null});if(D){let V=b?b(A):null;if(typeof V=="string"&&At(V)==="template"){let Z=A.content;Pe(Z)&&q.push({node:Z,shadow:null})}}if(D){let V=S(A);Pe(V)&&q.push({node:null,shadow:V},{node:V,shadow:null})}}},"_sanitizeAttachedShadowRoots");return t.sanitize=function(We){let q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},T=null,A=null,D=null,P=null;if(Lt=!We,Lt&&(We=""),typeof We!="string"&&!ii(We)&&(We=qge(We),typeof We!="string"))throw vp("dirty is not a string, aborting");if(!t.isSupported)return We;pe?(ge=Qe,Ne=W):Ei(q),(H.uponSanitizeElement.length>0||H.uponSanitizeAttribute.length>0)&&(ge=eo(ge)),H.uponSanitizeAttribute.length>0&&(Ne=eo(Ne)),t.removed=[];let V=Be&&typeof We!="string"&&ii(We);if(V){qr(We);let ae=b?b(We):We.nodeName;if(typeof ae=="string"){let we=At(ae);if(!ge[we]||Me[we])throw Ir(We),vp("root node is forbidden and cannot be sanitized in-place")}if(Qt(We))throw Ir(We),vp("root node is clobbered and cannot be sanitized in-place");try{An(We)}catch(we){throw Ir(We),we}}else if(ii(We))T=fi(""),A=T.ownerDocument.importNode(We,!0),A.nodeType===el.element&&A.nodeName==="BODY"||A.nodeName==="HTML"?T=A:T.appendChild(A),An(A);else{if(!K&&!ye&&!Ie&&We.indexOf("<")===-1)return v&&ee?N(We):We;if(T=fi(We),!T)return K?null:ee?x:""}T&&Ae&&kr(T.firstChild);let Z=V?We:T,re=gt(Z);try{for(;D=re.nextNode();)ka(D,Z),gs(D),Pe(D.content)&&ms(D.content)}catch(ae){throw V&&(Ir(We),F0(t.removed,we=>{we.element&&ki(we.element)})),ae}if(V)return F0(t.removed,ae=>{ae.element&&ki(ae.element)}),ye&&kt(We),We;if(K){if(ye&&kt(T),de)for(P=B.call(T.ownerDocument);T.firstChild;)P.appendChild(T.firstChild);else P=T;return(Ne.shadowroot||Ne.shadowrootmode)&&(P=U.call(i,P,!0)),P}let Q=Ie?T.outerHTML:T.innerHTML;return Ie&&ge["!doctype"]&&T.ownerDocument&&T.ownerDocument.doctype&&T.ownerDocument.doctype.name&&es(sme,T.ownerDocument.doctype.name)&&(Q=" +`+Q),ye&&(Q=Mt(Q)),v&&ee?N(Q):Q},t.setConfig=function(){let We=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};Ei(We),pe=!0,Qe=ge,W=Ne},t.clearConfig=function(){jr=null,pe=!1,Qe=null,W=null,v=w,x=""},t.isValidAttribute=function(We,q,T){jr||Ei({});let A=At(We),D=At(q);return Ao(A,D,T)},t.addHook=function(We,q){typeof q=="function"&&Ja(H,We)&&z0(H[We],q)},t.removeHook=function(We,q){if(Ja(H,We)){if(q!==void 0){let T=Yge(H[We],q);return T===-1?void 0:Hge(H[We],T,1)[0]}return SD(H[We])}},t.removeHooks=function(We){Ja(H,We)&&(H[We]=[])},t.removeAllHooks=function(){H=PD()},t}var CD,wD,jge,Vge,Uge,Ts,ks,P0,ED,bE,xE,F0,Yge,SD,z0,Hge,jh,sy,TE,_D,oy,AD,Gge,Xge,Kge,RD,$D,Ja,ly,es,vp,LD,kE,CE,Qge,wE,Zge,ID,MD,EE,DD,Fx,Jge,eme,tme,rme,ime,ND,nme,ame,sme,ome,OD,BD,lme,cme,el,ume,hme,PD,Vh,wu,zx=G(()=>{"use strict";s(TD,"_arrayLikeToArray"),s(Dge,"_arrayWithHoles"),s(Nge,"_iterableToArrayLimit"),s(Oge,"_nonIterableRest"),s(Bge,"_slicedToArray"),s(Pge,"_unsupportedIterableToArray"),CD=Object.entries,wD=Object.setPrototypeOf,jge=Object.isFrozen,Vge=Object.getPrototypeOf,Uge=Object.getOwnPropertyDescriptor,Ts=Object.freeze,ks=Object.seal,P0=Object.create,ED=typeof Reflect<"u"&&Reflect,bE=ED.apply,xE=ED.construct,Ts||(Ts=s(function(e){return e},"freeze")),ks||(ks=s(function(e){return e},"seal")),bE||(bE=s(function(e,t){for(var r=arguments.length,i=new Array(r>2?r-2:0),n=2;n1?t-1:0),i=1;i"u"?null:_a(BigInt.prototype.toString),$D=typeof Symbol>"u"?null:_a(Symbol.prototype.toString),Ja=_a(Object.prototype.hasOwnProperty),ly=_a(Object.prototype.toString),es=_a(RegExp.prototype.test),vp=Fge(TypeError),s(_a,"unapply"),s(Fge,"unconstruct"),s(Di,"addToSet"),s(zge,"cleanArray"),s(eo,"clone"),s(qge,"stringifyValue"),s(Ac,"lookupGetter"),s(Wge,"isRegex"),LD=Ts(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","search","section","select","shadow","slot","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),kE=Ts(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","enterkeyhint","exportparts","filter","font","g","glyph","glyphref","hkern","image","inputmode","line","lineargradient","marker","mask","metadata","mpath","part","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),CE=Ts(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),Qge=Ts(["animate","color-profile","cursor","discard","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),wE=Ts(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover","mprescripts"]),Zge=Ts(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),ID=Ts(["#text"]),MD=Ts(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","command","commandfor","controls","controlslist","coords","crossorigin","datetime","decoding","default","dir","disabled","disablepictureinpicture","disableremoteplayback","download","draggable","enctype","enterkeyhint","exportparts","face","for","headers","height","hidden","high","href","hreflang","id","inert","inputmode","integrity","ismap","kind","label","lang","list","loading","loop","low","max","maxlength","media","method","min","minlength","multiple","muted","name","nonce","noshade","novalidate","nowrap","open","optimum","part","pattern","placeholder","playsinline","popover","popovertarget","popovertargetaction","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","slot","span","srclang","start","src","srcset","step","style","summary","tabindex","title","translate","type","usemap","valign","value","width","wrap","xmlns"]),EE=Ts(["accent-height","accumulate","additive","alignment-baseline","amplitude","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clippathunits","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dominant-baseline","dur","edgemode","elevation","end","exponent","fill","fill-opacity","fill-rule","filter","filterunits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","intercept","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","mask-type","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","preserveaspectratio","primitiveunits","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","slope","specularconstant","specularexponent","spreadmethod","startoffset","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","systemlanguage","tabindex","tablevalues","targetx","targety","transform","transform-origin","text-anchor","text-decoration","text-orientation","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","version","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"]),DD=Ts(["accent","accentunder","align","bevelled","close","columnalign","columnlines","columnspacing","columnspan","denomalign","depth","dir","display","displaystyle","encoding","fence","frame","height","href","id","largeop","length","linethickness","lquote","lspace","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"]),Fx=Ts(["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"]),Jge=ks(/{{[\w\W]*|^[\w\W]*}}/g),eme=ks(/<%[\w\W]*|^[\w\W]*%>/g),tme=ks(/\${[\w\W]*/g),rme=ks(/^data-[\-\w.\u00B7-\uFFFF]+$/),ime=ks(/^aria-[\-\w]+$/),ND=ks(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),nme=ks(/^(?:\w+script|data):/i),ame=ks(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),sme=ks(/^html$/i),ome=ks(/^[a-z][.\w]*(-[.\w]+)+$/i),OD=ks(/<[/\w!]/g),BD=ks(/<[/\w]/g),lme=ks(/<\/no(script|embed|frames)/i),cme=ks(/\/>/i),el={element:1,attribute:2,text:3,cdataSection:4,entityReference:5,entityNode:6,processingInstruction:7,comment:8,document:9,documentType:10,documentFragment:11,notation:12},ume=s(function(){return typeof window>"u"?null:window},"getGlobal"),hme=s(function(e,t){if(typeof e!="object"||typeof e.createPolicy!="function")return null;let r=null,i="data-tt-policy-suffix";t&&t.hasAttribute(i)&&(r=t.getAttribute(i));let n="dompurify"+(r?"#"+r:"");try{return e.createPolicy(n,{createHTML(a){return a},createScriptURL(a){return a}})}catch{return console.warn("TrustedTypes policy "+n+" could not be created."),null}},"_createTrustedTypesPolicy"),PD=s(function(){return{afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},"_createHooksMap"),Vh=s(function(e,t,r,i){return Ja(e,t)&&jh(e[t])?Di(i.base?eo(i.base):{},e[t],i.transform):r},"_resolveSetOption"),s(kD,"createDOMPurify"),wu=kD()}),dme={};Mr(dme,{ParseError:()=>Xt,SETTINGS_SCHEMA:()=>Ux,__defineFunction:()=>dr,__defineMacro:()=>Oe,__defineSymbol:()=>ne,__domTree:()=>EO,__parse:()=>TO,__renderToDomTree:()=>fT,__renderToHTMLTree:()=>CO,__setFontMetrics:()=>zD,default:()=>nye,render:()=>kS,renderToString:()=>xO,version:()=>wO});function pme(e){if(typeof e!="string")return e.enum[0];switch(e){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{};default:throw new Error("Unexpected schema type; settings must declare an explicit default.")}}function fme(e){if(e.default!==void 0)return e.default;var t=Array.isArray(e.type)?e.type[0]:e.type;return pme(t)}function gme(e,t,r,i){var n=r[t];e[t]=n!==void 0?i.processor?i.processor(n):n:fme(i)}function mme(e){for(var t=0;t=n[0]&&e<=n[1])return r.name}return null}function FD(e){for(var t=0;t=Hx[t]&&e<=Hx[t+1])return!0;return!1}function yme(e){return"toText"in e}function vme(e){if(e instanceof tl)return e;throw new Error("Expected symbolNode but got "+String(e)+".")}function bme(e){if(e instanceof U0)return e;throw new Error("Expected span but got "+String(e)+".")}function zD(e,t){Lc[e]=t}function SE(e,t,r){if(!Lc[t])throw new Error("Font metrics not found for font: "+t+".");var i=e.charCodeAt(0),n=Lc[t][i];if(!n&&e[0]in eN&&(i=eN[e[0]].charCodeAt(0),n=Lc[t][i]),!n&&r==="text"&&FD(i)&&(n=Lc[t][77]),n)return{depth:n[0],height:n[1],italic:n[2],skew:n[3],width:n[4]}}function xme(e){var t;if(e>=5?t=0:e>=3?t=1:t=2,!PE[t]){var r=PE[t]={cssEmPerMu:Xx.quad[t]/18};for(var i in Xx)Xx.hasOwnProperty(i)&&(r[i]=Xx[i][t])}return PE[t]}function ne(e,t,r,i,n,a){Yn[e][n]={font:t,group:r,replace:i},a&&i&&(Yn[e][i]=Yn[e][n])}function dr(e){for(var{type:t,names:r,props:i,handler:n,htmlBuilder:a,mathmlBuilder:o}=e,l={type:t,numArgs:i.numArgs,argTypes:i.argTypes,allowedInArgument:!!i.allowedInArgument,allowedInText:!!i.allowedInText,allowedInMath:i.allowedInMath===void 0?!0:i.allowedInMath,numOptionalArgs:i.numOptionalArgs||0,infix:!!i.infix,primitive:!!i.primitive,handler:n},u=0;u0&&(a.push(qx(o,t)),o=[]),a.push(i[l]));o.length>0&&a.push(qx(o,t));var h;r?(h=qx(Fa(r,t,!0),t),h.classes=["tag"],a.push(h)):n&&a.push(n);var d=jt(["katex-html"],a);if(d.setAttribute("aria-hidden","true"),h){var p=h.children[0];p.style.height=er(d.height+d.depth),d.depth&&(p.style.verticalAlign=er(-d.depth))}return d}function qD(e){return new V0(e)}function AE(e){if(!e)return!1;if(e.type==="mi"&&e.children.length===1){var t=e.children[0];return t instanceof $a&&t.text==="."}else if(e.type==="mo"&&e.children.length===1&&e.getAttribute("separator")==="true"&&e.getAttribute("lspace")==="0em"&&e.getAttribute("rspace")==="0em"){var r=e.children[0];return r instanceof $a&&r.text===","}else return!1}function WD(e,t,r,i,n){var a=il(e,r),o;a.length===1&&a[0]instanceof Ht&&d1e.has(a[0].type)?o=a[0]:o=new Ht("mrow",a);var l=new Ht("annotation",[new $a(t)]);l.setAttribute("encoding","application/x-tex");var u=new Ht("semantics",[o,l]),h=new Ht("math",[u]);h.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),i&&h.setAttribute("display","block");var d=n?"katex":"katex-mathml";return jt([d],[h])}function Tme(e){return e in T1e}function gi(e,t){if(!e||e.type!==t)throw new Error("Expected node of type "+t+", but got "+(e?"node of type "+e.type:String(e)));return e}function Wx(e){var t=jx(e);if(!t)throw new Error("Expected node of symbol group type, but got "+(e?"node of type "+e.type:String(e)));return t}function jx(e){return e&&(e.type==="atom"||k1e.hasOwnProperty(e.type))?e:null}function jD(e,t){var r=Fa(e.body,t,!0);return jt([e.mclass],r,t)}function VD(e,t){var r,i=il(e.body,t);return e.mclass==="minner"?r=new Ht("mpadded",i):e.mclass==="mord"?e.isCharacterBox?(r=i[0],r.type="mi"):r=new Ht("mi",i):(e.isCharacterBox?(r=i[0],r.type="mo"):r=new Ht("mo",i),e.mclass==="mbin"?(r.attributes.lspace="0.22em",r.attributes.rspace="0.22em"):e.mclass==="mpunct"?(r.attributes.lspace="0em",r.attributes.rspace="0.17em"):e.mclass==="mopen"||e.mclass==="mclose"?(r.attributes.lspace="0em",r.attributes.rspace="0em"):e.mclass==="minner"&&(r.attributes.lspace="0.0556em",r.attributes.width="+0.1111em")),r}function kme(e,t,r){var i=w1e[e];switch(i){case"\\\\cdrightarrow":case"\\\\cdleftarrow":return r.callFunction(i,[t[0]],[t[1]]);case"\\uparrow":case"\\downarrow":{var n=r.callFunction("\\\\cdleft",[t[0]],[]),a={type:"atom",text:i,mode:"math",family:"rel"},o=r.callFunction("\\Big",[a],[]),l=r.callFunction("\\\\cdright",[t[1]],[]),u={type:"ordgroup",mode:"math",body:[n,o,l]};return r.callFunction("\\\\cdparent",[u],[])}case"\\\\cdlongequal":return r.callFunction("\\\\cdlongequal",[],[]);case"\\Vert":{var h={type:"textord",text:"\\Vert",mode:"math"};return r.callFunction("\\Big",[h],[])}default:return{type:"textord",text:" ",mode:"math"}}}function Cme(e){var t=[];for(e.gullet.beginGroup(),e.gullet.macros.set("\\cr","\\\\\\relax"),e.gullet.beginGroup();;){t.push(e.parseExpression(!1,"\\\\")),e.gullet.endGroup(),e.gullet.beginGroup();var r=e.fetch().text;if(r==="&"||r==="\\\\")e.consume();else if(r==="\\end"){t[t.length-1].length===0&&t.pop();break}else throw new Xt("Expected \\\\ or \\cr or \\end",e.nextToken)}for(var i=[],n=[i],a=0;aAV".includes(h))for(var p=0;p<2;p++){for(var f=!0,g=u+1;gAV=|." after @',o[u]);var m=kme(h,d,e),y={type:"styling",body:[m],mode:"math",style:"display",resetFont:!0};i.push(y),l=_N()}a%2===0?i.push(l):i.shift(),i=[],n.push(i)}e.gullet.endGroup(),e.gullet.endGroup();var k=new Array(n[0].length).fill({type:"align",align:"c",pregap:.25,postgap:.25});return{type:"array",mode:"math",body:n,arraystretch:1,addJot:!0,rowGaps:[null],cols:k,colSeparationType:"CD",hLinesBeforeRow:new Array(n.length+1).fill([])}}function UD(e){return"isMiddle"in e}function Vx(e,t){var r=jx(e);if(r&&O1e.has(r.text))return r;throw r?new Xt("Invalid delimiter '"+r.text+"' after '"+t.funcName+"'",e):new Xt("Invalid delimiter type '"+e.type+"'",e)}function YD(e){if(!e.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}function Rc(e){for(var{type:t,names:r,props:i,handler:n,htmlBuilder:a,mathmlBuilder:o}=e,l={type:t,numArgs:i.numArgs||0,allowedInText:!1,numOptionalArgs:0,handler:n},u=0;u1||!d)&&y.pop(),C.length{"use strict";var e,t,r,i,n,a,o,l,u,h,d,p,f,g,m,y,k,C,S,E,_;for(Xt=(e=class extends Error{constructor(v,x){var w="KaTeX parse error: "+v,R,$,M=x&&x.loc;if(M&&M.start<=M.end){var N=M.lexer.input;R=M.start,$=M.end,R===N.length?w+=" at end of input: ":w+=" at position "+(R+1)+": ";var j=N.slice(R,$).replace(/[^]/g,"$&̲"),I;R>15?I="…"+N.slice(R-15,R):I=N.slice(0,R);var z;$+15b.replace(Eme,"-$1").toLowerCase(),"hyphenate"),_me={"&":"&",">":">","<":"<",'"':""","'":"'"},Ame=/[&><"']/g,Fs=s(b=>String(b).replace(Ame,v=>_me[v]),"escape"),LE=s(b=>b.type==="ordgroup"||b.type==="color"?b.body.length===1?LE(b.body[0]):b:b.type==="font"?LE(b.body):b,"getBaseElem"),Rme=new Set(["mathord","textord","atom"]),Eu=s(b=>Rme.has(LE(b).type),"isCharacterBox"),$me=s(b=>{var v=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(b);return v?v[2]!==":"||!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(v[1])?null:v[1].toLowerCase():"_relative"},"protocolFromUrl"),Ux={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:s(b=>"#"+b,"cliProcessor")},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:s((b,v)=>(v.push(b),v),"cliProcessor")},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:s(b=>Math.max(0,b),"processor"),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:s(b=>Math.max(0,b),"processor"),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:s(b=>Math.max(0,b),"processor"),cli:"-e, --max-expand ",cliProcessor:s(b=>b==="Infinity"?1/0:parseInt(b),"cliProcessor")},globalGroup:{type:"boolean",cli:!1}},s(pme,"getImplicitDefault"),s(fme,"getDefaultValue"),s(gme,"applySetting"),IE=(t=class{constructor(v){v===void 0&&(v={}),this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,v=v||{};for(var x of Object.keys(Ux)){var w=Ux[x];w&&gme(this,x,v,w)}}reportNonstrict(v,x,w){var R=this.strict;if(typeof R=="function"&&(R=R(v,x,w)),!(!R||R==="ignore")){if(R===!0||R==="error")throw new Xt("LaTeX-incompatible input and strict mode is set to 'error': "+(x+" ["+v+"]"),w);R==="warn"?typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(x+" ["+v+"]")):typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+R+"': "+x+" ["+v+"]"))}}useStrictBehavior(v,x,w){var R=this.strict;if(typeof R=="function")try{R=R(v,x,w)}catch{R="error"}return!R||R==="ignore"?!1:R===!0||R==="error"?!0:R==="warn"?(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(x+" ["+v+"]")),!1):(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+R+"': "+x+" ["+v+"]")),!1)}isTrusted(v){if("url"in v&&v.url&&!v.protocol){var x=$me(v.url);if(x==null)return!1;v.protocol=x}var w=typeof this.trust=="function"?this.trust(v):this.trust;return!!w}},s(t,"Settings"),t),Yh=(r=class{constructor(v,x,w){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=v,this.size=x,this.cramped=w}sup(){return $c[Lme[this.id]]}sub(){return $c[Ime[this.id]]}fracNum(){return $c[Mme[this.id]]}fracDen(){return $c[Dme[this.id]]}cramp(){return $c[Nme[this.id]]}text(){return $c[Ome[this.id]]}isTight(){return this.size>=2}},s(r,"Style"),r),ME=0,Yx=1,q0=2,Su=3,cy=4,Tl=5,W0=6,to=7,$c=[new Yh(ME,0,!1),new Yh(Yx,0,!0),new Yh(q0,1,!1),new Yh(Su,1,!0),new Yh(cy,2,!1),new Yh(Tl,2,!0),new Yh(W0,3,!1),new Yh(to,3,!0)],Lme=[cy,Tl,cy,Tl,W0,to,W0,to],Ime=[Tl,Tl,Tl,Tl,to,to,to,to],Mme=[q0,Su,cy,Tl,W0,to,W0,to],Dme=[Su,Su,Tl,Tl,to,to,to,to],Nme=[Yx,Yx,Su,Su,Tl,Tl,to,to],Ome=[ME,Yx,q0,Su,q0,Su,q0,Su],si={DISPLAY:$c[ME],TEXT:$c[q0],SCRIPT:$c[cy],SCRIPTSCRIPT:$c[W0]},DE=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}],s(mme,"scriptFromCodepoint"),Hx=[],DE.forEach(b=>b.blocks.forEach(v=>Hx.push(...v))),s(FD,"supportedCodepoint"),Pa=s(b=>b+" "+b,"doubleBrushStroke"),j0=80,Bme=s(function(b,v){return"M95,"+(622+b+v)+` +c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14 +c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54 +c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10 +s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429 +c69,-144,104.5,-217.7,106.5,-221 +l`+b/2.075+" -"+b+` +c5.3,-9.3,12,-14,20,-14 +H400000v`+(40+b)+`H845.2724 +s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7 +c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z +M`+(834+b)+" "+v+"h400000v"+(40+b)+"h-400000z"},"sqrtMain"),Pme=s(function(b,v){return"M263,"+(601+b+v)+`c0.7,0,18,39.7,52,119 +c34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120 +c340,-704.7,510.7,-1060.3,512,-1067 +l`+b/2.084+" -"+b+` +c4.7,-7.3,11,-11,19,-11 +H40000v`+(40+b)+`H1012.3 +s-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232 +c-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1 +s-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26 +c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z +M`+(1001+b)+" "+v+"h400000v"+(40+b)+"h-400000z"},"sqrtSize1"),Fme=s(function(b,v){return"M983 "+(10+b+v)+` +l`+b/3.13+" -"+b+` +c4,-6.7,10,-10,18,-10 H400000v`+(40+b)+` +H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7 +s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744 +c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30 +c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722 +c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5 +c53.7,-170.3,84.5,-266.8,92.5,-289.5z +M`+(1001+b)+" "+v+"h400000v"+(40+b)+"h-400000z"},"sqrtSize2"),zme=s(function(b,v){return"M424,"+(2398+b+v)+` +c-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514 +c0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20 +s-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121 +s209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081 +l`+b/4.223+" -"+b+`c4,-6.7,10,-10,18,-10 H400000 +v`+(40+b)+`H1014.6 +s-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185 +c-2,6,-10,9,-24,9 +c-8,0,-12,-0.7,-12,-2z M`+(1001+b)+" "+v+` +h400000v`+(40+b)+"h-400000z"},"sqrtSize3"),qme=s(function(b,v){return"M473,"+(2713+b+v)+` +c339.3,-1799.3,509.3,-2700,510,-2702 l`+b/5.298+" -"+b+` +c3.3,-7.3,9.3,-11,18,-11 H400000v`+(40+b)+`H1017.7 +s-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9 +c-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200 +c0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26 +s76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104, +606zM`+(1001+b)+" "+v+"h400000v"+(40+b)+"H1017.7z"},"sqrtSize4"),Wme=s(function(b){var v=b/2;return"M400000 "+b+" H0 L"+v+" 0 l65 45 L145 "+(b-80)+" H400000z"},"phasePath"),jme=s(function(b,v,x){var w=x-54-v-b;return"M702 "+(b+v)+"H400000"+(40+b)+` +H742v`+w+`l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1 +h-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170 +c-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667 +219 661 l218 661zM702 `+v+"H400000v"+(40+b)+"H742z"},"sqrtTall"),Vme=s(function(b,v,x){v=1e3*v;var w="";switch(b){case"sqrtMain":w=Bme(v,j0);break;case"sqrtSize1":w=Pme(v,j0);break;case"sqrtSize2":w=Fme(v,j0);break;case"sqrtSize3":w=zme(v,j0);break;case"sqrtSize4":w=qme(v,j0);break;case"sqrtTall":w=jme(v,j0,x)}return w},"sqrtPath"),Ume=s(function(b,v){switch(b){case"⎜":return Pa("M291 0 H417 V"+v+" H291z");case"∣":return Pa("M145 0 H188 V"+v+" H145z");case"∥":return Pa("M145 0 H188 V"+v+" H145z")+Pa("M367 0 H410 V"+v+" H367z");case"⎟":return Pa("M457 0 H583 V"+v+" H457z");case"⎢":return Pa("M319 0 H403 V"+v+" H319z");case"⎥":return Pa("M263 0 H347 V"+v+" H263z");case"⎪":return Pa("M384 0 H504 V"+v+" H384z");case"⏐":return Pa("M312 0 H355 V"+v+" H312z");case"‖":return Pa("M257 0 H300 V"+v+" H257z")+Pa("M478 0 H521 V"+v+" H478z");default:return""}},"innerPath"),XD={doubleleftarrow:`M262 157 +l10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3 + 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28 + 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5 +c2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5 + 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87 +-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7 +-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z +m8 0v40h399730v-40zm0 194v40h399730v-40z`,doublerightarrow:`M399738 392l +-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5 + 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88 +-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68 +-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18 +-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782 +c-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3 +-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z`,leftarrow:`M400000 241H110l3-3c68.7-52.7 113.7-120 + 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8 +-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247 +c-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208 + 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3 + 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202 + l-3-3h399890zM100 241v40h399900v-40z`,leftbrace:`M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117 +-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7 + 5-6 9-10 13-.7 1-7.3 1-20 1H6z`,leftbraceunder:`M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 + 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 + 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7 +-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z`,leftgroup:`M400000 80 +H435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0 + 435 0h399565z`,leftgroupunder:`M400000 262 +H435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219 + 435 219h399565z`,leftharpoon:`M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3 +-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5 +-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7 +-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z`,leftharpoonplus:`M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5 + 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3 +-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7 +-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z +m0 0v40h400000v-40z`,leftharpoondown:`M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333 + 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5 + 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667 +-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z`,leftharpoondownplus:`M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12 + 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7 +-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0 +v40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z`,lefthook:`M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5 +-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3 +-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21 + 71.5 23h399859zM103 281v-40h399897v40z`,leftlinesegment:Pa("M40 281 V428 H0 V94 H40 V241 H400000 v40z"),leftbracketunder:Pa("M0 0 h120 V290 H399995 v120 H0z"),leftbracketover:Pa("M0 440 h120 V150 H399995 v-120 H0z"),leftmapsto:Pa("M40 281 V448H0V74H40V241H400000v40z"),leftToFrom:`M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23 +-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8 +c28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3 + 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z`,longequal:Pa("M0 50 h400000 v40H0z m0 194h40000v40H0z"),midbrace:`M200428 334 +c-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14 +-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7 + 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11 + 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z`,midbraceunder:`M199572 214 +c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 + 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 + 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0 +-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z`,oiintSize1:`M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6 +-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z +m368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8 +60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z`,oiintSize2:`M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8 +-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z +m502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2 +c0 110 84 276 504 276s502.4-166 502.4-276z`,oiiintSize1:`M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6 +-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z +m525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0 +85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z`,oiiintSize2:`M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8 +-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z +m770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1 +c0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z`,rightarrow:`M0 241v40h399891c-47.3 35.3-84 78-110 128 +-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 + 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 + 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85 +-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5 +-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67 + 151.7 139 205zm0 0v40h399900v-40z`,rightbrace:`M400000 542l +-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5 +s-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1 +c124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z`,rightbraceunder:`M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 + 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237 +-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z`,rightgroup:`M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0 + 3-1 3-3v-38c-76-158-257-219-435-219H0z`,rightgroupunder:`M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18 + 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z`,rightharpoon:`M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3 +-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2 +-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 + 69.2 92 94.5zm0 0v40h399900v-40z`,rightharpoonplus:`M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11 +-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7 + 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z +m0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z`,rightharpoondown:`M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8 + 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5 +-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95 +-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z`,rightharpoondownplus:`M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8 + 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 + 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3 +-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z +m0-194v40h400000v-40zm0 0v40h400000v-40z`,righthook:`M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3 + 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0 +-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21 + 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z`,rightlinesegment:Pa("M399960 241 V94 h40 V428 h-40 V281 H0 v-40z"),rightbracketunder:Pa("M399995 0 h-120 V290 H0 v120 H400000z"),rightbracketover:Pa("M399995 440 h-120 V150 H0 v-120 H399995z"),rightToFrom:`M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23 + 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32 +-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142 +-167z M100 147v40h399900v-40zM0 341v40h399900v-40z`,twoheadleftarrow:`M0 167c68 40 + 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69 +-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3 +-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19 +-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101 + 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z`,twoheadrightarrow:`M400000 167 +c-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3 + 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42 + 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333 +-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70 + 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z`,tilde1:`M200 55.538c-77 0-168 73.953-177 73.953-3 0-7 +-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0 + 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0 + 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128 +-68.267.847-113-73.952-191-73.952z`,tilde2:`M344 55.266c-142 0-300.638 81.316-311.5 86.418 +-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9 + 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114 +c1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751 + 181.476 676 181.476c-149 0-189-126.21-332-126.21z`,tilde3:`M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457 +-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0 + 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697 + 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696 + -338 0-409-156.573-744-156.573z`,tilde4:`M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345 +-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409 + 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9 + 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409 + -175.236-744-175.236z`,vec:`M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5 +3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11 +10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63 +-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1 +-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59 +H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359 +c-16-25.333-24-45-24-59z`,widehat1:`M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22 +c-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z`,widehat2:`M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10 +-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`,widehat3:`M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10 +-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`,widehat4:`M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10 +-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`,widecheck1:`M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1, +-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z`,widecheck2:`M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10, +-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`,widecheck3:`M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10, +-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`,widecheck4:`M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10, +-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`,baraboveleftarrow:`M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202 +c4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5 +c-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130 +s-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47 +121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6 +s2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11 +c0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z +M100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z`,rightarrowabovebar:`M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32 +-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0 +13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39 +-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5 +-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5 +-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67 +151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z`,baraboveshortleftharpoon:`M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11 +c1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17 +c2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21 +c-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40 +c-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z +M0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z`,rightharpoonaboveshortbar:`M0,241 l0,40c399126,0,399993,0,399993,0 +c4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199, +-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6 +c-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z +M0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z`,shortbaraboveleftharpoon:`M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11 +c1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9, +1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7, +-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z +M93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z`,shortrightharpoonabovebar:`M53,241l0,40c398570,0,399437,0,399437,0 +c4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199, +-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6 +c-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z +M500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z`},Yme=s(function(b,v){switch(b){case"lbrack":return"M403 1759 V84 H666 V0 H319 V1759 v"+v+` v1759 v84 h347 v-84 +H403z M403 1759 V0 H319 V1759 v`+v+" v1759 v84 h84z";case"rbrack":return"M347 1759 V0 H0 V84 H263 V1759 v"+v+` v1759 H0 v84 H347z +M347 1759 V0 H263 V1759 v`+v+" v1759 h84z";case"vert":return"M145 15 v585 v"+v+` v585 c2.667,10,9.667,15,21,15 +c10,0,16.667,-5,20,-15 v-585 v`+-v+` v-585 c-2.667,-10,-9.667,-15,-21,-15 +c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v`+v+" v585 h43z";case"doublevert":return"M145 15 v585 v"+v+` v585 c2.667,10,9.667,15,21,15 +c10,0,16.667,-5,20,-15 v-585 v`+-v+` v-585 c-2.667,-10,-9.667,-15,-21,-15 +c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v`+v+` v585 h43z +M367 15 v585 v`+v+` v585 c2.667,10,9.667,15,21,15 +c10,0,16.667,-5,20,-15 v-585 v`+-v+` v-585 c-2.667,-10,-9.667,-15,-21,-15 +c-10,0,-16.667,5,-20,15z M410 15 H367 v585 v`+v+" v585 h43z";case"lfloor":return"M319 602 V0 H403 V602 v"+v+` v1715 h263 v84 H319z +MM319 602 V0 H403 V602 v`+v+" v1715 H319z";case"rfloor":return"M319 602 V0 H403 V602 v"+v+` v1799 H0 v-84 H319z +MM319 602 V0 H403 V602 v`+v+" v1715 H319z";case"lceil":return"M403 1759 V84 H666 V0 H319 V1759 v"+v+` v602 h84z +M403 1759 V0 H319 V1759 v`+v+" v602 h84z";case"rceil":return"M347 1759 V0 H0 V84 H263 V1759 v"+v+` v602 h84z +M347 1759 V0 h-84 V1759 v`+v+" v602 h84z";case"lparen":return`M863,9c0,-2,-2,-5,-6,-9c0,0,-17,0,-17,0c-12.7,0,-19.3,0.3,-20,1 +c-5.3,5.3,-10.3,11,-15,17c-242.7,294.7,-395.3,682,-458,1162c-21.3,163.3,-33.3,349, +-36,557 l0,`+(v+84)+`c0.2,6,0,26,0,60c2,159.3,10,310.7,24,454c53.3,528,210, +949.7,470,1265c4.7,6,9.7,11.7,15,17c0.7,0.7,7,1,19,1c0,0,18,0,18,0c4,-4,6,-7,6,-9 +c0,-2.7,-3.3,-8.7,-10,-18c-135.3,-192.7,-235.5,-414.3,-300.5,-665c-65,-250.7,-102.5, +-544.7,-112.5,-882c-2,-104,-3,-167,-3,-189 +l0,-`+(v+92)+`c0,-162.7,5.7,-314,17,-454c20.7,-272,63.7,-513,129,-723c65.3, +-210,155.3,-396.3,270,-559c6.7,-9.3,10,-15.3,10,-18z`;case"rparen":return`M76,0c-16.7,0,-25,3,-25,9c0,2,2,6.3,6,13c21.3,28.7,42.3,60.3, +63,95c96.7,156.7,172.8,332.5,228.5,527.5c55.7,195,92.8,416.5,111.5,664.5 +c11.3,139.3,17,290.7,17,454c0,28,1.7,43,3.3,45l0,`+(v+9)+` +c-3,4,-3.3,16.7,-3.3,38c0,162,-5.7,313.7,-17,455c-18.7,248,-55.8,469.3,-111.5,664 +c-55.7,194.7,-131.8,370.3,-228.5,527c-20.7,34.7,-41.7,66.3,-63,95c-2,3.3,-4,7,-6,11 +c0,7.3,5.7,11,17,11c0,0,11,0,11,0c9.3,0,14.3,-0.3,15,-1c5.3,-5.3,10.3,-11,15,-17 +c242.7,-294.7,395.3,-681.7,458,-1161c21.3,-164.7,33.3,-350.7,36,-558 +l0,-`+(v+144)+`c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7, +-470,-1265c-4.7,-6,-9.7,-11.7,-15,-17c-0.7,-0.7,-6.7,-1,-18,-1z`;default:throw new Error("Unknown stretchy delimiter.")}},"tallDelim"),s(yme,"isMathDomNode"),V0=(i=class{constructor(v){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=v,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}hasClass(v){return this.classes.includes(v)}toNode(){for(var v=document.createDocumentFragment(),x=0;x{if(yme(v))return v.toText();throw new Error("Expected MathDomNode with toText, got "+v.constructor.name)}).join("")}},s(i,"DocumentFragment"),i),NE={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:803/800,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:803/800},Hme={ex:!0,em:!0,mu:!0},KD=s(function(b){return typeof b!="string"&&(b=b.unit),b in NE||b in Hme||b==="ex"},"validUnit"),Jn=s(function(b,v){var x;if(b.unit in NE)x=NE[b.unit]/v.fontMetrics().ptPerEm/v.sizeMultiplier;else if(b.unit==="mu")x=v.fontMetrics().cssEmPerMu;else{var w;if(v.style.isTight()?w=v.havingStyle(v.style.text()):w=v,b.unit==="ex")x=w.fontMetrics().xHeight;else if(b.unit==="em")x=w.fontMetrics().quad;else throw new Xt("Invalid unit: '"+b.unit+"'");w!==v&&(x*=w.sizeMultiplier/v.sizeMultiplier)}return Math.min(b.number*x,v.maxSize)},"calculateSize"),er=s(function(b){return+b.toFixed(4)+"em"},"makeEm"),Hh=s(function(b){return b.filter(v=>v).join(" ")},"createClass"),OE=s(function(b){var v="";for(var x of Object.keys(b)){var w=b[x];w!==void 0&&(v+=Sme(x)+":"+w+";")}return v},"cssStyleToString"),QD=s(function(b,v,x){if(this.classes=b||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=x||{},v){v.style.isTight()&&this.classes.push("mtight");var w=v.getColor();w&&(this.style.color=w)}},"initNode"),ZD=s(function(b){var v=document.createElement(b);v.className=Hh(this.classes),Object.assign(v.style,this.style);for(var x of Object.keys(this.attributes))v.setAttribute(x,this.attributes[x]);for(var w=0;w/=\x00-\x1f]/,JD=s(function(b){var v="<"+b;this.classes.length&&(v+=' class="'+Fs(Hh(this.classes))+'"');var x=OE(this.style);x&&(v+=' style="'+Fs(x)+'"');for(var w of Object.keys(this.attributes)){if(Gme.test(w))throw new Xt("Invalid attribute name '"+w+"'");v+=" "+w+'="'+Fs(this.attributes[w])+'"'}v+=">";for(var R=0;R",v},"toMarkup"),U0=(n=class{constructor(v,x,w,R){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,this.italic=void 0,QD.call(this,v,w,R),this.children=x||[]}setAttribute(v,x){this.attributes[v]=x}hasClass(v){return this.classes.includes(v)}toNode(){return ZD.call(this,"span")}toMarkup(){return JD.call(this,"span")}},s(n,"Span"),n),Gx=(a=class{constructor(v,x,w,R){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,QD.call(this,x,R),this.children=w||[],this.setAttribute("href",v)}setAttribute(v,x){this.attributes[v]=x}hasClass(v){return this.classes.includes(v)}toNode(){return ZD.call(this,"a")}toMarkup(){return JD.call(this,"a")}},s(a,"Anchor"),a),Xme=(o=class{constructor(v,x,w){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=x,this.src=v,this.classes=["mord"],this.height=0,this.depth=0,this.maxFontSize=0,this.style=w}hasClass(v){return this.classes.includes(v)}toNode(){var v=document.createElement("img");return v.src=this.src,v.alt=this.alt,v.className="mord",Object.assign(v.style,this.style),v}toMarkup(){var v=''+Fs(this.alt)+'0&&(x=document.createElement("span"),x.style.marginRight=er(this.italic)),this.classes.length>0&&(x=x||document.createElement("span"),x.className=Hh(this.classes)),Object.keys(this.style).length>0&&(x=x||document.createElement("span"),Object.assign(x.style,this.style)),x?(x.appendChild(v),x):v}toMarkup(){var v=!1,x="0&&(w+="margin-right:"+er(this.italic)+";"),w+=OE(this.style),w&&(v=!0,x+=' style="'+Fs(w)+'"');var R=Fs(this.text);return v?(x+=">",x+=R,x+="",x):R}},s(l,"SymbolNode"),l),_u=(u=class{constructor(v,x){this.children=void 0,this.attributes=void 0,this.children=v||[],this.attributes=x||{}}toNode(){var v="http://www.w3.org/2000/svg",x=document.createElementNS(v,"svg");for(var w of Object.keys(this.attributes))x.setAttribute(w,this.attributes[w]);for(var R=0;R':''}},s(h,"PathNode"),h),BE=(d=class{constructor(v){this.attributes=void 0,this.attributes=v||{}}toNode(){var v="http://www.w3.org/2000/svg",x=document.createElementNS(v,"line");for(var w of Object.keys(this.attributes))x.setAttribute(w,this.attributes[w]);return x}toMarkup(){var v="b instanceof U0||b instanceof Gx||b instanceof V0,"hasHtmlDomChildren"),Lc={"AMS-Regular":{32:[0,0,0,0,.25],65:[0,.68889,0,0,.72222],66:[0,.68889,0,0,.66667],67:[0,.68889,0,0,.72222],68:[0,.68889,0,0,.72222],69:[0,.68889,0,0,.66667],70:[0,.68889,0,0,.61111],71:[0,.68889,0,0,.77778],72:[0,.68889,0,0,.77778],73:[0,.68889,0,0,.38889],74:[.16667,.68889,0,0,.5],75:[0,.68889,0,0,.77778],76:[0,.68889,0,0,.66667],77:[0,.68889,0,0,.94445],78:[0,.68889,0,0,.72222],79:[.16667,.68889,0,0,.77778],80:[0,.68889,0,0,.61111],81:[.16667,.68889,0,0,.77778],82:[0,.68889,0,0,.72222],83:[0,.68889,0,0,.55556],84:[0,.68889,0,0,.66667],85:[0,.68889,0,0,.72222],86:[0,.68889,0,0,.72222],87:[0,.68889,0,0,1],88:[0,.68889,0,0,.72222],89:[0,.68889,0,0,.72222],90:[0,.68889,0,0,.66667],107:[0,.68889,0,0,.55556],160:[0,0,0,0,.25],165:[0,.675,.025,0,.75],174:[.15559,.69224,0,0,.94666],240:[0,.68889,0,0,.55556],295:[0,.68889,0,0,.54028],710:[0,.825,0,0,2.33334],732:[0,.9,0,0,2.33334],770:[0,.825,0,0,2.33334],771:[0,.9,0,0,2.33334],989:[.08167,.58167,0,0,.77778],1008:[0,.43056,.04028,0,.66667],8245:[0,.54986,0,0,.275],8463:[0,.68889,0,0,.54028],8487:[0,.68889,0,0,.72222],8498:[0,.68889,0,0,.55556],8502:[0,.68889,0,0,.66667],8503:[0,.68889,0,0,.44445],8504:[0,.68889,0,0,.66667],8513:[0,.68889,0,0,.63889],8592:[-.03598,.46402,0,0,.5],8594:[-.03598,.46402,0,0,.5],8602:[-.13313,.36687,0,0,1],8603:[-.13313,.36687,0,0,1],8606:[.01354,.52239,0,0,1],8608:[.01354,.52239,0,0,1],8610:[.01354,.52239,0,0,1.11111],8611:[.01354,.52239,0,0,1.11111],8619:[0,.54986,0,0,1],8620:[0,.54986,0,0,1],8621:[-.13313,.37788,0,0,1.38889],8622:[-.13313,.36687,0,0,1],8624:[0,.69224,0,0,.5],8625:[0,.69224,0,0,.5],8630:[0,.43056,0,0,1],8631:[0,.43056,0,0,1],8634:[.08198,.58198,0,0,.77778],8635:[.08198,.58198,0,0,.77778],8638:[.19444,.69224,0,0,.41667],8639:[.19444,.69224,0,0,.41667],8642:[.19444,.69224,0,0,.41667],8643:[.19444,.69224,0,0,.41667],8644:[.1808,.675,0,0,1],8646:[.1808,.675,0,0,1],8647:[.1808,.675,0,0,1],8648:[.19444,.69224,0,0,.83334],8649:[.1808,.675,0,0,1],8650:[.19444,.69224,0,0,.83334],8651:[.01354,.52239,0,0,1],8652:[.01354,.52239,0,0,1],8653:[-.13313,.36687,0,0,1],8654:[-.13313,.36687,0,0,1],8655:[-.13313,.36687,0,0,1],8666:[.13667,.63667,0,0,1],8667:[.13667,.63667,0,0,1],8669:[-.13313,.37788,0,0,1],8672:[-.064,.437,0,0,1.334],8674:[-.064,.437,0,0,1.334],8705:[0,.825,0,0,.5],8708:[0,.68889,0,0,.55556],8709:[.08167,.58167,0,0,.77778],8717:[0,.43056,0,0,.42917],8722:[-.03598,.46402,0,0,.5],8724:[.08198,.69224,0,0,.77778],8726:[.08167,.58167,0,0,.77778],8733:[0,.69224,0,0,.77778],8736:[0,.69224,0,0,.72222],8737:[0,.69224,0,0,.72222],8738:[.03517,.52239,0,0,.72222],8739:[.08167,.58167,0,0,.22222],8740:[.25142,.74111,0,0,.27778],8741:[.08167,.58167,0,0,.38889],8742:[.25142,.74111,0,0,.5],8756:[0,.69224,0,0,.66667],8757:[0,.69224,0,0,.66667],8764:[-.13313,.36687,0,0,.77778],8765:[-.13313,.37788,0,0,.77778],8769:[-.13313,.36687,0,0,.77778],8770:[-.03625,.46375,0,0,.77778],8774:[.30274,.79383,0,0,.77778],8776:[-.01688,.48312,0,0,.77778],8778:[.08167,.58167,0,0,.77778],8782:[.06062,.54986,0,0,.77778],8783:[.06062,.54986,0,0,.77778],8785:[.08198,.58198,0,0,.77778],8786:[.08198,.58198,0,0,.77778],8787:[.08198,.58198,0,0,.77778],8790:[0,.69224,0,0,.77778],8791:[.22958,.72958,0,0,.77778],8796:[.08198,.91667,0,0,.77778],8806:[.25583,.75583,0,0,.77778],8807:[.25583,.75583,0,0,.77778],8808:[.25142,.75726,0,0,.77778],8809:[.25142,.75726,0,0,.77778],8812:[.25583,.75583,0,0,.5],8814:[.20576,.70576,0,0,.77778],8815:[.20576,.70576,0,0,.77778],8816:[.30274,.79383,0,0,.77778],8817:[.30274,.79383,0,0,.77778],8818:[.22958,.72958,0,0,.77778],8819:[.22958,.72958,0,0,.77778],8822:[.1808,.675,0,0,.77778],8823:[.1808,.675,0,0,.77778],8828:[.13667,.63667,0,0,.77778],8829:[.13667,.63667,0,0,.77778],8830:[.22958,.72958,0,0,.77778],8831:[.22958,.72958,0,0,.77778],8832:[.20576,.70576,0,0,.77778],8833:[.20576,.70576,0,0,.77778],8840:[.30274,.79383,0,0,.77778],8841:[.30274,.79383,0,0,.77778],8842:[.13597,.63597,0,0,.77778],8843:[.13597,.63597,0,0,.77778],8847:[.03517,.54986,0,0,.77778],8848:[.03517,.54986,0,0,.77778],8858:[.08198,.58198,0,0,.77778],8859:[.08198,.58198,0,0,.77778],8861:[.08198,.58198,0,0,.77778],8862:[0,.675,0,0,.77778],8863:[0,.675,0,0,.77778],8864:[0,.675,0,0,.77778],8865:[0,.675,0,0,.77778],8872:[0,.69224,0,0,.61111],8873:[0,.69224,0,0,.72222],8874:[0,.69224,0,0,.88889],8876:[0,.68889,0,0,.61111],8877:[0,.68889,0,0,.61111],8878:[0,.68889,0,0,.72222],8879:[0,.68889,0,0,.72222],8882:[.03517,.54986,0,0,.77778],8883:[.03517,.54986,0,0,.77778],8884:[.13667,.63667,0,0,.77778],8885:[.13667,.63667,0,0,.77778],8888:[0,.54986,0,0,1.11111],8890:[.19444,.43056,0,0,.55556],8891:[.19444,.69224,0,0,.61111],8892:[.19444,.69224,0,0,.61111],8901:[0,.54986,0,0,.27778],8903:[.08167,.58167,0,0,.77778],8905:[.08167,.58167,0,0,.77778],8906:[.08167,.58167,0,0,.77778],8907:[0,.69224,0,0,.77778],8908:[0,.69224,0,0,.77778],8909:[-.03598,.46402,0,0,.77778],8910:[0,.54986,0,0,.76042],8911:[0,.54986,0,0,.76042],8912:[.03517,.54986,0,0,.77778],8913:[.03517,.54986,0,0,.77778],8914:[0,.54986,0,0,.66667],8915:[0,.54986,0,0,.66667],8916:[0,.69224,0,0,.66667],8918:[.0391,.5391,0,0,.77778],8919:[.0391,.5391,0,0,.77778],8920:[.03517,.54986,0,0,1.33334],8921:[.03517,.54986,0,0,1.33334],8922:[.38569,.88569,0,0,.77778],8923:[.38569,.88569,0,0,.77778],8926:[.13667,.63667,0,0,.77778],8927:[.13667,.63667,0,0,.77778],8928:[.30274,.79383,0,0,.77778],8929:[.30274,.79383,0,0,.77778],8934:[.23222,.74111,0,0,.77778],8935:[.23222,.74111,0,0,.77778],8936:[.23222,.74111,0,0,.77778],8937:[.23222,.74111,0,0,.77778],8938:[.20576,.70576,0,0,.77778],8939:[.20576,.70576,0,0,.77778],8940:[.30274,.79383,0,0,.77778],8941:[.30274,.79383,0,0,.77778],8994:[.19444,.69224,0,0,.77778],8995:[.19444,.69224,0,0,.77778],9416:[.15559,.69224,0,0,.90222],9484:[0,.69224,0,0,.5],9488:[0,.69224,0,0,.5],9492:[0,.37788,0,0,.5],9496:[0,.37788,0,0,.5],9585:[.19444,.68889,0,0,.88889],9586:[.19444,.74111,0,0,.88889],9632:[0,.675,0,0,.77778],9633:[0,.675,0,0,.77778],9650:[0,.54986,0,0,.72222],9651:[0,.54986,0,0,.72222],9654:[.03517,.54986,0,0,.77778],9660:[0,.54986,0,0,.72222],9661:[0,.54986,0,0,.72222],9664:[.03517,.54986,0,0,.77778],9674:[.11111,.69224,0,0,.66667],9733:[.19444,.69224,0,0,.94445],10003:[0,.69224,0,0,.83334],10016:[0,.69224,0,0,.83334],10731:[.11111,.69224,0,0,.66667],10846:[.19444,.75583,0,0,.61111],10877:[.13667,.63667,0,0,.77778],10878:[.13667,.63667,0,0,.77778],10885:[.25583,.75583,0,0,.77778],10886:[.25583,.75583,0,0,.77778],10887:[.13597,.63597,0,0,.77778],10888:[.13597,.63597,0,0,.77778],10889:[.26167,.75726,0,0,.77778],10890:[.26167,.75726,0,0,.77778],10891:[.48256,.98256,0,0,.77778],10892:[.48256,.98256,0,0,.77778],10901:[.13667,.63667,0,0,.77778],10902:[.13667,.63667,0,0,.77778],10933:[.25142,.75726,0,0,.77778],10934:[.25142,.75726,0,0,.77778],10935:[.26167,.75726,0,0,.77778],10936:[.26167,.75726,0,0,.77778],10937:[.26167,.75726,0,0,.77778],10938:[.26167,.75726,0,0,.77778],10949:[.25583,.75583,0,0,.77778],10950:[.25583,.75583,0,0,.77778],10955:[.28481,.79383,0,0,.77778],10956:[.28481,.79383,0,0,.77778],57350:[.08167,.58167,0,0,.22222],57351:[.08167,.58167,0,0,.38889],57352:[.08167,.58167,0,0,.77778],57353:[0,.43056,.04028,0,.66667],57356:[.25142,.75726,0,0,.77778],57357:[.25142,.75726,0,0,.77778],57358:[.41951,.91951,0,0,.77778],57359:[.30274,.79383,0,0,.77778],57360:[.30274,.79383,0,0,.77778],57361:[.41951,.91951,0,0,.77778],57366:[.25142,.75726,0,0,.77778],57367:[.25142,.75726,0,0,.77778],57368:[.25142,.75726,0,0,.77778],57369:[.25142,.75726,0,0,.77778],57370:[.13597,.63597,0,0,.77778],57371:[.13597,.63597,0,0,.77778]},"Caligraphic-Regular":{32:[0,0,0,0,.25],65:[0,.68333,0,.19445,.79847],66:[0,.68333,.03041,.13889,.65681],67:[0,.68333,.05834,.13889,.52653],68:[0,.68333,.02778,.08334,.77139],69:[0,.68333,.08944,.11111,.52778],70:[0,.68333,.09931,.11111,.71875],71:[.09722,.68333,.0593,.11111,.59487],72:[0,.68333,.00965,.11111,.84452],73:[0,.68333,.07382,0,.54452],74:[.09722,.68333,.18472,.16667,.67778],75:[0,.68333,.01445,.05556,.76195],76:[0,.68333,0,.13889,.68972],77:[0,.68333,0,.13889,1.2009],78:[0,.68333,.14736,.08334,.82049],79:[0,.68333,.02778,.11111,.79611],80:[0,.68333,.08222,.08334,.69556],81:[.09722,.68333,0,.11111,.81667],82:[0,.68333,0,.08334,.8475],83:[0,.68333,.075,.13889,.60556],84:[0,.68333,.25417,0,.54464],85:[0,.68333,.09931,.08334,.62583],86:[0,.68333,.08222,0,.61278],87:[0,.68333,.08222,.08334,.98778],88:[0,.68333,.14643,.13889,.7133],89:[.09722,.68333,.08222,.08334,.66834],90:[0,.68333,.07944,.13889,.72473],160:[0,0,0,0,.25]},"Fraktur-Regular":{32:[0,0,0,0,.25],33:[0,.69141,0,0,.29574],34:[0,.69141,0,0,.21471],38:[0,.69141,0,0,.73786],39:[0,.69141,0,0,.21201],40:[.24982,.74947,0,0,.38865],41:[.24982,.74947,0,0,.38865],42:[0,.62119,0,0,.27764],43:[.08319,.58283,0,0,.75623],44:[0,.10803,0,0,.27764],45:[.08319,.58283,0,0,.75623],46:[0,.10803,0,0,.27764],47:[.24982,.74947,0,0,.50181],48:[0,.47534,0,0,.50181],49:[0,.47534,0,0,.50181],50:[0,.47534,0,0,.50181],51:[.18906,.47534,0,0,.50181],52:[.18906,.47534,0,0,.50181],53:[.18906,.47534,0,0,.50181],54:[0,.69141,0,0,.50181],55:[.18906,.47534,0,0,.50181],56:[0,.69141,0,0,.50181],57:[.18906,.47534,0,0,.50181],58:[0,.47534,0,0,.21606],59:[.12604,.47534,0,0,.21606],61:[-.13099,.36866,0,0,.75623],63:[0,.69141,0,0,.36245],65:[0,.69141,0,0,.7176],66:[0,.69141,0,0,.88397],67:[0,.69141,0,0,.61254],68:[0,.69141,0,0,.83158],69:[0,.69141,0,0,.66278],70:[.12604,.69141,0,0,.61119],71:[0,.69141,0,0,.78539],72:[.06302,.69141,0,0,.7203],73:[0,.69141,0,0,.55448],74:[.12604,.69141,0,0,.55231],75:[0,.69141,0,0,.66845],76:[0,.69141,0,0,.66602],77:[0,.69141,0,0,1.04953],78:[0,.69141,0,0,.83212],79:[0,.69141,0,0,.82699],80:[.18906,.69141,0,0,.82753],81:[.03781,.69141,0,0,.82699],82:[0,.69141,0,0,.82807],83:[0,.69141,0,0,.82861],84:[0,.69141,0,0,.66899],85:[0,.69141,0,0,.64576],86:[0,.69141,0,0,.83131],87:[0,.69141,0,0,1.04602],88:[0,.69141,0,0,.71922],89:[.18906,.69141,0,0,.83293],90:[.12604,.69141,0,0,.60201],91:[.24982,.74947,0,0,.27764],93:[.24982,.74947,0,0,.27764],94:[0,.69141,0,0,.49965],97:[0,.47534,0,0,.50046],98:[0,.69141,0,0,.51315],99:[0,.47534,0,0,.38946],100:[0,.62119,0,0,.49857],101:[0,.47534,0,0,.40053],102:[.18906,.69141,0,0,.32626],103:[.18906,.47534,0,0,.5037],104:[.18906,.69141,0,0,.52126],105:[0,.69141,0,0,.27899],106:[0,.69141,0,0,.28088],107:[0,.69141,0,0,.38946],108:[0,.69141,0,0,.27953],109:[0,.47534,0,0,.76676],110:[0,.47534,0,0,.52666],111:[0,.47534,0,0,.48885],112:[.18906,.52396,0,0,.50046],113:[.18906,.47534,0,0,.48912],114:[0,.47534,0,0,.38919],115:[0,.47534,0,0,.44266],116:[0,.62119,0,0,.33301],117:[0,.47534,0,0,.5172],118:[0,.52396,0,0,.5118],119:[0,.52396,0,0,.77351],120:[.18906,.47534,0,0,.38865],121:[.18906,.47534,0,0,.49884],122:[.18906,.47534,0,0,.39054],160:[0,0,0,0,.25],8216:[0,.69141,0,0,.21471],8217:[0,.69141,0,0,.21471],58112:[0,.62119,0,0,.49749],58113:[0,.62119,0,0,.4983],58114:[.18906,.69141,0,0,.33328],58115:[.18906,.69141,0,0,.32923],58116:[.18906,.47534,0,0,.50343],58117:[0,.69141,0,0,.33301],58118:[0,.62119,0,0,.33409],58119:[0,.47534,0,0,.50073]},"Main-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.35],34:[0,.69444,0,0,.60278],35:[.19444,.69444,0,0,.95833],36:[.05556,.75,0,0,.575],37:[.05556,.75,0,0,.95833],38:[0,.69444,0,0,.89444],39:[0,.69444,0,0,.31944],40:[.25,.75,0,0,.44722],41:[.25,.75,0,0,.44722],42:[0,.75,0,0,.575],43:[.13333,.63333,0,0,.89444],44:[.19444,.15556,0,0,.31944],45:[0,.44444,0,0,.38333],46:[0,.15556,0,0,.31944],47:[.25,.75,0,0,.575],48:[0,.64444,0,0,.575],49:[0,.64444,0,0,.575],50:[0,.64444,0,0,.575],51:[0,.64444,0,0,.575],52:[0,.64444,0,0,.575],53:[0,.64444,0,0,.575],54:[0,.64444,0,0,.575],55:[0,.64444,0,0,.575],56:[0,.64444,0,0,.575],57:[0,.64444,0,0,.575],58:[0,.44444,0,0,.31944],59:[.19444,.44444,0,0,.31944],60:[.08556,.58556,0,0,.89444],61:[-.10889,.39111,0,0,.89444],62:[.08556,.58556,0,0,.89444],63:[0,.69444,0,0,.54305],64:[0,.69444,0,0,.89444],65:[0,.68611,0,0,.86944],66:[0,.68611,0,0,.81805],67:[0,.68611,0,0,.83055],68:[0,.68611,0,0,.88194],69:[0,.68611,0,0,.75555],70:[0,.68611,0,0,.72361],71:[0,.68611,0,0,.90416],72:[0,.68611,0,0,.9],73:[0,.68611,0,0,.43611],74:[0,.68611,0,0,.59444],75:[0,.68611,0,0,.90138],76:[0,.68611,0,0,.69166],77:[0,.68611,0,0,1.09166],78:[0,.68611,0,0,.9],79:[0,.68611,0,0,.86388],80:[0,.68611,0,0,.78611],81:[.19444,.68611,0,0,.86388],82:[0,.68611,0,0,.8625],83:[0,.68611,0,0,.63889],84:[0,.68611,0,0,.8],85:[0,.68611,0,0,.88472],86:[0,.68611,.01597,0,.86944],87:[0,.68611,.01597,0,1.18888],88:[0,.68611,0,0,.86944],89:[0,.68611,.02875,0,.86944],90:[0,.68611,0,0,.70277],91:[.25,.75,0,0,.31944],92:[.25,.75,0,0,.575],93:[.25,.75,0,0,.31944],94:[0,.69444,0,0,.575],95:[.31,.13444,.03194,0,.575],97:[0,.44444,0,0,.55902],98:[0,.69444,0,0,.63889],99:[0,.44444,0,0,.51111],100:[0,.69444,0,0,.63889],101:[0,.44444,0,0,.52708],102:[0,.69444,.10903,0,.35139],103:[.19444,.44444,.01597,0,.575],104:[0,.69444,0,0,.63889],105:[0,.69444,0,0,.31944],106:[.19444,.69444,0,0,.35139],107:[0,.69444,0,0,.60694],108:[0,.69444,0,0,.31944],109:[0,.44444,0,0,.95833],110:[0,.44444,0,0,.63889],111:[0,.44444,0,0,.575],112:[.19444,.44444,0,0,.63889],113:[.19444,.44444,0,0,.60694],114:[0,.44444,0,0,.47361],115:[0,.44444,0,0,.45361],116:[0,.63492,0,0,.44722],117:[0,.44444,0,0,.63889],118:[0,.44444,.01597,0,.60694],119:[0,.44444,.01597,0,.83055],120:[0,.44444,0,0,.60694],121:[.19444,.44444,.01597,0,.60694],122:[0,.44444,0,0,.51111],123:[.25,.75,0,0,.575],124:[.25,.75,0,0,.31944],125:[.25,.75,0,0,.575],126:[.35,.34444,0,0,.575],160:[0,0,0,0,.25],163:[0,.69444,0,0,.86853],168:[0,.69444,0,0,.575],172:[0,.44444,0,0,.76666],176:[0,.69444,0,0,.86944],177:[.13333,.63333,0,0,.89444],184:[.17014,0,0,0,.51111],198:[0,.68611,0,0,1.04166],215:[.13333,.63333,0,0,.89444],216:[.04861,.73472,0,0,.89444],223:[0,.69444,0,0,.59722],230:[0,.44444,0,0,.83055],247:[.13333,.63333,0,0,.89444],248:[.09722,.54167,0,0,.575],305:[0,.44444,0,0,.31944],338:[0,.68611,0,0,1.16944],339:[0,.44444,0,0,.89444],567:[.19444,.44444,0,0,.35139],710:[0,.69444,0,0,.575],711:[0,.63194,0,0,.575],713:[0,.59611,0,0,.575],714:[0,.69444,0,0,.575],715:[0,.69444,0,0,.575],728:[0,.69444,0,0,.575],729:[0,.69444,0,0,.31944],730:[0,.69444,0,0,.86944],732:[0,.69444,0,0,.575],733:[0,.69444,0,0,.575],915:[0,.68611,0,0,.69166],916:[0,.68611,0,0,.95833],920:[0,.68611,0,0,.89444],923:[0,.68611,0,0,.80555],926:[0,.68611,0,0,.76666],928:[0,.68611,0,0,.9],931:[0,.68611,0,0,.83055],933:[0,.68611,0,0,.89444],934:[0,.68611,0,0,.83055],936:[0,.68611,0,0,.89444],937:[0,.68611,0,0,.83055],8211:[0,.44444,.03194,0,.575],8212:[0,.44444,.03194,0,1.14999],8216:[0,.69444,0,0,.31944],8217:[0,.69444,0,0,.31944],8220:[0,.69444,0,0,.60278],8221:[0,.69444,0,0,.60278],8224:[.19444,.69444,0,0,.51111],8225:[.19444,.69444,0,0,.51111],8242:[0,.55556,0,0,.34444],8407:[0,.72444,.15486,0,.575],8463:[0,.69444,0,0,.66759],8465:[0,.69444,0,0,.83055],8467:[0,.69444,0,0,.47361],8472:[.19444,.44444,0,0,.74027],8476:[0,.69444,0,0,.83055],8501:[0,.69444,0,0,.70277],8592:[-.10889,.39111,0,0,1.14999],8593:[.19444,.69444,0,0,.575],8594:[-.10889,.39111,0,0,1.14999],8595:[.19444,.69444,0,0,.575],8596:[-.10889,.39111,0,0,1.14999],8597:[.25,.75,0,0,.575],8598:[.19444,.69444,0,0,1.14999],8599:[.19444,.69444,0,0,1.14999],8600:[.19444,.69444,0,0,1.14999],8601:[.19444,.69444,0,0,1.14999],8636:[-.10889,.39111,0,0,1.14999],8637:[-.10889,.39111,0,0,1.14999],8640:[-.10889,.39111,0,0,1.14999],8641:[-.10889,.39111,0,0,1.14999],8656:[-.10889,.39111,0,0,1.14999],8657:[.19444,.69444,0,0,.70277],8658:[-.10889,.39111,0,0,1.14999],8659:[.19444,.69444,0,0,.70277],8660:[-.10889,.39111,0,0,1.14999],8661:[.25,.75,0,0,.70277],8704:[0,.69444,0,0,.63889],8706:[0,.69444,.06389,0,.62847],8707:[0,.69444,0,0,.63889],8709:[.05556,.75,0,0,.575],8711:[0,.68611,0,0,.95833],8712:[.08556,.58556,0,0,.76666],8715:[.08556,.58556,0,0,.76666],8722:[.13333,.63333,0,0,.89444],8723:[.13333,.63333,0,0,.89444],8725:[.25,.75,0,0,.575],8726:[.25,.75,0,0,.575],8727:[-.02778,.47222,0,0,.575],8728:[-.02639,.47361,0,0,.575],8729:[-.02639,.47361,0,0,.575],8730:[.18,.82,0,0,.95833],8733:[0,.44444,0,0,.89444],8734:[0,.44444,0,0,1.14999],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.31944],8741:[.25,.75,0,0,.575],8743:[0,.55556,0,0,.76666],8744:[0,.55556,0,0,.76666],8745:[0,.55556,0,0,.76666],8746:[0,.55556,0,0,.76666],8747:[.19444,.69444,.12778,0,.56875],8764:[-.10889,.39111,0,0,.89444],8768:[.19444,.69444,0,0,.31944],8771:[.00222,.50222,0,0,.89444],8773:[.027,.638,0,0,.894],8776:[.02444,.52444,0,0,.89444],8781:[.00222,.50222,0,0,.89444],8801:[.00222,.50222,0,0,.89444],8804:[.19667,.69667,0,0,.89444],8805:[.19667,.69667,0,0,.89444],8810:[.08556,.58556,0,0,1.14999],8811:[.08556,.58556,0,0,1.14999],8826:[.08556,.58556,0,0,.89444],8827:[.08556,.58556,0,0,.89444],8834:[.08556,.58556,0,0,.89444],8835:[.08556,.58556,0,0,.89444],8838:[.19667,.69667,0,0,.89444],8839:[.19667,.69667,0,0,.89444],8846:[0,.55556,0,0,.76666],8849:[.19667,.69667,0,0,.89444],8850:[.19667,.69667,0,0,.89444],8851:[0,.55556,0,0,.76666],8852:[0,.55556,0,0,.76666],8853:[.13333,.63333,0,0,.89444],8854:[.13333,.63333,0,0,.89444],8855:[.13333,.63333,0,0,.89444],8856:[.13333,.63333,0,0,.89444],8857:[.13333,.63333,0,0,.89444],8866:[0,.69444,0,0,.70277],8867:[0,.69444,0,0,.70277],8868:[0,.69444,0,0,.89444],8869:[0,.69444,0,0,.89444],8900:[-.02639,.47361,0,0,.575],8901:[-.02639,.47361,0,0,.31944],8902:[-.02778,.47222,0,0,.575],8968:[.25,.75,0,0,.51111],8969:[.25,.75,0,0,.51111],8970:[.25,.75,0,0,.51111],8971:[.25,.75,0,0,.51111],8994:[-.13889,.36111,0,0,1.14999],8995:[-.13889,.36111,0,0,1.14999],9651:[.19444,.69444,0,0,1.02222],9657:[-.02778,.47222,0,0,.575],9661:[.19444,.69444,0,0,1.02222],9667:[-.02778,.47222,0,0,.575],9711:[.19444,.69444,0,0,1.14999],9824:[.12963,.69444,0,0,.89444],9825:[.12963,.69444,0,0,.89444],9826:[.12963,.69444,0,0,.89444],9827:[.12963,.69444,0,0,.89444],9837:[0,.75,0,0,.44722],9838:[.19444,.69444,0,0,.44722],9839:[.19444,.69444,0,0,.44722],10216:[.25,.75,0,0,.44722],10217:[.25,.75,0,0,.44722],10815:[0,.68611,0,0,.9],10927:[.19667,.69667,0,0,.89444],10928:[.19667,.69667,0,0,.89444],57376:[.19444,.69444,0,0,0]},"Main-BoldItalic":{32:[0,0,0,0,.25],33:[0,.69444,.11417,0,.38611],34:[0,.69444,.07939,0,.62055],35:[.19444,.69444,.06833,0,.94444],37:[.05556,.75,.12861,0,.94444],38:[0,.69444,.08528,0,.88555],39:[0,.69444,.12945,0,.35555],40:[.25,.75,.15806,0,.47333],41:[.25,.75,.03306,0,.47333],42:[0,.75,.14333,0,.59111],43:[.10333,.60333,.03306,0,.88555],44:[.19444,.14722,0,0,.35555],45:[0,.44444,.02611,0,.41444],46:[0,.14722,0,0,.35555],47:[.25,.75,.15806,0,.59111],48:[0,.64444,.13167,0,.59111],49:[0,.64444,.13167,0,.59111],50:[0,.64444,.13167,0,.59111],51:[0,.64444,.13167,0,.59111],52:[.19444,.64444,.13167,0,.59111],53:[0,.64444,.13167,0,.59111],54:[0,.64444,.13167,0,.59111],55:[.19444,.64444,.13167,0,.59111],56:[0,.64444,.13167,0,.59111],57:[0,.64444,.13167,0,.59111],58:[0,.44444,.06695,0,.35555],59:[.19444,.44444,.06695,0,.35555],61:[-.10889,.39111,.06833,0,.88555],63:[0,.69444,.11472,0,.59111],64:[0,.69444,.09208,0,.88555],65:[0,.68611,0,0,.86555],66:[0,.68611,.0992,0,.81666],67:[0,.68611,.14208,0,.82666],68:[0,.68611,.09062,0,.87555],69:[0,.68611,.11431,0,.75666],70:[0,.68611,.12903,0,.72722],71:[0,.68611,.07347,0,.89527],72:[0,.68611,.17208,0,.8961],73:[0,.68611,.15681,0,.47166],74:[0,.68611,.145,0,.61055],75:[0,.68611,.14208,0,.89499],76:[0,.68611,0,0,.69777],77:[0,.68611,.17208,0,1.07277],78:[0,.68611,.17208,0,.8961],79:[0,.68611,.09062,0,.85499],80:[0,.68611,.0992,0,.78721],81:[.19444,.68611,.09062,0,.85499],82:[0,.68611,.02559,0,.85944],83:[0,.68611,.11264,0,.64999],84:[0,.68611,.12903,0,.7961],85:[0,.68611,.17208,0,.88083],86:[0,.68611,.18625,0,.86555],87:[0,.68611,.18625,0,1.15999],88:[0,.68611,.15681,0,.86555],89:[0,.68611,.19803,0,.86555],90:[0,.68611,.14208,0,.70888],91:[.25,.75,.1875,0,.35611],93:[.25,.75,.09972,0,.35611],94:[0,.69444,.06709,0,.59111],95:[.31,.13444,.09811,0,.59111],97:[0,.44444,.09426,0,.59111],98:[0,.69444,.07861,0,.53222],99:[0,.44444,.05222,0,.53222],100:[0,.69444,.10861,0,.59111],101:[0,.44444,.085,0,.53222],102:[.19444,.69444,.21778,0,.4],103:[.19444,.44444,.105,0,.53222],104:[0,.69444,.09426,0,.59111],105:[0,.69326,.11387,0,.35555],106:[.19444,.69326,.1672,0,.35555],107:[0,.69444,.11111,0,.53222],108:[0,.69444,.10861,0,.29666],109:[0,.44444,.09426,0,.94444],110:[0,.44444,.09426,0,.64999],111:[0,.44444,.07861,0,.59111],112:[.19444,.44444,.07861,0,.59111],113:[.19444,.44444,.105,0,.53222],114:[0,.44444,.11111,0,.50167],115:[0,.44444,.08167,0,.48694],116:[0,.63492,.09639,0,.385],117:[0,.44444,.09426,0,.62055],118:[0,.44444,.11111,0,.53222],119:[0,.44444,.11111,0,.76777],120:[0,.44444,.12583,0,.56055],121:[.19444,.44444,.105,0,.56166],122:[0,.44444,.13889,0,.49055],126:[.35,.34444,.11472,0,.59111],160:[0,0,0,0,.25],168:[0,.69444,.11473,0,.59111],176:[0,.69444,0,0,.94888],184:[.17014,0,0,0,.53222],198:[0,.68611,.11431,0,1.02277],216:[.04861,.73472,.09062,0,.88555],223:[.19444,.69444,.09736,0,.665],230:[0,.44444,.085,0,.82666],248:[.09722,.54167,.09458,0,.59111],305:[0,.44444,.09426,0,.35555],338:[0,.68611,.11431,0,1.14054],339:[0,.44444,.085,0,.82666],567:[.19444,.44444,.04611,0,.385],710:[0,.69444,.06709,0,.59111],711:[0,.63194,.08271,0,.59111],713:[0,.59444,.10444,0,.59111],714:[0,.69444,.08528,0,.59111],715:[0,.69444,0,0,.59111],728:[0,.69444,.10333,0,.59111],729:[0,.69444,.12945,0,.35555],730:[0,.69444,0,0,.94888],732:[0,.69444,.11472,0,.59111],733:[0,.69444,.11472,0,.59111],915:[0,.68611,.12903,0,.69777],916:[0,.68611,0,0,.94444],920:[0,.68611,.09062,0,.88555],923:[0,.68611,0,0,.80666],926:[0,.68611,.15092,0,.76777],928:[0,.68611,.17208,0,.8961],931:[0,.68611,.11431,0,.82666],933:[0,.68611,.10778,0,.88555],934:[0,.68611,.05632,0,.82666],936:[0,.68611,.10778,0,.88555],937:[0,.68611,.0992,0,.82666],8211:[0,.44444,.09811,0,.59111],8212:[0,.44444,.09811,0,1.18221],8216:[0,.69444,.12945,0,.35555],8217:[0,.69444,.12945,0,.35555],8220:[0,.69444,.16772,0,.62055],8221:[0,.69444,.07939,0,.62055]},"Main-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.12417,0,.30667],34:[0,.69444,.06961,0,.51444],35:[.19444,.69444,.06616,0,.81777],37:[.05556,.75,.13639,0,.81777],38:[0,.69444,.09694,0,.76666],39:[0,.69444,.12417,0,.30667],40:[.25,.75,.16194,0,.40889],41:[.25,.75,.03694,0,.40889],42:[0,.75,.14917,0,.51111],43:[.05667,.56167,.03694,0,.76666],44:[.19444,.10556,0,0,.30667],45:[0,.43056,.02826,0,.35778],46:[0,.10556,0,0,.30667],47:[.25,.75,.16194,0,.51111],48:[0,.64444,.13556,0,.51111],49:[0,.64444,.13556,0,.51111],50:[0,.64444,.13556,0,.51111],51:[0,.64444,.13556,0,.51111],52:[.19444,.64444,.13556,0,.51111],53:[0,.64444,.13556,0,.51111],54:[0,.64444,.13556,0,.51111],55:[.19444,.64444,.13556,0,.51111],56:[0,.64444,.13556,0,.51111],57:[0,.64444,.13556,0,.51111],58:[0,.43056,.0582,0,.30667],59:[.19444,.43056,.0582,0,.30667],61:[-.13313,.36687,.06616,0,.76666],63:[0,.69444,.1225,0,.51111],64:[0,.69444,.09597,0,.76666],65:[0,.68333,0,0,.74333],66:[0,.68333,.10257,0,.70389],67:[0,.68333,.14528,0,.71555],68:[0,.68333,.09403,0,.755],69:[0,.68333,.12028,0,.67833],70:[0,.68333,.13305,0,.65277],71:[0,.68333,.08722,0,.77361],72:[0,.68333,.16389,0,.74333],73:[0,.68333,.15806,0,.38555],74:[0,.68333,.14028,0,.525],75:[0,.68333,.14528,0,.76888],76:[0,.68333,0,0,.62722],77:[0,.68333,.16389,0,.89666],78:[0,.68333,.16389,0,.74333],79:[0,.68333,.09403,0,.76666],80:[0,.68333,.10257,0,.67833],81:[.19444,.68333,.09403,0,.76666],82:[0,.68333,.03868,0,.72944],83:[0,.68333,.11972,0,.56222],84:[0,.68333,.13305,0,.71555],85:[0,.68333,.16389,0,.74333],86:[0,.68333,.18361,0,.74333],87:[0,.68333,.18361,0,.99888],88:[0,.68333,.15806,0,.74333],89:[0,.68333,.19383,0,.74333],90:[0,.68333,.14528,0,.61333],91:[.25,.75,.1875,0,.30667],93:[.25,.75,.10528,0,.30667],94:[0,.69444,.06646,0,.51111],95:[.31,.12056,.09208,0,.51111],97:[0,.43056,.07671,0,.51111],98:[0,.69444,.06312,0,.46],99:[0,.43056,.05653,0,.46],100:[0,.69444,.10333,0,.51111],101:[0,.43056,.07514,0,.46],102:[.19444,.69444,.21194,0,.30667],103:[.19444,.43056,.08847,0,.46],104:[0,.69444,.07671,0,.51111],105:[0,.65536,.1019,0,.30667],106:[.19444,.65536,.14467,0,.30667],107:[0,.69444,.10764,0,.46],108:[0,.69444,.10333,0,.25555],109:[0,.43056,.07671,0,.81777],110:[0,.43056,.07671,0,.56222],111:[0,.43056,.06312,0,.51111],112:[.19444,.43056,.06312,0,.51111],113:[.19444,.43056,.08847,0,.46],114:[0,.43056,.10764,0,.42166],115:[0,.43056,.08208,0,.40889],116:[0,.61508,.09486,0,.33222],117:[0,.43056,.07671,0,.53666],118:[0,.43056,.10764,0,.46],119:[0,.43056,.10764,0,.66444],120:[0,.43056,.12042,0,.46389],121:[.19444,.43056,.08847,0,.48555],122:[0,.43056,.12292,0,.40889],126:[.35,.31786,.11585,0,.51111],160:[0,0,0,0,.25],168:[0,.66786,.10474,0,.51111],176:[0,.69444,0,0,.83129],184:[.17014,0,0,0,.46],198:[0,.68333,.12028,0,.88277],216:[.04861,.73194,.09403,0,.76666],223:[.19444,.69444,.10514,0,.53666],230:[0,.43056,.07514,0,.71555],248:[.09722,.52778,.09194,0,.51111],338:[0,.68333,.12028,0,.98499],339:[0,.43056,.07514,0,.71555],710:[0,.69444,.06646,0,.51111],711:[0,.62847,.08295,0,.51111],713:[0,.56167,.10333,0,.51111],714:[0,.69444,.09694,0,.51111],715:[0,.69444,0,0,.51111],728:[0,.69444,.10806,0,.51111],729:[0,.66786,.11752,0,.30667],730:[0,.69444,0,0,.83129],732:[0,.66786,.11585,0,.51111],733:[0,.69444,.1225,0,.51111],915:[0,.68333,.13305,0,.62722],916:[0,.68333,0,0,.81777],920:[0,.68333,.09403,0,.76666],923:[0,.68333,0,0,.69222],926:[0,.68333,.15294,0,.66444],928:[0,.68333,.16389,0,.74333],931:[0,.68333,.12028,0,.71555],933:[0,.68333,.11111,0,.76666],934:[0,.68333,.05986,0,.71555],936:[0,.68333,.11111,0,.76666],937:[0,.68333,.10257,0,.71555],8211:[0,.43056,.09208,0,.51111],8212:[0,.43056,.09208,0,1.02222],8216:[0,.69444,.12417,0,.30667],8217:[0,.69444,.12417,0,.30667],8220:[0,.69444,.1685,0,.51444],8221:[0,.69444,.06961,0,.51444],8463:[0,.68889,0,0,.54028]},"Main-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.27778],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.77778],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.19444,.10556,0,0,.27778],45:[0,.43056,0,0,.33333],46:[0,.10556,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.64444,0,0,.5],49:[0,.64444,0,0,.5],50:[0,.64444,0,0,.5],51:[0,.64444,0,0,.5],52:[0,.64444,0,0,.5],53:[0,.64444,0,0,.5],54:[0,.64444,0,0,.5],55:[0,.64444,0,0,.5],56:[0,.64444,0,0,.5],57:[0,.64444,0,0,.5],58:[0,.43056,0,0,.27778],59:[.19444,.43056,0,0,.27778],60:[.0391,.5391,0,0,.77778],61:[-.13313,.36687,0,0,.77778],62:[.0391,.5391,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.77778],65:[0,.68333,0,0,.75],66:[0,.68333,0,0,.70834],67:[0,.68333,0,0,.72222],68:[0,.68333,0,0,.76389],69:[0,.68333,0,0,.68056],70:[0,.68333,0,0,.65278],71:[0,.68333,0,0,.78472],72:[0,.68333,0,0,.75],73:[0,.68333,0,0,.36111],74:[0,.68333,0,0,.51389],75:[0,.68333,0,0,.77778],76:[0,.68333,0,0,.625],77:[0,.68333,0,0,.91667],78:[0,.68333,0,0,.75],79:[0,.68333,0,0,.77778],80:[0,.68333,0,0,.68056],81:[.19444,.68333,0,0,.77778],82:[0,.68333,0,0,.73611],83:[0,.68333,0,0,.55556],84:[0,.68333,0,0,.72222],85:[0,.68333,0,0,.75],86:[0,.68333,.01389,0,.75],87:[0,.68333,.01389,0,1.02778],88:[0,.68333,0,0,.75],89:[0,.68333,.025,0,.75],90:[0,.68333,0,0,.61111],91:[.25,.75,0,0,.27778],92:[.25,.75,0,0,.5],93:[.25,.75,0,0,.27778],94:[0,.69444,0,0,.5],95:[.31,.12056,.02778,0,.5],97:[0,.43056,0,0,.5],98:[0,.69444,0,0,.55556],99:[0,.43056,0,0,.44445],100:[0,.69444,0,0,.55556],101:[0,.43056,0,0,.44445],102:[0,.69444,.07778,0,.30556],103:[.19444,.43056,.01389,0,.5],104:[0,.69444,0,0,.55556],105:[0,.66786,0,0,.27778],106:[.19444,.66786,0,0,.30556],107:[0,.69444,0,0,.52778],108:[0,.69444,0,0,.27778],109:[0,.43056,0,0,.83334],110:[0,.43056,0,0,.55556],111:[0,.43056,0,0,.5],112:[.19444,.43056,0,0,.55556],113:[.19444,.43056,0,0,.52778],114:[0,.43056,0,0,.39167],115:[0,.43056,0,0,.39445],116:[0,.61508,0,0,.38889],117:[0,.43056,0,0,.55556],118:[0,.43056,.01389,0,.52778],119:[0,.43056,.01389,0,.72222],120:[0,.43056,0,0,.52778],121:[.19444,.43056,.01389,0,.52778],122:[0,.43056,0,0,.44445],123:[.25,.75,0,0,.5],124:[.25,.75,0,0,.27778],125:[.25,.75,0,0,.5],126:[.35,.31786,0,0,.5],160:[0,0,0,0,.25],163:[0,.69444,0,0,.76909],167:[.19444,.69444,0,0,.44445],168:[0,.66786,0,0,.5],172:[0,.43056,0,0,.66667],176:[0,.69444,0,0,.75],177:[.08333,.58333,0,0,.77778],182:[.19444,.69444,0,0,.61111],184:[.17014,0,0,0,.44445],198:[0,.68333,0,0,.90278],215:[.08333,.58333,0,0,.77778],216:[.04861,.73194,0,0,.77778],223:[0,.69444,0,0,.5],230:[0,.43056,0,0,.72222],247:[.08333,.58333,0,0,.77778],248:[.09722,.52778,0,0,.5],305:[0,.43056,0,0,.27778],338:[0,.68333,0,0,1.01389],339:[0,.43056,0,0,.77778],567:[.19444,.43056,0,0,.30556],710:[0,.69444,0,0,.5],711:[0,.62847,0,0,.5],713:[0,.56778,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.66786,0,0,.27778],730:[0,.69444,0,0,.75],732:[0,.66786,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.68333,0,0,.625],916:[0,.68333,0,0,.83334],920:[0,.68333,0,0,.77778],923:[0,.68333,0,0,.69445],926:[0,.68333,0,0,.66667],928:[0,.68333,0,0,.75],931:[0,.68333,0,0,.72222],933:[0,.68333,0,0,.77778],934:[0,.68333,0,0,.72222],936:[0,.68333,0,0,.77778],937:[0,.68333,0,0,.72222],8211:[0,.43056,.02778,0,.5],8212:[0,.43056,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5],8224:[.19444,.69444,0,0,.44445],8225:[.19444,.69444,0,0,.44445],8230:[0,.123,0,0,1.172],8242:[0,.55556,0,0,.275],8407:[0,.71444,.15382,0,.5],8463:[0,.68889,0,0,.54028],8465:[0,.69444,0,0,.72222],8467:[0,.69444,0,.11111,.41667],8472:[.19444,.43056,0,.11111,.63646],8476:[0,.69444,0,0,.72222],8501:[0,.69444,0,0,.61111],8592:[-.13313,.36687,0,0,1],8593:[.19444,.69444,0,0,.5],8594:[-.13313,.36687,0,0,1],8595:[.19444,.69444,0,0,.5],8596:[-.13313,.36687,0,0,1],8597:[.25,.75,0,0,.5],8598:[.19444,.69444,0,0,1],8599:[.19444,.69444,0,0,1],8600:[.19444,.69444,0,0,1],8601:[.19444,.69444,0,0,1],8614:[.011,.511,0,0,1],8617:[.011,.511,0,0,1.126],8618:[.011,.511,0,0,1.126],8636:[-.13313,.36687,0,0,1],8637:[-.13313,.36687,0,0,1],8640:[-.13313,.36687,0,0,1],8641:[-.13313,.36687,0,0,1],8652:[.011,.671,0,0,1],8656:[-.13313,.36687,0,0,1],8657:[.19444,.69444,0,0,.61111],8658:[-.13313,.36687,0,0,1],8659:[.19444,.69444,0,0,.61111],8660:[-.13313,.36687,0,0,1],8661:[.25,.75,0,0,.61111],8704:[0,.69444,0,0,.55556],8706:[0,.69444,.05556,.08334,.5309],8707:[0,.69444,0,0,.55556],8709:[.05556,.75,0,0,.5],8711:[0,.68333,0,0,.83334],8712:[.0391,.5391,0,0,.66667],8715:[.0391,.5391,0,0,.66667],8722:[.08333,.58333,0,0,.77778],8723:[.08333,.58333,0,0,.77778],8725:[.25,.75,0,0,.5],8726:[.25,.75,0,0,.5],8727:[-.03472,.46528,0,0,.5],8728:[-.05555,.44445,0,0,.5],8729:[-.05555,.44445,0,0,.5],8730:[.2,.8,0,0,.83334],8733:[0,.43056,0,0,.77778],8734:[0,.43056,0,0,1],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.27778],8741:[.25,.75,0,0,.5],8743:[0,.55556,0,0,.66667],8744:[0,.55556,0,0,.66667],8745:[0,.55556,0,0,.66667],8746:[0,.55556,0,0,.66667],8747:[.19444,.69444,.11111,0,.41667],8764:[-.13313,.36687,0,0,.77778],8768:[.19444,.69444,0,0,.27778],8771:[-.03625,.46375,0,0,.77778],8773:[-.022,.589,0,0,.778],8776:[-.01688,.48312,0,0,.77778],8781:[-.03625,.46375,0,0,.77778],8784:[-.133,.673,0,0,.778],8801:[-.03625,.46375,0,0,.77778],8804:[.13597,.63597,0,0,.77778],8805:[.13597,.63597,0,0,.77778],8810:[.0391,.5391,0,0,1],8811:[.0391,.5391,0,0,1],8826:[.0391,.5391,0,0,.77778],8827:[.0391,.5391,0,0,.77778],8834:[.0391,.5391,0,0,.77778],8835:[.0391,.5391,0,0,.77778],8838:[.13597,.63597,0,0,.77778],8839:[.13597,.63597,0,0,.77778],8846:[0,.55556,0,0,.66667],8849:[.13597,.63597,0,0,.77778],8850:[.13597,.63597,0,0,.77778],8851:[0,.55556,0,0,.66667],8852:[0,.55556,0,0,.66667],8853:[.08333,.58333,0,0,.77778],8854:[.08333,.58333,0,0,.77778],8855:[.08333,.58333,0,0,.77778],8856:[.08333,.58333,0,0,.77778],8857:[.08333,.58333,0,0,.77778],8866:[0,.69444,0,0,.61111],8867:[0,.69444,0,0,.61111],8868:[0,.69444,0,0,.77778],8869:[0,.69444,0,0,.77778],8872:[.249,.75,0,0,.867],8900:[-.05555,.44445,0,0,.5],8901:[-.05555,.44445,0,0,.27778],8902:[-.03472,.46528,0,0,.5],8904:[.005,.505,0,0,.9],8942:[.03,.903,0,0,.278],8943:[-.19,.313,0,0,1.172],8945:[-.1,.823,0,0,1.282],8968:[.25,.75,0,0,.44445],8969:[.25,.75,0,0,.44445],8970:[.25,.75,0,0,.44445],8971:[.25,.75,0,0,.44445],8994:[-.14236,.35764,0,0,1],8995:[-.14236,.35764,0,0,1],9136:[.244,.744,0,0,.412],9137:[.244,.745,0,0,.412],9651:[.19444,.69444,0,0,.88889],9657:[-.03472,.46528,0,0,.5],9661:[.19444,.69444,0,0,.88889],9667:[-.03472,.46528,0,0,.5],9711:[.19444,.69444,0,0,1],9824:[.12963,.69444,0,0,.77778],9825:[.12963,.69444,0,0,.77778],9826:[.12963,.69444,0,0,.77778],9827:[.12963,.69444,0,0,.77778],9837:[0,.75,0,0,.38889],9838:[.19444,.69444,0,0,.38889],9839:[.19444,.69444,0,0,.38889],10216:[.25,.75,0,0,.38889],10217:[.25,.75,0,0,.38889],10222:[.244,.744,0,0,.412],10223:[.244,.745,0,0,.412],10229:[.011,.511,0,0,1.609],10230:[.011,.511,0,0,1.638],10231:[.011,.511,0,0,1.859],10232:[.024,.525,0,0,1.609],10233:[.024,.525,0,0,1.638],10234:[.024,.525,0,0,1.858],10236:[.011,.511,0,0,1.638],10815:[0,.68333,0,0,.75],10927:[.13597,.63597,0,0,.77778],10928:[.13597,.63597,0,0,.77778],57376:[.19444,.69444,0,0,0]},"Math-BoldItalic":{32:[0,0,0,0,.25],48:[0,.44444,0,0,.575],49:[0,.44444,0,0,.575],50:[0,.44444,0,0,.575],51:[.19444,.44444,0,0,.575],52:[.19444,.44444,0,0,.575],53:[.19444,.44444,0,0,.575],54:[0,.64444,0,0,.575],55:[.19444,.44444,0,0,.575],56:[0,.64444,0,0,.575],57:[.19444,.44444,0,0,.575],65:[0,.68611,0,0,.86944],66:[0,.68611,.04835,0,.8664],67:[0,.68611,.06979,0,.81694],68:[0,.68611,.03194,0,.93812],69:[0,.68611,.05451,0,.81007],70:[0,.68611,.15972,0,.68889],71:[0,.68611,0,0,.88673],72:[0,.68611,.08229,0,.98229],73:[0,.68611,.07778,0,.51111],74:[0,.68611,.10069,0,.63125],75:[0,.68611,.06979,0,.97118],76:[0,.68611,0,0,.75555],77:[0,.68611,.11424,0,1.14201],78:[0,.68611,.11424,0,.95034],79:[0,.68611,.03194,0,.83666],80:[0,.68611,.15972,0,.72309],81:[.19444,.68611,0,0,.86861],82:[0,.68611,.00421,0,.87235],83:[0,.68611,.05382,0,.69271],84:[0,.68611,.15972,0,.63663],85:[0,.68611,.11424,0,.80027],86:[0,.68611,.25555,0,.67778],87:[0,.68611,.15972,0,1.09305],88:[0,.68611,.07778,0,.94722],89:[0,.68611,.25555,0,.67458],90:[0,.68611,.06979,0,.77257],97:[0,.44444,0,0,.63287],98:[0,.69444,0,0,.52083],99:[0,.44444,0,0,.51342],100:[0,.69444,0,0,.60972],101:[0,.44444,0,0,.55361],102:[.19444,.69444,.11042,0,.56806],103:[.19444,.44444,.03704,0,.5449],104:[0,.69444,0,0,.66759],105:[0,.69326,0,0,.4048],106:[.19444,.69326,.0622,0,.47083],107:[0,.69444,.01852,0,.6037],108:[0,.69444,.0088,0,.34815],109:[0,.44444,0,0,1.0324],110:[0,.44444,0,0,.71296],111:[0,.44444,0,0,.58472],112:[.19444,.44444,0,0,.60092],113:[.19444,.44444,.03704,0,.54213],114:[0,.44444,.03194,0,.5287],115:[0,.44444,0,0,.53125],116:[0,.63492,0,0,.41528],117:[0,.44444,0,0,.68102],118:[0,.44444,.03704,0,.56666],119:[0,.44444,.02778,0,.83148],120:[0,.44444,0,0,.65903],121:[.19444,.44444,.03704,0,.59028],122:[0,.44444,.04213,0,.55509],160:[0,0,0,0,.25],915:[0,.68611,.15972,0,.65694],916:[0,.68611,0,0,.95833],920:[0,.68611,.03194,0,.86722],923:[0,.68611,0,0,.80555],926:[0,.68611,.07458,0,.84125],928:[0,.68611,.08229,0,.98229],931:[0,.68611,.05451,0,.88507],933:[0,.68611,.15972,0,.67083],934:[0,.68611,0,0,.76666],936:[0,.68611,.11653,0,.71402],937:[0,.68611,.04835,0,.8789],945:[0,.44444,0,0,.76064],946:[.19444,.69444,.03403,0,.65972],947:[.19444,.44444,.06389,0,.59003],948:[0,.69444,.03819,0,.52222],949:[0,.44444,0,0,.52882],950:[.19444,.69444,.06215,0,.50833],951:[.19444,.44444,.03704,0,.6],952:[0,.69444,.03194,0,.5618],953:[0,.44444,0,0,.41204],954:[0,.44444,0,0,.66759],955:[0,.69444,0,0,.67083],956:[.19444,.44444,0,0,.70787],957:[0,.44444,.06898,0,.57685],958:[.19444,.69444,.03021,0,.50833],959:[0,.44444,0,0,.58472],960:[0,.44444,.03704,0,.68241],961:[.19444,.44444,0,0,.6118],962:[.09722,.44444,.07917,0,.42361],963:[0,.44444,.03704,0,.68588],964:[0,.44444,.13472,0,.52083],965:[0,.44444,.03704,0,.63055],966:[.19444,.44444,0,0,.74722],967:[.19444,.44444,0,0,.71805],968:[.19444,.69444,.03704,0,.75833],969:[0,.44444,.03704,0,.71782],977:[0,.69444,0,0,.69155],981:[.19444,.69444,0,0,.7125],982:[0,.44444,.03194,0,.975],1009:[.19444,.44444,0,0,.6118],1013:[0,.44444,0,0,.48333],57649:[0,.44444,0,0,.39352],57911:[.19444,.44444,0,0,.43889]},"Math-Italic":{32:[0,0,0,0,.25],48:[0,.43056,0,0,.5],49:[0,.43056,0,0,.5],50:[0,.43056,0,0,.5],51:[.19444,.43056,0,0,.5],52:[.19444,.43056,0,0,.5],53:[.19444,.43056,0,0,.5],54:[0,.64444,0,0,.5],55:[.19444,.43056,0,0,.5],56:[0,.64444,0,0,.5],57:[.19444,.43056,0,0,.5],65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],160:[0,0,0,0,.25],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059],57649:[0,.43056,0,.02778,.32246],57911:[.19444,.43056,0,.08334,.38403]},"SansSerif-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.36667],34:[0,.69444,0,0,.55834],35:[.19444,.69444,0,0,.91667],36:[.05556,.75,0,0,.55],37:[.05556,.75,0,0,1.02912],38:[0,.69444,0,0,.83056],39:[0,.69444,0,0,.30556],40:[.25,.75,0,0,.42778],41:[.25,.75,0,0,.42778],42:[0,.75,0,0,.55],43:[.11667,.61667,0,0,.85556],44:[.10556,.13056,0,0,.30556],45:[0,.45833,0,0,.36667],46:[0,.13056,0,0,.30556],47:[.25,.75,0,0,.55],48:[0,.69444,0,0,.55],49:[0,.69444,0,0,.55],50:[0,.69444,0,0,.55],51:[0,.69444,0,0,.55],52:[0,.69444,0,0,.55],53:[0,.69444,0,0,.55],54:[0,.69444,0,0,.55],55:[0,.69444,0,0,.55],56:[0,.69444,0,0,.55],57:[0,.69444,0,0,.55],58:[0,.45833,0,0,.30556],59:[.10556,.45833,0,0,.30556],61:[-.09375,.40625,0,0,.85556],63:[0,.69444,0,0,.51945],64:[0,.69444,0,0,.73334],65:[0,.69444,0,0,.73334],66:[0,.69444,0,0,.73334],67:[0,.69444,0,0,.70278],68:[0,.69444,0,0,.79445],69:[0,.69444,0,0,.64167],70:[0,.69444,0,0,.61111],71:[0,.69444,0,0,.73334],72:[0,.69444,0,0,.79445],73:[0,.69444,0,0,.33056],74:[0,.69444,0,0,.51945],75:[0,.69444,0,0,.76389],76:[0,.69444,0,0,.58056],77:[0,.69444,0,0,.97778],78:[0,.69444,0,0,.79445],79:[0,.69444,0,0,.79445],80:[0,.69444,0,0,.70278],81:[.10556,.69444,0,0,.79445],82:[0,.69444,0,0,.70278],83:[0,.69444,0,0,.61111],84:[0,.69444,0,0,.73334],85:[0,.69444,0,0,.76389],86:[0,.69444,.01528,0,.73334],87:[0,.69444,.01528,0,1.03889],88:[0,.69444,0,0,.73334],89:[0,.69444,.0275,0,.73334],90:[0,.69444,0,0,.67223],91:[.25,.75,0,0,.34306],93:[.25,.75,0,0,.34306],94:[0,.69444,0,0,.55],95:[.35,.10833,.03056,0,.55],97:[0,.45833,0,0,.525],98:[0,.69444,0,0,.56111],99:[0,.45833,0,0,.48889],100:[0,.69444,0,0,.56111],101:[0,.45833,0,0,.51111],102:[0,.69444,.07639,0,.33611],103:[.19444,.45833,.01528,0,.55],104:[0,.69444,0,0,.56111],105:[0,.69444,0,0,.25556],106:[.19444,.69444,0,0,.28611],107:[0,.69444,0,0,.53056],108:[0,.69444,0,0,.25556],109:[0,.45833,0,0,.86667],110:[0,.45833,0,0,.56111],111:[0,.45833,0,0,.55],112:[.19444,.45833,0,0,.56111],113:[.19444,.45833,0,0,.56111],114:[0,.45833,.01528,0,.37222],115:[0,.45833,0,0,.42167],116:[0,.58929,0,0,.40417],117:[0,.45833,0,0,.56111],118:[0,.45833,.01528,0,.5],119:[0,.45833,.01528,0,.74445],120:[0,.45833,0,0,.5],121:[.19444,.45833,.01528,0,.5],122:[0,.45833,0,0,.47639],126:[.35,.34444,0,0,.55],160:[0,0,0,0,.25],168:[0,.69444,0,0,.55],176:[0,.69444,0,0,.73334],180:[0,.69444,0,0,.55],184:[.17014,0,0,0,.48889],305:[0,.45833,0,0,.25556],567:[.19444,.45833,0,0,.28611],710:[0,.69444,0,0,.55],711:[0,.63542,0,0,.55],713:[0,.63778,0,0,.55],728:[0,.69444,0,0,.55],729:[0,.69444,0,0,.30556],730:[0,.69444,0,0,.73334],732:[0,.69444,0,0,.55],733:[0,.69444,0,0,.55],915:[0,.69444,0,0,.58056],916:[0,.69444,0,0,.91667],920:[0,.69444,0,0,.85556],923:[0,.69444,0,0,.67223],926:[0,.69444,0,0,.73334],928:[0,.69444,0,0,.79445],931:[0,.69444,0,0,.79445],933:[0,.69444,0,0,.85556],934:[0,.69444,0,0,.79445],936:[0,.69444,0,0,.85556],937:[0,.69444,0,0,.79445],8211:[0,.45833,.03056,0,.55],8212:[0,.45833,.03056,0,1.10001],8216:[0,.69444,0,0,.30556],8217:[0,.69444,0,0,.30556],8220:[0,.69444,0,0,.55834],8221:[0,.69444,0,0,.55834]},"SansSerif-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.05733,0,.31945],34:[0,.69444,.00316,0,.5],35:[.19444,.69444,.05087,0,.83334],36:[.05556,.75,.11156,0,.5],37:[.05556,.75,.03126,0,.83334],38:[0,.69444,.03058,0,.75834],39:[0,.69444,.07816,0,.27778],40:[.25,.75,.13164,0,.38889],41:[.25,.75,.02536,0,.38889],42:[0,.75,.11775,0,.5],43:[.08333,.58333,.02536,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,.01946,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,.13164,0,.5],48:[0,.65556,.11156,0,.5],49:[0,.65556,.11156,0,.5],50:[0,.65556,.11156,0,.5],51:[0,.65556,.11156,0,.5],52:[0,.65556,.11156,0,.5],53:[0,.65556,.11156,0,.5],54:[0,.65556,.11156,0,.5],55:[0,.65556,.11156,0,.5],56:[0,.65556,.11156,0,.5],57:[0,.65556,.11156,0,.5],58:[0,.44444,.02502,0,.27778],59:[.125,.44444,.02502,0,.27778],61:[-.13,.37,.05087,0,.77778],63:[0,.69444,.11809,0,.47222],64:[0,.69444,.07555,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,.08293,0,.66667],67:[0,.69444,.11983,0,.63889],68:[0,.69444,.07555,0,.72223],69:[0,.69444,.11983,0,.59722],70:[0,.69444,.13372,0,.56945],71:[0,.69444,.11983,0,.66667],72:[0,.69444,.08094,0,.70834],73:[0,.69444,.13372,0,.27778],74:[0,.69444,.08094,0,.47222],75:[0,.69444,.11983,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,.08094,0,.875],78:[0,.69444,.08094,0,.70834],79:[0,.69444,.07555,0,.73611],80:[0,.69444,.08293,0,.63889],81:[.125,.69444,.07555,0,.73611],82:[0,.69444,.08293,0,.64584],83:[0,.69444,.09205,0,.55556],84:[0,.69444,.13372,0,.68056],85:[0,.69444,.08094,0,.6875],86:[0,.69444,.1615,0,.66667],87:[0,.69444,.1615,0,.94445],88:[0,.69444,.13372,0,.66667],89:[0,.69444,.17261,0,.66667],90:[0,.69444,.11983,0,.61111],91:[.25,.75,.15942,0,.28889],93:[.25,.75,.08719,0,.28889],94:[0,.69444,.0799,0,.5],95:[.35,.09444,.08616,0,.5],97:[0,.44444,.00981,0,.48056],98:[0,.69444,.03057,0,.51667],99:[0,.44444,.08336,0,.44445],100:[0,.69444,.09483,0,.51667],101:[0,.44444,.06778,0,.44445],102:[0,.69444,.21705,0,.30556],103:[.19444,.44444,.10836,0,.5],104:[0,.69444,.01778,0,.51667],105:[0,.67937,.09718,0,.23889],106:[.19444,.67937,.09162,0,.26667],107:[0,.69444,.08336,0,.48889],108:[0,.69444,.09483,0,.23889],109:[0,.44444,.01778,0,.79445],110:[0,.44444,.01778,0,.51667],111:[0,.44444,.06613,0,.5],112:[.19444,.44444,.0389,0,.51667],113:[.19444,.44444,.04169,0,.51667],114:[0,.44444,.10836,0,.34167],115:[0,.44444,.0778,0,.38333],116:[0,.57143,.07225,0,.36111],117:[0,.44444,.04169,0,.51667],118:[0,.44444,.10836,0,.46111],119:[0,.44444,.10836,0,.68334],120:[0,.44444,.09169,0,.46111],121:[.19444,.44444,.10836,0,.46111],122:[0,.44444,.08752,0,.43472],126:[.35,.32659,.08826,0,.5],160:[0,0,0,0,.25],168:[0,.67937,.06385,0,.5],176:[0,.69444,0,0,.73752],184:[.17014,0,0,0,.44445],305:[0,.44444,.04169,0,.23889],567:[.19444,.44444,.04169,0,.26667],710:[0,.69444,.0799,0,.5],711:[0,.63194,.08432,0,.5],713:[0,.60889,.08776,0,.5],714:[0,.69444,.09205,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,.09483,0,.5],729:[0,.67937,.07774,0,.27778],730:[0,.69444,0,0,.73752],732:[0,.67659,.08826,0,.5],733:[0,.69444,.09205,0,.5],915:[0,.69444,.13372,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,.07555,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,.12816,0,.66667],928:[0,.69444,.08094,0,.70834],931:[0,.69444,.11983,0,.72222],933:[0,.69444,.09031,0,.77778],934:[0,.69444,.04603,0,.72222],936:[0,.69444,.09031,0,.77778],937:[0,.69444,.08293,0,.72222],8211:[0,.44444,.08616,0,.5],8212:[0,.44444,.08616,0,1],8216:[0,.69444,.07816,0,.27778],8217:[0,.69444,.07816,0,.27778],8220:[0,.69444,.14205,0,.5],8221:[0,.69444,.00316,0,.5]},"SansSerif-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.31945],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.75834],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,0,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.65556,0,0,.5],49:[0,.65556,0,0,.5],50:[0,.65556,0,0,.5],51:[0,.65556,0,0,.5],52:[0,.65556,0,0,.5],53:[0,.65556,0,0,.5],54:[0,.65556,0,0,.5],55:[0,.65556,0,0,.5],56:[0,.65556,0,0,.5],57:[0,.65556,0,0,.5],58:[0,.44444,0,0,.27778],59:[.125,.44444,0,0,.27778],61:[-.13,.37,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,0,0,.66667],67:[0,.69444,0,0,.63889],68:[0,.69444,0,0,.72223],69:[0,.69444,0,0,.59722],70:[0,.69444,0,0,.56945],71:[0,.69444,0,0,.66667],72:[0,.69444,0,0,.70834],73:[0,.69444,0,0,.27778],74:[0,.69444,0,0,.47222],75:[0,.69444,0,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,0,0,.875],78:[0,.69444,0,0,.70834],79:[0,.69444,0,0,.73611],80:[0,.69444,0,0,.63889],81:[.125,.69444,0,0,.73611],82:[0,.69444,0,0,.64584],83:[0,.69444,0,0,.55556],84:[0,.69444,0,0,.68056],85:[0,.69444,0,0,.6875],86:[0,.69444,.01389,0,.66667],87:[0,.69444,.01389,0,.94445],88:[0,.69444,0,0,.66667],89:[0,.69444,.025,0,.66667],90:[0,.69444,0,0,.61111],91:[.25,.75,0,0,.28889],93:[.25,.75,0,0,.28889],94:[0,.69444,0,0,.5],95:[.35,.09444,.02778,0,.5],97:[0,.44444,0,0,.48056],98:[0,.69444,0,0,.51667],99:[0,.44444,0,0,.44445],100:[0,.69444,0,0,.51667],101:[0,.44444,0,0,.44445],102:[0,.69444,.06944,0,.30556],103:[.19444,.44444,.01389,0,.5],104:[0,.69444,0,0,.51667],105:[0,.67937,0,0,.23889],106:[.19444,.67937,0,0,.26667],107:[0,.69444,0,0,.48889],108:[0,.69444,0,0,.23889],109:[0,.44444,0,0,.79445],110:[0,.44444,0,0,.51667],111:[0,.44444,0,0,.5],112:[.19444,.44444,0,0,.51667],113:[.19444,.44444,0,0,.51667],114:[0,.44444,.01389,0,.34167],115:[0,.44444,0,0,.38333],116:[0,.57143,0,0,.36111],117:[0,.44444,0,0,.51667],118:[0,.44444,.01389,0,.46111],119:[0,.44444,.01389,0,.68334],120:[0,.44444,0,0,.46111],121:[.19444,.44444,.01389,0,.46111],122:[0,.44444,0,0,.43472],126:[.35,.32659,0,0,.5],160:[0,0,0,0,.25],168:[0,.67937,0,0,.5],176:[0,.69444,0,0,.66667],184:[.17014,0,0,0,.44445],305:[0,.44444,0,0,.23889],567:[.19444,.44444,0,0,.26667],710:[0,.69444,0,0,.5],711:[0,.63194,0,0,.5],713:[0,.60889,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.67937,0,0,.27778],730:[0,.69444,0,0,.66667],732:[0,.67659,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.69444,0,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,0,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,0,0,.66667],928:[0,.69444,0,0,.70834],931:[0,.69444,0,0,.72222],933:[0,.69444,0,0,.77778],934:[0,.69444,0,0,.72222],936:[0,.69444,0,0,.77778],937:[0,.69444,0,0,.72222],8211:[0,.44444,.02778,0,.5],8212:[0,.44444,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5]},"Script-Regular":{32:[0,0,0,0,.25],65:[0,.7,.22925,0,.80253],66:[0,.7,.04087,0,.90757],67:[0,.7,.1689,0,.66619],68:[0,.7,.09371,0,.77443],69:[0,.7,.18583,0,.56162],70:[0,.7,.13634,0,.89544],71:[0,.7,.17322,0,.60961],72:[0,.7,.29694,0,.96919],73:[0,.7,.19189,0,.80907],74:[.27778,.7,.19189,0,1.05159],75:[0,.7,.31259,0,.91364],76:[0,.7,.19189,0,.87373],77:[0,.7,.15981,0,1.08031],78:[0,.7,.3525,0,.9015],79:[0,.7,.08078,0,.73787],80:[0,.7,.08078,0,1.01262],81:[0,.7,.03305,0,.88282],82:[0,.7,.06259,0,.85],83:[0,.7,.19189,0,.86767],84:[0,.7,.29087,0,.74697],85:[0,.7,.25815,0,.79996],86:[0,.7,.27523,0,.62204],87:[0,.7,.27523,0,.80532],88:[0,.7,.26006,0,.94445],89:[0,.7,.2939,0,.70961],90:[0,.7,.24037,0,.8212],160:[0,0,0,0,.25]},"Size1-Regular":{32:[0,0,0,0,.25],40:[.35001,.85,0,0,.45834],41:[.35001,.85,0,0,.45834],47:[.35001,.85,0,0,.57778],91:[.35001,.85,0,0,.41667],92:[.35001,.85,0,0,.57778],93:[.35001,.85,0,0,.41667],123:[.35001,.85,0,0,.58334],125:[.35001,.85,0,0,.58334],160:[0,0,0,0,.25],710:[0,.72222,0,0,.55556],732:[0,.72222,0,0,.55556],770:[0,.72222,0,0,.55556],771:[0,.72222,0,0,.55556],8214:[-99e-5,.601,0,0,.77778],8593:[1e-5,.6,0,0,.66667],8595:[1e-5,.6,0,0,.66667],8657:[1e-5,.6,0,0,.77778],8659:[1e-5,.6,0,0,.77778],8719:[.25001,.75,0,0,.94445],8720:[.25001,.75,0,0,.94445],8721:[.25001,.75,0,0,1.05556],8730:[.35001,.85,0,0,1],8739:[-.00599,.606,0,0,.33333],8741:[-.00599,.606,0,0,.55556],8747:[.30612,.805,.19445,0,.47222],8748:[.306,.805,.19445,0,.47222],8749:[.306,.805,.19445,0,.47222],8750:[.30612,.805,.19445,0,.47222],8896:[.25001,.75,0,0,.83334],8897:[.25001,.75,0,0,.83334],8898:[.25001,.75,0,0,.83334],8899:[.25001,.75,0,0,.83334],8968:[.35001,.85,0,0,.47222],8969:[.35001,.85,0,0,.47222],8970:[.35001,.85,0,0,.47222],8971:[.35001,.85,0,0,.47222],9168:[-99e-5,.601,0,0,.66667],10216:[.35001,.85,0,0,.47222],10217:[.35001,.85,0,0,.47222],10752:[.25001,.75,0,0,1.11111],10753:[.25001,.75,0,0,1.11111],10754:[.25001,.75,0,0,1.11111],10756:[.25001,.75,0,0,.83334],10758:[.25001,.75,0,0,.83334]},"Size2-Regular":{32:[0,0,0,0,.25],40:[.65002,1.15,0,0,.59722],41:[.65002,1.15,0,0,.59722],47:[.65002,1.15,0,0,.81111],91:[.65002,1.15,0,0,.47222],92:[.65002,1.15,0,0,.81111],93:[.65002,1.15,0,0,.47222],123:[.65002,1.15,0,0,.66667],125:[.65002,1.15,0,0,.66667],160:[0,0,0,0,.25],710:[0,.75,0,0,1],732:[0,.75,0,0,1],770:[0,.75,0,0,1],771:[0,.75,0,0,1],8719:[.55001,1.05,0,0,1.27778],8720:[.55001,1.05,0,0,1.27778],8721:[.55001,1.05,0,0,1.44445],8730:[.65002,1.15,0,0,1],8747:[.86225,1.36,.44445,0,.55556],8748:[.862,1.36,.44445,0,.55556],8749:[.862,1.36,.44445,0,.55556],8750:[.86225,1.36,.44445,0,.55556],8896:[.55001,1.05,0,0,1.11111],8897:[.55001,1.05,0,0,1.11111],8898:[.55001,1.05,0,0,1.11111],8899:[.55001,1.05,0,0,1.11111],8968:[.65002,1.15,0,0,.52778],8969:[.65002,1.15,0,0,.52778],8970:[.65002,1.15,0,0,.52778],8971:[.65002,1.15,0,0,.52778],10216:[.65002,1.15,0,0,.61111],10217:[.65002,1.15,0,0,.61111],10752:[.55001,1.05,0,0,1.51112],10753:[.55001,1.05,0,0,1.51112],10754:[.55001,1.05,0,0,1.51112],10756:[.55001,1.05,0,0,1.11111],10758:[.55001,1.05,0,0,1.11111]},"Size3-Regular":{32:[0,0,0,0,.25],40:[.95003,1.45,0,0,.73611],41:[.95003,1.45,0,0,.73611],47:[.95003,1.45,0,0,1.04445],91:[.95003,1.45,0,0,.52778],92:[.95003,1.45,0,0,1.04445],93:[.95003,1.45,0,0,.52778],123:[.95003,1.45,0,0,.75],125:[.95003,1.45,0,0,.75],160:[0,0,0,0,.25],710:[0,.75,0,0,1.44445],732:[0,.75,0,0,1.44445],770:[0,.75,0,0,1.44445],771:[0,.75,0,0,1.44445],8730:[.95003,1.45,0,0,1],8968:[.95003,1.45,0,0,.58334],8969:[.95003,1.45,0,0,.58334],8970:[.95003,1.45,0,0,.58334],8971:[.95003,1.45,0,0,.58334],10216:[.95003,1.45,0,0,.75],10217:[.95003,1.45,0,0,.75]},"Size4-Regular":{32:[0,0,0,0,.25],40:[1.25003,1.75,0,0,.79167],41:[1.25003,1.75,0,0,.79167],47:[1.25003,1.75,0,0,1.27778],91:[1.25003,1.75,0,0,.58334],92:[1.25003,1.75,0,0,1.27778],93:[1.25003,1.75,0,0,.58334],123:[1.25003,1.75,0,0,.80556],125:[1.25003,1.75,0,0,.80556],160:[0,0,0,0,.25],710:[0,.825,0,0,1.8889],732:[0,.825,0,0,1.8889],770:[0,.825,0,0,1.8889],771:[0,.825,0,0,1.8889],8730:[1.25003,1.75,0,0,1],8968:[1.25003,1.75,0,0,.63889],8969:[1.25003,1.75,0,0,.63889],8970:[1.25003,1.75,0,0,.63889],8971:[1.25003,1.75,0,0,.63889],9115:[.64502,1.155,0,0,.875],9116:[1e-5,.6,0,0,.875],9117:[.64502,1.155,0,0,.875],9118:[.64502,1.155,0,0,.875],9119:[1e-5,.6,0,0,.875],9120:[.64502,1.155,0,0,.875],9121:[.64502,1.155,0,0,.66667],9122:[-99e-5,.601,0,0,.66667],9123:[.64502,1.155,0,0,.66667],9124:[.64502,1.155,0,0,.66667],9125:[-99e-5,.601,0,0,.66667],9126:[.64502,1.155,0,0,.66667],9127:[1e-5,.9,0,0,.88889],9128:[.65002,1.15,0,0,.88889],9129:[.90001,0,0,0,.88889],9130:[0,.3,0,0,.88889],9131:[1e-5,.9,0,0,.88889],9132:[.65002,1.15,0,0,.88889],9133:[.90001,0,0,0,.88889],9143:[.88502,.915,0,0,1.05556],10216:[1.25003,1.75,0,0,.80556],10217:[1.25003,1.75,0,0,.80556],57344:[-.00499,.605,0,0,1.05556],57345:[-.00499,.605,0,0,1.05556],57680:[0,.12,0,0,.45],57681:[0,.12,0,0,.45],57682:[0,.12,0,0,.45],57683:[0,.12,0,0,.45]},"Typewriter-Regular":{32:[0,0,0,0,.525],33:[0,.61111,0,0,.525],34:[0,.61111,0,0,.525],35:[0,.61111,0,0,.525],36:[.08333,.69444,0,0,.525],37:[.08333,.69444,0,0,.525],38:[0,.61111,0,0,.525],39:[0,.61111,0,0,.525],40:[.08333,.69444,0,0,.525],41:[.08333,.69444,0,0,.525],42:[0,.52083,0,0,.525],43:[-.08056,.53055,0,0,.525],44:[.13889,.125,0,0,.525],45:[-.08056,.53055,0,0,.525],46:[0,.125,0,0,.525],47:[.08333,.69444,0,0,.525],48:[0,.61111,0,0,.525],49:[0,.61111,0,0,.525],50:[0,.61111,0,0,.525],51:[0,.61111,0,0,.525],52:[0,.61111,0,0,.525],53:[0,.61111,0,0,.525],54:[0,.61111,0,0,.525],55:[0,.61111,0,0,.525],56:[0,.61111,0,0,.525],57:[0,.61111,0,0,.525],58:[0,.43056,0,0,.525],59:[.13889,.43056,0,0,.525],60:[-.05556,.55556,0,0,.525],61:[-.19549,.41562,0,0,.525],62:[-.05556,.55556,0,0,.525],63:[0,.61111,0,0,.525],64:[0,.61111,0,0,.525],65:[0,.61111,0,0,.525],66:[0,.61111,0,0,.525],67:[0,.61111,0,0,.525],68:[0,.61111,0,0,.525],69:[0,.61111,0,0,.525],70:[0,.61111,0,0,.525],71:[0,.61111,0,0,.525],72:[0,.61111,0,0,.525],73:[0,.61111,0,0,.525],74:[0,.61111,0,0,.525],75:[0,.61111,0,0,.525],76:[0,.61111,0,0,.525],77:[0,.61111,0,0,.525],78:[0,.61111,0,0,.525],79:[0,.61111,0,0,.525],80:[0,.61111,0,0,.525],81:[.13889,.61111,0,0,.525],82:[0,.61111,0,0,.525],83:[0,.61111,0,0,.525],84:[0,.61111,0,0,.525],85:[0,.61111,0,0,.525],86:[0,.61111,0,0,.525],87:[0,.61111,0,0,.525],88:[0,.61111,0,0,.525],89:[0,.61111,0,0,.525],90:[0,.61111,0,0,.525],91:[.08333,.69444,0,0,.525],92:[.08333,.69444,0,0,.525],93:[.08333,.69444,0,0,.525],94:[0,.61111,0,0,.525],95:[.09514,0,0,0,.525],96:[0,.61111,0,0,.525],97:[0,.43056,0,0,.525],98:[0,.61111,0,0,.525],99:[0,.43056,0,0,.525],100:[0,.61111,0,0,.525],101:[0,.43056,0,0,.525],102:[0,.61111,0,0,.525],103:[.22222,.43056,0,0,.525],104:[0,.61111,0,0,.525],105:[0,.61111,0,0,.525],106:[.22222,.61111,0,0,.525],107:[0,.61111,0,0,.525],108:[0,.61111,0,0,.525],109:[0,.43056,0,0,.525],110:[0,.43056,0,0,.525],111:[0,.43056,0,0,.525],112:[.22222,.43056,0,0,.525],113:[.22222,.43056,0,0,.525],114:[0,.43056,0,0,.525],115:[0,.43056,0,0,.525],116:[0,.55358,0,0,.525],117:[0,.43056,0,0,.525],118:[0,.43056,0,0,.525],119:[0,.43056,0,0,.525],120:[0,.43056,0,0,.525],121:[.22222,.43056,0,0,.525],122:[0,.43056,0,0,.525],123:[.08333,.69444,0,0,.525],124:[.08333,.69444,0,0,.525],125:[.08333,.69444,0,0,.525],126:[0,.61111,0,0,.525],127:[0,.61111,0,0,.525],160:[0,0,0,0,.525],176:[0,.61111,0,0,.525],184:[.19445,0,0,0,.525],305:[0,.43056,0,0,.525],567:[.22222,.43056,0,0,.525],711:[0,.56597,0,0,.525],713:[0,.56555,0,0,.525],714:[0,.61111,0,0,.525],715:[0,.61111,0,0,.525],728:[0,.61111,0,0,.525],730:[0,.61111,0,0,.525],770:[0,.61111,0,0,.525],771:[0,.61111,0,0,.525],776:[0,.61111,0,0,.525],915:[0,.61111,0,0,.525],916:[0,.61111,0,0,.525],920:[0,.61111,0,0,.525],923:[0,.61111,0,0,.525],926:[0,.61111,0,0,.525],928:[0,.61111,0,0,.525],931:[0,.61111,0,0,.525],933:[0,.61111,0,0,.525],934:[0,.61111,0,0,.525],936:[0,.61111,0,0,.525],937:[0,.61111,0,0,.525],8216:[0,.61111,0,0,.525],8217:[0,.61111,0,0,.525],8242:[0,.61111,0,0,.525],9251:[.11111,.21944,0,0,.525]}},Xx={slant:[.25,.25,.25],space:[0,0,0],stretch:[0,0,0],shrink:[0,0,0],xHeight:[.431,.431,.431],quad:[1,1.171,1.472],extraSpace:[0,0,0],num1:[.677,.732,.925],num2:[.394,.384,.387],num3:[.444,.471,.504],denom1:[.686,.752,1.025],denom2:[.345,.344,.532],sup1:[.413,.503,.504],sup2:[.363,.431,.404],sup3:[.289,.286,.294],sub1:[.15,.143,.2],sub2:[.247,.286,.4],supDrop:[.386,.353,.494],subDrop:[.05,.071,.1],delim1:[2.39,1.7,1.98],delim2:[1.01,1.157,1.42],axisHeight:[.25,.25,.25],defaultRuleThickness:[.04,.049,.049],bigOpSpacing1:[.111,.111,.111],bigOpSpacing2:[.166,.166,.166],bigOpSpacing3:[.2,.2,.2],bigOpSpacing4:[.6,.611,.611],bigOpSpacing5:[.1,.143,.143],sqrtRuleThickness:[.04,.04,.04],ptPerEm:[10,10,10],doubleRuleSep:[.2,.2,.2],arrayRuleWidth:[.04,.04,.04],fboxsep:[.3,.3,.3],fboxrule:[.04,.04,.04]},eN={Å:"A",Ð:"D",Þ:"o",å:"a",ð:"d",þ:"o",А:"A",Б:"B",В:"B",Г:"F",Д:"A",Е:"E",Ж:"K",З:"3",И:"N",Й:"N",К:"K",Л:"N",М:"M",Н:"H",О:"O",П:"N",Р:"P",С:"C",Т:"T",У:"y",Ф:"O",Х:"X",Ц:"U",Ч:"h",Ш:"W",Щ:"W",Ъ:"B",Ы:"X",Ь:"B",Э:"3",Ю:"X",Я:"R",а:"a",б:"b",в:"a",г:"r",д:"y",е:"e",ж:"m",з:"e",и:"n",й:"n",к:"n",л:"n",м:"m",н:"n",о:"o",п:"n",р:"p",с:"c",т:"o",у:"y",ф:"b",х:"x",ц:"n",ч:"n",ш:"w",щ:"w",ъ:"a",ы:"m",ь:"a",э:"e",ю:"m",я:"r"},s(zD,"setFontMetrics"),s(SE,"getCharacterMetrics"),PE={},s(xme,"getGlobalMetrics"),Yn={math:{},text:{}},s(ne,"defineSymbol"),ce="math",Ft="text",_e="main",tt="ams",Hn="accent-token",yr="bin",ro="close",Y0="inner",Vr="mathord",Aa="op-token",rl="open",uy="punct",rt="rel",Au="spacing",st="textord",ne(ce,_e,rt,"≡","\\equiv",!0),ne(ce,_e,rt,"≺","\\prec",!0),ne(ce,_e,rt,"≻","\\succ",!0),ne(ce,_e,rt,"∼","\\sim",!0),ne(ce,_e,rt,"⊥","\\perp"),ne(ce,_e,rt,"⪯","\\preceq",!0),ne(ce,_e,rt,"⪰","\\succeq",!0),ne(ce,_e,rt,"≃","\\simeq",!0),ne(ce,_e,rt,"∣","\\mid",!0),ne(ce,_e,rt,"≪","\\ll",!0),ne(ce,_e,rt,"≫","\\gg",!0),ne(ce,_e,rt,"≍","\\asymp",!0),ne(ce,_e,rt,"∥","\\parallel"),ne(ce,_e,rt,"⋈","\\bowtie",!0),ne(ce,_e,rt,"⌣","\\smile",!0),ne(ce,_e,rt,"⊑","\\sqsubseteq",!0),ne(ce,_e,rt,"⊒","\\sqsupseteq",!0),ne(ce,_e,rt,"≐","\\doteq",!0),ne(ce,_e,rt,"⌢","\\frown",!0),ne(ce,_e,rt,"∋","\\ni",!0),ne(ce,_e,rt,"∝","\\propto",!0),ne(ce,_e,rt,"⊢","\\vdash",!0),ne(ce,_e,rt,"⊣","\\dashv",!0),ne(ce,_e,rt,"∋","\\owns"),ne(ce,_e,uy,".","\\ldotp"),ne(ce,_e,uy,"⋅","\\cdotp"),ne(ce,_e,uy,"⋅","·"),ne(Ft,_e,st,"⋅","·"),ne(ce,_e,st,"#","\\#"),ne(Ft,_e,st,"#","\\#"),ne(ce,_e,st,"&","\\&"),ne(Ft,_e,st,"&","\\&"),ne(ce,_e,st,"ℵ","\\aleph",!0),ne(ce,_e,st,"∀","\\forall",!0),ne(ce,_e,st,"ℏ","\\hbar",!0),ne(ce,_e,st,"∃","\\exists",!0),ne(ce,_e,st,"∇","\\nabla",!0),ne(ce,_e,st,"♭","\\flat",!0),ne(ce,_e,st,"ℓ","\\ell",!0),ne(ce,_e,st,"♮","\\natural",!0),ne(ce,_e,st,"♣","\\clubsuit",!0),ne(ce,_e,st,"℘","\\wp",!0),ne(ce,_e,st,"♯","\\sharp",!0),ne(ce,_e,st,"♢","\\diamondsuit",!0),ne(ce,_e,st,"ℜ","\\Re",!0),ne(ce,_e,st,"♡","\\heartsuit",!0),ne(ce,_e,st,"ℑ","\\Im",!0),ne(ce,_e,st,"♠","\\spadesuit",!0),ne(ce,_e,st,"§","\\S",!0),ne(Ft,_e,st,"§","\\S"),ne(ce,_e,st,"¶","\\P",!0),ne(Ft,_e,st,"¶","\\P"),ne(ce,_e,st,"†","\\dag"),ne(Ft,_e,st,"†","\\dag"),ne(Ft,_e,st,"†","\\textdagger"),ne(ce,_e,st,"‡","\\ddag"),ne(Ft,_e,st,"‡","\\ddag"),ne(Ft,_e,st,"‡","\\textdaggerdbl"),ne(ce,_e,ro,"⎱","\\rmoustache",!0),ne(ce,_e,rl,"⎰","\\lmoustache",!0),ne(ce,_e,ro,"⟯","\\rgroup",!0),ne(ce,_e,rl,"⟮","\\lgroup",!0),ne(ce,_e,yr,"∓","\\mp",!0),ne(ce,_e,yr,"⊖","\\ominus",!0),ne(ce,_e,yr,"⊎","\\uplus",!0),ne(ce,_e,yr,"⊓","\\sqcap",!0),ne(ce,_e,yr,"∗","\\ast"),ne(ce,_e,yr,"⊔","\\sqcup",!0),ne(ce,_e,yr,"◯","\\bigcirc",!0),ne(ce,_e,yr,"∙","\\bullet",!0),ne(ce,_e,yr,"‡","\\ddagger"),ne(ce,_e,yr,"≀","\\wr",!0),ne(ce,_e,yr,"⨿","\\amalg"),ne(ce,_e,yr,"&","\\And"),ne(ce,_e,rt,"⟵","\\longleftarrow",!0),ne(ce,_e,rt,"⇐","\\Leftarrow",!0),ne(ce,_e,rt,"⟸","\\Longleftarrow",!0),ne(ce,_e,rt,"⟶","\\longrightarrow",!0),ne(ce,_e,rt,"⇒","\\Rightarrow",!0),ne(ce,_e,rt,"⟹","\\Longrightarrow",!0),ne(ce,_e,rt,"↔","\\leftrightarrow",!0),ne(ce,_e,rt,"⟷","\\longleftrightarrow",!0),ne(ce,_e,rt,"⇔","\\Leftrightarrow",!0),ne(ce,_e,rt,"⟺","\\Longleftrightarrow",!0),ne(ce,_e,rt,"↦","\\mapsto",!0),ne(ce,_e,rt,"⟼","\\longmapsto",!0),ne(ce,_e,rt,"↗","\\nearrow",!0),ne(ce,_e,rt,"↩","\\hookleftarrow",!0),ne(ce,_e,rt,"↪","\\hookrightarrow",!0),ne(ce,_e,rt,"↘","\\searrow",!0),ne(ce,_e,rt,"↼","\\leftharpoonup",!0),ne(ce,_e,rt,"⇀","\\rightharpoonup",!0),ne(ce,_e,rt,"↙","\\swarrow",!0),ne(ce,_e,rt,"↽","\\leftharpoondown",!0),ne(ce,_e,rt,"⇁","\\rightharpoondown",!0),ne(ce,_e,rt,"↖","\\nwarrow",!0),ne(ce,_e,rt,"⇌","\\rightleftharpoons",!0),ne(ce,tt,rt,"≮","\\nless",!0),ne(ce,tt,rt,"","\\@nleqslant"),ne(ce,tt,rt,"","\\@nleqq"),ne(ce,tt,rt,"⪇","\\lneq",!0),ne(ce,tt,rt,"≨","\\lneqq",!0),ne(ce,tt,rt,"","\\@lvertneqq"),ne(ce,tt,rt,"⋦","\\lnsim",!0),ne(ce,tt,rt,"⪉","\\lnapprox",!0),ne(ce,tt,rt,"⊀","\\nprec",!0),ne(ce,tt,rt,"⋠","\\npreceq",!0),ne(ce,tt,rt,"⋨","\\precnsim",!0),ne(ce,tt,rt,"⪹","\\precnapprox",!0),ne(ce,tt,rt,"≁","\\nsim",!0),ne(ce,tt,rt,"","\\@nshortmid"),ne(ce,tt,rt,"∤","\\nmid",!0),ne(ce,tt,rt,"⊬","\\nvdash",!0),ne(ce,tt,rt,"⊭","\\nvDash",!0),ne(ce,tt,rt,"⋪","\\ntriangleleft"),ne(ce,tt,rt,"⋬","\\ntrianglelefteq",!0),ne(ce,tt,rt,"⊊","\\subsetneq",!0),ne(ce,tt,rt,"","\\@varsubsetneq"),ne(ce,tt,rt,"⫋","\\subsetneqq",!0),ne(ce,tt,rt,"","\\@varsubsetneqq"),ne(ce,tt,rt,"≯","\\ngtr",!0),ne(ce,tt,rt,"","\\@ngeqslant"),ne(ce,tt,rt,"","\\@ngeqq"),ne(ce,tt,rt,"⪈","\\gneq",!0),ne(ce,tt,rt,"≩","\\gneqq",!0),ne(ce,tt,rt,"","\\@gvertneqq"),ne(ce,tt,rt,"⋧","\\gnsim",!0),ne(ce,tt,rt,"⪊","\\gnapprox",!0),ne(ce,tt,rt,"⊁","\\nsucc",!0),ne(ce,tt,rt,"⋡","\\nsucceq",!0),ne(ce,tt,rt,"⋩","\\succnsim",!0),ne(ce,tt,rt,"⪺","\\succnapprox",!0),ne(ce,tt,rt,"≆","\\ncong",!0),ne(ce,tt,rt,"","\\@nshortparallel"),ne(ce,tt,rt,"∦","\\nparallel",!0),ne(ce,tt,rt,"⊯","\\nVDash",!0),ne(ce,tt,rt,"⋫","\\ntriangleright"),ne(ce,tt,rt,"⋭","\\ntrianglerighteq",!0),ne(ce,tt,rt,"","\\@nsupseteqq"),ne(ce,tt,rt,"⊋","\\supsetneq",!0),ne(ce,tt,rt,"","\\@varsupsetneq"),ne(ce,tt,rt,"⫌","\\supsetneqq",!0),ne(ce,tt,rt,"","\\@varsupsetneqq"),ne(ce,tt,rt,"⊮","\\nVdash",!0),ne(ce,tt,rt,"⪵","\\precneqq",!0),ne(ce,tt,rt,"⪶","\\succneqq",!0),ne(ce,tt,rt,"","\\@nsubseteqq"),ne(ce,tt,yr,"⊴","\\unlhd"),ne(ce,tt,yr,"⊵","\\unrhd"),ne(ce,tt,rt,"↚","\\nleftarrow",!0),ne(ce,tt,rt,"↛","\\nrightarrow",!0),ne(ce,tt,rt,"⇍","\\nLeftarrow",!0),ne(ce,tt,rt,"⇏","\\nRightarrow",!0),ne(ce,tt,rt,"↮","\\nleftrightarrow",!0),ne(ce,tt,rt,"⇎","\\nLeftrightarrow",!0),ne(ce,tt,rt,"△","\\vartriangle"),ne(ce,tt,st,"ℏ","\\hslash"),ne(ce,tt,st,"▽","\\triangledown"),ne(ce,tt,st,"◊","\\lozenge"),ne(ce,tt,st,"Ⓢ","\\circledS"),ne(ce,tt,st,"®","\\circledR"),ne(Ft,tt,st,"®","\\circledR"),ne(ce,tt,st,"∡","\\measuredangle",!0),ne(ce,tt,st,"∄","\\nexists"),ne(ce,tt,st,"℧","\\mho"),ne(ce,tt,st,"Ⅎ","\\Finv",!0),ne(ce,tt,st,"⅁","\\Game",!0),ne(ce,tt,st,"‵","\\backprime"),ne(ce,tt,st,"▲","\\blacktriangle"),ne(ce,tt,st,"▼","\\blacktriangledown"),ne(ce,tt,st,"■","\\blacksquare"),ne(ce,tt,st,"⧫","\\blacklozenge"),ne(ce,tt,st,"★","\\bigstar"),ne(ce,tt,st,"∢","\\sphericalangle",!0),ne(ce,tt,st,"∁","\\complement",!0),ne(ce,tt,st,"ð","\\eth",!0),ne(Ft,_e,st,"ð","ð"),ne(ce,tt,st,"╱","\\diagup"),ne(ce,tt,st,"╲","\\diagdown"),ne(ce,tt,st,"□","\\square"),ne(ce,tt,st,"□","\\Box"),ne(ce,tt,st,"◊","\\Diamond"),ne(ce,tt,st,"¥","\\yen",!0),ne(Ft,tt,st,"¥","\\yen",!0),ne(ce,tt,st,"✓","\\checkmark",!0),ne(Ft,tt,st,"✓","\\checkmark"),ne(ce,tt,st,"ℶ","\\beth",!0),ne(ce,tt,st,"ℸ","\\daleth",!0),ne(ce,tt,st,"ℷ","\\gimel",!0),ne(ce,tt,st,"ϝ","\\digamma",!0),ne(ce,tt,st,"ϰ","\\varkappa"),ne(ce,tt,rl,"┌","\\@ulcorner",!0),ne(ce,tt,ro,"┐","\\@urcorner",!0),ne(ce,tt,rl,"└","\\@llcorner",!0),ne(ce,tt,ro,"┘","\\@lrcorner",!0),ne(ce,tt,rt,"≦","\\leqq",!0),ne(ce,tt,rt,"⩽","\\leqslant",!0),ne(ce,tt,rt,"⪕","\\eqslantless",!0),ne(ce,tt,rt,"≲","\\lesssim",!0),ne(ce,tt,rt,"⪅","\\lessapprox",!0),ne(ce,tt,rt,"≊","\\approxeq",!0),ne(ce,tt,yr,"⋖","\\lessdot"),ne(ce,tt,rt,"⋘","\\lll",!0),ne(ce,tt,rt,"≶","\\lessgtr",!0),ne(ce,tt,rt,"⋚","\\lesseqgtr",!0),ne(ce,tt,rt,"⪋","\\lesseqqgtr",!0),ne(ce,tt,rt,"≑","\\doteqdot"),ne(ce,tt,rt,"≓","\\risingdotseq",!0),ne(ce,tt,rt,"≒","\\fallingdotseq",!0),ne(ce,tt,rt,"∽","\\backsim",!0),ne(ce,tt,rt,"⋍","\\backsimeq",!0),ne(ce,tt,rt,"⫅","\\subseteqq",!0),ne(ce,tt,rt,"⋐","\\Subset",!0),ne(ce,tt,rt,"⊏","\\sqsubset",!0),ne(ce,tt,rt,"≼","\\preccurlyeq",!0),ne(ce,tt,rt,"⋞","\\curlyeqprec",!0),ne(ce,tt,rt,"≾","\\precsim",!0),ne(ce,tt,rt,"⪷","\\precapprox",!0),ne(ce,tt,rt,"⊲","\\vartriangleleft"),ne(ce,tt,rt,"⊴","\\trianglelefteq"),ne(ce,tt,rt,"⊨","\\vDash",!0),ne(ce,tt,rt,"⊪","\\Vvdash",!0),ne(ce,tt,rt,"⌣","\\smallsmile"),ne(ce,tt,rt,"⌢","\\smallfrown"),ne(ce,tt,rt,"≏","\\bumpeq",!0),ne(ce,tt,rt,"≎","\\Bumpeq",!0),ne(ce,tt,rt,"≧","\\geqq",!0),ne(ce,tt,rt,"⩾","\\geqslant",!0),ne(ce,tt,rt,"⪖","\\eqslantgtr",!0),ne(ce,tt,rt,"≳","\\gtrsim",!0),ne(ce,tt,rt,"⪆","\\gtrapprox",!0),ne(ce,tt,yr,"⋗","\\gtrdot"),ne(ce,tt,rt,"⋙","\\ggg",!0),ne(ce,tt,rt,"≷","\\gtrless",!0),ne(ce,tt,rt,"⋛","\\gtreqless",!0),ne(ce,tt,rt,"⪌","\\gtreqqless",!0),ne(ce,tt,rt,"≖","\\eqcirc",!0),ne(ce,tt,rt,"≗","\\circeq",!0),ne(ce,tt,rt,"≜","\\triangleq",!0),ne(ce,tt,rt,"∼","\\thicksim"),ne(ce,tt,rt,"≈","\\thickapprox"),ne(ce,tt,rt,"⫆","\\supseteqq",!0),ne(ce,tt,rt,"⋑","\\Supset",!0),ne(ce,tt,rt,"⊐","\\sqsupset",!0),ne(ce,tt,rt,"≽","\\succcurlyeq",!0),ne(ce,tt,rt,"⋟","\\curlyeqsucc",!0),ne(ce,tt,rt,"≿","\\succsim",!0),ne(ce,tt,rt,"⪸","\\succapprox",!0),ne(ce,tt,rt,"⊳","\\vartriangleright"),ne(ce,tt,rt,"⊵","\\trianglerighteq"),ne(ce,tt,rt,"⊩","\\Vdash",!0),ne(ce,tt,rt,"∣","\\shortmid"),ne(ce,tt,rt,"∥","\\shortparallel"),ne(ce,tt,rt,"≬","\\between",!0),ne(ce,tt,rt,"⋔","\\pitchfork",!0),ne(ce,tt,rt,"∝","\\varpropto"),ne(ce,tt,rt,"◀","\\blacktriangleleft"),ne(ce,tt,rt,"∴","\\therefore",!0),ne(ce,tt,rt,"∍","\\backepsilon"),ne(ce,tt,rt,"▶","\\blacktriangleright"),ne(ce,tt,rt,"∵","\\because",!0),ne(ce,tt,rt,"⋘","\\llless"),ne(ce,tt,rt,"⋙","\\gggtr"),ne(ce,tt,yr,"⊲","\\lhd"),ne(ce,tt,yr,"⊳","\\rhd"),ne(ce,tt,rt,"≂","\\eqsim",!0),ne(ce,_e,rt,"⋈","\\Join"),ne(ce,tt,rt,"≑","\\Doteq",!0),ne(ce,tt,yr,"∔","\\dotplus",!0),ne(ce,tt,yr,"∖","\\smallsetminus"),ne(ce,tt,yr,"⋒","\\Cap",!0),ne(ce,tt,yr,"⋓","\\Cup",!0),ne(ce,tt,yr,"⩞","\\doublebarwedge",!0),ne(ce,tt,yr,"⊟","\\boxminus",!0),ne(ce,tt,yr,"⊞","\\boxplus",!0),ne(ce,tt,yr,"⋇","\\divideontimes",!0),ne(ce,tt,yr,"⋉","\\ltimes",!0),ne(ce,tt,yr,"⋊","\\rtimes",!0),ne(ce,tt,yr,"⋋","\\leftthreetimes",!0),ne(ce,tt,yr,"⋌","\\rightthreetimes",!0),ne(ce,tt,yr,"⋏","\\curlywedge",!0),ne(ce,tt,yr,"⋎","\\curlyvee",!0),ne(ce,tt,yr,"⊝","\\circleddash",!0),ne(ce,tt,yr,"⊛","\\circledast",!0),ne(ce,tt,yr,"⋅","\\centerdot"),ne(ce,tt,yr,"⊺","\\intercal",!0),ne(ce,tt,yr,"⋒","\\doublecap"),ne(ce,tt,yr,"⋓","\\doublecup"),ne(ce,tt,yr,"⊠","\\boxtimes",!0),ne(ce,tt,rt,"⇢","\\dashrightarrow",!0),ne(ce,tt,rt,"⇠","\\dashleftarrow",!0),ne(ce,tt,rt,"⇇","\\leftleftarrows",!0),ne(ce,tt,rt,"⇆","\\leftrightarrows",!0),ne(ce,tt,rt,"⇚","\\Lleftarrow",!0),ne(ce,tt,rt,"↞","\\twoheadleftarrow",!0),ne(ce,tt,rt,"↢","\\leftarrowtail",!0),ne(ce,tt,rt,"↫","\\looparrowleft",!0),ne(ce,tt,rt,"⇋","\\leftrightharpoons",!0),ne(ce,tt,rt,"↶","\\curvearrowleft",!0),ne(ce,tt,rt,"↺","\\circlearrowleft",!0),ne(ce,tt,rt,"↰","\\Lsh",!0),ne(ce,tt,rt,"⇈","\\upuparrows",!0),ne(ce,tt,rt,"↿","\\upharpoonleft",!0),ne(ce,tt,rt,"⇃","\\downharpoonleft",!0),ne(ce,_e,rt,"⊶","\\origof",!0),ne(ce,_e,rt,"⊷","\\imageof",!0),ne(ce,tt,rt,"⊸","\\multimap",!0),ne(ce,tt,rt,"↭","\\leftrightsquigarrow",!0),ne(ce,tt,rt,"⇉","\\rightrightarrows",!0),ne(ce,tt,rt,"⇄","\\rightleftarrows",!0),ne(ce,tt,rt,"↠","\\twoheadrightarrow",!0),ne(ce,tt,rt,"↣","\\rightarrowtail",!0),ne(ce,tt,rt,"↬","\\looparrowright",!0),ne(ce,tt,rt,"↷","\\curvearrowright",!0),ne(ce,tt,rt,"↻","\\circlearrowright",!0),ne(ce,tt,rt,"↱","\\Rsh",!0),ne(ce,tt,rt,"⇊","\\downdownarrows",!0),ne(ce,tt,rt,"↾","\\upharpoonright",!0),ne(ce,tt,rt,"⇂","\\downharpoonright",!0),ne(ce,tt,rt,"⇝","\\rightsquigarrow",!0),ne(ce,tt,rt,"⇝","\\leadsto"),ne(ce,tt,rt,"⇛","\\Rrightarrow",!0),ne(ce,tt,rt,"↾","\\restriction"),ne(ce,_e,st,"‘","`"),ne(ce,_e,st,"$","\\$"),ne(Ft,_e,st,"$","\\$"),ne(Ft,_e,st,"$","\\textdollar"),ne(ce,_e,st,"%","\\%"),ne(Ft,_e,st,"%","\\%"),ne(ce,_e,st,"_","\\_"),ne(Ft,_e,st,"_","\\_"),ne(Ft,_e,st,"_","\\textunderscore"),ne(ce,_e,st,"∠","\\angle",!0),ne(ce,_e,st,"∞","\\infty",!0),ne(ce,_e,st,"′","\\prime"),ne(ce,_e,st,"△","\\triangle"),ne(ce,_e,st,"Γ","\\Gamma",!0),ne(ce,_e,st,"Δ","\\Delta",!0),ne(ce,_e,st,"Θ","\\Theta",!0),ne(ce,_e,st,"Λ","\\Lambda",!0),ne(ce,_e,st,"Ξ","\\Xi",!0),ne(ce,_e,st,"Π","\\Pi",!0),ne(ce,_e,st,"Σ","\\Sigma",!0),ne(ce,_e,st,"Υ","\\Upsilon",!0),ne(ce,_e,st,"Φ","\\Phi",!0),ne(ce,_e,st,"Ψ","\\Psi",!0),ne(ce,_e,st,"Ω","\\Omega",!0),ne(ce,_e,st,"A","Α"),ne(ce,_e,st,"B","Β"),ne(ce,_e,st,"E","Ε"),ne(ce,_e,st,"Z","Ζ"),ne(ce,_e,st,"H","Η"),ne(ce,_e,st,"I","Ι"),ne(ce,_e,st,"K","Κ"),ne(ce,_e,st,"M","Μ"),ne(ce,_e,st,"N","Ν"),ne(ce,_e,st,"O","Ο"),ne(ce,_e,st,"P","Ρ"),ne(ce,_e,st,"T","Τ"),ne(ce,_e,st,"X","Χ"),ne(ce,_e,st,"¬","\\neg",!0),ne(ce,_e,st,"¬","\\lnot"),ne(ce,_e,st,"⊤","\\top"),ne(ce,_e,st,"⊥","\\bot"),ne(ce,_e,st,"∅","\\emptyset"),ne(ce,tt,st,"∅","\\varnothing"),ne(ce,_e,Vr,"α","\\alpha",!0),ne(ce,_e,Vr,"β","\\beta",!0),ne(ce,_e,Vr,"γ","\\gamma",!0),ne(ce,_e,Vr,"δ","\\delta",!0),ne(ce,_e,Vr,"ϵ","\\epsilon",!0),ne(ce,_e,Vr,"ζ","\\zeta",!0),ne(ce,_e,Vr,"η","\\eta",!0),ne(ce,_e,Vr,"θ","\\theta",!0),ne(ce,_e,Vr,"ι","\\iota",!0),ne(ce,_e,Vr,"κ","\\kappa",!0),ne(ce,_e,Vr,"λ","\\lambda",!0),ne(ce,_e,Vr,"μ","\\mu",!0),ne(ce,_e,Vr,"ν","\\nu",!0),ne(ce,_e,Vr,"ξ","\\xi",!0),ne(ce,_e,Vr,"ο","\\omicron",!0),ne(ce,_e,Vr,"π","\\pi",!0),ne(ce,_e,Vr,"ρ","\\rho",!0),ne(ce,_e,Vr,"σ","\\sigma",!0),ne(ce,_e,Vr,"τ","\\tau",!0),ne(ce,_e,Vr,"υ","\\upsilon",!0),ne(ce,_e,Vr,"ϕ","\\phi",!0),ne(ce,_e,Vr,"χ","\\chi",!0),ne(ce,_e,Vr,"ψ","\\psi",!0),ne(ce,_e,Vr,"ω","\\omega",!0),ne(ce,_e,Vr,"ε","\\varepsilon",!0),ne(ce,_e,Vr,"ϑ","\\vartheta",!0),ne(ce,_e,Vr,"ϖ","\\varpi",!0),ne(ce,_e,Vr,"ϱ","\\varrho",!0),ne(ce,_e,Vr,"ς","\\varsigma",!0),ne(ce,_e,Vr,"φ","\\varphi",!0),ne(ce,_e,yr,"∗","*",!0),ne(ce,_e,yr,"+","+"),ne(ce,_e,yr,"−","-",!0),ne(ce,_e,yr,"⋅","\\cdot",!0),ne(ce,_e,yr,"∘","\\circ",!0),ne(ce,_e,yr,"÷","\\div",!0),ne(ce,_e,yr,"±","\\pm",!0),ne(ce,_e,yr,"×","\\times",!0),ne(ce,_e,yr,"∩","\\cap",!0),ne(ce,_e,yr,"∪","\\cup",!0),ne(ce,_e,yr,"∖","\\setminus",!0),ne(ce,_e,yr,"∧","\\land"),ne(ce,_e,yr,"∨","\\lor"),ne(ce,_e,yr,"∧","\\wedge",!0),ne(ce,_e,yr,"∨","\\vee",!0),ne(ce,_e,st,"√","\\surd"),ne(ce,_e,rl,"⟨","\\langle",!0),ne(ce,_e,rl,"∣","\\lvert"),ne(ce,_e,rl,"∥","\\lVert"),ne(ce,_e,ro,"?","?"),ne(ce,_e,ro,"!","!"),ne(ce,_e,ro,"⟩","\\rangle",!0),ne(ce,_e,ro,"∣","\\rvert"),ne(ce,_e,ro,"∥","\\rVert"),ne(ce,_e,rt,"=","="),ne(ce,_e,rt,":",":"),ne(ce,_e,rt,"≈","\\approx",!0),ne(ce,_e,rt,"≅","\\cong",!0),ne(ce,_e,rt,"≥","\\ge"),ne(ce,_e,rt,"≥","\\geq",!0),ne(ce,_e,rt,"←","\\gets"),ne(ce,_e,rt,">","\\gt",!0),ne(ce,_e,rt,"∈","\\in",!0),ne(ce,_e,rt,"","\\@not"),ne(ce,_e,rt,"⊂","\\subset",!0),ne(ce,_e,rt,"⊃","\\supset",!0),ne(ce,_e,rt,"⊆","\\subseteq",!0),ne(ce,_e,rt,"⊇","\\supseteq",!0),ne(ce,tt,rt,"⊈","\\nsubseteq",!0),ne(ce,tt,rt,"⊉","\\nsupseteq",!0),ne(ce,_e,rt,"⊨","\\models"),ne(ce,_e,rt,"←","\\leftarrow",!0),ne(ce,_e,rt,"≤","\\le"),ne(ce,_e,rt,"≤","\\leq",!0),ne(ce,_e,rt,"<","\\lt",!0),ne(ce,_e,rt,"→","\\rightarrow",!0),ne(ce,_e,rt,"→","\\to"),ne(ce,tt,rt,"≱","\\ngeq",!0),ne(ce,tt,rt,"≰","\\nleq",!0),ne(ce,_e,Au," ","\\ "),ne(ce,_e,Au," ","\\space"),ne(ce,_e,Au," ","\\nobreakspace"),ne(Ft,_e,Au," ","\\ "),ne(Ft,_e,Au," "," "),ne(Ft,_e,Au," ","\\space"),ne(Ft,_e,Au," ","\\nobreakspace"),ne(ce,_e,Au,"","\\nobreak"),ne(ce,_e,Au,"","\\allowbreak"),ne(ce,_e,uy,",",","),ne(ce,_e,uy,";",";"),ne(ce,tt,yr,"⊼","\\barwedge",!0),ne(ce,tt,yr,"⊻","\\veebar",!0),ne(ce,_e,yr,"⊙","\\odot",!0),ne(ce,_e,yr,"⊕","\\oplus",!0),ne(ce,_e,yr,"⊗","\\otimes",!0),ne(ce,_e,st,"∂","\\partial",!0),ne(ce,_e,yr,"⊘","\\oslash",!0),ne(ce,tt,yr,"⊚","\\circledcirc",!0),ne(ce,tt,yr,"⊡","\\boxdot",!0),ne(ce,_e,yr,"△","\\bigtriangleup"),ne(ce,_e,yr,"▽","\\bigtriangledown"),ne(ce,_e,yr,"†","\\dagger"),ne(ce,_e,yr,"⋄","\\diamond"),ne(ce,_e,yr,"⋆","\\star"),ne(ce,_e,yr,"◃","\\triangleleft"),ne(ce,_e,yr,"▹","\\triangleright"),ne(ce,_e,rl,"{","\\{"),ne(Ft,_e,st,"{","\\{"),ne(Ft,_e,st,"{","\\textbraceleft"),ne(ce,_e,ro,"}","\\}"),ne(Ft,_e,st,"}","\\}"),ne(Ft,_e,st,"}","\\textbraceright"),ne(ce,_e,rl,"{","\\lbrace"),ne(ce,_e,ro,"}","\\rbrace"),ne(ce,_e,rl,"[","\\lbrack",!0),ne(Ft,_e,st,"[","\\lbrack",!0),ne(ce,_e,ro,"]","\\rbrack",!0),ne(Ft,_e,st,"]","\\rbrack",!0),ne(ce,_e,rl,"(","\\lparen",!0),ne(ce,_e,ro,")","\\rparen",!0),ne(Ft,_e,st,"<","\\textless",!0),ne(Ft,_e,st,">","\\textgreater",!0),ne(ce,_e,rl,"⌊","\\lfloor",!0),ne(ce,_e,ro,"⌋","\\rfloor",!0),ne(ce,_e,rl,"⌈","\\lceil",!0),ne(ce,_e,ro,"⌉","\\rceil",!0),ne(ce,_e,st,"\\","\\backslash"),ne(ce,_e,st,"∣","|"),ne(ce,_e,st,"∣","\\vert"),ne(Ft,_e,st,"|","\\textbar",!0),ne(ce,_e,st,"∥","\\|"),ne(ce,_e,st,"∥","\\Vert"),ne(Ft,_e,st,"∥","\\textbardbl"),ne(Ft,_e,st,"~","\\textasciitilde"),ne(Ft,_e,st,"\\","\\textbackslash"),ne(Ft,_e,st,"^","\\textasciicircum"),ne(ce,_e,rt,"↑","\\uparrow",!0),ne(ce,_e,rt,"⇑","\\Uparrow",!0),ne(ce,_e,rt,"↓","\\downarrow",!0),ne(ce,_e,rt,"⇓","\\Downarrow",!0),ne(ce,_e,rt,"↕","\\updownarrow",!0),ne(ce,_e,rt,"⇕","\\Updownarrow",!0),ne(ce,_e,Aa,"∐","\\coprod"),ne(ce,_e,Aa,"⋁","\\bigvee"),ne(ce,_e,Aa,"⋀","\\bigwedge"),ne(ce,_e,Aa,"⨄","\\biguplus"),ne(ce,_e,Aa,"⋂","\\bigcap"),ne(ce,_e,Aa,"⋃","\\bigcup"),ne(ce,_e,Aa,"∫","\\int"),ne(ce,_e,Aa,"∫","\\intop"),ne(ce,_e,Aa,"∬","\\iint"),ne(ce,_e,Aa,"∭","\\iiint"),ne(ce,_e,Aa,"∏","\\prod"),ne(ce,_e,Aa,"∑","\\sum"),ne(ce,_e,Aa,"⨂","\\bigotimes"),ne(ce,_e,Aa,"⨁","\\bigoplus"),ne(ce,_e,Aa,"⨀","\\bigodot"),ne(ce,_e,Aa,"∮","\\oint"),ne(ce,_e,Aa,"∯","\\oiint"),ne(ce,_e,Aa,"∰","\\oiiint"),ne(ce,_e,Aa,"⨆","\\bigsqcup"),ne(ce,_e,Aa,"∫","\\smallint"),ne(Ft,_e,Y0,"…","\\textellipsis"),ne(ce,_e,Y0,"…","\\mathellipsis"),ne(Ft,_e,Y0,"…","\\ldots",!0),ne(ce,_e,Y0,"…","\\ldots",!0),ne(ce,_e,Y0,"⋯","\\@cdots",!0),ne(ce,_e,Y0,"⋱","\\ddots",!0),ne(ce,_e,st,"⋮","\\varvdots"),ne(Ft,_e,st,"⋮","\\varvdots"),ne(ce,_e,Hn,"ˊ","\\acute"),ne(ce,_e,Hn,"ˋ","\\grave"),ne(ce,_e,Hn,"¨","\\ddot"),ne(ce,_e,Hn,"~","\\tilde"),ne(ce,_e,Hn,"ˉ","\\bar"),ne(ce,_e,Hn,"˘","\\breve"),ne(ce,_e,Hn,"ˇ","\\check"),ne(ce,_e,Hn,"^","\\hat"),ne(ce,_e,Hn,"⃗","\\vec"),ne(ce,_e,Hn,"˙","\\dot"),ne(ce,_e,Hn,"˚","\\mathring"),ne(ce,_e,Vr,"","\\@imath"),ne(ce,_e,Vr,"","\\@jmath"),ne(ce,_e,st,"ı","ı"),ne(ce,_e,st,"ȷ","ȷ"),ne(Ft,_e,st,"ı","\\i",!0),ne(Ft,_e,st,"ȷ","\\j",!0),ne(Ft,_e,st,"ß","\\ss",!0),ne(Ft,_e,st,"æ","\\ae",!0),ne(Ft,_e,st,"œ","\\oe",!0),ne(Ft,_e,st,"ø","\\o",!0),ne(Ft,_e,st,"Æ","\\AE",!0),ne(Ft,_e,st,"Œ","\\OE",!0),ne(Ft,_e,st,"Ø","\\O",!0),ne(Ft,_e,Hn,"ˊ","\\'"),ne(Ft,_e,Hn,"ˋ","\\`"),ne(Ft,_e,Hn,"ˆ","\\^"),ne(Ft,_e,Hn,"˜","\\~"),ne(Ft,_e,Hn,"ˉ","\\="),ne(Ft,_e,Hn,"˘","\\u"),ne(Ft,_e,Hn,"˙","\\."),ne(Ft,_e,Hn,"¸","\\c"),ne(Ft,_e,Hn,"˚","\\r"),ne(Ft,_e,Hn,"ˇ","\\v"),ne(Ft,_e,Hn,"¨",'\\"'),ne(Ft,_e,Hn,"˝","\\H"),ne(Ft,_e,Hn,"◯","\\textcircled"),tN={"--":!0,"---":!0,"``":!0,"''":!0},ne(Ft,_e,st,"–","--",!0),ne(Ft,_e,st,"–","\\textendash"),ne(Ft,_e,st,"—","---",!0),ne(Ft,_e,st,"—","\\textemdash"),ne(Ft,_e,st,"‘","`",!0),ne(Ft,_e,st,"‘","\\textquoteleft"),ne(Ft,_e,st,"’","'",!0),ne(Ft,_e,st,"’","\\textquoteright"),ne(Ft,_e,st,"“","``",!0),ne(Ft,_e,st,"“","\\textquotedblleft"),ne(Ft,_e,st,"”","''",!0),ne(Ft,_e,st,"”","\\textquotedblright"),ne(ce,_e,st,"°","\\degree",!0),ne(Ft,_e,st,"°","\\degree"),ne(Ft,_e,st,"°","\\textdegree",!0),ne(ce,_e,st,"£","\\pounds"),ne(ce,_e,st,"£","\\mathsterling",!0),ne(Ft,_e,st,"£","\\pounds"),ne(Ft,_e,st,"£","\\textsterling",!0),ne(ce,tt,st,"✠","\\maltese"),ne(Ft,tt,st,"✠","\\maltese"),rN='0123456789/@."',FE=0;FE{var v=b.charCodeAt(0),x=b.charCodeAt(1),w=(v-55296)*1024+(x-56320)+65536;if(119808<=w&&w<120484){var R=Math.floor((w-119808)/26);return dN[R]}else if(120782<=w&&w<=120831){var $=Math.floor((w-120782)/10);return Jme[$]}else{if(w===120485||w===120486)return dN[0];if(120486{if(Hh(b.classes)!==Hh(v.classes)||b.skew!==v.skew||b.maxFontSize!==v.maxFontSize||b.italic!==0&&b.hasClass("mathnormal"))return!1;if(b.classes.length===1){var x=b.classes[0];if(x==="mbin"||x==="mord")return!1}for(var w of Object.keys(b.style))if(b.style[w]!==v.style[w])return!1;for(var R of Object.keys(v.style))if(b.style[R]!==v.style[R])return!1;return!0},"canCombine"),pN=s(b=>{for(var v=0;vv&&(v=$.height),$.depth>x&&(x=$.depth),$.maxFontSize>w&&(w=$.maxFontSize)}b.height=v,b.depth=x,b.maxFontSize=w},"sizeElementFromChildren"),jt=s(function(b,v,x,w){var R=new U0(b,v,x,w);return XE(R),R},"makeSpan"),Kh=s((b,v,x,w)=>new U0(b,v,x,w),"makeSvgSpan"),H0=s(function(b,v,x){var w=jt([b],[],v);return w.height=Math.max(x||v.fontMetrics().defaultRuleThickness,v.minRuleThickness),w.style.borderBottomWidth=er(w.height),w.maxFontSize=1,w},"makeLineSpan"),i1e=s(function(b,v,x,w){var R=new Gx(b,v,x,w);return XE(R),R},"makeAnchor"),Ru=s(function(b){var v=new V0(b);return XE(v),v},"makeFragment"),G0=s(function(b,v){return b instanceof V0?jt([],[b],v):b},"wrapFragment"),n1e=s(function(b){if(b.positionType==="individualShift"){for(var v=b.children,x=[v[0]],w=-v[0].shift-v[0].elem.depth,R=w,$=1;${var x=jt(["mspace"],[],v),w=Jn(b,v);return x.style.marginRight=er(w),x},"makeGlue"),tT=s((b,v,x)=>{var w,R;switch(b){case"amsrm":w="AMS";break;case"textrm":w="Main";break;case"textsf":w="SansSerif";break;case"texttt":w="Typewriter";break;default:w=b}return v==="textbf"&&x==="textit"?R="BoldItalic":v==="textbf"?R="Bold":x==="textit"?R="Italic":R="Regular",w+"-"+R},"retrieveTextFontName"),KE={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathnormal:{variant:"italic",fontName:"Math-Italic"},mathsfit:{variant:"sans-serif-italic",fontName:"SansSerif-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},gN={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]},mN=s(function(b,v){var[x,w,R]=gN[b],$=new Gh(x),M=new _u([$],{width:er(w),height:er(R),style:"width:"+er(w),viewBox:"0 0 "+1e3*w+" "+1e3*R,preserveAspectRatio:"xMinYMin"}),N=Kh(["overlay"],[M],v);return N.height=R,N.style.height=er(R),N.style.width=er(w),N},"staticSvg"),ea={number:3,unit:"mu"},kp={number:4,unit:"mu"},$u={number:5,unit:"mu"},a1e={mord:{mop:ea,mbin:kp,mrel:$u,minner:ea},mop:{mord:ea,mop:ea,mrel:$u,minner:ea},mbin:{mord:kp,mop:kp,mopen:kp,minner:kp},mrel:{mord:$u,mop:$u,mopen:$u,minner:$u},mopen:{},mclose:{mop:ea,mbin:kp,mrel:$u,minner:ea},mpunct:{mord:ea,mop:ea,mrel:$u,mopen:ea,mclose:ea,mpunct:ea,minner:ea},minner:{mord:ea,mop:ea,mbin:kp,mrel:$u,mopen:ea,mpunct:ea,minner:ea}},s1e={mord:{mop:ea},mop:{mord:ea,mop:ea},mbin:{},mrel:{},mopen:{},mclose:{mop:ea},mpunct:{},minner:{mop:ea}},yN={},rT={},iT={},s(dr,"defineFunction"),s(bp,"defineFunctionBuilders"),nT=s(function(b){return b.type==="ordgroup"&&b.body.length===1?b.body[0]:b},"normalizeArgument"),Ra=s(function(b){return b.type==="ordgroup"?b.body:[b]},"ordargument"),o1e=new Set(["leftmost","mbin","mopen","mrel","mop","mpunct"]),l1e=new Set(["rightmost","mrel","mclose","mpunct"]),c1e={display:si.DISPLAY,text:si.TEXT,script:si.SCRIPT,scriptscript:si.SCRIPTSCRIPT},u1e={mord:"mord",mop:"mop",mbin:"mbin",mrel:"mrel",mopen:"mopen",mclose:"mclose",mpunct:"mpunct",minner:"minner"},Fa=s(function(b,v,x,w){w===void 0&&(w=[null,null]);for(var R=[],$=0;${var U=Y.classes[0],H=B.classes[0];U==="mbin"&&l1e.has(H)?Y.classes[0]="mord":H==="mbin"&&o1e.has(U)&&(B.classes[0]="mord")},{node:z},F,O),QE(R,(B,Y)=>{var U,H,J=JE(Y),te=JE(B),ie=J&&te?B.hasClass("mtight")?(U=s1e[J])==null?void 0:U[te]:(H=a1e[J])==null?void 0:H[te]:null;if(ie)return fN(ie,j)},{node:z},F,O),R},"buildExpression"),QE=s(function(b,v,x,w,R){w&&b.push(w);for(var $=0;$F=>{b.splice(z+1,0,F),$++})($)}w&&b.pop()},"traverseNonSpaceNodes"),vN=s(function(b){return b instanceof V0||b instanceof Gx||b instanceof U0&&b.hasClass("enclosing")?b:null},"checkPartialGroup"),ZE=s(function(b,v){var x=vN(b);if(x){var w=x.children;if(w.length){if(v==="right")return ZE(w[w.length-1],"right");if(v==="left")return ZE(w[0],"left")}}return b},"getOutermostNode"),JE=s(function(b,v){if(!b)return null;v&&(b=ZE(b,v));var x=b.classes[0];return u1e[x]||null},"getTypeOfDomTree"),hy=s(function(b,v){var x=["nulldelimiter"].concat(b.baseSizingClasses());return jt(v.concat(x))},"makeNullDelimiter"),Ki=s(function(b,v,x){if(!b)return jt();if(rT[b.type]){var w=rT[b.type](b,v);if(x&&v.size!==x.size){w=jt(v.sizingClasses(x),[w],v);var R=v.sizeMultiplier/x.sizeMultiplier;w.height*=R,w.depth*=R}return w}else throw new Xt("Got group of unknown type: '"+b.type+"'")},"buildGroup"),s(qx,"buildHTMLUnbreakable"),s(_E,"buildHTML"),s(qD,"newDocumentFragment"),Ht=(p=class{constructor(v,x,w){this.type=void 0,this.attributes=void 0,this.children=void 0,this.classes=void 0,this.type=v,this.attributes={},this.children=x||[],this.classes=w||[]}setAttribute(v,x){this.attributes[v]=x}getAttribute(v){return this.attributes[v]}toNode(){var v=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(var x in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,x)&&v.setAttribute(x,this.attributes[x]);this.classes.length>0&&(v.className=Hh(this.classes));for(var w=0;w0&&(v+=' class ="'+Fs(Hh(this.classes))+'"'),v+=">";for(var w=0;w",v}toText(){return this.children.map(v=>v.toText()).join("")}},s(p,"MathNode"),p),$a=(f=class{constructor(v){this.text=void 0,this.text=v}toNode(){return document.createTextNode(this.text)}toMarkup(){return Fs(this.toText())}toText(){return this.text}},s(f,"TextNode"),f),bN=(g=class{constructor(v){this.width=void 0,this.character=void 0,this.width=v,v>=.05555&&v<=.05556?this.character=" ":v>=.1666&&v<=.1667?this.character=" ":v>=.2222&&v<=.2223?this.character=" ":v>=.2777&&v<=.2778?this.character="  ":v>=-.05556&&v<=-.05555?this.character=" ⁣":v>=-.1667&&v<=-.1666?this.character=" ⁣":v>=-.2223&&v<=-.2222?this.character=" ⁣":v>=-.2778&&v<=-.2777?this.character=" ⁣":this.character=null}toNode(){if(this.character)return document.createTextNode(this.character);var v=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return v.setAttribute("width",er(this.width)),v}toMarkup(){return this.character?""+this.character+"":''}toText(){return this.character?this.character:" "}},s(g,"SpaceNode"),g),h1e=new Set(["\\imath","\\jmath"]),d1e=new Set(["mrow","mtable"]),kl=s(function(b,v,x){return Yn[v][b]&&Yn[v][b].replace&&b.charCodeAt(0)!==55349&&!(tN.hasOwnProperty(b)&&x&&(x.fontFamily&&x.fontFamily.slice(4,6)==="tt"||x.font&&x.font.slice(4,6)==="tt"))&&(b=Yn[v][b].replace),new $a(b)},"makeText"),eS=s(function(b){return b.length===1?b[0]:new Ht("mrow",b)},"makeRow"),p1e={mathit:"italic",boldsymbol:s(b=>b.type==="textord"?"bold":"bold-italic","boldsymbol"),mathbf:"bold",mathbb:"double-struck",mathsfit:"sans-serif-italic",mathfrak:"fraktur",mathscr:"script",mathcal:"script",mathsf:"sans-serif",mathtt:"monospace"},tS=s((b,v)=>{if(b.mode==="text"){if(v.fontFamily==="texttt")return"monospace";if(v.fontFamily==="textsf")return v.fontShape==="textit"&&v.fontWeight==="textbf"?"sans-serif-bold-italic":v.fontShape==="textit"?"sans-serif-italic":v.fontWeight==="textbf"?"bold-sans-serif":"sans-serif";if(v.fontShape==="textit"&&v.fontWeight==="textbf")return"bold-italic";if(v.fontShape==="textit")return"italic";if(v.fontWeight==="textbf")return"bold"}var x=v.font;if(!x||x==="mathnormal")return null;var w=b.mode,R=p1e[x];if(R)return typeof R=="function"?R(b):R;var $=b.text;if(h1e.has($))return null;if(Yn[w][$]){var M=Yn[w][$].replace;M&&($=M)}var N=KE[x].fontName;return SE($,N,w)?KE[x].variant:null},"getVariant"),s(AE,"isNumberPunctuation"),il=s(function(b,v,x){if(b.length===1){var w=dn(b[0],v);return x&&w instanceof Ht&&w.type==="mo"&&(w.setAttribute("lspace","0em"),w.setAttribute("rspace","0em")),[w]}for(var R=[],$,M=0;M=1&&($.type==="mn"||AE($))){var j=N.children[0];j instanceof Ht&&j.type==="mn"&&(j.children=[...$.children,...j.children],R.pop())}else if($.type==="mi"&&$.children.length===1){var I=$.children[0];if(I instanceof $a&&I.text==="̸"&&(N.type==="mo"||N.type==="mi"||N.type==="mn")){var z=N.children[0];z instanceof $a&&z.text.length>0&&(z.text=z.text.slice(0,1)+"̸"+z.text.slice(1),R.pop())}}}R.push(N),$=N}return R},"buildExpression"),Qh=s(function(b,v,x){return eS(il(b,v,x))},"buildExpressionRow"),dn=s(function(b,v){if(!b)return new Ht("mrow");if(iT[b.type])return iT[b.type](b,v);throw new Xt("Got group of unknown type: '"+b.type+"'")},"buildGroup"),s(WD,"buildMathML"),f1e=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],xN=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],TN=s(function(b,v){return v.size<2?b:f1e[b-1][v.size-1]},"sizeAtStyle"),kN=(m=class{constructor(v){this.style=void 0,this.color=void 0,this.size=void 0,this.textSize=void 0,this.phantom=void 0,this.font=void 0,this.fontFamily=void 0,this.fontWeight=void 0,this.fontShape=void 0,this.sizeMultiplier=void 0,this.maxSize=void 0,this.minRuleThickness=void 0,this._fontMetrics=void 0,this.style=v.style,this.color=v.color,this.size=v.size||m.BASESIZE,this.textSize=v.textSize||this.size,this.phantom=!!v.phantom,this.font=v.font||"",this.fontFamily=v.fontFamily||"",this.fontWeight=v.fontWeight||"",this.fontShape=v.fontShape||"",this.sizeMultiplier=xN[this.size-1],this.maxSize=v.maxSize,this.minRuleThickness=v.minRuleThickness,this._fontMetrics=void 0}extend(v){var x={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};return Object.assign(x,v),new m(x)}havingStyle(v){return this.style===v?this:this.extend({style:v,size:TN(this.textSize,v)})}havingCrampedStyle(){return this.havingStyle(this.style.cramp())}havingSize(v){return this.size===v&&this.textSize===v?this:this.extend({style:this.style.text(),size:v,textSize:v,sizeMultiplier:xN[v-1]})}havingBaseStyle(v){v=v||this.style.text();var x=TN(m.BASESIZE,v);return this.size===x&&this.textSize===m.BASESIZE&&this.style===v?this:this.extend({style:v,size:x})}havingBaseSizing(){var v;switch(this.style.id){case 4:case 5:v=3;break;case 6:case 7:v=1;break;default:v=6}return this.extend({style:this.style.text(),size:v})}withColor(v){return this.extend({color:v})}withPhantom(){return this.extend({phantom:!0})}withFont(v){return this.extend({font:v})}withTextFontFamily(v){return this.extend({fontFamily:v,font:""})}withTextFontWeight(v){return this.extend({fontWeight:v,font:""})}withTextFontShape(v){return this.extend({fontShape:v,font:""})}sizingClasses(v){return v.size!==this.size?["sizing","reset-size"+v.size,"size"+this.size]:[]}baseSizingClasses(){return this.size!==m.BASESIZE?["sizing","reset-size"+this.size,"size"+m.BASESIZE]:[]}fontMetrics(){return this._fontMetrics||(this._fontMetrics=xme(this.size)),this._fontMetrics}getColor(){return this.phantom?"transparent":this.color}},s(m,"Options"),m),kN.BASESIZE=6,CN=s(function(b){return new kN({style:b.displayMode?si.DISPLAY:si.TEXT,maxSize:b.maxSize,minRuleThickness:b.minRuleThickness})},"optionsFromSettings"),wN=s(function(b,v){if(v.displayMode){var x=["katex-display"];v.leqno&&x.push("leqno"),v.fleqn&&x.push("fleqn"),b=jt(x,[b])}return b},"displayWrap"),g1e=s(function(b,v,x){var w=CN(x),R;if(x.output==="mathml")return WD(b,v,w,x.displayMode,!0);if(x.output==="html"){var $=_E(b,w);R=jt(["katex"],[$])}else{var M=WD(b,v,w,x.displayMode,!1),N=_E(b,w);R=jt(["katex"],[M,N])}return wN(R,x)},"buildTree"),m1e=s(function(b,v,x){var w=CN(x),R=_E(b,w),$=jt(["katex"],[R]);return wN($,x)},"buildHTMLTree"),y1e={widehat:"^",widecheck:"ˇ",widetilde:"~",utilde:"~",overleftarrow:"←",underleftarrow:"←",xleftarrow:"←",overrightarrow:"→",underrightarrow:"→",xrightarrow:"→",underbrace:"⏟",overbrace:"⏞",underbracket:"⎵",overbracket:"⎴",overgroup:"⏠",undergroup:"⏡",overleftrightarrow:"↔",underleftrightarrow:"↔",xleftrightarrow:"↔",Overrightarrow:"⇒",xRightarrow:"⇒",overleftharpoon:"↼",xleftharpoonup:"↼",overrightharpoon:"⇀",xrightharpoonup:"⇀",xLeftarrow:"⇐",xLeftrightarrow:"⇔",xhookleftarrow:"↩",xhookrightarrow:"↪",xmapsto:"↦",xrightharpoondown:"⇁",xleftharpoondown:"↽",xrightleftharpoons:"⇌",xleftrightharpoons:"⇋",xtwoheadleftarrow:"↞",xtwoheadrightarrow:"↠",xlongequal:"=",xtofrom:"⇄",xrightleftarrows:"⇄",xrightequilibrium:"⇌",xleftequilibrium:"⇋","\\cdrightarrow":"→","\\cdleftarrow":"←","\\cdlongequal":"="},aT=s(function(b){var v=new Ht("mo",[new $a(y1e[b.replace(/^\\/,"")])]);return v.setAttribute("stretchy","true"),v},"stretchyMathML"),v1e={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],"\\cdrightarrow":[["rightarrow"],3,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],"\\cdleftarrow":[["leftarrow"],3,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],"\\cdlongequal":[["longequal"],3,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overbracket:[["leftbracketover","rightbracketover"],1.6,440],underbracket:[["leftbracketunder","rightbracketunder"],1.6,410],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]},b1e=new Set(["widehat","widecheck","widetilde","utilde"]),sT=s(function(b,v){function x(){var M=4e5,N=b.label.slice(1);if(b1e.has(N)&&"base"in b){var j=b.base.type==="ordgroup"?b.base.body.length:1,I,z,F;if(j>5)N==="widehat"||N==="widecheck"?(I=420,M=2364,F=.42,z=N+"4"):(I=312,M=2340,F=.34,z="tilde4");else{var O=[1,1,2,2,3,3][j];N==="widehat"||N==="widecheck"?(M=[0,1062,2364,2364,2364][O],I=[0,239,300,360,420][O],F=[0,.24,.3,.3,.36,.42][O],z=N+O):(M=[0,600,1033,2339,2340][O],I=[0,260,286,306,312][O],F=[0,.26,.286,.3,.306,.34][O],z="tilde"+O)}var B=new Gh(z),Y=new _u([B],{width:"100%",height:er(F),viewBox:"0 0 "+M+" "+I,preserveAspectRatio:"none"});return{span:Kh([],[Y],v),minWidth:0,height:F}}else{var U=[],H=v1e[N];if(!H)throw new Error('No SVG data for "'+N+'".');var[J,te,ie]=H,ue=ie/1e3,se=J.length,le,Te;if(se===1){if(H.length!==4)throw new Error('Expected 4-tuple for single-path SVG data "'+N+'".');le=["hide-tail"],Te=[H[3]]}else if(se===2)le=["halfarrow-left","halfarrow-right"],Te=["xMinYMin","xMaxYMin"];else if(se===3)le=["brace-left","brace-center","brace-right"],Te=["xMinYMin","xMidYMin","xMaxYMin"];else throw new Error(`Correct katexImagesData or update code here to support + `+se+" children.");for(var oe=0;oe0&&(w.style.minWidth=er(R)),w},"stretchySvg"),x1e=s(function(b,v,x,w,R){var $,M=b.height+b.depth+x+w;if(/fbox|color|angl/.test(v)){if($=jt(["stretchy",v],[],R),v==="fbox"){var N=R.color&&R.getColor();N&&($.style.borderColor=N)}}else{var j=[];/^[bx]cancel$/.test(v)&&j.push(new BE({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(v)&&j.push(new BE({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));var I=new _u(j,{width:"100%",height:er(M)});$=Kh([],[I],R)}return $.height=M,$.style.height=er(M),$},"stretchyEnclose"),T1e={bin:1,close:1,inner:1,open:1,punct:1,rel:1},k1e={"accent-token":1,mathord:1,"op-token":1,spacing:1,textord:1},s(Tme,"isAtom"),s(gi,"assertNodeType"),s(Wx,"assertSymbolNodeType"),s(jx,"checkSymbolNodeType"),EN=s(b=>{if(b instanceof tl)return b;if(Qme(b)&&b.children.length===1)return EN(b.children[0])},"getBaseSymbol"),rS=s((b,v)=>{var x,w,R;b&&b.type==="supsub"?(w=gi(b.base,"accent"),x=w.base,b.base=x,R=bme(Ki(b,v)),b.base=w):(w=gi(b,"accent"),x=w.base);var $=Ki(x,v.havingCrampedStyle()),M=w.isShifty&&Eu(x),N=0;if(M){var j,I;N=(j=(I=EN($))==null?void 0:I.skew)!=null?j:0}var z=w.label==="\\c",F=z?$.height+$.depth:Math.min($.height,v.fontMetrics().xHeight),O;if(w.isStretchy)O=sT(w,v),O=Xi({positionType:"firstBaseline",children:[{type:"elem",elem:$},{type:"elem",elem:O,wrapperClasses:["svg-align"],wrapperStyle:N>0?{width:"calc(100% - "+er(2*N)+")",marginLeft:er(2*N)}:void 0}]});else{var B,Y;w.label==="\\vec"?(B=mN("vec",v),Y=gN.vec[1]):(B=eT({type:"textord",mode:w.mode,text:w.label},v,"textord"),B=vme(B),B.italic=0,Y=B.width,z&&(F+=B.depth)),O=jt(["accent-body"],[B]);var U=w.label==="\\textcircled";U&&(O.classes.push("accent-full"),F=$.height);var H=N;U||(H-=Y/2),O.style.left=er(H),w.label==="\\textcircled"&&(O.style.top=".2em"),O=Xi({positionType:"firstBaseline",children:[{type:"elem",elem:$},{type:"kern",size:-F},{type:"elem",elem:O}]})}var J=jt(["mord","accent"],[O],v);return R?(R.children[0]=J,R.height=Math.max(J.height,R.height),R.classes[0]="mord",R):J},"htmlBuilder$a"),SN=s((b,v)=>{var x=b.isStretchy?aT(b.label):new Ht("mo",[kl(b.label,b.mode)]),w=new Ht("mover",[dn(b.base,v),x]);return w.setAttribute("accent","true"),w},"mathmlBuilder$9"),C1e=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map(b=>"\\"+b).join("|")),dr({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:s((b,v)=>{var x=nT(v[0]),w=!C1e.test(b.funcName),R=!w||b.funcName==="\\widehat"||b.funcName==="\\widetilde"||b.funcName==="\\widecheck";return{type:"accent",mode:b.parser.mode,label:b.funcName,isStretchy:w,isShifty:R,base:x}},"handler"),htmlBuilder:rS,mathmlBuilder:SN}),dr({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\c","\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"]},handler:s((b,v)=>{var x=v[0],w=b.parser.mode;return w==="math"&&(b.parser.settings.reportNonstrict("mathVsTextAccents","LaTeX's accent "+b.funcName+" works only in text mode"),w="text"),{type:"accent",mode:w,label:b.funcName,isStretchy:!1,isShifty:!0,base:x}},"handler"),htmlBuilder:rS,mathmlBuilder:SN}),dr({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:s((b,v)=>{var{parser:x,funcName:w}=b,R=v[0];return{type:"accentUnder",mode:x.mode,label:w,base:R}},"handler"),htmlBuilder:s((b,v)=>{var x=Ki(b.base,v),w=sT(b,v),R=b.label==="\\utilde"?.12:0,$=Xi({positionType:"top",positionData:x.height,children:[{type:"elem",elem:w,wrapperClasses:["svg-align"]},{type:"kern",size:R},{type:"elem",elem:x}]});return jt(["mord","accentunder"],[$],v)},"htmlBuilder"),mathmlBuilder:s((b,v)=>{var x=aT(b.label),w=new Ht("munder",[dn(b.base,v),x]);return w.setAttribute("accentunder","true"),w},"mathmlBuilder")}),oT=s(b=>{var v=new Ht("mpadded",b?[b]:[]);return v.setAttribute("width","+0.6em"),v.setAttribute("lspace","0.3em"),v},"paddedNode"),dr({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium","\\\\cdrightarrow","\\\\cdleftarrow","\\\\cdlongequal"],props:{numArgs:1,numOptionalArgs:1},handler(b,v,x){var{parser:w,funcName:R}=b;return{type:"xArrow",mode:w.mode,label:R,body:v[0],below:x[0]}},htmlBuilder(b,v){var x=v.style,w=v.havingStyle(x.sup()),R=G0(Ki(b.body,w,v),v),$=b.label.slice(0,2)==="\\x"?"x":"cd";R.classes.push($+"-arrow-pad");var M;b.below&&(w=v.havingStyle(x.sub()),M=G0(Ki(b.below,w,v),v),M.classes.push($+"-arrow-pad"));var N=sT(b,v),j=-v.fontMetrics().axisHeight+.5*N.height,I=-v.fontMetrics().axisHeight-.5*N.height-.111;(R.depth>.25||b.label==="\\xleftequilibrium")&&(I-=R.depth);var z;if(M){var F=-v.fontMetrics().axisHeight+M.height+.5*N.height+.111;z=Xi({positionType:"individualShift",children:[{type:"elem",elem:R,shift:I},{type:"elem",elem:N,shift:j,wrapperClasses:["svg-align"]},{type:"elem",elem:M,shift:F}]})}else z=Xi({positionType:"individualShift",children:[{type:"elem",elem:R,shift:I},{type:"elem",elem:N,shift:j,wrapperClasses:["svg-align"]}]});return jt(["mrel","x-arrow"],[z],v)},mathmlBuilder(b,v){var x=aT(b.label);x.setAttribute("minsize",b.label.charAt(0)==="x"?"1.75em":"3.0em");var w;if(b.body){var R=oT(dn(b.body,v));if(b.below){var $=oT(dn(b.below,v));w=new Ht("munderover",[x,$,R])}else w=new Ht("mover",[x,R])}else if(b.below){var M=oT(dn(b.below,v));w=new Ht("munder",[x,M])}else w=oT(),w=new Ht("mover",[x,w]);return w}}),s(jD,"htmlBuilder$9"),s(VD,"mathmlBuilder$8"),dr({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1,primitive:!0},handler(b,v){var{parser:x,funcName:w}=b,R=v[0];return{type:"mclass",mode:x.mode,mclass:"m"+w.slice(5),body:Ra(R),isCharacterBox:Eu(R)}},htmlBuilder:jD,mathmlBuilder:VD}),lT=s(b=>{var v=b.type==="ordgroup"&&b.body.length?b.body[0]:b;return v.type==="atom"&&(v.family==="bin"||v.family==="rel")?"m"+v.family:"mord"},"binrelClass"),dr({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler(b,v){var{parser:x}=b;return{type:"mclass",mode:x.mode,mclass:lT(v[0]),body:Ra(v[1]),isCharacterBox:Eu(v[1])}}}),dr({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler(b,v){var{parser:x,funcName:w}=b,R=v[1],$=v[0],M;w!=="\\stackrel"?M=lT(R):M="mrel";var N={type:"op",mode:R.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:w!=="\\stackrel",body:Ra(R)},j={type:"supsub",mode:$.mode,base:N,sup:w==="\\underset"?null:$,sub:w==="\\underset"?$:null};return{type:"mclass",mode:x.mode,mclass:M,body:[j],isCharacterBox:Eu(j)}},htmlBuilder:jD,mathmlBuilder:VD}),dr({type:"pmb",names:["\\pmb"],props:{numArgs:1,allowedInText:!0},handler(b,v){var{parser:x}=b;return{type:"pmb",mode:x.mode,mclass:lT(v[0]),body:Ra(v[0])}},htmlBuilder(b,v){var x=Fa(b.body,v,!0),w=jt([b.mclass],x,v);return w.style.textShadow="0.02em 0.01em 0.04px",w},mathmlBuilder(b,v){var x=il(b.body,v),w=new Ht("mstyle",x);return w.setAttribute("style","text-shadow: 0.02em 0.01em 0.04px"),w}}),w1e={">":"\\\\cdrightarrow","<":"\\\\cdleftarrow","=":"\\\\cdlongequal",A:"\\uparrow",V:"\\downarrow","|":"\\Vert",".":"no arrow"},_N=s(()=>({type:"styling",body:[],mode:"math",style:"display",resetFont:!0}),"newCell"),AN=s(b=>b.type==="textord"&&b.text==="@","isStartOfArrow"),E1e=s((b,v)=>(b.type==="mathord"||b.type==="atom")&&b.text===v,"isLabelEnd"),s(kme,"cdArrow"),s(Cme,"parseCD"),dr({type:"cdlabel",names:["\\\\cdleft","\\\\cdright"],props:{numArgs:1},handler(b,v){var{parser:x,funcName:w}=b;return{type:"cdlabel",mode:x.mode,side:w.slice(4),label:v[0]}},htmlBuilder(b,v){var x=v.havingStyle(v.style.sup()),w=G0(Ki(b.label,x,v),v);return w.classes.push("cd-label-"+b.side),w.style.bottom=er(.8-w.depth),w.height=0,w.depth=0,w},mathmlBuilder(b,v){var x=new Ht("mrow",[dn(b.label,v)]);return x=new Ht("mpadded",[x]),x.setAttribute("width","0"),b.side==="left"&&x.setAttribute("lspace","-1width"),x.setAttribute("voffset","0.7em"),x=new Ht("mstyle",[x]),x.setAttribute("displaystyle","false"),x.setAttribute("scriptlevel","1"),x}}),dr({type:"cdlabelparent",names:["\\\\cdparent"],props:{numArgs:1},handler(b,v){var{parser:x}=b;return{type:"cdlabelparent",mode:x.mode,fragment:v[0]}},htmlBuilder(b,v){var x=G0(Ki(b.fragment,v),v);return x.classes.push("cd-vert-arrow"),x},mathmlBuilder(b,v){return new Ht("mrow",[dn(b.fragment,v)])}}),dr({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler(b,v){for(var{parser:x}=b,w=gi(v[0],"ordgroup"),R=w.body,$="",M=0;M=1114111)throw new Xt("\\@char with invalid code point "+$);return j<=65535?I=String.fromCharCode(j):(j-=65536,I=String.fromCharCode((j>>10)+55296,(j&1023)+56320)),{type:"textord",mode:x.mode,text:I}}}),RN=s((b,v)=>{var x=Fa(b.body,v.withColor(b.color),!1);return Ru(x)},"htmlBuilder$8"),$N=s((b,v)=>{var x=il(b.body,v.withColor(b.color)),w=new Ht("mstyle",x);return w.setAttribute("mathcolor",b.color),w},"mathmlBuilder$7"),dr({type:"color",names:["\\textcolor"],props:{numArgs:2,allowedInText:!0,argTypes:["color","original"]},handler(b,v){var{parser:x}=b,w=gi(v[0],"color-token").color,R=v[1];return{type:"color",mode:x.mode,color:w,body:Ra(R)}},htmlBuilder:RN,mathmlBuilder:$N}),dr({type:"color",names:["\\color"],props:{numArgs:1,allowedInText:!0,argTypes:["color"]},handler(b,v){var{parser:x,breakOnTokenText:w}=b,R=gi(v[0],"color-token").color;x.gullet.macros.set("\\current@color",R);var $=x.parseExpression(!0,w);return{type:"color",mode:x.mode,color:R,body:$}},htmlBuilder:RN,mathmlBuilder:$N}),dr({type:"cr",names:["\\\\"],props:{numArgs:0,numOptionalArgs:0,allowedInText:!0},handler(b,v,x){var{parser:w}=b,R=w.gullet.future().text==="["?w.parseSizeGroup(!0):null,$=!w.settings.displayMode||!w.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode");return{type:"cr",mode:w.mode,newLine:$,size:R&&gi(R,"size").value}},htmlBuilder(b,v){var x=jt(["mspace"],[],v);return b.newLine&&(x.classes.push("newline"),b.size&&(x.style.marginTop=er(Jn(b.size,v)))),x},mathmlBuilder(b,v){var x=new Ht("mspace");return b.newLine&&(x.setAttribute("linebreak","newline"),b.size&&x.setAttribute("height",er(Jn(b.size,v)))),x}}),iS={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},LN=s(b=>{var v=b.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(v))throw new Xt("Expected a control sequence",b);return v},"checkControlSequence"),S1e=s(b=>{var v=b.gullet.popToken();return v.text==="="&&(v=b.gullet.popToken(),v.text===" "&&(v=b.gullet.popToken())),v},"getRHS"),IN=s((b,v,x,w)=>{var R=b.gullet.macros.get(x.text);R==null&&(x.noexpand=!0,R={tokens:[x],numArgs:0,unexpandable:!b.gullet.isExpandable(x.text)}),b.gullet.macros.set(v,R,w)},"letCommand"),dr({type:"internal",names:["\\global","\\long","\\\\globallong"],props:{numArgs:0,allowedInText:!0},handler(b){var{parser:v,funcName:x}=b;v.consumeSpaces();var w=v.fetch();if(iS[w.text])return(x==="\\global"||x==="\\\\globallong")&&(w.text=iS[w.text]),gi(v.parseFunction(),"internal");throw new Xt("Invalid token after macro prefix",w)}}),dr({type:"internal",names:["\\def","\\gdef","\\edef","\\xdef"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(b){var{parser:v,funcName:x}=b,w=v.gullet.popToken(),R=w.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(R))throw new Xt("Expected a control sequence",w);for(var $=0,M,N=[[]];v.gullet.future().text!=="{";)if(w=v.gullet.popToken(),w.text==="#"){if(v.gullet.future().text==="{"){M=v.gullet.future(),N[$].push("{");break}if(w=v.gullet.popToken(),!/^[1-9]$/.test(w.text))throw new Xt('Invalid argument number "'+w.text+'"');if(parseInt(w.text)!==$+1)throw new Xt('Argument number "'+w.text+'" out of order');$++,N.push([])}else{if(w.text==="EOF")throw new Xt("Expected a macro definition");N[$].push(w.text)}var{tokens:j}=v.gullet.consumeArg();return M&&j.unshift(M),(x==="\\edef"||x==="\\xdef")&&(j=v.gullet.expandTokens(j),j.reverse()),v.gullet.macros.set(R,{tokens:j,numArgs:$,delimiters:N},x===iS[x]),{type:"internal",mode:v.mode}}}),dr({type:"internal",names:["\\let","\\\\globallet"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(b){var{parser:v,funcName:x}=b,w=LN(v.gullet.popToken());v.gullet.consumeSpaces();var R=S1e(v);return IN(v,w,R,x==="\\\\globallet"),{type:"internal",mode:v.mode}}}),dr({type:"internal",names:["\\futurelet","\\\\globalfuture"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(b){var{parser:v,funcName:x}=b,w=LN(v.gullet.popToken()),R=v.gullet.popToken(),$=v.gullet.popToken();return IN(v,w,$,x==="\\\\globalfuture"),v.gullet.pushToken($),v.gullet.pushToken(R),{type:"internal",mode:v.mode}}}),dy=s(function(b,v,x){var w=Yn.math[b]&&Yn.math[b].replace,R=SE(w||b,v,x);if(!R)throw new Error("Unsupported symbol "+b+" and font size "+v+".");return R},"getMetrics"),nS=s(function(b,v,x,w){var R=x.havingBaseStyle(v),$=jt(w.concat(R.sizingClasses(x)),[b],x),M=R.sizeMultiplier/x.sizeMultiplier;return $.height*=M,$.depth*=M,$.maxFontSize=R.sizeMultiplier,$},"styleWrap"),MN=s(function(b,v,x){var w=v.havingBaseStyle(x),R=(1-v.sizeMultiplier/w.sizeMultiplier)*v.fontMetrics().axisHeight;b.classes.push("delimcenter"),b.style.top=er(R),b.height-=R,b.depth+=R},"centerSpan"),_1e=s(function(b,v,x,w,R,$){var M=no(b,"Main-Regular",R,w),N=nS(M,v,w,$);return x&&MN(N,w,v),N},"makeSmallDelim"),A1e=s(function(b,v,x,w){return no(b,"Size"+v+"-Regular",x,w)},"mathrmSize"),DN=s(function(b,v,x,w,R,$){var M=A1e(b,v,R,w),N=nS(jt(["delimsizing","size"+v],[M],w),si.TEXT,w,$);return x&&MN(N,w,si.TEXT),N},"makeLargeDelim"),aS=s(function(b,v,x){var w;v==="Size1-Regular"?w="delim-size1":w="delim-size4";var R=jt(["delimsizinginner",w],[jt([],[no(b,v,x)])]);return{type:"elem",elem:R}},"makeGlyphSpan"),sS=s(function(b,v,x){var w=Lc["Size4-Regular"][b.charCodeAt(0)]?Lc["Size4-Regular"][b.charCodeAt(0)][4]:Lc["Size1-Regular"][b.charCodeAt(0)][4],R=new Gh("inner",Ume(b,Math.round(1e3*v))),$=new _u([R],{width:er(w),height:er(v),style:"width:"+er(w),viewBox:"0 0 "+1e3*w+" "+Math.round(1e3*v),preserveAspectRatio:"xMinYMin"}),M=Kh([],[$],x);return M.height=v,M.style.height=er(v),M.style.width=er(w),{type:"elem",elem:M}},"makeInner"),oS=.008,cT={type:"kern",size:-1*oS},R1e=new Set(["|","\\lvert","\\rvert","\\vert"]),$1e=new Set(["\\|","\\lVert","\\rVert","\\Vert"]),NN=s(function(b,v,x,w,R,$){var M,N,j,I,z="",F=0;M=j=I=b,N=null;var O="Size1-Regular";b==="\\uparrow"?j=I="⏐":b==="\\Uparrow"?j=I="‖":b==="\\downarrow"?M=j="⏐":b==="\\Downarrow"?M=j="‖":b==="\\updownarrow"?(M="\\uparrow",j="⏐",I="\\downarrow"):b==="\\Updownarrow"?(M="\\Uparrow",j="‖",I="\\Downarrow"):R1e.has(b)?(j="∣",z="vert",F=333):$1e.has(b)?(j="∥",z="doublevert",F=556):b==="["||b==="\\lbrack"?(M="⎡",j="⎢",I="⎣",O="Size4-Regular",z="lbrack",F=667):b==="]"||b==="\\rbrack"?(M="⎤",j="⎥",I="⎦",O="Size4-Regular",z="rbrack",F=667):b==="\\lfloor"||b==="⌊"?(j=M="⎢",I="⎣",O="Size4-Regular",z="lfloor",F=667):b==="\\lceil"||b==="⌈"?(M="⎡",j=I="⎢",O="Size4-Regular",z="lceil",F=667):b==="\\rfloor"||b==="⌋"?(j=M="⎥",I="⎦",O="Size4-Regular",z="rfloor",F=667):b==="\\rceil"||b==="⌉"?(M="⎤",j=I="⎥",O="Size4-Regular",z="rceil",F=667):b==="("||b==="\\lparen"?(M="⎛",j="⎜",I="⎝",O="Size4-Regular",z="lparen",F=875):b===")"||b==="\\rparen"?(M="⎞",j="⎟",I="⎠",O="Size4-Regular",z="rparen",F=875):b==="\\{"||b==="\\lbrace"?(M="⎧",N="⎨",I="⎩",j="⎪",O="Size4-Regular"):b==="\\}"||b==="\\rbrace"?(M="⎫",N="⎬",I="⎭",j="⎪",O="Size4-Regular"):b==="\\lgroup"||b==="⟮"?(M="⎧",I="⎩",j="⎪",O="Size4-Regular"):b==="\\rgroup"||b==="⟯"?(M="⎫",I="⎭",j="⎪",O="Size4-Regular"):b==="\\lmoustache"||b==="⎰"?(M="⎧",I="⎭",j="⎪",O="Size4-Regular"):(b==="\\rmoustache"||b==="⎱")&&(M="⎫",I="⎩",j="⎪",O="Size4-Regular");var B=dy(M,O,R),Y=B.height+B.depth,U=dy(j,O,R),H=U.height+U.depth,J=dy(I,O,R),te=J.height+J.depth,ie=0,ue=1;if(N!==null){var se=dy(N,O,R);ie=se.height+se.depth,ue=2}var le=Y+te+ie,Te=Math.max(0,Math.ceil((v-le)/(ue*H))),oe=le+Te*ue*H,fe=w.fontMetrics().axisHeight;x&&(fe*=w.sizeMultiplier);var ge=oe/2-fe,ke=[];if(z.length>0){var Ne=oe-Y-te,xe=Math.round(oe*1e3),$e=Yme(z,Math.round(Ne*1e3)),Me=new Gh(z,$e),He=er(F/1e3),Ge=er(xe/1e3),ze=new _u([Me],{width:He,height:Ge,viewBox:"0 0 "+F+" "+xe}),Ve=Kh([],[ze],w);Ve.height=xe/1e3,Ve.style.width=He,Ve.style.height=Ge,ke.push({type:"elem",elem:Ve})}else{if(ke.push(aS(I,O,R)),ke.push(cT),N===null){var et=oe-Y-te+2*oS;ke.push(sS(j,et,w))}else{var he=(oe-Y-te-ie)/2+2*oS;ke.push(sS(j,he,w)),ke.push(cT),ke.push(aS(N,O,R)),ke.push(cT),ke.push(sS(j,he,w))}ke.push(cT),ke.push(aS(M,O,R))}var ye=w.havingBaseStyle(si.TEXT),be=Xi({positionType:"bottom",positionData:ge,children:ke});return nS(jt(["delimsizing","mult"],[be],ye),si.TEXT,w,$)},"makeStackedDelim"),lS=80,cS=.08,uS=s(function(b,v,x,w,R){var $=Vme(b,w,x),M=new Gh(b,$),N=new _u([M],{width:"400em",height:er(v),viewBox:"0 0 400000 "+x,preserveAspectRatio:"xMinYMin slice"});return Kh(["hide-tail"],[N],R)},"sqrtSvg"),L1e=s(function(b,v){var x=v.havingBaseSizing(),w=zN("\\surd",b*x.sizeMultiplier,FN,x),R=x.sizeMultiplier,$=Math.max(0,v.minRuleThickness-v.fontMetrics().sqrtRuleThickness),M,N,j,I,z;return w.type==="small"?(I=1e3+1e3*$+lS,b<1?R=1:b<1.4&&(R=.7),N=(1+$+cS)/R,j=(1+$)/R,M=uS("sqrtMain",N,I,$,v),M.style.minWidth="0.853em",z=.833/R):w.type==="large"?(I=(1e3+lS)*py[w.size],j=(py[w.size]+$)/R,N=(py[w.size]+$+cS)/R,M=uS("sqrtSize"+w.size,N,I,$,v),M.style.minWidth="1.02em",z=1/R):(N=b+$+cS,j=b+$,I=Math.floor(1e3*b+$)+lS,M=uS("sqrtTall",N,I,$,v),M.style.minWidth="0.742em",z=1.056),M.height=j,M.style.height=er(N),{span:M,advanceWidth:z,ruleWidth:(v.fontMetrics().sqrtRuleThickness+$)*R}},"makeSqrtImage"),ON=new Set(["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","⌊","⌋","\\lceil","\\rceil","⌈","⌉","\\surd"]),I1e=new Set(["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","⟮","⟯","\\lmoustache","\\rmoustache","⎰","⎱"]),BN=new Set(["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"]),py=[0,1.2,1.8,2.4,3],PN=s(function(b,v,x,w,R){if(b==="<"||b==="\\lt"||b==="⟨"?b="\\langle":(b===">"||b==="\\gt"||b==="⟩")&&(b="\\rangle"),ON.has(b)||BN.has(b))return DN(b,v,!1,x,w,R);if(I1e.has(b))return NN(b,py[v],!1,x,w,R);throw new Xt("Illegal delimiter: '"+b+"'")},"makeSizedDelim"),M1e=[{type:"small",style:si.SCRIPTSCRIPT},{type:"small",style:si.SCRIPT},{type:"small",style:si.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],D1e=[{type:"small",style:si.SCRIPTSCRIPT},{type:"small",style:si.SCRIPT},{type:"small",style:si.TEXT},{type:"stack"}],FN=[{type:"small",style:si.SCRIPTSCRIPT},{type:"small",style:si.SCRIPT},{type:"small",style:si.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],N1e=s(function(b){if(b.type==="small")return"Main-Regular";if(b.type==="large")return"Size"+b.size+"-Regular";if(b.type==="stack")return"Size4-Regular";var v=b.type;throw new Error("Add support for delim type '"+v+"' here.")},"delimTypeToFont"),zN=s(function(b,v,x,w){for(var R=Math.min(2,3-w.style.size),$=R;$v)return M}return x[x.length-1]},"traverseSequence"),hS=s(function(b,v,x,w,R,$){b==="<"||b==="\\lt"||b==="⟨"?b="\\langle":(b===">"||b==="\\gt"||b==="⟩")&&(b="\\rangle");var M;BN.has(b)?M=M1e:ON.has(b)?M=FN:M=D1e;var N=zN(b,v,M,w);return N.type==="small"?_1e(b,N.style,x,w,R,$):N.type==="large"?DN(b,N.size,x,w,R,$):NN(b,v,x,w,R,$)},"makeCustomSizedDelim"),dS=s(function(b,v,x,w,R,$){var M=w.fontMetrics().axisHeight*w.sizeMultiplier,N=901,j=5/w.fontMetrics().ptPerEm,I=Math.max(v-M,x+M),z=Math.max(I/500*N,2*I-j);return hS(b,z,!0,w,R,$)},"makeLeftRightDelim"),qN={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},O1e=new Set(["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","⌊","⌋","\\lceil","\\rceil","⌈","⌉","<",">","\\langle","⟨","\\rangle","⟩","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","⟮","⟯","\\lmoustache","\\rmoustache","⎰","⎱","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."]),s(UD,"isMiddleDelimNode"),s(Vx,"checkDelimiter"),dr({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1,argTypes:["primitive"]},handler:s((b,v)=>{var x=Vx(v[0],b);return{type:"delimsizing",mode:b.parser.mode,size:qN[b.funcName].size,mclass:qN[b.funcName].mclass,delim:x.text}},"handler"),htmlBuilder:s((b,v)=>b.delim==="."?jt([b.mclass]):PN(b.delim,b.size,v,b.mode,[b.mclass]),"htmlBuilder"),mathmlBuilder:s(b=>{var v=[];b.delim!=="."&&v.push(kl(b.delim,b.mode));var x=new Ht("mo",v);b.mclass==="mopen"||b.mclass==="mclose"?x.setAttribute("fence","true"):x.setAttribute("fence","false"),x.setAttribute("stretchy","true");var w=er(py[b.size]);return x.setAttribute("minsize",w),x.setAttribute("maxsize",w),x},"mathmlBuilder")}),s(YD,"assertParsed"),dr({type:"leftright-right",names:["\\right"],props:{numArgs:1,primitive:!0},handler:s((b,v)=>{var x=b.parser.gullet.macros.get("\\current@color");if(x&&typeof x!="string")throw new Xt("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:b.parser.mode,delim:Vx(v[0],b).text,color:x}},"handler")}),dr({type:"leftright",names:["\\left"],props:{numArgs:1,primitive:!0},handler:s((b,v)=>{var x=Vx(v[0],b),w=b.parser;++w.leftrightDepth;var R=w.parseExpression(!1);--w.leftrightDepth,w.expect("\\right",!1);var $=gi(w.parseFunction(),"leftright-right");return{type:"leftright",mode:w.mode,body:R,left:x.text,right:$.delim,rightColor:$.color}},"handler"),htmlBuilder:s((b,v)=>{YD(b);for(var x=Fa(b.body,v,!0,["mopen","mclose"]),w=0,R=0,$=!1,M=0;M{YD(b);var x=il(b.body,v);if(b.left!=="."){var w=new Ht("mo",[kl(b.left,b.mode)]);w.setAttribute("fence","true"),x.unshift(w)}if(b.right!=="."){var R=new Ht("mo",[kl(b.right,b.mode)]);R.setAttribute("fence","true"),b.rightColor&&R.setAttribute("mathcolor",b.rightColor),x.push(R)}return eS(x)},"mathmlBuilder")}),dr({type:"middle",names:["\\middle"],props:{numArgs:1,primitive:!0},handler:s((b,v)=>{var x=Vx(v[0],b);if(!b.parser.leftrightDepth)throw new Xt("\\middle without preceding \\left",x);return{type:"middle",mode:b.parser.mode,delim:x.text}},"handler"),htmlBuilder:s((b,v)=>{var x;return b.delim==="."?x=hy(v,[]):(x=PN(b.delim,1,v,b.mode,[]),x.isMiddle={delim:b.delim,options:v}),x},"htmlBuilder"),mathmlBuilder:s((b,v)=>{var x=b.delim==="\\vert"||b.delim==="|"?kl("|","text"):kl(b.delim,b.mode),w=new Ht("mo",[x]);return w.setAttribute("fence","true"),w.setAttribute("lspace","0.05em"),w.setAttribute("rspace","0.05em"),w},"mathmlBuilder")}),uT=s((b,v)=>{var x=G0(Ki(b.body,v),v),w=b.label.slice(1),R=v.sizeMultiplier,$,M,N=Eu(b.body);if(w==="sout")$=jt(["stretchy","sout"]),$.height=v.fontMetrics().defaultRuleThickness/R,M=-.5*v.fontMetrics().xHeight;else if(w==="phase"){var j=Jn({number:.6,unit:"pt"},v),I=Jn({number:.35,unit:"ex"},v),z=v.havingBaseSizing();R=R/z.sizeMultiplier;var F=x.height+x.depth+j+I;x.style.paddingLeft=er(F/2+j);var O=Math.floor(1e3*F*R),B=Wme(O),Y=new _u([new Gh("phase",B)],{width:"400em",height:er(O/1e3),viewBox:"0 0 400000 "+O,preserveAspectRatio:"xMinYMin slice"});$=Kh(["hide-tail"],[Y],v),$.style.height=er(F),M=x.depth+j+I}else{/cancel/.test(w)?N||x.classes.push("cancel-pad"):w==="angl"?x.classes.push("anglpad"):x.classes.push("boxpad");var U,H,J=0;/box/.test(w)?(J=Math.max(v.fontMetrics().fboxrule,v.minRuleThickness),U=v.fontMetrics().fboxsep+(w==="colorbox"?0:J),H=U):w==="angl"?(J=Math.max(v.fontMetrics().defaultRuleThickness,v.minRuleThickness),U=4*J,H=Math.max(0,.25-x.depth)):(U=N?.2:0,H=U),$=x1e(x,w,U,H,v),/fbox|boxed|fcolorbox/.test(w)?($.style.borderStyle="solid",$.style.borderWidth=er(J)):w==="angl"&&J!==.049&&($.style.borderTopWidth=er(J),$.style.borderRightWidth=er(J)),M=x.depth+H,b.backgroundColor&&($.style.backgroundColor=b.backgroundColor,b.borderColor&&($.style.borderColor=b.borderColor))}var te;if(b.backgroundColor)te=Xi({positionType:"individualShift",children:[{type:"elem",elem:$,shift:M},{type:"elem",elem:x,shift:0}]});else{var ie=/cancel|phase/.test(w)?["svg-align"]:[];te=Xi({positionType:"individualShift",children:[{type:"elem",elem:x,shift:0},{type:"elem",elem:$,shift:M,wrapperClasses:ie}]})}return/cancel/.test(w)&&(te.height=x.height,te.depth=x.depth),/cancel/.test(w)&&!N?jt(["mord","cancel-lap"],[te],v):jt(["mord"],[te],v)},"htmlBuilder$7"),hT=s((b,v)=>{var x,w=new Ht(b.label.includes("colorbox")?"mpadded":"menclose",[dn(b.body,v)]);switch(b.label){case"\\cancel":w.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":w.setAttribute("notation","downdiagonalstrike");break;case"\\phase":w.setAttribute("notation","phasorangle");break;case"\\sout":w.setAttribute("notation","horizontalstrike");break;case"\\fbox":w.setAttribute("notation","box");break;case"\\angl":w.setAttribute("notation","actuarial");break;case"\\fcolorbox":case"\\colorbox":if(x=v.fontMetrics().fboxsep*v.fontMetrics().ptPerEm,w.setAttribute("width","+"+2*x+"pt"),w.setAttribute("height","+"+2*x+"pt"),w.setAttribute("lspace",x+"pt"),w.setAttribute("voffset",x+"pt"),b.label==="\\fcolorbox"){var R=Math.max(v.fontMetrics().fboxrule,v.minRuleThickness);w.setAttribute("style","border: "+er(R)+" solid "+b.borderColor)}break;case"\\xcancel":w.setAttribute("notation","updiagonalstrike downdiagonalstrike");break}return b.backgroundColor&&w.setAttribute("mathbackground",b.backgroundColor),w},"mathmlBuilder$6"),dr({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,argTypes:["color","hbox"]},handler(b,v,x){var{parser:w,funcName:R}=b,$=gi(v[0],"color-token").color,M=v[1];return{type:"enclose",mode:w.mode,label:R,backgroundColor:$,body:M}},htmlBuilder:uT,mathmlBuilder:hT}),dr({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,argTypes:["color","color","hbox"]},handler(b,v,x){var{parser:w,funcName:R}=b,$=gi(v[0],"color-token").color,M=gi(v[1],"color-token").color,N=v[2];return{type:"enclose",mode:w.mode,label:R,backgroundColor:M,borderColor:$,body:N}},htmlBuilder:uT,mathmlBuilder:hT}),dr({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler(b,v){var{parser:x}=b;return{type:"enclose",mode:x.mode,label:"\\fbox",body:v[0]}}}),dr({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\phase"],props:{numArgs:1},handler(b,v){var{parser:x,funcName:w}=b,R=v[0];return{type:"enclose",mode:x.mode,label:w,body:R}},htmlBuilder:uT,mathmlBuilder:hT}),dr({type:"enclose",names:["\\sout"],props:{numArgs:1,allowedInText:!0},handler(b,v){var{parser:x,funcName:w}=b;x.mode==="math"&&x.settings.reportNonstrict("mathVsSout","LaTeX's \\sout works only in text mode");var R=v[0];return{type:"enclose",mode:x.mode,label:w,body:R}},htmlBuilder:uT,mathmlBuilder:hT}),dr({type:"enclose",names:["\\angl"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!1},handler(b,v){var{parser:x}=b;return{type:"enclose",mode:x.mode,label:"\\angl",body:v[0]}}}),WN={},s(Rc,"defineEnvironment"),jN={},s(Oe,"defineMacro"),Cl=(y=class{constructor(v,x,w){this.lexer=void 0,this.start=void 0,this.end=void 0,this.lexer=v,this.start=x,this.end=w}static range(v,x){return x?!v||!v.loc||!x.loc||v.loc.lexer!==x.loc.lexer?null:new y(v.loc.lexer,v.loc.start,x.loc.end):v&&v.loc}},s(y,"SourceLocation"),y),Hl=(k=class{constructor(v,x){this.text=void 0,this.loc=void 0,this.noexpand=void 0,this.treatAsRelax=void 0,this.text=v,this.loc=x}range(v,x){return new k(x,Cl.range(this,v))}},s(k,"Token"),k),s(HD,"getHLines"),dT=s(b=>{var v=b.parser.settings;if(!v.displayMode)throw new Xt("{"+b.envName+"} can be used only in display mode.")},"validateAmsEnvironmentContext"),B1e=new Set(["gather","gather*"]),s(RE,"getAutoTag"),s(Uh,"parseArray"),s($E,"dCellStyle"),Ic=s(function(b,v){var x,w,R=b.body.length,$=b.hLinesBeforeRow,M=0,N=new Array(R),j=[],I=Math.max(v.fontMetrics().arrayRuleWidth,v.minRuleThickness),z=1/v.fontMetrics().ptPerEm,F=5*z;if(b.colSeparationType&&b.colSeparationType==="small"){var O=v.havingStyle(si.SCRIPT).sizeMultiplier;F=.2778*(O/v.sizeMultiplier)}var B=b.colSeparationType==="CD"?Jn({number:3,unit:"ex"},v):12*z,Y=3*z,U=b.arraystretch*B,H=.7*U,J=.3*U,te=0;function ie(St){for(var Lt=0;Lt0&&(te+=.25),j.push({pos:te,isDashed:St[Lt]})}for(s(ie,"setHLinePos"),ie($[0]),x=0;x0&&(ge+=J,leSt))for(x=0;x=M)){var Ae=void 0;if(w>0||b.hskipBeforeAndAfter){var K,de;Ae=(K=(de=ye)==null?void 0:de.pregap)!=null?K:F,Ae!==0&&($e=jt(["arraycolsep"],[]),$e.style.width=er(Ae),xe.push($e))}var ee=[];for(x=0;x0){for(var Ze=H0("hline",v,I),Fe=H0("hdashline",v,I),Je=[{type:"elem",elem:De,shift:0}];j.length>0;){var Le=j.pop(),ct=Le.pos-ke;Le.isDashed?Je.push({type:"elem",elem:Fe,shift:ct}):Je.push({type:"elem",elem:Ze,shift:ct})}De=Xi({positionType:"individualShift",children:Je})}if(He.length===0)return jt(["mord"],[De],v);var qe=Xi({positionType:"individualShift",children:He}),mt=jt(["tag"],[qe],v);return Ru([De,mt])},"htmlBuilder"),P1e={c:"center ",l:"left ",r:"right "},Mc=s(function(b,v){for(var x=[],w=new Ht("mtd",[],["mtr-glue"]),R=new Ht("mtd",[],["mml-eqn-num"]),$=0;$0){var B=b.cols,Y="",U=!1,H=0,J=B.length;B[0].type==="separator"&&(F+="top ",H=1),B[B.length-1].type==="separator"&&(F+="bottom ",J-=1);for(var te=H;te0?"left ":"",F+=oe[oe.length-1].length>0?"right ":"";for(var fe=1;fe0&&O&&(U=1),x[B]={type:"align",align:Y,pregap:U,postgap:0}}return $.colSeparationType=O?"align":"alignat",$},"alignedHandler"),Rc({type:"array",names:["array","darray"],props:{numArgs:1},handler(b,v){var x=jx(v[0]),w=x?[v[0]]:gi(v[0],"ordgroup").body,R=w.map(function(M){var N=Wx(M),j=N.text;if("lcr".includes(j))return{type:"align",align:j};if(j==="|")return{type:"separator",separator:"|"};if(j===":")return{type:"separator",separator:":"};throw new Xt("Unknown column alignment: "+j,M)}),$={cols:R,hskipBeforeAndAfter:!0,maxNumCols:R.length};return Uh(b.parser,$,$E(b.envName))},htmlBuilder:Ic,mathmlBuilder:Mc}),Rc({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix","matrix*","pmatrix*","bmatrix*","Bmatrix*","vmatrix*","Vmatrix*"],props:{numArgs:0},handler(b){var v={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[b.envName.replace("*","")],x="c",w={hskipBeforeAndAfter:!1,cols:[{type:"align",align:x}]};if(b.envName.charAt(b.envName.length-1)==="*"){var R=b.parser;if(R.consumeSpaces(),R.fetch().text==="["){if(R.consume(),R.consumeSpaces(),x=R.fetch().text,!"lcr".includes(x))throw new Xt("Expected l or c or r",R.nextToken);R.consume(),R.consumeSpaces(),R.expect("]"),R.consume(),w.cols=[{type:"align",align:x}]}}var $=Uh(b.parser,w,$E(b.envName)),M=Math.max(0,...$.body.map(N=>N.length));return $.cols=new Array(M).fill({type:"align",align:x}),v?{type:"leftright",mode:b.mode,body:[$],left:v[0],right:v[1],rightColor:void 0}:$},htmlBuilder:Ic,mathmlBuilder:Mc}),Rc({type:"array",names:["smallmatrix"],props:{numArgs:0},handler(b){var v={arraystretch:.5},x=Uh(b.parser,v,"script");return x.colSeparationType="small",x},htmlBuilder:Ic,mathmlBuilder:Mc}),Rc({type:"array",names:["subarray"],props:{numArgs:1},handler(b,v){var x=jx(v[0]),w=x?[v[0]]:gi(v[0],"ordgroup").body,R=w.map(function(N){var j=Wx(N),I=j.text;if("lc".includes(I))return{type:"align",align:I};throw new Xt("Unknown column alignment: "+I,N)});if(R.length>1)throw new Xt("{subarray} can contain only one column");var $={cols:R,hskipBeforeAndAfter:!1,arraystretch:.5},M=Uh(b.parser,$,"script");if(M.body.length>0&&M.body[0].length>1)throw new Xt("{subarray} can contain only one column");return M},htmlBuilder:Ic,mathmlBuilder:Mc}),Rc({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(b){var v={arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},x=Uh(b.parser,v,$E(b.envName));return{type:"leftright",mode:b.mode,body:[x],left:b.envName.includes("r")?".":"\\{",right:b.envName.includes("r")?"\\}":".",rightColor:void 0}},htmlBuilder:Ic,mathmlBuilder:Mc}),Rc({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:VN,htmlBuilder:Ic,mathmlBuilder:Mc}),Rc({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(b){B1e.has(b.envName)&&dT(b);var v={cols:[{type:"align",align:"c"}],addJot:!0,colSeparationType:"gather",autoTag:RE(b.envName),emptySingleRow:!0,leqno:b.parser.settings.leqno};return Uh(b.parser,v,"display")},htmlBuilder:Ic,mathmlBuilder:Mc}),Rc({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:VN,htmlBuilder:Ic,mathmlBuilder:Mc}),Rc({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(b){dT(b);var v={autoTag:RE(b.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,leqno:b.parser.settings.leqno};return Uh(b.parser,v,"display")},htmlBuilder:Ic,mathmlBuilder:Mc}),Rc({type:"array",names:["CD"],props:{numArgs:0},handler(b){return dT(b),Cme(b.parser)},htmlBuilder:Ic,mathmlBuilder:Mc}),Oe("\\nonumber","\\gdef\\@eqnsw{0}"),Oe("\\notag","\\nonumber"),dr({type:"text",names:["\\hline","\\hdashline"],props:{numArgs:0,allowedInText:!0,allowedInMath:!0},handler(b,v){throw new Xt(b.funcName+" valid only within array environment")}}),UN=WN,dr({type:"environment",names:["\\begin","\\end"],props:{numArgs:1,argTypes:["text"]},handler(b,v){var{parser:x,funcName:w}=b,R=v[0];if(R.type!=="ordgroup")throw new Xt("Invalid environment name",R);for(var $="",M=0;M{var x=b.font,w=v.withFont(x);return Ki(b.body,w)},"htmlBuilder$5"),HN=s((b,v)=>{var x=b.font,w=v.withFont(x);return dn(b.body,w)},"mathmlBuilder$4"),GN={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak"},dr({type:"font",names:["\\mathrm","\\mathit","\\mathbf","\\mathnormal","\\mathsfit","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],props:{numArgs:1,allowedInArgument:!0},handler:s((b,v)=>{var{parser:x,funcName:w}=b,R=nT(v[0]),$=w;return $ in GN&&($=GN[$]),{type:"font",mode:x.mode,font:$.slice(1),body:R}},"handler"),htmlBuilder:YN,mathmlBuilder:HN}),dr({type:"mclass",names:["\\boldsymbol","\\bm"],props:{numArgs:1},handler:s((b,v)=>{var{parser:x}=b,w=v[0];return{type:"mclass",mode:x.mode,mclass:lT(w),body:[{type:"font",mode:x.mode,font:"boldsymbol",body:w}],isCharacterBox:Eu(w)}},"handler")}),dr({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it","\\cal"],props:{numArgs:0,allowedInText:!0},handler:s((b,v)=>{var{parser:x,funcName:w,breakOnTokenText:R}=b,{mode:$}=x,M=x.parseExpression(!0,R);return{type:"font",mode:$,font:"math"+w.slice(1),body:{type:"ordgroup",mode:x.mode,body:M}}},"handler"),htmlBuilder:YN,mathmlBuilder:HN}),F1e=s((b,v)=>{var x=v.style,w=x.fracNum(),R=x.fracDen(),$;$=v.havingStyle(w);var M=Ki(b.numer,$,v);if(b.continued){var N=8.5/v.fontMetrics().ptPerEm,j=3.5/v.fontMetrics().ptPerEm;M.height=M.height0?Y=3*O:Y=7*O,U=v.fontMetrics().denom1):(F>0?(B=v.fontMetrics().num2,Y=O):(B=v.fontMetrics().num3,Y=3*O),U=v.fontMetrics().denom2);var H;if(z){var J=v.fontMetrics().axisHeight;B-M.depth-(J+.5*F){var x=new Ht("mfrac",[dn(b.numer,v),dn(b.denom,v)]);if(!b.hasBarLine)x.setAttribute("linethickness","0px");else if(b.barSize){var w=Jn(b.barSize,v);x.setAttribute("linethickness",er(w))}if(b.leftDelim!=null||b.rightDelim!=null){var R=[];if(b.leftDelim!=null){var $=new Ht("mo",[new $a(b.leftDelim.replace("\\",""))]);$.setAttribute("fence","true"),R.push($)}if(R.push(x),b.rightDelim!=null){var M=new Ht("mo",[new $a(b.rightDelim.replace("\\",""))]);M.setAttribute("fence","true"),R.push(M)}return eS(R)}return x},"mathmlBuilder$3"),XN=s((b,v)=>{if(!v)return b;var x={type:"styling",mode:b.mode,style:v,body:[b]};return x},"wrapWithStyle"),dr({type:"genfrac",names:["\\cfrac","\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,allowedInArgument:!0},handler:s((b,v)=>{var{parser:x,funcName:w}=b,R=v[0],$=v[1],M,N=null,j=null;switch(w){case"\\cfrac":case"\\dfrac":case"\\frac":case"\\tfrac":M=!0;break;case"\\\\atopfrac":M=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":M=!1,N="(",j=")";break;case"\\\\bracefrac":M=!1,N="\\{",j="\\}";break;case"\\\\brackfrac":M=!1,N="[",j="]";break;default:throw new Error("Unrecognized genfrac command")}var I=w==="\\cfrac",z=null;return I||w.startsWith("\\d")?z="display":w.startsWith("\\t")&&(z="text"),XN({type:"genfrac",mode:x.mode,numer:R,denom:$,continued:I,hasBarLine:M,leftDelim:N,rightDelim:j,barSize:null},z)},"handler"),htmlBuilder:F1e,mathmlBuilder:z1e}),dr({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler(b){var{parser:v,funcName:x,token:w}=b,R;switch(x){case"\\over":R="\\frac";break;case"\\choose":R="\\binom";break;case"\\atop":R="\\\\atopfrac";break;case"\\brace":R="\\\\bracefrac";break;case"\\brack":R="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:v.mode,replaceWith:R,token:w}}}),KN=["display","text","script","scriptscript"],QN=s(function(b){var v=null;return b.length>0&&(v=b,v=v==="."?null:v),v},"delimFromValue"),dr({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,allowedInArgument:!0,argTypes:["math","math","size","text","math","math"]},handler(b,v){var{parser:x}=b,w=v[4],R=v[5],$=nT(v[0]),M=$.type==="atom"&&$.family==="open"?QN($.text):null,N=nT(v[1]),j=N.type==="atom"&&N.family==="close"?QN(N.text):null,I=gi(v[2],"size"),z,F=null;I.isBlank?z=!0:(F=I.value,z=F.number>0);var O=null,B=v[3];if(B.type==="ordgroup"){if(B.body.length>0){var Y=gi(B.body[0],"textord");O=KN[Number(Y.text)]}}else B=gi(B,"textord"),O=KN[Number(B.text)];return XN({type:"genfrac",mode:x.mode,numer:w,denom:R,continued:!1,hasBarLine:z,barSize:F,leftDelim:M,rightDelim:j},O)}}),dr({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler(b,v){var{parser:x,funcName:w,token:R}=b;return{type:"infix",mode:x.mode,replaceWith:"\\\\abovefrac",size:gi(v[0],"size").value,token:R}}}),dr({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:s((b,v)=>{var{parser:x,funcName:w}=b,R=v[0],$=gi(v[1],"infix").size;if(!$)throw new Error("\\\\abovefrac expected size, but got "+String($));var M=v[2],N=$.number>0;return{type:"genfrac",mode:x.mode,numer:R,denom:M,continued:!1,hasBarLine:N,barSize:$,leftDelim:null,rightDelim:null}},"handler")}),ZN=s((b,v)=>{var x=v.style,w,R;b.type==="supsub"?(w=b.sup?Ki(b.sup,v.havingStyle(x.sup()),v):Ki(b.sub,v.havingStyle(x.sub()),v),R=gi(b.base,"horizBrace")):R=gi(b,"horizBrace");var $=Ki(R.base,v.havingBaseStyle(si.DISPLAY)),M=sT(R,v),N;if(R.isOver?N=Xi({positionType:"firstBaseline",children:[{type:"elem",elem:$},{type:"kern",size:.1},{type:"elem",elem:M,wrapperClasses:["svg-align"]}]}):N=Xi({positionType:"bottom",positionData:$.depth+.1+M.height,children:[{type:"elem",elem:M,wrapperClasses:["svg-align"]},{type:"kern",size:.1},{type:"elem",elem:$}]}),w){var j=jt(["minner",R.isOver?"mover":"munder"],[N],v);R.isOver?N=Xi({positionType:"firstBaseline",children:[{type:"elem",elem:j},{type:"kern",size:.2},{type:"elem",elem:w}]}):N=Xi({positionType:"bottom",positionData:j.depth+.2+w.height+w.depth,children:[{type:"elem",elem:w},{type:"kern",size:.2},{type:"elem",elem:j}]})}return jt(["minner",R.isOver?"mover":"munder"],[N],v)},"htmlBuilder$3"),q1e=s((b,v)=>{var x=aT(b.label);return new Ht(b.isOver?"mover":"munder",[dn(b.base,v),x])},"mathmlBuilder$2"),dr({type:"horizBrace",names:["\\overbrace","\\underbrace","\\overbracket","\\underbracket"],props:{numArgs:1},handler(b,v){var{parser:x,funcName:w}=b;return{type:"horizBrace",mode:x.mode,label:w,isOver:w.includes("\\over"),base:v[0]}},htmlBuilder:ZN,mathmlBuilder:q1e}),dr({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:s((b,v)=>{var{parser:x}=b,w=v[1],R=gi(v[0],"url").url;return x.settings.isTrusted({command:"\\href",url:R})?{type:"href",mode:x.mode,href:R,body:Ra(w)}:x.formatUnsupportedCmd("\\href")},"handler"),htmlBuilder:s((b,v)=>{var x=Fa(b.body,v,!1);return i1e(b.href,[],x,v)},"htmlBuilder"),mathmlBuilder:s((b,v)=>{var x=Qh(b.body,v);return x instanceof Ht||(x=new Ht("mrow",[x])),x.setAttribute("href",b.href),x},"mathmlBuilder")}),dr({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:s((b,v)=>{var{parser:x}=b,w=gi(v[0],"url").url;if(!x.settings.isTrusted({command:"\\url",url:w}))return x.formatUnsupportedCmd("\\url");for(var R=[],$=0;${var{parser:x,funcName:w,token:R}=b,$=gi(v[0],"raw").string,M=v[1];x.settings.strict&&x.settings.reportNonstrict("htmlExtension","HTML extension is disabled on strict mode");var N,j={};switch(w){case"\\htmlClass":j.class=$,N={command:"\\htmlClass",class:$};break;case"\\htmlId":j.id=$,N={command:"\\htmlId",id:$};break;case"\\htmlStyle":j.style=$,N={command:"\\htmlStyle",style:$};break;case"\\htmlData":{for(var I=$.split(","),z=0;z{var x=Fa(b.body,v,!1),w=["enclosing"];b.attributes.class&&w.push(...b.attributes.class.trim().split(/\s+/));var R=jt(w,x,v);for(var $ in b.attributes)$!=="class"&&b.attributes.hasOwnProperty($)&&R.setAttribute($,b.attributes[$]);return R},"htmlBuilder"),mathmlBuilder:s((b,v)=>Qh(b.body,v),"mathmlBuilder")}),dr({type:"htmlmathml",names:["\\html@mathml"],props:{numArgs:2,allowedInArgument:!0,allowedInText:!0},handler:s((b,v)=>{var{parser:x}=b;return{type:"htmlmathml",mode:x.mode,html:Ra(v[0]),mathml:Ra(v[1])}},"handler"),htmlBuilder:s((b,v)=>{var x=Fa(b.html,v,!1);return Ru(x)},"htmlBuilder"),mathmlBuilder:s((b,v)=>Qh(b.mathml,v),"mathmlBuilder")}),pS=s(function(b){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(b))return{number:+b,unit:"bp"};var v=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(b);if(!v)throw new Xt("Invalid size: '"+b+"' in \\includegraphics");var x={number:+(v[1]+v[2]),unit:v[3]};if(!KD(x))throw new Xt("Invalid unit: '"+x.unit+"' in \\includegraphics.");return x},"sizeData"),dr({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:s((b,v,x)=>{var{parser:w}=b,R={number:0,unit:"em"},$={number:.9,unit:"em"},M={number:0,unit:"em"},N="";if(x[0])for(var j=gi(x[0],"raw").string,I=j.split(","),z=0;z{var x=Jn(b.height,v),w=0;b.totalheight.number>0&&(w=Jn(b.totalheight,v)-x);var R=0;b.width.number>0&&(R=Jn(b.width,v));var $={height:er(x+w)};R>0&&($.width=er(R)),w>0&&($.verticalAlign=er(-w));var M=new Xme(b.src,b.alt,$);return M.height=x,M.depth=w,M},"htmlBuilder"),mathmlBuilder:s((b,v)=>{var x=new Ht("mglyph",[]);x.setAttribute("alt",b.alt);var w=Jn(b.height,v),R=0;if(b.totalheight.number>0&&(R=Jn(b.totalheight,v)-w,x.setAttribute("valign",er(-R))),x.setAttribute("height",er(w+R)),b.width.number>0){var $=Jn(b.width,v);x.setAttribute("width",er($))}return x.setAttribute("src",b.src),x},"mathmlBuilder")}),dr({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],primitive:!0,allowedInText:!0},handler(b,v){var{parser:x,funcName:w}=b,R=gi(v[0],"size");if(x.settings.strict){var $=w[1]==="m",M=R.value.unit==="mu";$?(M||x.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+w+" supports only mu units, "+("not "+R.value.unit+" units")),x.mode!=="math"&&x.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+w+" works only in math mode")):M&&x.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+w+" doesn't support mu units")}return{type:"kern",mode:x.mode,dimension:R.value}},htmlBuilder(b,v){return fN(b.dimension,v)},mathmlBuilder(b,v){var x=Jn(b.dimension,v);return new bN(x)}}),dr({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:s((b,v)=>{var{parser:x,funcName:w}=b,R=v[0];return{type:"lap",mode:x.mode,alignment:w.slice(5),body:R}},"handler"),htmlBuilder:s((b,v)=>{var x;b.alignment==="clap"?(x=jt([],[Ki(b.body,v)]),x=jt(["inner"],[x],v)):x=jt(["inner"],[Ki(b.body,v)]);var w=jt(["fix"],[]),R=jt([b.alignment],[x,w],v),$=jt(["strut"]);return $.style.height=er(R.height+R.depth),R.depth&&($.style.verticalAlign=er(-R.depth)),R.children.unshift($),R=jt(["thinbox"],[R],v),jt(["mord","vbox"],[R],v)},"htmlBuilder"),mathmlBuilder:s((b,v)=>{var x=new Ht("mpadded",[dn(b.body,v)]);if(b.alignment!=="rlap"){var w=b.alignment==="llap"?"-1":"-0.5";x.setAttribute("lspace",w+"width")}return x.setAttribute("width","0px"),x},"mathmlBuilder")}),dr({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(b,v){var{funcName:x,parser:w}=b,R=w.mode;w.switchMode("math");var $=x==="\\("?"\\)":"$",M=w.parseExpression(!1,$);return w.expect($),w.switchMode(R),{type:"styling",mode:w.mode,style:"text",resetFont:!0,body:M}}}),dr({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(b,v){throw new Xt("Mismatched "+b.funcName)}}),JN=s((b,v)=>{switch(v.style.size){case si.DISPLAY.size:return b.display;case si.TEXT.size:return b.text;case si.SCRIPT.size:return b.script;case si.SCRIPTSCRIPT.size:return b.scriptscript;default:return b.text}},"chooseMathStyle"),dr({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4,primitive:!0},handler:s((b,v)=>{var{parser:x}=b;return{type:"mathchoice",mode:x.mode,display:Ra(v[0]),text:Ra(v[1]),script:Ra(v[2]),scriptscript:Ra(v[3])}},"handler"),htmlBuilder:s((b,v)=>{var x=JN(b,v),w=Fa(x,v,!1);return Ru(w)},"htmlBuilder"),mathmlBuilder:s((b,v)=>{var x=JN(b,v);return Qh(x,v)},"mathmlBuilder")}),eO=s((b,v,x,w,R,$,M)=>{b=jt([],[b]);var N=x&&Eu(x),j,I;if(v){var z=Ki(v,w.havingStyle(R.sup()),w);I={elem:z,kern:Math.max(w.fontMetrics().bigOpSpacing1,w.fontMetrics().bigOpSpacing3-z.depth)}}if(x){var F=Ki(x,w.havingStyle(R.sub()),w);j={elem:F,kern:Math.max(w.fontMetrics().bigOpSpacing2,w.fontMetrics().bigOpSpacing4-F.height)}}var O;if(I&&j){var B=w.fontMetrics().bigOpSpacing5+j.elem.height+j.elem.depth+j.kern+b.depth+M;O=Xi({positionType:"bottom",positionData:B,children:[{type:"kern",size:w.fontMetrics().bigOpSpacing5},{type:"elem",elem:j.elem,marginLeft:er(-$)},{type:"kern",size:j.kern},{type:"elem",elem:b},{type:"kern",size:I.kern},{type:"elem",elem:I.elem,marginLeft:er($)},{type:"kern",size:w.fontMetrics().bigOpSpacing5}]})}else if(j){var Y=b.height-M;O=Xi({positionType:"top",positionData:Y,children:[{type:"kern",size:w.fontMetrics().bigOpSpacing5},{type:"elem",elem:j.elem,marginLeft:er(-$)},{type:"kern",size:j.kern},{type:"elem",elem:b}]})}else if(I){var U=b.depth+M;O=Xi({positionType:"bottom",positionData:U,children:[{type:"elem",elem:b},{type:"kern",size:I.kern},{type:"elem",elem:I.elem,marginLeft:er($)},{type:"kern",size:w.fontMetrics().bigOpSpacing5}]})}else return b;var H=[O];if(j&&$!==0&&!N){var J=jt(["mspace"],[],w);J.style.marginRight=er($),H.unshift(J)}return jt(["mop","op-limits"],H,w)},"assembleSupSub"),tO=new Set(["\\smallint"]),X0=s((b,v)=>{var x,w,R=!1,$;b.type==="supsub"?(x=b.sup,w=b.sub,$=gi(b.base,"op"),R=!0):$=gi(b,"op");var M=v.style,N=!1;M.size===si.DISPLAY.size&&$.symbol&&!tO.has($.name)&&(N=!0);var j,I;if($.symbol){var z=N?"Size2-Regular":"Size1-Regular",F="";if(($.name==="\\oiint"||$.name==="\\oiiint")&&(F=$.name.slice(1),$.name=F==="oiint"?"\\iint":"\\iiint"),j=no($.name,z,"math",v,["mop","op-symbol",N?"large-op":"small-op"]),I=j.italic,F.length>0){var O=mN(F+"Size"+(N?"2":"1"),v);j=Xi({positionType:"individualShift",children:[{type:"elem",elem:j,shift:0},{type:"elem",elem:O,shift:N?.08:0}]}),$.name="\\"+F,j.classes.unshift("mop"),j.italic=I}}else if($.body){var B=Fa($.body,v,!0);B.length===1&&B[0]instanceof tl?(j=B[0],j.classes[0]="mop"):j=jt(["mop"],B,v)}else{for(var Y=[],U=1;U<$.name.length;U++)Y.push(GE($.name[U],$.mode,v));j=jt(["mop"],Y,v)}var H=0,J=0;if((j instanceof tl||$.name==="\\oiint"||$.name==="\\oiiint")&&!$.suppressBaseShift){var te;H=(j.height-j.depth)/2-v.fontMetrics().axisHeight,J=(te=j.italic)!=null?te:0}return R?eO(j,x,w,v,M,J,H):(H&&(j.style.position="relative",j.style.top=er(H)),j)},"htmlBuilder$2"),fy=s((b,v)=>{var x;if(b.symbol)x=new Ht("mo",[kl(b.name,b.mode)]),tO.has(b.name)&&x.setAttribute("largeop","false");else if(b.body)x=new Ht("mo",il(b.body,v));else{x=new Ht("mi",[new $a(b.name.slice(1))]);var w=new Ht("mo",[kl("⁡","text")]);b.parentIsSupSub?x=new Ht("mrow",[x,w]):x=qD([x,w])}return x},"mathmlBuilder$1"),W1e={"∏":"\\prod","∐":"\\coprod","∑":"\\sum","⋀":"\\bigwedge","⋁":"\\bigvee","⋂":"\\bigcap","⋃":"\\bigcup","⨀":"\\bigodot","⨁":"\\bigoplus","⨂":"\\bigotimes","⨄":"\\biguplus","⨆":"\\bigsqcup"},dr({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint","∏","∐","∑","⋀","⋁","⋂","⋃","⨀","⨁","⨂","⨄","⨆"],props:{numArgs:0},handler:s((b,v)=>{var{parser:x,funcName:w}=b,R=w;return R.length===1&&(R=W1e[R]),{type:"op",mode:x.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:R}},"handler"),htmlBuilder:X0,mathmlBuilder:fy}),dr({type:"op",names:["\\mathop"],props:{numArgs:1,primitive:!0},handler:s((b,v)=>{var{parser:x}=b,w=v[0];return{type:"op",mode:x.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:Ra(w)}},"handler"),htmlBuilder:X0,mathmlBuilder:fy}),j1e={"∫":"\\int","∬":"\\iint","∭":"\\iiint","∮":"\\oint","∯":"\\oiint","∰":"\\oiiint"},dr({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler(b){var{parser:v,funcName:x}=b;return{type:"op",mode:v.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:x}},htmlBuilder:X0,mathmlBuilder:fy}),dr({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler(b){var{parser:v,funcName:x}=b;return{type:"op",mode:v.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:x}},htmlBuilder:X0,mathmlBuilder:fy}),dr({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","∫","∬","∭","∮","∯","∰"],props:{numArgs:0,allowedInArgument:!0},handler(b){var{parser:v,funcName:x}=b,w=x;return w.length===1&&(w=j1e[w]),{type:"op",mode:v.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:w}},htmlBuilder:X0,mathmlBuilder:fy}),rO=s((b,v)=>{var x,w,R=!1,$;b.type==="supsub"?(x=b.sup,w=b.sub,$=gi(b.base,"operatorname"),R=!0):$=gi(b,"operatorname");var M;if($.body.length>0){for(var N=$.body.map(F=>{var O="text"in F?F.text:void 0;return typeof O=="string"?{type:"textord",mode:F.mode,text:O}:F}),j=Fa(N,v.withFont("mathrm"),!0),I=0;I{for(var x=il(b.body,v.withFont("mathrm")),w=!0,R=0;Rz.toText()).join("");x=[new $a(N)]}var j=new Ht("mi",x);j.setAttribute("mathvariant","normal");var I=new Ht("mo",[kl("⁡","text")]);return b.parentIsSupSub?new Ht("mrow",[j,I]):qD([j,I])},"mathmlBuilder"),dr({type:"operatorname",names:["\\operatorname@","\\operatornamewithlimits"],props:{numArgs:1},handler:s((b,v)=>{var{parser:x,funcName:w}=b,R=v[0];return{type:"operatorname",mode:x.mode,body:Ra(R),alwaysHandleSupSub:w==="\\operatornamewithlimits",limits:!1,parentIsSupSub:!1}},"handler"),htmlBuilder:rO,mathmlBuilder:V1e}),Oe("\\operatorname","\\@ifstar\\operatornamewithlimits\\operatorname@"),bp({type:"ordgroup",htmlBuilder(b,v){return b.semisimple?Ru(Fa(b.body,v,!1)):jt(["mord"],Fa(b.body,v,!0),v)},mathmlBuilder(b,v){return Qh(b.body,v,!0)}}),dr({type:"overline",names:["\\overline"],props:{numArgs:1},handler(b,v){var{parser:x}=b,w=v[0];return{type:"overline",mode:x.mode,body:w}},htmlBuilder(b,v){var x=Ki(b.body,v.havingCrampedStyle()),w=H0("overline-line",v),R=v.fontMetrics().defaultRuleThickness,$=Xi({positionType:"firstBaseline",children:[{type:"elem",elem:x},{type:"kern",size:3*R},{type:"elem",elem:w},{type:"kern",size:R}]});return jt(["mord","overline"],[$],v)},mathmlBuilder(b,v){var x=new Ht("mo",[new $a("‾")]);x.setAttribute("stretchy","true");var w=new Ht("mover",[dn(b.body,v),x]);return w.setAttribute("accent","true"),w}}),dr({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:s((b,v)=>{var{parser:x}=b,w=v[0];return{type:"phantom",mode:x.mode,body:Ra(w)}},"handler"),htmlBuilder:s((b,v)=>{var x=Fa(b.body,v.withPhantom(),!1);return Ru(x)},"htmlBuilder"),mathmlBuilder:s((b,v)=>{var x=il(b.body,v);return new Ht("mphantom",x)},"mathmlBuilder")}),Oe("\\hphantom","\\smash{\\phantom{#1}}"),dr({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:s((b,v)=>{var{parser:x}=b,w=v[0];return{type:"vphantom",mode:x.mode,body:w}},"handler"),htmlBuilder:s((b,v)=>{var x=jt(["inner"],[Ki(b.body,v.withPhantom())]),w=jt(["fix"],[]);return jt(["mord","rlap"],[x,w],v)},"htmlBuilder"),mathmlBuilder:s((b,v)=>{var x=il(Ra(b.body),v),w=new Ht("mphantom",x),R=new Ht("mpadded",[w]);return R.setAttribute("width","0px"),R},"mathmlBuilder")}),dr({type:"raisebox",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler(b,v){var{parser:x}=b,w=gi(v[0],"size").value,R=v[1];return{type:"raisebox",mode:x.mode,dy:w,body:R}},htmlBuilder(b,v){var x=Ki(b.body,v),w=Jn(b.dy,v);return Xi({positionType:"shift",positionData:-w,children:[{type:"elem",elem:x}]})},mathmlBuilder(b,v){var x=new Ht("mpadded",[dn(b.body,v)]),w=b.dy.number+b.dy.unit;return x.setAttribute("voffset",w),x}}),dr({type:"internal",names:["\\relax"],props:{numArgs:0,allowedInText:!0,allowedInArgument:!0},handler(b){var{parser:v}=b;return{type:"internal",mode:v.mode}}}),dr({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["size","size","size"]},handler(b,v,x){var{parser:w}=b,R=x[0],$=gi(v[0],"size"),M=gi(v[1],"size");return{type:"rule",mode:w.mode,shift:R&&gi(R,"size").value,width:$.value,height:M.value}},htmlBuilder(b,v){var x=jt(["mord","rule"],[],v),w=Jn(b.width,v),R=Jn(b.height,v),$=b.shift?Jn(b.shift,v):0;return x.style.borderRightWidth=er(w),x.style.borderTopWidth=er(R),x.style.bottom=er($),x.width=w,x.height=R+$,x.depth=-$,x.maxFontSize=R*1.125*v.sizeMultiplier,x},mathmlBuilder(b,v){var x=Jn(b.width,v),w=Jn(b.height,v),R=b.shift?Jn(b.shift,v):0,$=v.color&&v.getColor()||"black",M=new Ht("mspace");M.setAttribute("mathbackground",$),M.setAttribute("width",er(x)),M.setAttribute("height",er(w));var N=new Ht("mpadded",[M]);return R>=0?N.setAttribute("height",er(R)):(N.setAttribute("height",er(R)),N.setAttribute("depth",er(-R))),N.setAttribute("voffset",er(R)),N}}),s(GD,"sizingGroup"),iO=["\\tiny","\\sixptsize","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"],U1e=s((b,v)=>{var x=v.havingSize(b.size);return GD(b.body,x,v)},"htmlBuilder"),dr({type:"sizing",names:iO,props:{numArgs:0,allowedInText:!0},handler:s((b,v)=>{var{breakOnTokenText:x,funcName:w,parser:R}=b,$=R.parseExpression(!1,x);return{type:"sizing",mode:R.mode,size:iO.indexOf(w)+1,body:$}},"handler"),htmlBuilder:U1e,mathmlBuilder:s((b,v)=>{var x=v.havingSize(b.size),w=il(b.body,x),R=new Ht("mstyle",w);return R.setAttribute("mathsize",er(x.sizeMultiplier)),R},"mathmlBuilder")}),dr({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:s((b,v,x)=>{var{parser:w}=b,R=!1,$=!1,M=x[0]&&gi(x[0],"ordgroup");if(M)for(var N,j=0;j{var x=jt([],[Ki(b.body,v)]);if(!b.smashHeight&&!b.smashDepth)return x;if(b.smashHeight&&(x.height=0),b.smashDepth&&(x.depth=0),b.smashHeight&&b.smashDepth)return jt(["mord","smash"],[x],v);if(x.children)for(var w=0;w{var x=new Ht("mpadded",[dn(b.body,v)]);return b.smashHeight&&x.setAttribute("height","0px"),b.smashDepth&&x.setAttribute("depth","0px"),x},"mathmlBuilder")}),dr({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler(b,v,x){var{parser:w}=b,R=x[0],$=v[0];return{type:"sqrt",mode:w.mode,body:$,index:R}},htmlBuilder(b,v){var x=Ki(b.body,v.havingCrampedStyle());x.height===0&&(x.height=v.fontMetrics().xHeight),x=G0(x,v);var w=v.fontMetrics(),R=w.defaultRuleThickness,$=R;v.style.idx.height+x.depth+M&&(M=(M+F-x.height-x.depth)/2);var O=j.height-x.height-M-I;x.style.paddingLeft=er(z);var B=Xi({positionType:"firstBaseline",children:[{type:"elem",elem:x,wrapperClasses:["svg-align"]},{type:"kern",size:-(x.height+O)},{type:"elem",elem:j},{type:"kern",size:I}]});if(b.index){var Y=v.havingStyle(si.SCRIPTSCRIPT),U=Ki(b.index,Y,v),H=.6*(B.height-B.depth),J=Xi({positionType:"shift",positionData:-H,children:[{type:"elem",elem:U}]}),te=jt(["root"],[J]);return jt(["mord","sqrt"],[te,B],v)}else return jt(["mord","sqrt"],[B],v)},mathmlBuilder(b,v){var{body:x,index:w}=b;return w?new Ht("mroot",[dn(x,v),dn(w,v)]):new Ht("msqrt",[dn(x,v)])}}),fS={display:si.DISPLAY,text:si.TEXT,script:si.SCRIPT,scriptscript:si.SCRIPTSCRIPT},s(wme,"isStyleStr"),dr({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(b,v){var{breakOnTokenText:x,funcName:w,parser:R}=b,$=R.parseExpression(!0,x),M=w.slice(1,w.length-5);if(!wme(M))throw new Error("Unknown style: "+M);return{type:"styling",mode:R.mode,style:M,body:$}},htmlBuilder(b,v){var x=fS[b.style],w=v.havingStyle(x);return b.resetFont&&(w=w.withFont("")),GD(b.body,w,v)},mathmlBuilder(b,v){var x=fS[b.style],w=v.havingStyle(x);b.resetFont&&(w=w.withFont(""));var R=il(b.body,w),$=new Ht("mstyle",R),M={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]},N=M[b.style];return $.setAttribute("scriptlevel",N[0]),$.setAttribute("displaystyle",N[1]),$}}),Y1e=s(function(b,v){var x=b.base;if(x)if(x.type==="op"){var w=x.limits&&(v.style.size===si.DISPLAY.size||x.alwaysHandleSupSub);return w?X0:null}else if(x.type==="operatorname"){var R=x.alwaysHandleSupSub&&(v.style.size===si.DISPLAY.size||x.limits);return R?rO:null}else{if(x.type==="accent")return Eu(x.base)?rS:null;if(x.type==="horizBrace"){var $=!b.sub;return $===x.isOver?ZN:null}else return null}else return null},"htmlBuilderDelegate"),bp({type:"supsub",htmlBuilder(b,v){var x=Y1e(b,v);if(x)return x(b,v);var{base:w,sup:R,sub:$}=b,M=Ki(w,v),N,j,I=v.fontMetrics(),z=0,F=0,O=w&&Eu(w);if(R){var B=v.havingStyle(v.style.sup());N=Ki(R,B,v),O||(z=M.height-B.fontMetrics().supDrop*B.sizeMultiplier/v.sizeMultiplier)}if($){var Y=v.havingStyle(v.style.sub());j=Ki($,Y,v),O||(F=M.depth+Y.fontMetrics().subDrop*Y.sizeMultiplier/v.sizeMultiplier)}var U;v.style===si.DISPLAY?U=I.sup1:v.style.cramped?U=I.sup3:U=I.sup2;var H=v.sizeMultiplier,J=er(.5/I.ptPerEm/H),te=null;if(j){var ie=b.base&&b.base.type==="op"&&b.base.name&&(b.base.name==="\\oiint"||b.base.name==="\\oiiint");if(M instanceof tl||ie){var ue;te=er(-((ue=M.italic)!=null?ue:0))}}var se;if(N&&j){z=Math.max(z,U,N.depth+.25*I.xHeight),F=Math.max(F,I.sub2);var le=I.defaultRuleThickness,Te=4*le;if(z-N.depth-(j.height-F)0&&(z+=oe,F-=oe)}var fe=[{type:"elem",elem:j,shift:F,marginRight:J,marginLeft:te},{type:"elem",elem:N,shift:-z,marginRight:J}];se=Xi({positionType:"individualShift",children:fe})}else if(j){F=Math.max(F,I.sub1,j.height-.8*I.xHeight);var ge=[{type:"elem",elem:j,marginLeft:te,marginRight:J}];se=Xi({positionType:"shift",positionData:F,children:ge})}else if(N)z=Math.max(z,U,N.depth+.25*I.xHeight),se=Xi({positionType:"shift",positionData:-z,children:[{type:"elem",elem:N,marginRight:J}]});else throw new Error("supsub must have either sup or sub.");var ke=JE(M,"right")||"mord";return jt([ke],[M,jt(["msupsub"],[se])],v)},mathmlBuilder(b,v){var x=!1,w,R;b.base&&b.base.type==="horizBrace"&&(R=!!b.sup,R===b.base.isOver&&(x=!0,w=b.base.isOver)),b.base&&(b.base.type==="op"||b.base.type==="operatorname")&&(b.base.parentIsSupSub=!0);var $=[dn(b.base,v)];b.sub&&$.push(dn(b.sub,v)),b.sup&&$.push(dn(b.sup,v));var M;if(x)M=w?"mover":"munder";else if(b.sub)if(b.sup){var N=b.base;N&&N.type==="op"&&N.limits&&v.style===si.DISPLAY||N&&N.type==="operatorname"&&N.alwaysHandleSupSub&&(v.style===si.DISPLAY||N.limits)?M="munderover":M="msubsup"}else{var j=b.base;j&&j.type==="op"&&j.limits&&(v.style===si.DISPLAY||j.alwaysHandleSupSub)||j&&j.type==="operatorname"&&j.alwaysHandleSupSub&&(j.limits||v.style===si.DISPLAY)?M="munder":M="msub"}else{var I=b.base;I&&I.type==="op"&&I.limits&&(v.style===si.DISPLAY||I.alwaysHandleSupSub)||I&&I.type==="operatorname"&&I.alwaysHandleSupSub&&(I.limits||v.style===si.DISPLAY)?M="mover":M="msup"}return new Ht(M,$)}}),bp({type:"atom",htmlBuilder(b,v){return GE(b.text,b.mode,v,["m"+b.family])},mathmlBuilder(b,v){var x=new Ht("mo",[kl(b.text,b.mode)]);if(b.family==="bin"){var w=tS(b,v);w==="bold-italic"&&x.setAttribute("mathvariant",w)}else b.family==="punct"?x.setAttribute("separator","true"):(b.family==="open"||b.family==="close")&&x.setAttribute("stretchy","false");return x}}),nO={mi:"italic",mn:"normal",mtext:"normal"},bp({type:"mathord",htmlBuilder(b,v){return eT(b,v,"mathord")},mathmlBuilder(b,v){var x=new Ht("mi",[kl(b.text,b.mode,v)]),w=tS(b,v)||"italic";return w!==nO[x.type]&&x.setAttribute("mathvariant",w),x}}),bp({type:"textord",htmlBuilder(b,v){return eT(b,v,"textord")},mathmlBuilder(b,v){var x=kl(b.text,b.mode,v),w=tS(b,v)||"normal",R;return b.mode==="text"?R=new Ht("mtext",[x]):/[0-9]/.test(b.text)?R=new Ht("mn",[x]):b.text==="\\prime"?R=new Ht("mo",[x]):R=new Ht("mi",[x]),w!==nO[R.type]&&R.setAttribute("mathvariant",w),R}}),gS={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},mS={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}},bp({type:"spacing",htmlBuilder(b,v){if(mS.hasOwnProperty(b.text)){var x=mS[b.text].className||"";if(b.mode==="text"){var w=eT(b,v,"textord");return w.classes.push(x),w}else return jt(["mspace",x],[GE(b.text,b.mode,v)],v)}else{if(gS.hasOwnProperty(b.text))return jt(["mspace",gS[b.text]],[],v);throw new Xt('Unknown type of space "'+b.text+'"')}},mathmlBuilder(b,v){var x;if(mS.hasOwnProperty(b.text))x=new Ht("mtext",[new $a(" ")]);else{if(gS.hasOwnProperty(b.text))return new Ht("mspace");throw new Xt('Unknown type of space "'+b.text+'"')}return x}}),aO=s(()=>{var b=new Ht("mtd",[]);return b.setAttribute("width","50%"),b},"pad"),bp({type:"tag",mathmlBuilder(b,v){var x=new Ht("mtable",[new Ht("mtr",[aO(),new Ht("mtd",[Qh(b.body,v)]),aO(),new Ht("mtd",[Qh(b.tag,v)])])]);return x.setAttribute("width","100%"),x}}),sO={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},oO={"\\textbf":"textbf","\\textmd":"textmd"},H1e={"\\textit":"textit","\\textup":"textup"},lO=s((b,v)=>{var x=b.font;if(x){if(sO[x])return v.withTextFontFamily(sO[x]);if(oO[x])return v.withTextFontWeight(oO[x]);if(x==="\\emph")return v.fontShape==="textit"?v.withTextFontShape("textup"):v.withTextFontShape("textit")}else return v;return v.withTextFontShape(H1e[x])},"optionsWithFont"),dr({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup","\\emph"],props:{numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0},handler(b,v){var{parser:x,funcName:w}=b,R=v[0];return{type:"text",mode:x.mode,body:Ra(R),font:w}},htmlBuilder(b,v){var x=lO(b,v),w=Fa(b.body,x,!0);return jt(["mord","text"],w,x)},mathmlBuilder(b,v){var x=lO(b,v);return Qh(b.body,x)}}),dr({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler(b,v){var{parser:x}=b;return{type:"underline",mode:x.mode,body:v[0]}},htmlBuilder(b,v){var x=Ki(b.body,v),w=H0("underline-line",v),R=v.fontMetrics().defaultRuleThickness,$=Xi({positionType:"top",positionData:x.height,children:[{type:"kern",size:R},{type:"elem",elem:w},{type:"kern",size:3*R},{type:"elem",elem:x}]});return jt(["mord","underline"],[$],v)},mathmlBuilder(b,v){var x=new Ht("mo",[new $a("‾")]);x.setAttribute("stretchy","true");var w=new Ht("munder",[dn(b.body,v),x]);return w.setAttribute("accentunder","true"),w}}),dr({type:"vcenter",names:["\\vcenter"],props:{numArgs:1,argTypes:["original"],allowedInText:!1},handler(b,v){var{parser:x}=b;return{type:"vcenter",mode:x.mode,body:v[0]}},htmlBuilder(b,v){var x=Ki(b.body,v),w=v.fontMetrics().axisHeight,R=.5*(x.height-w-(x.depth+w));return Xi({positionType:"shift",positionData:R,children:[{type:"elem",elem:x}]})},mathmlBuilder(b,v){var x=new Ht("mpadded",[dn(b.body,v)],["vcenter"]);return new Ht("mrow",[x])}}),dr({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler(b,v,x){throw new Xt("\\verb ended by end of line instead of matching delimiter")},htmlBuilder(b,v){for(var x=cO(b),w=[],R=v.havingStyle(v.style.text()),$=0;$b.body.replace(/ /g,b.star?"␣":" "),"makeVerb"),Zh=yN,uO=`[ \r + ]`,G1e="\\\\[a-zA-Z@]+",X1e="\\\\[^\uD800-\uDFFF]",K1e="("+G1e+")"+uO+"*",Q1e=`\\\\( +|[ \r ]+ +?)[ \r ]*`,yS="[̀-ͯ]",Z1e=new RegExp(yS+"+$"),J1e="("+uO+"+)|"+(Q1e+"|")+"([!-\\[\\]-‧‪-퟿豈-￿]"+(yS+"*")+"|[\uD800-\uDBFF][\uDC00-\uDFFF]"+(yS+"*")+"|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5"+("|"+K1e)+("|"+X1e+")"),hO=(C=class{constructor(v,x){this.input=void 0,this.settings=void 0,this.tokenRegex=void 0,this.catcodes=void 0,this.input=v,this.settings=x,this.tokenRegex=new RegExp(J1e,"g"),this.catcodes={"%":14,"~":13}}setCatcode(v,x){this.catcodes[v]=x}lex(){var v=this.input,x=this.tokenRegex.lastIndex;if(x===v.length)return new Hl("EOF",new Cl(this,x,x));var w=this.tokenRegex.exec(v);if(w===null||w.index!==x)throw new Xt("Unexpected character: '"+v[x]+"'",new Hl(v[x],new Cl(this,x,x+1)));var R=w[6]||w[3]||(w[2]?"\\ ":" ");if(this.catcodes[R]===14){var $=v.indexOf(` +`,this.tokenRegex.lastIndex);return $===-1?(this.tokenRegex.lastIndex=v.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=$+1,this.lex()}return new Hl(R,new Cl(this,x,this.tokenRegex.lastIndex))}},s(C,"Lexer"),C),eye=(S=class{constructor(v,x){v===void 0&&(v={}),x===void 0&&(x={}),this.current=void 0,this.builtins=void 0,this.undefStack=void 0,this.current=x,this.builtins=v,this.undefStack=[]}beginGroup(){this.undefStack.push({})}endGroup(){if(this.undefStack.length===0)throw new Xt("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");var v=this.undefStack.pop();for(var x in v)v.hasOwnProperty(x)&&(v[x]==null?delete this.current[x]:this.current[x]=v[x])}endGroups(){for(;this.undefStack.length>0;)this.endGroup()}has(v){return this.current.hasOwnProperty(v)||this.builtins.hasOwnProperty(v)}get(v){return this.current.hasOwnProperty(v)?this.current[v]:this.builtins[v]}set(v,x,w){if(w===void 0&&(w=!1),w){for(var R=0;R0&&(this.undefStack[this.undefStack.length-1][v]=x)}else{var $=this.undefStack[this.undefStack.length-1];$&&!$.hasOwnProperty(v)&&($[v]=this.current[v])}x==null?delete this.current[v]:this.current[v]=x}},s(S,"Namespace"),S),tye=jN,Oe("\\noexpand",function(b){var v=b.popToken();return b.isExpandable(v.text)&&(v.noexpand=!0,v.treatAsRelax=!0),{tokens:[v],numArgs:0}}),Oe("\\expandafter",function(b){var v=b.popToken();return b.expandOnce(!0),{tokens:[v],numArgs:0}}),Oe("\\@firstoftwo",function(b){var v=b.consumeArgs(2);return{tokens:v[0],numArgs:0}}),Oe("\\@secondoftwo",function(b){var v=b.consumeArgs(2);return{tokens:v[1],numArgs:0}}),Oe("\\@ifnextchar",function(b){var v=b.consumeArgs(3);b.consumeSpaces();var x=b.future();return v[0].length===1&&v[0][0].text===x.text?{tokens:v[1],numArgs:0}:{tokens:v[2],numArgs:0}}),Oe("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),Oe("\\TextOrMath",function(b){var v=b.consumeArgs(2);return b.mode==="text"?{tokens:v[0],numArgs:0}:{tokens:v[1],numArgs:0}}),dO={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15},Oe("\\char",function(b){var v=b.popToken(),x,w=0;if(v.text==="'")x=8,v=b.popToken();else if(v.text==='"')x=16,v=b.popToken();else if(v.text==="`")if(v=b.popToken(),v.text[0]==="\\")w=v.text.charCodeAt(1);else{if(v.text==="EOF")throw new Xt("\\char` missing argument");w=v.text.charCodeAt(0)}else x=10;if(x){if(w=dO[v.text],w==null||w>=x)throw new Xt("Invalid base-"+x+" digit "+v.text);for(var R;(R=dO[b.future().text])!=null&&R{var R=b.consumeArg().tokens;if(R.length!==1)throw new Xt("\\newcommand's first argument must be a macro name");var $=R[0].text,M=b.isDefined($);if(M&&!v)throw new Xt("\\newcommand{"+$+"} attempting to redefine "+($+"; use \\renewcommand"));if(!M&&!x)throw new Xt("\\renewcommand{"+$+"} when command "+$+" does not yet exist; use \\newcommand");var N=0;if(R=b.consumeArg().tokens,R.length===1&&R[0].text==="["){for(var j="",I=b.expandNextToken();I.text!=="]"&&I.text!=="EOF";)j+=I.text,I=b.expandNextToken();if(!j.match(/^\s*[0-9]+\s*$/))throw new Xt("Invalid number of arguments: "+j);N=parseInt(j),R=b.consumeArg().tokens}return M&&w||b.macros.set($,{tokens:R,numArgs:N}),""},"newcommand"),Oe("\\newcommand",b=>vS(b,!1,!0,!1)),Oe("\\renewcommand",b=>vS(b,!0,!1,!1)),Oe("\\providecommand",b=>vS(b,!0,!0,!0)),Oe("\\message",b=>{var v=b.consumeArgs(1)[0];return console.log(v.reverse().map(x=>x.text).join("")),""}),Oe("\\errmessage",b=>{var v=b.consumeArgs(1)[0];return console.error(v.reverse().map(x=>x.text).join("")),""}),Oe("\\show",b=>{var v=b.popToken(),x=v.text;return console.log(v,b.macros.get(x),Zh[x],Yn.math[x],Yn.text[x]),""}),Oe("\\bgroup","{"),Oe("\\egroup","}"),Oe("~","\\nobreakspace"),Oe("\\lq","`"),Oe("\\rq","'"),Oe("\\aa","\\r a"),Oe("\\AA","\\r A"),Oe("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`©}"),Oe("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"),Oe("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`®}"),Oe("ℬ","\\mathscr{B}"),Oe("ℰ","\\mathscr{E}"),Oe("ℱ","\\mathscr{F}"),Oe("ℋ","\\mathscr{H}"),Oe("ℐ","\\mathscr{I}"),Oe("ℒ","\\mathscr{L}"),Oe("ℳ","\\mathscr{M}"),Oe("ℛ","\\mathscr{R}"),Oe("ℭ","\\mathfrak{C}"),Oe("ℌ","\\mathfrak{H}"),Oe("ℨ","\\mathfrak{Z}"),Oe("\\Bbbk","\\Bbb{k}"),Oe("\\llap","\\mathllap{\\textrm{#1}}"),Oe("\\rlap","\\mathrlap{\\textrm{#1}}"),Oe("\\clap","\\mathclap{\\textrm{#1}}"),Oe("\\mathstrut","\\vphantom{(}"),Oe("\\underbar","\\underline{\\text{#1}}"),Oe("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}\\nobreak}{\\char"338}'),Oe("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`≠}}"),Oe("\\ne","\\neq"),Oe("≠","\\neq"),Oe("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`∉}}"),Oe("∉","\\notin"),Oe("≘","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`≘}}"),Oe("≙","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`≘}}"),Oe("≚","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`≚}}"),Oe("≛","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`≛}}"),Oe("≝","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`≝}}"),Oe("≞","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`≞}}"),Oe("≟","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`≟}}"),Oe("⟂","\\perp"),Oe("‼","\\mathclose{!\\mkern-0.8mu!}"),Oe("∌","\\notni"),Oe("⌜","\\ulcorner"),Oe("⌝","\\urcorner"),Oe("⌞","\\llcorner"),Oe("⌟","\\lrcorner"),Oe("©","\\copyright"),Oe("®","\\textregistered"),Oe("\\ulcorner",'\\html@mathml{\\@ulcorner}{\\mathop{\\char"231c}}'),Oe("\\urcorner",'\\html@mathml{\\@urcorner}{\\mathop{\\char"231d}}'),Oe("\\llcorner",'\\html@mathml{\\@llcorner}{\\mathop{\\char"231e}}'),Oe("\\lrcorner",'\\html@mathml{\\@lrcorner}{\\mathop{\\char"231f}}'),Oe("\\vdots","{\\varvdots\\rule{0pt}{15pt}}"),Oe("⋮","\\vdots"),Oe("\\varGamma","\\mathit{\\Gamma}"),Oe("\\varDelta","\\mathit{\\Delta}"),Oe("\\varTheta","\\mathit{\\Theta}"),Oe("\\varLambda","\\mathit{\\Lambda}"),Oe("\\varXi","\\mathit{\\Xi}"),Oe("\\varPi","\\mathit{\\Pi}"),Oe("\\varSigma","\\mathit{\\Sigma}"),Oe("\\varUpsilon","\\mathit{\\Upsilon}"),Oe("\\varPhi","\\mathit{\\Phi}"),Oe("\\varPsi","\\mathit{\\Psi}"),Oe("\\varOmega","\\mathit{\\Omega}"),Oe("\\substack","\\begin{subarray}{c}#1\\end{subarray}"),Oe("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax"),Oe("\\boxed","\\fbox{$\\displaystyle{#1}$}"),Oe("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;"),Oe("\\implies","\\DOTSB\\;\\Longrightarrow\\;"),Oe("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;"),Oe("\\dddot","{\\overset{\\raisebox{-0.1ex}{\\normalsize ...}}{#1}}"),Oe("\\ddddot","{\\overset{\\raisebox{-0.1ex}{\\normalsize ....}}{#1}}"),pO={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"},rye=new Set(["bin","rel"]),Oe("\\dots",function(b){var v="\\dotso",x=b.expandAfterFuture().text;return x in pO?v=pO[x]:(x.slice(0,4)==="\\not"||x in Yn.math&&rye.has(Yn.math[x].group))&&(v="\\dotsb"),v}),bS={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0},Oe("\\dotso",function(b){var v=b.future().text;return v in bS?"\\ldots\\,":"\\ldots"}),Oe("\\dotsc",function(b){var v=b.future().text;return v in bS&&v!==","?"\\ldots\\,":"\\ldots"}),Oe("\\cdots",function(b){var v=b.future().text;return v in bS?"\\@cdots\\,":"\\@cdots"}),Oe("\\dotsb","\\cdots"),Oe("\\dotsm","\\cdots"),Oe("\\dotsi","\\!\\cdots"),Oe("\\dotsx","\\ldots\\,"),Oe("\\DOTSI","\\relax"),Oe("\\DOTSB","\\relax"),Oe("\\DOTSX","\\relax"),Oe("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),Oe("\\,","\\tmspace+{3mu}{.1667em}"),Oe("\\thinspace","\\,"),Oe("\\>","\\mskip{4mu}"),Oe("\\:","\\tmspace+{4mu}{.2222em}"),Oe("\\medspace","\\:"),Oe("\\;","\\tmspace+{5mu}{.2777em}"),Oe("\\thickspace","\\;"),Oe("\\!","\\tmspace-{3mu}{.1667em}"),Oe("\\negthinspace","\\!"),Oe("\\negmedspace","\\tmspace-{4mu}{.2222em}"),Oe("\\negthickspace","\\tmspace-{5mu}{.277em}"),Oe("\\enspace","\\kern.5em "),Oe("\\enskip","\\hskip.5em\\relax"),Oe("\\quad","\\hskip1em\\relax"),Oe("\\qquad","\\hskip2em\\relax"),Oe("\\tag","\\@ifstar\\tag@literal\\tag@paren"),Oe("\\tag@paren","\\tag@literal{({#1})}"),Oe("\\tag@literal",b=>{if(b.macros.get("\\df@tag"))throw new Xt("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"}),Oe("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),Oe("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),Oe("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),Oe("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),Oe("\\newline","\\\\\\relax"),Oe("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}"),fO=er(Lc["Main-Regular"][84][1]-.7*Lc["Main-Regular"][65][1]),Oe("\\LaTeX","\\textrm{\\html@mathml{"+("L\\kern-.36em\\raisebox{"+fO+"}{\\scriptstyle A}")+"\\kern-.15em\\TeX}{LaTeX}}"),Oe("\\KaTeX","\\textrm{\\html@mathml{"+("K\\kern-.17em\\raisebox{"+fO+"}{\\scriptstyle A}")+"\\kern-.15em\\TeX}{KaTeX}}"),Oe("\\hspace","\\@ifstar\\@hspacer\\@hspace"),Oe("\\@hspace","\\hskip #1\\relax"),Oe("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),Oe("\\ordinarycolon",":"),Oe("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}"),Oe("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}'),Oe("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}'),Oe("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}'),Oe("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}'),Oe("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}'),Oe("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}'),Oe("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}'),Oe("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}'),Oe("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}'),Oe("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}'),Oe("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}'),Oe("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}'),Oe("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}'),Oe("∷","\\dblcolon"),Oe("∹","\\eqcolon"),Oe("≔","\\coloneqq"),Oe("≕","\\eqqcolon"),Oe("⩴","\\Coloneqq"),Oe("\\ratio","\\vcentcolon"),Oe("\\coloncolon","\\dblcolon"),Oe("\\colonequals","\\coloneqq"),Oe("\\coloncolonequals","\\Coloneqq"),Oe("\\equalscolon","\\eqqcolon"),Oe("\\equalscoloncolon","\\Eqqcolon"),Oe("\\colonminus","\\coloneq"),Oe("\\coloncolonminus","\\Coloneq"),Oe("\\minuscolon","\\eqcolon"),Oe("\\minuscoloncolon","\\Eqcolon"),Oe("\\coloncolonapprox","\\Colonapprox"),Oe("\\coloncolonsim","\\Colonsim"),Oe("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Oe("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Oe("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Oe("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Oe("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`∌}}"),Oe("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),Oe("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),Oe("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}"),Oe("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}"),Oe("\\varlimsup","\\DOTSB\\operatorname*{\\overline{lim}}"),Oe("\\varliminf","\\DOTSB\\operatorname*{\\underline{lim}}"),Oe("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{lim}}"),Oe("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{lim}}"),Oe("\\gvertneqq","\\html@mathml{\\@gvertneqq}{≩}"),Oe("\\lvertneqq","\\html@mathml{\\@lvertneqq}{≨}"),Oe("\\ngeqq","\\html@mathml{\\@ngeqq}{≱}"),Oe("\\ngeqslant","\\html@mathml{\\@ngeqslant}{≱}"),Oe("\\nleqq","\\html@mathml{\\@nleqq}{≰}"),Oe("\\nleqslant","\\html@mathml{\\@nleqslant}{≰}"),Oe("\\nshortmid","\\html@mathml{\\@nshortmid}{∤}"),Oe("\\nshortparallel","\\html@mathml{\\@nshortparallel}{∦}"),Oe("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{⊈}"),Oe("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{⊉}"),Oe("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{⊊}"),Oe("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{⫋}"),Oe("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{⊋}"),Oe("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{⫌}"),Oe("\\imath","\\html@mathml{\\@imath}{ı}"),Oe("\\jmath","\\html@mathml{\\@jmath}{ȷ}"),Oe("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`⟦}}"),Oe("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`⟧}}"),Oe("⟦","\\llbracket"),Oe("⟧","\\rrbracket"),Oe("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`⦃}}"),Oe("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`⦄}}"),Oe("⦃","\\lBrace"),Oe("⦄","\\rBrace"),Oe("\\minuso","\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`⦵}}"),Oe("⦵","\\minuso"),Oe("\\darr","\\downarrow"),Oe("\\dArr","\\Downarrow"),Oe("\\Darr","\\Downarrow"),Oe("\\lang","\\langle"),Oe("\\rang","\\rangle"),Oe("\\uarr","\\uparrow"),Oe("\\uArr","\\Uparrow"),Oe("\\Uarr","\\Uparrow"),Oe("\\N","\\mathbb{N}"),Oe("\\R","\\mathbb{R}"),Oe("\\Z","\\mathbb{Z}"),Oe("\\alef","\\aleph"),Oe("\\alefsym","\\aleph"),Oe("\\Alpha","\\mathrm{A}"),Oe("\\Beta","\\mathrm{B}"),Oe("\\bull","\\bullet"),Oe("\\Chi","\\mathrm{X}"),Oe("\\clubs","\\clubsuit"),Oe("\\cnums","\\mathbb{C}"),Oe("\\Complex","\\mathbb{C}"),Oe("\\Dagger","\\ddagger"),Oe("\\diamonds","\\diamondsuit"),Oe("\\empty","\\emptyset"),Oe("\\Epsilon","\\mathrm{E}"),Oe("\\Eta","\\mathrm{H}"),Oe("\\exist","\\exists"),Oe("\\harr","\\leftrightarrow"),Oe("\\hArr","\\Leftrightarrow"),Oe("\\Harr","\\Leftrightarrow"),Oe("\\hearts","\\heartsuit"),Oe("\\image","\\Im"),Oe("\\infin","\\infty"),Oe("\\Iota","\\mathrm{I}"),Oe("\\isin","\\in"),Oe("\\Kappa","\\mathrm{K}"),Oe("\\larr","\\leftarrow"),Oe("\\lArr","\\Leftarrow"),Oe("\\Larr","\\Leftarrow"),Oe("\\lrarr","\\leftrightarrow"),Oe("\\lrArr","\\Leftrightarrow"),Oe("\\Lrarr","\\Leftrightarrow"),Oe("\\Mu","\\mathrm{M}"),Oe("\\natnums","\\mathbb{N}"),Oe("\\Nu","\\mathrm{N}"),Oe("\\Omicron","\\mathrm{O}"),Oe("\\plusmn","\\pm"),Oe("\\rarr","\\rightarrow"),Oe("\\rArr","\\Rightarrow"),Oe("\\Rarr","\\Rightarrow"),Oe("\\real","\\Re"),Oe("\\reals","\\mathbb{R}"),Oe("\\Reals","\\mathbb{R}"),Oe("\\Rho","\\mathrm{P}"),Oe("\\sdot","\\cdot"),Oe("\\sect","\\S"),Oe("\\spades","\\spadesuit"),Oe("\\sub","\\subset"),Oe("\\sube","\\subseteq"),Oe("\\supe","\\supseteq"),Oe("\\Tau","\\mathrm{T}"),Oe("\\thetasym","\\vartheta"),Oe("\\weierp","\\wp"),Oe("\\Zeta","\\mathrm{Z}"),Oe("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),Oe("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),Oe("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),Oe("\\bra","\\mathinner{\\langle{#1}|}"),Oe("\\ket","\\mathinner{|{#1}\\rangle}"),Oe("\\braket","\\mathinner{\\langle{#1}\\rangle}"),Oe("\\Bra","\\left\\langle#1\\right|"),Oe("\\Ket","\\left|#1\\right\\rangle"),gO=s(b=>v=>{var x=v.consumeArg().tokens,w=v.consumeArg().tokens,R=v.consumeArg().tokens,$=v.consumeArg().tokens,M=v.macros.get("|"),N=v.macros.get("\\|");v.macros.beginGroup();var j=s(F=>O=>{b&&(O.macros.set("|",M),R.length&&O.macros.set("\\|",N));var B=F;if(!F&&R.length){var Y=O.future();Y.text==="|"&&(O.popToken(),B=!0)}return{tokens:B?R:w,numArgs:0}},"midMacro");v.macros.set("|",j(!1)),R.length&&v.macros.set("\\|",j(!0));var I=v.consumeArg().tokens,z=v.expandTokens([...$,...I,...x]);return v.macros.endGroup(),{tokens:z.reverse(),numArgs:0}},"braketHelper"),Oe("\\bra@ket",gO(!1)),Oe("\\bra@set",gO(!0)),Oe("\\Braket","\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}"),Oe("\\Set","\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}"),Oe("\\set","\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}"),Oe("\\angln","{\\angl n}"),Oe("\\blue","\\textcolor{##6495ed}{#1}"),Oe("\\orange","\\textcolor{##ffa500}{#1}"),Oe("\\pink","\\textcolor{##ff00af}{#1}"),Oe("\\red","\\textcolor{##df0030}{#1}"),Oe("\\green","\\textcolor{##28ae7b}{#1}"),Oe("\\gray","\\textcolor{gray}{#1}"),Oe("\\purple","\\textcolor{##9d38bd}{#1}"),Oe("\\blueA","\\textcolor{##ccfaff}{#1}"),Oe("\\blueB","\\textcolor{##80f6ff}{#1}"),Oe("\\blueC","\\textcolor{##63d9ea}{#1}"),Oe("\\blueD","\\textcolor{##11accd}{#1}"),Oe("\\blueE","\\textcolor{##0c7f99}{#1}"),Oe("\\tealA","\\textcolor{##94fff5}{#1}"),Oe("\\tealB","\\textcolor{##26edd5}{#1}"),Oe("\\tealC","\\textcolor{##01d1c1}{#1}"),Oe("\\tealD","\\textcolor{##01a995}{#1}"),Oe("\\tealE","\\textcolor{##208170}{#1}"),Oe("\\greenA","\\textcolor{##b6ffb0}{#1}"),Oe("\\greenB","\\textcolor{##8af281}{#1}"),Oe("\\greenC","\\textcolor{##74cf70}{#1}"),Oe("\\greenD","\\textcolor{##1fab54}{#1}"),Oe("\\greenE","\\textcolor{##0d923f}{#1}"),Oe("\\goldA","\\textcolor{##ffd0a9}{#1}"),Oe("\\goldB","\\textcolor{##ffbb71}{#1}"),Oe("\\goldC","\\textcolor{##ff9c39}{#1}"),Oe("\\goldD","\\textcolor{##e07d10}{#1}"),Oe("\\goldE","\\textcolor{##a75a05}{#1}"),Oe("\\redA","\\textcolor{##fca9a9}{#1}"),Oe("\\redB","\\textcolor{##ff8482}{#1}"),Oe("\\redC","\\textcolor{##f9685d}{#1}"),Oe("\\redD","\\textcolor{##e84d39}{#1}"),Oe("\\redE","\\textcolor{##bc2612}{#1}"),Oe("\\maroonA","\\textcolor{##ffbde0}{#1}"),Oe("\\maroonB","\\textcolor{##ff92c6}{#1}"),Oe("\\maroonC","\\textcolor{##ed5fa6}{#1}"),Oe("\\maroonD","\\textcolor{##ca337c}{#1}"),Oe("\\maroonE","\\textcolor{##9e034e}{#1}"),Oe("\\purpleA","\\textcolor{##ddd7ff}{#1}"),Oe("\\purpleB","\\textcolor{##c6b9fc}{#1}"),Oe("\\purpleC","\\textcolor{##aa87ff}{#1}"),Oe("\\purpleD","\\textcolor{##7854ab}{#1}"),Oe("\\purpleE","\\textcolor{##543b78}{#1}"),Oe("\\mintA","\\textcolor{##f5f9e8}{#1}"),Oe("\\mintB","\\textcolor{##edf2df}{#1}"),Oe("\\mintC","\\textcolor{##e0e5cc}{#1}"),Oe("\\grayA","\\textcolor{##f6f7f7}{#1}"),Oe("\\grayB","\\textcolor{##f0f1f2}{#1}"),Oe("\\grayC","\\textcolor{##e3e5e6}{#1}"),Oe("\\grayD","\\textcolor{##d6d8da}{#1}"),Oe("\\grayE","\\textcolor{##babec2}{#1}"),Oe("\\grayF","\\textcolor{##888d93}{#1}"),Oe("\\grayG","\\textcolor{##626569}{#1}"),Oe("\\grayH","\\textcolor{##3b3e40}{#1}"),Oe("\\grayI","\\textcolor{##21242c}{#1}"),Oe("\\kaBlue","\\textcolor{##314453}{#1}"),Oe("\\kaGreen","\\textcolor{##71B307}{#1}"),mO={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0},iye=(E=class{constructor(v,x,w){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=x,this.expansionCount=0,this.feed(v),this.macros=new eye(tye,x.macros),this.mode=w,this.stack=[]}feed(v){this.lexer=new hO(v,this.settings)}switchMode(v){this.mode=v}beginGroup(){this.macros.beginGroup()}endGroup(){this.macros.endGroup()}endGroups(){this.macros.endGroups()}future(){return this.stack.length===0&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]}popToken(){return this.future(),this.stack.pop()}pushToken(v){this.stack.push(v)}pushTokens(v){this.stack.push(...v)}scanArgument(v){var x,w,R;if(v){if(this.consumeSpaces(),this.future().text!=="[")return null;x=this.popToken(),{tokens:R,end:w}=this.consumeArg(["]"])}else({tokens:R,start:x,end:w}=this.consumeArg());return this.pushToken(new Hl("EOF",w.loc)),this.pushTokens(R),new Hl("",Cl.range(x,w))}consumeSpaces(){for(;;){var v=this.future();if(v.text===" ")this.stack.pop();else break}}consumeArg(v){var x=[],w=v&&v.length>0;w||this.consumeSpaces();var R=this.future(),$,M=0,N=0;do{if($=this.popToken(),x.push($),$.text==="{")++M;else if($.text==="}"){if(--M,M===-1)throw new Xt("Extra }",$)}else if($.text==="EOF")throw new Xt("Unexpected end of input in a macro argument, expected '"+(v&&w?v[N]:"}")+"'",$);if(v&&w)if((M===0||M===1&&v[N]==="{")&&$.text===v[N]){if(++N,N===v.length){x.splice(-N,N);break}}else N=0}while(M!==0||w);return R.text==="{"&&x[x.length-1].text==="}"&&(x.pop(),x.shift()),x.reverse(),{tokens:x,start:R,end:$}}consumeArgs(v,x){if(x){if(x.length!==v+1)throw new Xt("The length of delimiters doesn't match the number of args!");for(var w=x[0],R=0;Rthis.settings.maxExpand)throw new Xt("Too many expansions: infinite loop or need to increase maxExpand setting")}expandOnce(v){var x=this.popToken(),w=x.text,R=x.noexpand?null:this._getExpansion(w);if(R==null||v&&R.unexpandable){if(v&&R==null&&w[0]==="\\"&&!this.isDefined(w))throw new Xt("Undefined control sequence: "+w);return this.pushToken(x),!1}this.countExpansion(1);var $=R.tokens,M=this.consumeArgs(R.numArgs,R.delimiters);if(R.numArgs){$=$.slice();for(var N=$.length-1;N>=0;--N){var j=$[N];if(j.text==="#"){if(N===0)throw new Xt("Incomplete placeholder at end of macro body",j);if(j=$[--N],j.text==="#")$.splice(N+1,1);else if(/^[1-9]$/.test(j.text))$.splice(N,2,...M[+j.text-1]);else throw new Xt("Not a valid argument number",j)}}}return this.pushTokens($),$.length}expandAfterFuture(){return this.expandOnce(),this.future()}expandNextToken(){for(;;)if(this.expandOnce()===!1){var v=this.stack.pop();return v.treatAsRelax&&(v.text="\\relax"),v}}expandMacro(v){return this.macros.has(v)?this.expandTokens([new Hl(v)]):void 0}expandTokens(v){var x=[],w=this.stack.length;for(this.pushTokens(v);this.stack.length>w;)if(this.expandOnce(!0)===!1){var R=this.stack.pop();R.treatAsRelax&&(R.noexpand=!1,R.treatAsRelax=!1),x.push(R)}return this.countExpansion(x.length),x}expandMacroAsText(v){var x=this.expandMacro(v);return x&&x.map(w=>w.text).join("")}_getExpansion(v){var x=this.macros.get(v);if(x==null)return x;if(v.length===1){var w=this.lexer.catcodes[v];if(w!=null&&w!==13)return}var R=typeof x=="function"?x(this):x;if(typeof R=="string"){var $=0;if(R.includes("#"))for(var M=R.replace(/##/g,"");M.includes("#"+($+1));)++$;for(var N=new hO(R,this.settings),j=[],I=N.lex();I.text!=="EOF";)j.push(I),I=N.lex();j.reverse();var z={tokens:j,numArgs:$};return z}return R}isDefined(v){return this.macros.has(v)||Zh.hasOwnProperty(v)||Yn.math.hasOwnProperty(v)||Yn.text.hasOwnProperty(v)||mO.hasOwnProperty(v)}isExpandable(v){var x=this.macros.get(v);return x!=null?typeof x=="string"||typeof x=="function"||!x.unexpandable:Zh.hasOwnProperty(v)&&!Zh[v].primitive}},s(E,"MacroExpander"),E),yO=/^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/,pT=Object.freeze({"₊":"+","₋":"-","₌":"=","₍":"(","₎":")","₀":"0","₁":"1","₂":"2","₃":"3","₄":"4","₅":"5","₆":"6","₇":"7","₈":"8","₉":"9","ₐ":"a","ₑ":"e","ₕ":"h","ᵢ":"i","ⱼ":"j","ₖ":"k","ₗ":"l","ₘ":"m","ₙ":"n","ₒ":"o","ₚ":"p","ᵣ":"r","ₛ":"s","ₜ":"t","ᵤ":"u","ᵥ":"v","ₓ":"x","ᵦ":"β","ᵧ":"γ","ᵨ":"ρ","ᵩ":"ϕ","ᵪ":"χ","⁺":"+","⁻":"-","⁼":"=","⁽":"(","⁾":")","⁰":"0","¹":"1","²":"2","³":"3","⁴":"4","⁵":"5","⁶":"6","⁷":"7","⁸":"8","⁹":"9","ᴬ":"A","ᴮ":"B","ᴰ":"D","ᴱ":"E","ᴳ":"G","ᴴ":"H","ᴵ":"I","ᴶ":"J","ᴷ":"K","ᴸ":"L","ᴹ":"M","ᴺ":"N","ᴼ":"O","ᴾ":"P","ᴿ":"R","ᵀ":"T","ᵁ":"U","ⱽ":"V","ᵂ":"W","ᵃ":"a","ᵇ":"b","ᶜ":"c","ᵈ":"d","ᵉ":"e","ᶠ":"f","ᵍ":"g",ʰ:"h","ⁱ":"i",ʲ:"j","ᵏ":"k",ˡ:"l","ᵐ":"m",ⁿ:"n","ᵒ":"o","ᵖ":"p",ʳ:"r",ˢ:"s","ᵗ":"t","ᵘ":"u","ᵛ":"v",ʷ:"w",ˣ:"x",ʸ:"y","ᶻ":"z","ᵝ":"β","ᵞ":"γ","ᵟ":"δ","ᵠ":"ϕ","ᵡ":"χ","ᶿ":"θ"}),xS={"́":{text:"\\'",math:"\\acute"},"̀":{text:"\\`",math:"\\grave"},"̈":{text:'\\"',math:"\\ddot"},"̃":{text:"\\~",math:"\\tilde"},"̄":{text:"\\=",math:"\\bar"},"̆":{text:"\\u",math:"\\breve"},"̌":{text:"\\v",math:"\\check"},"̂":{text:"\\^",math:"\\hat"},"̇":{text:"\\.",math:"\\dot"},"̊":{text:"\\r",math:"\\mathring"},"̋":{text:"\\H"},"̧":{text:"\\c"}},vO={á:"á",à:"à",ä:"ä",ǟ:"ǟ",ã:"ã",ā:"ā",ă:"ă",ắ:"ắ",ằ:"ằ",ẵ:"ẵ",ǎ:"ǎ",â:"â",ấ:"ấ",ầ:"ầ",ẫ:"ẫ",ȧ:"ȧ",ǡ:"ǡ",å:"å",ǻ:"ǻ",ḃ:"ḃ",ć:"ć",ḉ:"ḉ",č:"č",ĉ:"ĉ",ċ:"ċ",ç:"ç",ď:"ď",ḋ:"ḋ",ḑ:"ḑ",é:"é",è:"è",ë:"ë",ẽ:"ẽ",ē:"ē",ḗ:"ḗ",ḕ:"ḕ",ĕ:"ĕ",ḝ:"ḝ",ě:"ě",ê:"ê",ế:"ế",ề:"ề",ễ:"ễ",ė:"ė",ȩ:"ȩ",ḟ:"ḟ",ǵ:"ǵ",ḡ:"ḡ",ğ:"ğ",ǧ:"ǧ",ĝ:"ĝ",ġ:"ġ",ģ:"ģ",ḧ:"ḧ",ȟ:"ȟ",ĥ:"ĥ",ḣ:"ḣ",ḩ:"ḩ",í:"í",ì:"ì",ï:"ï",ḯ:"ḯ",ĩ:"ĩ",ī:"ī",ĭ:"ĭ",ǐ:"ǐ",î:"î",ǰ:"ǰ",ĵ:"ĵ",ḱ:"ḱ",ǩ:"ǩ",ķ:"ķ",ĺ:"ĺ",ľ:"ľ",ļ:"ļ",ḿ:"ḿ",ṁ:"ṁ",ń:"ń",ǹ:"ǹ",ñ:"ñ",ň:"ň",ṅ:"ṅ",ņ:"ņ",ó:"ó",ò:"ò",ö:"ö",ȫ:"ȫ",õ:"õ",ṍ:"ṍ",ṏ:"ṏ",ȭ:"ȭ",ō:"ō",ṓ:"ṓ",ṑ:"ṑ",ŏ:"ŏ",ǒ:"ǒ",ô:"ô",ố:"ố",ồ:"ồ",ỗ:"ỗ",ȯ:"ȯ",ȱ:"ȱ",ő:"ő",ṕ:"ṕ",ṗ:"ṗ",ŕ:"ŕ",ř:"ř",ṙ:"ṙ",ŗ:"ŗ",ś:"ś",ṥ:"ṥ",š:"š",ṧ:"ṧ",ŝ:"ŝ",ṡ:"ṡ",ş:"ş",ẗ:"ẗ",ť:"ť",ṫ:"ṫ",ţ:"ţ",ú:"ú",ù:"ù",ü:"ü",ǘ:"ǘ",ǜ:"ǜ",ǖ:"ǖ",ǚ:"ǚ",ũ:"ũ",ṹ:"ṹ",ū:"ū",ṻ:"ṻ",ŭ:"ŭ",ǔ:"ǔ",û:"û",ů:"ů",ű:"ű",ṽ:"ṽ",ẃ:"ẃ",ẁ:"ẁ",ẅ:"ẅ",ŵ:"ŵ",ẇ:"ẇ",ẘ:"ẘ",ẍ:"ẍ",ẋ:"ẋ",ý:"ý",ỳ:"ỳ",ÿ:"ÿ",ỹ:"ỹ",ȳ:"ȳ",ŷ:"ŷ",ẏ:"ẏ",ẙ:"ẙ",ź:"ź",ž:"ž",ẑ:"ẑ",ż:"ż",Á:"Á",À:"À",Ä:"Ä",Ǟ:"Ǟ",Ã:"Ã",Ā:"Ā",Ă:"Ă",Ắ:"Ắ",Ằ:"Ằ",Ẵ:"Ẵ",Ǎ:"Ǎ",Â:"Â",Ấ:"Ấ",Ầ:"Ầ",Ẫ:"Ẫ",Ȧ:"Ȧ",Ǡ:"Ǡ",Å:"Å",Ǻ:"Ǻ",Ḃ:"Ḃ",Ć:"Ć",Ḉ:"Ḉ",Č:"Č",Ĉ:"Ĉ",Ċ:"Ċ",Ç:"Ç",Ď:"Ď",Ḋ:"Ḋ",Ḑ:"Ḑ",É:"É",È:"È",Ë:"Ë",Ẽ:"Ẽ",Ē:"Ē",Ḗ:"Ḗ",Ḕ:"Ḕ",Ĕ:"Ĕ",Ḝ:"Ḝ",Ě:"Ě",Ê:"Ê",Ế:"Ế",Ề:"Ề",Ễ:"Ễ",Ė:"Ė",Ȩ:"Ȩ",Ḟ:"Ḟ",Ǵ:"Ǵ",Ḡ:"Ḡ",Ğ:"Ğ",Ǧ:"Ǧ",Ĝ:"Ĝ",Ġ:"Ġ",Ģ:"Ģ",Ḧ:"Ḧ",Ȟ:"Ȟ",Ĥ:"Ĥ",Ḣ:"Ḣ",Ḩ:"Ḩ",Í:"Í",Ì:"Ì",Ï:"Ï",Ḯ:"Ḯ",Ĩ:"Ĩ",Ī:"Ī",Ĭ:"Ĭ",Ǐ:"Ǐ",Î:"Î",İ:"İ",Ĵ:"Ĵ",Ḱ:"Ḱ",Ǩ:"Ǩ",Ķ:"Ķ",Ĺ:"Ĺ",Ľ:"Ľ",Ļ:"Ļ",Ḿ:"Ḿ",Ṁ:"Ṁ",Ń:"Ń",Ǹ:"Ǹ",Ñ:"Ñ",Ň:"Ň",Ṅ:"Ṅ",Ņ:"Ņ",Ó:"Ó",Ò:"Ò",Ö:"Ö",Ȫ:"Ȫ",Õ:"Õ",Ṍ:"Ṍ",Ṏ:"Ṏ",Ȭ:"Ȭ",Ō:"Ō",Ṓ:"Ṓ",Ṑ:"Ṑ",Ŏ:"Ŏ",Ǒ:"Ǒ",Ô:"Ô",Ố:"Ố",Ồ:"Ồ",Ỗ:"Ỗ",Ȯ:"Ȯ",Ȱ:"Ȱ",Ő:"Ő",Ṕ:"Ṕ",Ṗ:"Ṗ",Ŕ:"Ŕ",Ř:"Ř",Ṙ:"Ṙ",Ŗ:"Ŗ",Ś:"Ś",Ṥ:"Ṥ",Š:"Š",Ṧ:"Ṧ",Ŝ:"Ŝ",Ṡ:"Ṡ",Ş:"Ş",Ť:"Ť",Ṫ:"Ṫ",Ţ:"Ţ",Ú:"Ú",Ù:"Ù",Ü:"Ü",Ǘ:"Ǘ",Ǜ:"Ǜ",Ǖ:"Ǖ",Ǚ:"Ǚ",Ũ:"Ũ",Ṹ:"Ṹ",Ū:"Ū",Ṻ:"Ṻ",Ŭ:"Ŭ",Ǔ:"Ǔ",Û:"Û",Ů:"Ů",Ű:"Ű",Ṽ:"Ṽ",Ẃ:"Ẃ",Ẁ:"Ẁ",Ẅ:"Ẅ",Ŵ:"Ŵ",Ẇ:"Ẇ",Ẍ:"Ẍ",Ẋ:"Ẋ",Ý:"Ý",Ỳ:"Ỳ",Ÿ:"Ÿ",Ỹ:"Ỹ",Ȳ:"Ȳ",Ŷ:"Ŷ",Ẏ:"Ẏ",Ź:"Ź",Ž:"Ž",Ẑ:"Ẑ",Ż:"Ż",ά:"ά",ὰ:"ὰ",ᾱ:"ᾱ",ᾰ:"ᾰ",έ:"έ",ὲ:"ὲ",ή:"ή",ὴ:"ὴ",ί:"ί",ὶ:"ὶ",ϊ:"ϊ",ΐ:"ΐ",ῒ:"ῒ",ῑ:"ῑ",ῐ:"ῐ",ό:"ό",ὸ:"ὸ",ύ:"ύ",ὺ:"ὺ",ϋ:"ϋ",ΰ:"ΰ",ῢ:"ῢ",ῡ:"ῡ",ῠ:"ῠ",ώ:"ώ",ὼ:"ὼ",Ύ:"Ύ",Ὺ:"Ὺ",Ϋ:"Ϋ",Ῡ:"Ῡ",Ῠ:"Ῠ",Ώ:"Ώ",Ὼ:"Ὼ"},bO=(_=class{constructor(v,x){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new iye(v,x,this.mode),this.settings=x,this.leftrightDepth=0,this.nextToken=null}expect(v,x){if(x===void 0&&(x=!0),this.fetch().text!==v)throw new Xt("Expected '"+v+"', got '"+this.fetch().text+"'",this.fetch());x&&this.consume()}consume(){this.nextToken=null}fetch(){return this.nextToken==null&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken}switchMode(v){this.mode=v,this.gullet.switchMode(v)}parse(){this.settings.globalGroup||this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");try{var v=this.parseExpression(!1);return this.expect("EOF"),this.settings.globalGroup||this.gullet.endGroup(),v}finally{this.gullet.endGroups()}}subparse(v){var x=this.nextToken;this.consume(),this.gullet.pushToken(new Hl("}")),this.gullet.pushTokens(v);var w=this.parseExpression(!1);return this.expect("}"),this.nextToken=x,w}parseExpression(v,x){for(var w=[];;){this.mode==="math"&&this.consumeSpaces();var R=this.fetch();if(_.endOfExpression.has(R.text)||x&&R.text===x||v&&Zh[R.text]&&Zh[R.text].infix)break;var $=this.parseAtom(x);if($){if($.type==="internal")continue}else break;w.push($)}return this.mode==="text"&&this.formLigatures(w),this.handleInfixNodes(w)}handleInfixNodes(v){for(var x=-1,w,R=0;R=128)this.settings.strict&&(FD(x.charCodeAt(0))?this.mode==="math"&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+x[0]+'" used in math mode',v):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+x[0]+'"'+(" ("+x.charCodeAt(0)+")"),v)),M={type:"textord",mode:"text",loc:Cl.range(v),text:x};else return null;if(this.consume(),$)for(var z=0;z<$[0].length;z++){var F=$[0][z];if(!xS[F])throw new Xt("Unknown accent ' "+F+"'",v);var O=xS[F][this.mode]||xS[F].text;if(!O)throw new Xt("Accent "+F+" unsupported in "+this.mode+" mode",v);M={type:"accent",mode:this.mode,loc:Cl.range(v),label:O,isStretchy:!1,isShifty:!0,base:M}}return M}},s(_,"Parser"),_),bO.endOfExpression=new Set(["}","\\endgroup","\\end","\\right","&"]),TS=s(function(b,v){if(!(typeof b=="string"||b instanceof String))throw new TypeError("KaTeX can only parse string typed expression");var x=new bO(b,v);delete x.gullet.macros.current["\\df@tag"];var w=x.parse();if(delete x.gullet.macros.current["\\current@color"],delete x.gullet.macros.current["\\color"],x.gullet.macros.get("\\df@tag")){if(!v.displayMode)throw new Xt("\\tag works only in display equations");w=[{type:"tag",mode:"text",body:w,tag:x.subparse([new Hl("\\df@tag")])}]}return w},"parseTree"),kS=s(function(b,v,x){v.textContent="";var w=fT(b,x).toNode();v.appendChild(w)},"render"),typeof document<"u"&&document.compatMode!=="CSS1Compat"&&(typeof console<"u"&&console.warn("Warning: KaTeX doesn't work in quirks mode. Make sure your website has a suitable doctype."),kS=s(function(){throw new Xt("KaTeX doesn't work in quirks mode.")},"render")),xO=s(function(b,v){var x=fT(b,v).toMarkup();return x},"renderToString"),TO=s(function(b,v){var x=new IE(v);return TS(b,x)},"generateParseTree"),kO=s(function(b,v,x){if(x.throwOnError||!(b instanceof Xt))throw b;var w=jt(["katex-error"],[new tl(v)]);return w.setAttribute("title",b.toString()),w.setAttribute("style","color:"+x.errorColor),w},"renderError"),fT=s(function(b,v){var x=new IE(v);try{var w=TS(b,x);return g1e(w,b,x)}catch(R){return kO(R,b,x)}},"renderToDomTree"),CO=s(function(b,v){var x=new IE(v);try{var w=TS(b,x);return m1e(w,b,x)}catch(R){return kO(R,b,x)}},"renderToHTMLTree"),wO="0.16.47",EO={Span:U0,Anchor:Gx,SymbolNode:tl,SvgNode:_u,PathNode:Gh,LineNode:BE},nye={version:wO,render:kS,renderToString:xO,ParseError:Xt,SETTINGS_SCHEMA:Ux,__parse:TO,__renderToDomTree:fT,__renderToHTMLTree:CO,__setFontMetrics:zD,__defineSymbol:ne,__defineFunction:dr,__defineMacro:Oe,__domTree:EO}});function aye(){let e="data-temp-href-target";wu.addHook("beforeSanitizeAttributes",t=>{t.tagName==="A"&&t.hasAttribute("target")&&t.setAttribute(e,t.getAttribute("target")??"")}),wu.addHook("afterSanitizeAttributes",t=>{t.tagName==="A"&&t.hasAttribute(e)&&(t.setAttribute("target",t.getAttribute(e)??""),t.removeAttribute(e),t.getAttribute("target")==="_blank"&&t.setAttribute("rel","noopener"))})}var K0,sye,oye,SO,_O,Si,lye,cye,uye,hye,AO,CS,dye,pye,Jh,wS,fye,gye,RO,gT,pa,gy,mye,mT,Vt,mi=G(()=>{"use strict";zx(),Gr(),K0=//gi,sye=s(e=>e?AO(e).replace(/\\n/g,"#br#").split("#br#"):[""],"getRows"),oye=(()=>{let e=!1;return()=>{e||(aye(),e=!0)}})(),s(aye,"setupDompurifyHooks"),SO=s(e=>(oye(),wu.sanitize(e)),"removeScript"),_O=s((e,t)=>{if(Ps(t)){let r=t.securityLevel;r==="antiscript"||r==="strict"||r==="sandbox"?e=SO(e):r!=="loose"&&(e=AO(e),e=e.replace(//g,">"),e=e.replace(/=/g,"="),e=hye(e))}return e},"sanitizeMore"),Si=s((e,t)=>e&&(t.dompurifyConfig?e=wu.sanitize(_O(e,t),t.dompurifyConfig).toString():e=wu.sanitize(_O(e,t),{FORBID_TAGS:["style"]}).toString(),e),"sanitizeText"),lye=s((e,t)=>typeof e=="string"?Si(e,t):e.flat().map(r=>Si(r,t)),"sanitizeTextOrArray"),cye=s(e=>K0.test(e),"hasBreaks"),uye=s(e=>e.split(K0),"splitBreaks"),hye=s(e=>e.replace(/#br#/g,"
"),"placeholderToBreak"),AO=s(e=>e.replace(K0,"#br#"),"breakToPlaceholder"),CS=s(e=>{let t="";return e&&(t=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,t=CSS.escape(t)),t},"getUrl"),dye=s(function(...e){let t=e.filter(r=>!isNaN(r));return Math.max(...t)},"getMax"),pye=s(function(...e){let t=e.filter(r=>!isNaN(r));return Math.min(...t)},"getMin"),Jh=s(function(e){let t=e.split(/(,)/),r=[];for(let i=0;i0&&i+1Math.max(0,e.split(t).length-1),"countOccurrence"),fye=s((e,t)=>{let r=wS(e,"~"),i=wS(t,"~");return r===1&&i===1},"shouldCombineSets"),gye=s(e=>{let t=wS(e,"~"),r=!1;if(t<=1)return e;t%2!==0&&e.startsWith("~")&&(e=e.substring(1),r=!0);let i=[...e],n=i.indexOf("~"),a=i.lastIndexOf("~");for(;n!==-1&&a!==-1&&n!==a;)i[n]="<",i[a]=">",n=i.indexOf("~"),a=i.lastIndexOf("~");return r&&i.unshift("~"),i.join("")},"processSet"),RO=s(()=>window.MathMLElement!==void 0,"isMathMLSupported"),gT=/\$\$(.*?)\$\$/g,pa=s(e=>(e.match(gT)?.length??0)>0,"hasKatex"),gy=s(async(e,t)=>{let r=document.createElement("div");r.innerHTML=await mT(e,t),r.id="katex-temp",r.style.visibility="hidden",r.style.position="absolute",r.style.top="0",document.querySelector("body")?.insertAdjacentElement("beforeend",r);let i={width:r.clientWidth,height:r.clientHeight};return r.remove(),i},"calculateMathMLDimensions"),mye=s(async(e,t)=>{if(!pa(e))return e;if(!(RO()||t.legacyMathML||t.forceLegacyMathML))return e.replace(gT,"MathML is unsupported in this environment.");{let{default:r}=await Promise.resolve().then(()=>(B3t(),dme)),i=t.forceLegacyMathML||!RO()&&t.legacyMathML?"htmlAndMathml":"mathml";return e.split(K0).map(n=>pa(n)?`
${n}
`:`
${n}
`).join("").replace(gT,(n,a)=>r.renderToString(a,{throwOnError:!0,displayMode:!0,output:i}).replace(/\n/g," ").replace(//g,""))}return e.replace(gT,"Katex is not supported in @mermaid-js/tiny. Please use the full mermaid library.")},"renderKatexUnsanitized"),mT=s(async(e,t)=>Si(await mye(e,t),t),"renderKatexSanitized"),Vt={getRows:sye,sanitizeText:Si,sanitizeTextOrArray:lye,hasBreaks:cye,splitBreaks:uye,lineBreakRegex:K0,removeScript:SO,getUrl:CS,evaluate:_c,getMax:dye,getMin:pye}}),$O,ES,LO,SS,IO,yye,Lu,Iu=G(()=>{"use strict";s3t(),Gr(),mi(),Nt(),$O={body:'?',height:80,width:80},ES=new Map,LO=new Map,SS=s(e=>{for(let t of e){if(!t.name)throw new Error('Invalid icon loader. Must have a "name" property with non-empty string value.');if(Ee.debug("Registering icon pack:",t.name),"loader"in t)LO.set(t.name,t.loader);else if("icons"in t)ES.set(t.name,t.icons);else throw Ee.error("Invalid icon loader:",t),new Error('Invalid icon loader. Must have either "icons" or "loader" property.')}},"registerIconPacks"),IO=s(async(e,t)=>{let r=u0e(e,!0,t!==void 0);if(!r)throw new Error(`Invalid icon name: ${e}`);let i=r.prefix||t;if(!i)throw new Error(`Icon name must contain a prefix: ${e}`);let n=ES.get(i);if(!n){let o=LO.get(i);if(!o)throw new Error(`Icon set not found: ${r.prefix}`);try{n={...await o(),prefix:i},ES.set(i,n)}catch(l){throw Ee.error(l),new Error(`Failed to load icon set: ${r.prefix}`)}}let a=p0e(n,r.name);if(!a)throw new Error(`Icon not found: ${e}`);return a},"getRegisteredIconData"),yye=s(async e=>{try{return await IO(e),!0}catch{return!1}},"isIconAvailable"),Lu=s(async(e,t,r)=>{let i;try{i=await IO(e,t?.fallbackPrefix)}catch(o){Ee.error(o),i=$O}let n=b0e(i,t),a=E0e(T0e(n.body),{...n.attributes,...r});return Si(a,Rr())},"getIconSVG")});function MO(e){for(var t=[],r=1;r{"use strict";s(MO,"dedent")}),DO,my,bye,NO=G(()=>{"use strict";DO=/^([^\S\n\r]*)-{3}\s*[\n\r](.*?)[\n\r]\1-{3}\s*[\n\r]+/s,my=/%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi,bye=/\s*%%.*\n/gm}),OO,xye=G(()=>{"use strict";var e;OO=(e=class extends Error{constructor(r){super(r),this.name="UnknownDiagramError"}},s(e,"UnknownDiagramError"),e)}),Cp,_S,AS,BO,Tye,yy=G(()=>{"use strict";Nt(),NO(),xye(),Cp={},_S=s(function(e,t){e=e.replace(DO,"").replace(my,"").replace(bye,` +`);for(let[r,{detector:i}]of Object.entries(Cp))if(i(e,t))return r;throw new OO(`No diagram type detected matching given configuration for text: ${e}`)},"detectType"),AS=s((...e)=>{for(let{id:t,detector:r,loader:i}of e)BO(t,r,i)},"registerLazyLoadedDiagrams"),BO=s((e,t,r)=>{Cp[e]&&Ee.warn(`Detector with key ${e} already exists. Overwriting.`),Cp[e]={detector:t,loader:r},Ee.debug(`Detector with key ${e} added${r?" with loader":""}`)},"addDetector"),Tye=s(e=>Cp[e].loader,"getDiagramLoader")}),yT,kye,Cye=G(()=>{"use strict";yT=(function(){var e=s(function(ye,be,Ie,pe){for(Ie=Ie||{},pe=ye.length;pe--;Ie[ye[pe]]=be);return Ie},"o"),t=[1,24],r=[1,25],i=[1,26],n=[1,27],a=[1,28],o=[1,63],l=[1,64],u=[1,65],h=[1,66],d=[1,67],p=[1,68],f=[1,69],g=[1,29],m=[1,30],y=[1,31],k=[1,32],C=[1,33],S=[1,34],E=[1,35],_=[1,36],b=[1,37],v=[1,38],x=[1,39],w=[1,40],R=[1,41],$=[1,42],M=[1,43],N=[1,44],j=[1,45],I=[1,46],z=[1,47],F=[1,48],O=[1,50],B=[1,51],Y=[1,52],U=[1,53],H=[1,54],J=[1,55],te=[1,56],ie=[1,57],ue=[1,58],se=[1,59],le=[1,60],Te=[14,42],oe=[14,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74],fe=[12,14,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74],ge=[1,82],ke=[1,83],Ne=[1,84],xe=[1,85],$e=[12,14,42],Me=[12,14,33,42],He=[12,14,33,42,76,77,79,80],Ge=[12,33],ze=[34,36,37,38,39,40,41,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74],Ve={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mermaidDoc:4,direction:5,direction_tb:6,direction_bt:7,direction_rl:8,direction_lr:9,graphConfig:10,C4_CONTEXT:11,NEWLINE:12,statements:13,EOF:14,C4_CONTAINER:15,C4_COMPONENT:16,C4_DYNAMIC:17,C4_DEPLOYMENT:18,otherStatements:19,diagramStatements:20,otherStatement:21,title:22,accDescription:23,acc_title:24,acc_title_value:25,acc_descr:26,acc_descr_value:27,acc_descr_multiline_value:28,boundaryStatement:29,boundaryStartStatement:30,boundaryStopStatement:31,boundaryStart:32,LBRACE:33,ENTERPRISE_BOUNDARY:34,attributes:35,SYSTEM_BOUNDARY:36,BOUNDARY:37,CONTAINER_BOUNDARY:38,NODE:39,NODE_L:40,NODE_R:41,RBRACE:42,diagramStatement:43,PERSON:44,PERSON_EXT:45,SYSTEM:46,SYSTEM_DB:47,SYSTEM_QUEUE:48,SYSTEM_EXT:49,SYSTEM_EXT_DB:50,SYSTEM_EXT_QUEUE:51,CONTAINER:52,CONTAINER_DB:53,CONTAINER_QUEUE:54,CONTAINER_EXT:55,CONTAINER_EXT_DB:56,CONTAINER_EXT_QUEUE:57,COMPONENT:58,COMPONENT_DB:59,COMPONENT_QUEUE:60,COMPONENT_EXT:61,COMPONENT_EXT_DB:62,COMPONENT_EXT_QUEUE:63,REL:64,BIREL:65,REL_U:66,REL_D:67,REL_L:68,REL_R:69,REL_B:70,REL_INDEX:71,UPDATE_EL_STYLE:72,UPDATE_REL_STYLE:73,UPDATE_LAYOUT_CONFIG:74,attribute:75,STR:76,STR_KEY:77,STR_VALUE:78,ATTRIBUTE:79,ATTRIBUTE_EMPTY:80,$accept:0,$end:1},terminals_:{2:"error",6:"direction_tb",7:"direction_bt",8:"direction_rl",9:"direction_lr",11:"C4_CONTEXT",12:"NEWLINE",14:"EOF",15:"C4_CONTAINER",16:"C4_COMPONENT",17:"C4_DYNAMIC",18:"C4_DEPLOYMENT",22:"title",23:"accDescription",24:"acc_title",25:"acc_title_value",26:"acc_descr",27:"acc_descr_value",28:"acc_descr_multiline_value",33:"LBRACE",34:"ENTERPRISE_BOUNDARY",36:"SYSTEM_BOUNDARY",37:"BOUNDARY",38:"CONTAINER_BOUNDARY",39:"NODE",40:"NODE_L",41:"NODE_R",42:"RBRACE",44:"PERSON",45:"PERSON_EXT",46:"SYSTEM",47:"SYSTEM_DB",48:"SYSTEM_QUEUE",49:"SYSTEM_EXT",50:"SYSTEM_EXT_DB",51:"SYSTEM_EXT_QUEUE",52:"CONTAINER",53:"CONTAINER_DB",54:"CONTAINER_QUEUE",55:"CONTAINER_EXT",56:"CONTAINER_EXT_DB",57:"CONTAINER_EXT_QUEUE",58:"COMPONENT",59:"COMPONENT_DB",60:"COMPONENT_QUEUE",61:"COMPONENT_EXT",62:"COMPONENT_EXT_DB",63:"COMPONENT_EXT_QUEUE",64:"REL",65:"BIREL",66:"REL_U",67:"REL_D",68:"REL_L",69:"REL_R",70:"REL_B",71:"REL_INDEX",72:"UPDATE_EL_STYLE",73:"UPDATE_REL_STYLE",74:"UPDATE_LAYOUT_CONFIG",76:"STR",77:"STR_KEY",78:"STR_VALUE",79:"ATTRIBUTE",80:"ATTRIBUTE_EMPTY"},productions_:[0,[3,1],[3,1],[5,1],[5,1],[5,1],[5,1],[4,1],[10,4],[10,4],[10,4],[10,4],[10,4],[13,1],[13,1],[13,2],[19,1],[19,2],[19,3],[21,1],[21,1],[21,2],[21,2],[21,1],[29,3],[30,3],[30,3],[30,4],[32,2],[32,2],[32,2],[32,2],[32,2],[32,2],[32,2],[31,1],[20,1],[20,2],[20,3],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,1],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[35,1],[35,2],[75,1],[75,2],[75,1],[75,1]],performAction:s(function(ye,be,Ie,pe,Qe,W,Ae){var K=W.length-1;switch(Qe){case 3:pe.setDirection("TB");break;case 4:pe.setDirection("BT");break;case 5:pe.setDirection("RL");break;case 6:pe.setDirection("LR");break;case 8:case 9:case 10:case 11:case 12:pe.setC4Type(W[K-3]);break;case 19:pe.setTitle(W[K].substring(6)),this.$=W[K].substring(6);break;case 20:pe.setAccDescription(W[K].substring(15)),this.$=W[K].substring(15);break;case 21:this.$=W[K].trim(),pe.setTitle(this.$);break;case 22:case 23:this.$=W[K].trim(),pe.setAccDescription(this.$);break;case 28:W[K].splice(2,0,"ENTERPRISE"),pe.addPersonOrSystemBoundary(...W[K]),this.$=W[K];break;case 29:W[K].splice(2,0,"SYSTEM"),pe.addPersonOrSystemBoundary(...W[K]),this.$=W[K];break;case 30:pe.addPersonOrSystemBoundary(...W[K]),this.$=W[K];break;case 31:W[K].splice(2,0,"CONTAINER"),pe.addContainerBoundary(...W[K]),this.$=W[K];break;case 32:pe.addDeploymentNode("node",...W[K]),this.$=W[K];break;case 33:pe.addDeploymentNode("nodeL",...W[K]),this.$=W[K];break;case 34:pe.addDeploymentNode("nodeR",...W[K]),this.$=W[K];break;case 35:pe.popBoundaryParseStack();break;case 39:pe.addPersonOrSystem("person",...W[K]),this.$=W[K];break;case 40:pe.addPersonOrSystem("external_person",...W[K]),this.$=W[K];break;case 41:pe.addPersonOrSystem("system",...W[K]),this.$=W[K];break;case 42:pe.addPersonOrSystem("system_db",...W[K]),this.$=W[K];break;case 43:pe.addPersonOrSystem("system_queue",...W[K]),this.$=W[K];break;case 44:pe.addPersonOrSystem("external_system",...W[K]),this.$=W[K];break;case 45:pe.addPersonOrSystem("external_system_db",...W[K]),this.$=W[K];break;case 46:pe.addPersonOrSystem("external_system_queue",...W[K]),this.$=W[K];break;case 47:pe.addContainer("container",...W[K]),this.$=W[K];break;case 48:pe.addContainer("container_db",...W[K]),this.$=W[K];break;case 49:pe.addContainer("container_queue",...W[K]),this.$=W[K];break;case 50:pe.addContainer("external_container",...W[K]),this.$=W[K];break;case 51:pe.addContainer("external_container_db",...W[K]),this.$=W[K];break;case 52:pe.addContainer("external_container_queue",...W[K]),this.$=W[K];break;case 53:pe.addComponent("component",...W[K]),this.$=W[K];break;case 54:pe.addComponent("component_db",...W[K]),this.$=W[K];break;case 55:pe.addComponent("component_queue",...W[K]),this.$=W[K];break;case 56:pe.addComponent("external_component",...W[K]),this.$=W[K];break;case 57:pe.addComponent("external_component_db",...W[K]),this.$=W[K];break;case 58:pe.addComponent("external_component_queue",...W[K]),this.$=W[K];break;case 60:pe.addRel("rel",...W[K]),this.$=W[K];break;case 61:pe.addRel("birel",...W[K]),this.$=W[K];break;case 62:pe.addRel("rel_u",...W[K]),this.$=W[K];break;case 63:pe.addRel("rel_d",...W[K]),this.$=W[K];break;case 64:pe.addRel("rel_l",...W[K]),this.$=W[K];break;case 65:pe.addRel("rel_r",...W[K]),this.$=W[K];break;case 66:pe.addRel("rel_b",...W[K]),this.$=W[K];break;case 67:W[K].splice(0,1),pe.addRel("rel",...W[K]),this.$=W[K];break;case 68:pe.updateElStyle("update_el_style",...W[K]),this.$=W[K];break;case 69:pe.updateRelStyle("update_rel_style",...W[K]),this.$=W[K];break;case 70:pe.updateLayoutConfig("update_layout_config",...W[K]),this.$=W[K];break;case 71:this.$=[W[K]];break;case 72:W[K].unshift(W[K-1]),this.$=W[K];break;case 73:case 75:this.$=W[K].trim();break;case 74:let de={};de[W[K-1].trim()]=W[K].trim(),this.$=de;break;case 76:this.$="";break}},"anonymous"),table:[{3:1,4:2,5:3,6:[1,5],7:[1,6],8:[1,7],9:[1,8],10:4,11:[1,9],15:[1,10],16:[1,11],17:[1,12],18:[1,13]},{1:[3]},{1:[2,1]},{1:[2,2]},{1:[2,7]},{1:[2,3]},{1:[2,4]},{1:[2,5]},{1:[2,6]},{12:[1,14]},{12:[1,15]},{12:[1,16]},{12:[1,17]},{12:[1,18]},{13:19,19:20,20:21,21:22,22:t,23:r,24:i,26:n,28:a,29:49,30:61,32:62,34:o,36:l,37:u,38:h,39:d,40:p,41:f,43:23,44:g,45:m,46:y,47:k,48:C,49:S,50:E,51:_,52:b,53:v,54:x,55:w,56:R,57:$,58:M,59:N,60:j,61:I,62:z,63:F,64:O,65:B,66:Y,67:U,68:H,69:J,70:te,71:ie,72:ue,73:se,74:le},{13:70,19:20,20:21,21:22,22:t,23:r,24:i,26:n,28:a,29:49,30:61,32:62,34:o,36:l,37:u,38:h,39:d,40:p,41:f,43:23,44:g,45:m,46:y,47:k,48:C,49:S,50:E,51:_,52:b,53:v,54:x,55:w,56:R,57:$,58:M,59:N,60:j,61:I,62:z,63:F,64:O,65:B,66:Y,67:U,68:H,69:J,70:te,71:ie,72:ue,73:se,74:le},{13:71,19:20,20:21,21:22,22:t,23:r,24:i,26:n,28:a,29:49,30:61,32:62,34:o,36:l,37:u,38:h,39:d,40:p,41:f,43:23,44:g,45:m,46:y,47:k,48:C,49:S,50:E,51:_,52:b,53:v,54:x,55:w,56:R,57:$,58:M,59:N,60:j,61:I,62:z,63:F,64:O,65:B,66:Y,67:U,68:H,69:J,70:te,71:ie,72:ue,73:se,74:le},{13:72,19:20,20:21,21:22,22:t,23:r,24:i,26:n,28:a,29:49,30:61,32:62,34:o,36:l,37:u,38:h,39:d,40:p,41:f,43:23,44:g,45:m,46:y,47:k,48:C,49:S,50:E,51:_,52:b,53:v,54:x,55:w,56:R,57:$,58:M,59:N,60:j,61:I,62:z,63:F,64:O,65:B,66:Y,67:U,68:H,69:J,70:te,71:ie,72:ue,73:se,74:le},{13:73,19:20,20:21,21:22,22:t,23:r,24:i,26:n,28:a,29:49,30:61,32:62,34:o,36:l,37:u,38:h,39:d,40:p,41:f,43:23,44:g,45:m,46:y,47:k,48:C,49:S,50:E,51:_,52:b,53:v,54:x,55:w,56:R,57:$,58:M,59:N,60:j,61:I,62:z,63:F,64:O,65:B,66:Y,67:U,68:H,69:J,70:te,71:ie,72:ue,73:se,74:le},{14:[1,74]},e(Te,[2,13],{43:23,29:49,30:61,32:62,20:75,34:o,36:l,37:u,38:h,39:d,40:p,41:f,44:g,45:m,46:y,47:k,48:C,49:S,50:E,51:_,52:b,53:v,54:x,55:w,56:R,57:$,58:M,59:N,60:j,61:I,62:z,63:F,64:O,65:B,66:Y,67:U,68:H,69:J,70:te,71:ie,72:ue,73:se,74:le}),e(Te,[2,14]),e(oe,[2,16],{12:[1,76]}),e(Te,[2,36],{12:[1,77]}),e(fe,[2,19]),e(fe,[2,20]),{25:[1,78]},{27:[1,79]},e(fe,[2,23]),{35:80,75:81,76:ge,77:ke,79:Ne,80:xe},{35:86,75:81,76:ge,77:ke,79:Ne,80:xe},{35:87,75:81,76:ge,77:ke,79:Ne,80:xe},{35:88,75:81,76:ge,77:ke,79:Ne,80:xe},{35:89,75:81,76:ge,77:ke,79:Ne,80:xe},{35:90,75:81,76:ge,77:ke,79:Ne,80:xe},{35:91,75:81,76:ge,77:ke,79:Ne,80:xe},{35:92,75:81,76:ge,77:ke,79:Ne,80:xe},{35:93,75:81,76:ge,77:ke,79:Ne,80:xe},{35:94,75:81,76:ge,77:ke,79:Ne,80:xe},{35:95,75:81,76:ge,77:ke,79:Ne,80:xe},{35:96,75:81,76:ge,77:ke,79:Ne,80:xe},{35:97,75:81,76:ge,77:ke,79:Ne,80:xe},{35:98,75:81,76:ge,77:ke,79:Ne,80:xe},{35:99,75:81,76:ge,77:ke,79:Ne,80:xe},{35:100,75:81,76:ge,77:ke,79:Ne,80:xe},{35:101,75:81,76:ge,77:ke,79:Ne,80:xe},{35:102,75:81,76:ge,77:ke,79:Ne,80:xe},{35:103,75:81,76:ge,77:ke,79:Ne,80:xe},{35:104,75:81,76:ge,77:ke,79:Ne,80:xe},e($e,[2,59]),{35:105,75:81,76:ge,77:ke,79:Ne,80:xe},{35:106,75:81,76:ge,77:ke,79:Ne,80:xe},{35:107,75:81,76:ge,77:ke,79:Ne,80:xe},{35:108,75:81,76:ge,77:ke,79:Ne,80:xe},{35:109,75:81,76:ge,77:ke,79:Ne,80:xe},{35:110,75:81,76:ge,77:ke,79:Ne,80:xe},{35:111,75:81,76:ge,77:ke,79:Ne,80:xe},{35:112,75:81,76:ge,77:ke,79:Ne,80:xe},{35:113,75:81,76:ge,77:ke,79:Ne,80:xe},{35:114,75:81,76:ge,77:ke,79:Ne,80:xe},{35:115,75:81,76:ge,77:ke,79:Ne,80:xe},{20:116,29:49,30:61,32:62,34:o,36:l,37:u,38:h,39:d,40:p,41:f,43:23,44:g,45:m,46:y,47:k,48:C,49:S,50:E,51:_,52:b,53:v,54:x,55:w,56:R,57:$,58:M,59:N,60:j,61:I,62:z,63:F,64:O,65:B,66:Y,67:U,68:H,69:J,70:te,71:ie,72:ue,73:se,74:le},{12:[1,118],33:[1,117]},{35:119,75:81,76:ge,77:ke,79:Ne,80:xe},{35:120,75:81,76:ge,77:ke,79:Ne,80:xe},{35:121,75:81,76:ge,77:ke,79:Ne,80:xe},{35:122,75:81,76:ge,77:ke,79:Ne,80:xe},{35:123,75:81,76:ge,77:ke,79:Ne,80:xe},{35:124,75:81,76:ge,77:ke,79:Ne,80:xe},{35:125,75:81,76:ge,77:ke,79:Ne,80:xe},{14:[1,126]},{14:[1,127]},{14:[1,128]},{14:[1,129]},{1:[2,8]},e(Te,[2,15]),e(oe,[2,17],{21:22,19:130,22:t,23:r,24:i,26:n,28:a}),e(Te,[2,37],{19:20,20:21,21:22,43:23,29:49,30:61,32:62,13:131,22:t,23:r,24:i,26:n,28:a,34:o,36:l,37:u,38:h,39:d,40:p,41:f,44:g,45:m,46:y,47:k,48:C,49:S,50:E,51:_,52:b,53:v,54:x,55:w,56:R,57:$,58:M,59:N,60:j,61:I,62:z,63:F,64:O,65:B,66:Y,67:U,68:H,69:J,70:te,71:ie,72:ue,73:se,74:le}),e(fe,[2,21]),e(fe,[2,22]),e($e,[2,39]),e(Me,[2,71],{75:81,35:132,76:ge,77:ke,79:Ne,80:xe}),e(He,[2,73]),{78:[1,133]},e(He,[2,75]),e(He,[2,76]),e($e,[2,40]),e($e,[2,41]),e($e,[2,42]),e($e,[2,43]),e($e,[2,44]),e($e,[2,45]),e($e,[2,46]),e($e,[2,47]),e($e,[2,48]),e($e,[2,49]),e($e,[2,50]),e($e,[2,51]),e($e,[2,52]),e($e,[2,53]),e($e,[2,54]),e($e,[2,55]),e($e,[2,56]),e($e,[2,57]),e($e,[2,58]),e($e,[2,60]),e($e,[2,61]),e($e,[2,62]),e($e,[2,63]),e($e,[2,64]),e($e,[2,65]),e($e,[2,66]),e($e,[2,67]),e($e,[2,68]),e($e,[2,69]),e($e,[2,70]),{31:134,42:[1,135]},{12:[1,136]},{33:[1,137]},e(Ge,[2,28]),e(Ge,[2,29]),e(Ge,[2,30]),e(Ge,[2,31]),e(Ge,[2,32]),e(Ge,[2,33]),e(Ge,[2,34]),{1:[2,9]},{1:[2,10]},{1:[2,11]},{1:[2,12]},e(oe,[2,18]),e(Te,[2,38]),e(Me,[2,72]),e(He,[2,74]),e($e,[2,24]),e($e,[2,35]),e(ze,[2,25]),e(ze,[2,26],{12:[1,138]}),e(ze,[2,27])],defaultActions:{2:[2,1],3:[2,2],4:[2,7],5:[2,3],6:[2,4],7:[2,5],8:[2,6],74:[2,8],126:[2,9],127:[2,10],128:[2,11],129:[2,12]},parseError:s(function(ye,be){if(be.recoverable)this.trace(ye);else{var Ie=new Error(ye);throw Ie.hash=be,Ie}},"parseError"),parse:s(function(ye){var be=this,Ie=[0],pe=[],Qe=[null],W=[],Ae=this.table,K="",de=0,ee=0,ve=0,X=2,me=1,Ce=W.slice.call(arguments,1),Be=Object.create(this.lexer),Re={yy:{}};for(var Ke in this.yy)Object.prototype.hasOwnProperty.call(this.yy,Ke)&&(Re.yy[Ke]=this.yy[Ke]);Be.setInput(ye,Re.yy),Re.yy.lexer=Be,Re.yy.parser=this,typeof Be.yylloc>"u"&&(Be.yylloc={});var De=Be.yylloc;W.push(De);var Ze=Be.options&&Be.options.ranges;typeof Re.yy.parseError=="function"?this.parseError=Re.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function Fe($t){Ie.length=Ie.length-2*$t,Qe.length=Qe.length-$t,W.length=W.length-$t}s(Fe,"popStack");function Je(){var $t;return $t=pe.pop()||Be.lex()||me,typeof $t!="number"&&($t instanceof Array&&(pe=$t,$t=pe.pop()),$t=be.symbols_[$t]||$t),$t}s(Je,"lex");for(var Le,ct,qe,mt,St,Lt,Bt={},qt,tr,Et,_t;;){if(qe=Ie[Ie.length-1],this.defaultActions[qe]?mt=this.defaultActions[qe]:((Le===null||typeof Le>"u")&&(Le=Je()),mt=Ae[qe]&&Ae[qe][Le]),typeof mt>"u"||!mt.length||!mt[0]){var Kt="";_t=[];for(qt in Ae[qe])this.terminals_[qt]&&qt>X&&_t.push("'"+this.terminals_[qt]+"'");Be.showPosition?Kt="Parse error on line "+(de+1)+`: +`+Be.showPosition()+` +Expecting `+_t.join(", ")+", got '"+(this.terminals_[Le]||Le)+"'":Kt="Parse error on line "+(de+1)+": Unexpected "+(Le==me?"end of input":"'"+(this.terminals_[Le]||Le)+"'"),this.parseError(Kt,{text:Be.match,token:this.terminals_[Le]||Le,line:Be.yylineno,loc:De,expected:_t})}if(mt[0]instanceof Array&&mt.length>1)throw new Error("Parse Error: multiple actions possible at state: "+qe+", token: "+Le);switch(mt[0]){case 1:Ie.push(Le),Qe.push(Be.yytext),W.push(Be.yylloc),Ie.push(mt[1]),Le=null,ct?(Le=ct,ct=null):(ee=Be.yyleng,K=Be.yytext,de=Be.yylineno,De=Be.yylloc,ve>0&&ve--);break;case 2:if(tr=this.productions_[mt[1]][1],Bt.$=Qe[Qe.length-tr],Bt._$={first_line:W[W.length-(tr||1)].first_line,last_line:W[W.length-1].last_line,first_column:W[W.length-(tr||1)].first_column,last_column:W[W.length-1].last_column},Ze&&(Bt._$.range=[W[W.length-(tr||1)].range[0],W[W.length-1].range[1]]),Lt=this.performAction.apply(Bt,[K,ee,de,Re.yy,mt[1],Qe,W].concat(Ce)),typeof Lt<"u")return Lt;tr&&(Ie=Ie.slice(0,-1*tr*2),Qe=Qe.slice(0,-1*tr),W=W.slice(0,-1*tr)),Ie.push(this.productions_[mt[1]][0]),Qe.push(Bt.$),W.push(Bt._$),Et=Ae[Ie[Ie.length-2]][Ie[Ie.length-1]],Ie.push(Et);break;case 3:return!0}}return!0},"parse")},et=(function(){var ye={EOF:1,parseError:s(function(be,Ie){if(this.yy.parser)this.yy.parser.parseError(be,Ie);else throw new Error(be)},"parseError"),setInput:s(function(be,Ie){return this.yy=Ie||this.yy||{},this._input=be,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var be=this._input[0];this.yytext+=be,this.yyleng++,this.offset++,this.match+=be,this.matched+=be;var Ie=be.match(/(?:\r\n?|\n).*/g);return Ie?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),be},"input"),unput:s(function(be){var Ie=be.length,pe=be.split(/(?:\r\n?|\n)/g);this._input=be+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-Ie),this.offset-=Ie;var Qe=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),pe.length-1&&(this.yylineno-=pe.length-1);var W=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:pe?(pe.length===Qe.length?this.yylloc.first_column:0)+Qe[Qe.length-pe.length].length-pe[0].length:this.yylloc.first_column-Ie},this.options.ranges&&(this.yylloc.range=[W[0],W[0]+this.yyleng-Ie]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(be){this.unput(this.match.slice(be))},"less"),pastInput:s(function(){var be=this.matched.substr(0,this.matched.length-this.match.length);return(be.length>20?"...":"")+be.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var be=this.match;return be.length<20&&(be+=this._input.substr(0,20-be.length)),(be.substr(0,20)+(be.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var be=this.pastInput(),Ie=new Array(be.length+1).join("-");return be+this.upcomingInput()+` +`+Ie+"^"},"showPosition"),test_match:s(function(be,Ie){var pe,Qe,W;if(this.options.backtrack_lexer&&(W={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(W.yylloc.range=this.yylloc.range.slice(0))),Qe=be[0].match(/(?:\r\n?|\n).*/g),Qe&&(this.yylineno+=Qe.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:Qe?Qe[Qe.length-1].length-Qe[Qe.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+be[0].length},this.yytext+=be[0],this.match+=be[0],this.matches=be,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(be[0].length),this.matched+=be[0],pe=this.performAction.call(this,this.yy,this,Ie,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),pe)return pe;if(this._backtrack){for(var Ae in W)this[Ae]=W[Ae];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var be,Ie,pe,Qe;this._more||(this.yytext="",this.match="");for(var W=this._currentRules(),Ae=0;AeIe[0].length)){if(Ie=pe,Qe=Ae,this.options.backtrack_lexer){if(be=this.test_match(pe,W[Ae]),be!==!1)return be;if(this._backtrack){Ie=!1;continue}else return!1}else if(!this.options.flex)break}return Ie?(be=this.test_match(Ie,W[Qe]),be!==!1?be:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var be=this.next();return be||this.lex()},"lex"),begin:s(function(be){this.conditionStack.push(be)},"begin"),popState:s(function(){var be=this.conditionStack.length-1;return be>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(be){return be=this.conditionStack.length-1-Math.abs(be||0),be>=0?this.conditionStack[be]:"INITIAL"},"topState"),pushState:s(function(be){this.begin(be)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:s(function(be,Ie,pe,Qe){var W=Qe;switch(pe){case 0:return 6;case 1:return 7;case 2:return 8;case 3:return 9;case 4:return 22;case 5:return 23;case 6:return this.begin("acc_title"),24;case 7:return this.popState(),"acc_title_value";case 8:return this.begin("acc_descr"),26;case 9:return this.popState(),"acc_descr_value";case 10:this.begin("acc_descr_multiline");break;case 11:this.popState();break;case 12:return"acc_descr_multiline_value";case 13:break;case 14:c;break;case 15:return 12;case 16:break;case 17:return 11;case 18:return 15;case 19:return 16;case 20:return 17;case 21:return 18;case 22:return this.begin("person_ext"),45;case 23:return this.begin("person"),44;case 24:return this.begin("system_ext_queue"),51;case 25:return this.begin("system_ext_db"),50;case 26:return this.begin("system_ext"),49;case 27:return this.begin("system_queue"),48;case 28:return this.begin("system_db"),47;case 29:return this.begin("system"),46;case 30:return this.begin("boundary"),37;case 31:return this.begin("enterprise_boundary"),34;case 32:return this.begin("system_boundary"),36;case 33:return this.begin("container_ext_queue"),57;case 34:return this.begin("container_ext_db"),56;case 35:return this.begin("container_ext"),55;case 36:return this.begin("container_queue"),54;case 37:return this.begin("container_db"),53;case 38:return this.begin("container"),52;case 39:return this.begin("container_boundary"),38;case 40:return this.begin("component_ext_queue"),63;case 41:return this.begin("component_ext_db"),62;case 42:return this.begin("component_ext"),61;case 43:return this.begin("component_queue"),60;case 44:return this.begin("component_db"),59;case 45:return this.begin("component"),58;case 46:return this.begin("node"),39;case 47:return this.begin("node"),39;case 48:return this.begin("node_l"),40;case 49:return this.begin("node_r"),41;case 50:return this.begin("rel"),64;case 51:return this.begin("birel"),65;case 52:return this.begin("rel_u"),66;case 53:return this.begin("rel_u"),66;case 54:return this.begin("rel_d"),67;case 55:return this.begin("rel_d"),67;case 56:return this.begin("rel_l"),68;case 57:return this.begin("rel_l"),68;case 58:return this.begin("rel_r"),69;case 59:return this.begin("rel_r"),69;case 60:return this.begin("rel_b"),70;case 61:return this.begin("rel_index"),71;case 62:return this.begin("update_el_style"),72;case 63:return this.begin("update_rel_style"),73;case 64:return this.begin("update_layout_config"),74;case 65:return"EOF_IN_STRUCT";case 66:return this.begin("attribute"),"ATTRIBUTE_EMPTY";case 67:this.begin("attribute");break;case 68:this.popState(),this.popState();break;case 69:return 80;case 70:break;case 71:return 80;case 72:this.begin("string");break;case 73:this.popState();break;case 74:return"STR";case 75:this.begin("string_kv");break;case 76:return this.begin("string_kv_key"),"STR_KEY";case 77:this.popState(),this.begin("string_kv_value");break;case 78:return"STR_VALUE";case 79:this.popState(),this.popState();break;case 80:return"STR";case 81:return"LBRACE";case 82:return"RBRACE";case 83:return"SPACE";case 84:return"EOL";case 85:return 14}},"anonymous"),rules:[/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:title\s[^#\n;]+)/,/^(?:accDescription\s[^#\n;]+)/,/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:%%(?!\{)*[^\n]*(\r?\n?)+)/,/^(?:%%[^\n]*(\r?\n)*)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:C4Context\b)/,/^(?:C4Container\b)/,/^(?:C4Component\b)/,/^(?:C4Dynamic\b)/,/^(?:C4Deployment\b)/,/^(?:Person_Ext\b)/,/^(?:Person\b)/,/^(?:SystemQueue_Ext\b)/,/^(?:SystemDb_Ext\b)/,/^(?:System_Ext\b)/,/^(?:SystemQueue\b)/,/^(?:SystemDb\b)/,/^(?:System\b)/,/^(?:Boundary\b)/,/^(?:Enterprise_Boundary\b)/,/^(?:System_Boundary\b)/,/^(?:ContainerQueue_Ext\b)/,/^(?:ContainerDb_Ext\b)/,/^(?:Container_Ext\b)/,/^(?:ContainerQueue\b)/,/^(?:ContainerDb\b)/,/^(?:Container\b)/,/^(?:Container_Boundary\b)/,/^(?:ComponentQueue_Ext\b)/,/^(?:ComponentDb_Ext\b)/,/^(?:Component_Ext\b)/,/^(?:ComponentQueue\b)/,/^(?:ComponentDb\b)/,/^(?:Component\b)/,/^(?:Deployment_Node\b)/,/^(?:Node\b)/,/^(?:Node_L\b)/,/^(?:Node_R\b)/,/^(?:Rel\b)/,/^(?:BiRel\b)/,/^(?:Rel_Up\b)/,/^(?:Rel_U\b)/,/^(?:Rel_Down\b)/,/^(?:Rel_D\b)/,/^(?:Rel_Left\b)/,/^(?:Rel_L\b)/,/^(?:Rel_Right\b)/,/^(?:Rel_R\b)/,/^(?:Rel_Back\b)/,/^(?:RelIndex\b)/,/^(?:UpdateElementStyle\b)/,/^(?:UpdateRelStyle\b)/,/^(?:UpdateLayoutConfig\b)/,/^(?:$)/,/^(?:[(][ ]*[,])/,/^(?:[(])/,/^(?:[)])/,/^(?:,,)/,/^(?:,)/,/^(?:[ ]*["]["])/,/^(?:[ ]*["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:[ ]*[\$])/,/^(?:[^=]*)/,/^(?:[=][ ]*["])/,/^(?:[^"]+)/,/^(?:["])/,/^(?:[^,]+)/,/^(?:\{)/,/^(?:\})/,/^(?:[\s]+)/,/^(?:[\n\r]+)/,/^(?:$)/],conditions:{acc_descr_multiline:{rules:[11,12],inclusive:!1},acc_descr:{rules:[9],inclusive:!1},acc_title:{rules:[7],inclusive:!1},string_kv_value:{rules:[78,79],inclusive:!1},string_kv_key:{rules:[77],inclusive:!1},string_kv:{rules:[76],inclusive:!1},string:{rules:[73,74],inclusive:!1},attribute:{rules:[68,69,70,71,72,75,80],inclusive:!1},update_layout_config:{rules:[65,66,67,68],inclusive:!1},update_rel_style:{rules:[65,66,67,68],inclusive:!1},update_el_style:{rules:[65,66,67,68],inclusive:!1},rel_b:{rules:[65,66,67,68],inclusive:!1},rel_r:{rules:[65,66,67,68],inclusive:!1},rel_l:{rules:[65,66,67,68],inclusive:!1},rel_d:{rules:[65,66,67,68],inclusive:!1},rel_u:{rules:[65,66,67,68],inclusive:!1},rel_bi:{rules:[],inclusive:!1},rel:{rules:[65,66,67,68],inclusive:!1},node_r:{rules:[65,66,67,68],inclusive:!1},node_l:{rules:[65,66,67,68],inclusive:!1},node:{rules:[65,66,67,68],inclusive:!1},index:{rules:[],inclusive:!1},rel_index:{rules:[65,66,67,68],inclusive:!1},component_ext_queue:{rules:[65,66,67,68],inclusive:!1},component_ext_db:{rules:[65,66,67,68],inclusive:!1},component_ext:{rules:[65,66,67,68],inclusive:!1},component_queue:{rules:[65,66,67,68],inclusive:!1},component_db:{rules:[65,66,67,68],inclusive:!1},component:{rules:[65,66,67,68],inclusive:!1},container_boundary:{rules:[65,66,67,68],inclusive:!1},container_ext_queue:{rules:[65,66,67,68],inclusive:!1},container_ext_db:{rules:[65,66,67,68],inclusive:!1},container_ext:{rules:[65,66,67,68],inclusive:!1},container_queue:{rules:[65,66,67,68],inclusive:!1},container_db:{rules:[65,66,67,68],inclusive:!1},container:{rules:[65,66,67,68],inclusive:!1},birel:{rules:[65,66,67,68],inclusive:!1},system_boundary:{rules:[65,66,67,68],inclusive:!1},enterprise_boundary:{rules:[65,66,67,68],inclusive:!1},boundary:{rules:[65,66,67,68],inclusive:!1},system_ext_queue:{rules:[65,66,67,68],inclusive:!1},system_ext_db:{rules:[65,66,67,68],inclusive:!1},system_ext:{rules:[65,66,67,68],inclusive:!1},system_queue:{rules:[65,66,67,68],inclusive:!1},system_db:{rules:[65,66,67,68],inclusive:!1},system:{rules:[65,66,67,68],inclusive:!1},person_ext:{rules:[65,66,67,68],inclusive:!1},person:{rules:[65,66,67,68],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,8,10,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,81,82,83,84,85],inclusive:!0}}};return ye})();Ve.lexer=et;function he(){this.yy={}}return s(he,"Parser"),he.prototype=Ve,Ve.Parser=he,new he})(),yT.parser=yT,kye=yT}),wye,Eye,aa,Q0,vn=G(()=>{"use strict";Nt(),wye=s(function(e,t){for(let r of t)e.attr(r[0],r[1])},"d3Attrs"),Eye=s(function(e,t,r){let i=new Map;return r?(i.set("width","100%"),i.set("style",`max-width: ${t}px;`)):(i.set("height",e),i.set("width",t)),i},"calculateSvgSizeAttrs"),aa=s(function(e,t,r,i){let n=Eye(t,r,i);wye(e,n)},"configureSvgSize"),Q0=s(function(e,t,r,i){let n=t.node().getBBox(),a=n.width,o=n.height;Ee.info(`SVG bounds: ${a}x${o}`,n);let l=0,u=0;Ee.info(`Graph bounds: ${l}x${u}`,e),l=a+r*2,u=o+r*2,Ee.info(`Calculated bounds: ${l}x${u}`),aa(t,u,l,i);let h=`${n.x-r} ${n.y-r} ${n.width+2*r} ${n.height+2*r}`;t.attr("viewBox",h)},"setupGraphViewbox")});function PO(e){return[...e.cssRules].map(t=>t.cssText).join(` +`)}var vT,Sye,_ye,Aye,Rye=G(()=>{"use strict";Nt(),vT={},s(PO,"cssStyleSheetToString"),Sye=s((e,t,r,i)=>{let n="";return e in vT&&vT[e]?n=vT[e]({...r,svgId:i}):Ee.warn(`No theme found for ${e}`),`& { + font-family: ${r.fontFamily}; + font-size: ${r.fontSize}; + fill: ${r.textColor} + } + @keyframes edge-animation-frame { + from { + stroke-dashoffset: 0; + } + } + @keyframes dash { + to { + stroke-dashoffset: 0; + } + } + & .edge-animation-slow { + stroke-dasharray: 9,5 !important; + stroke-dashoffset: 900; + animation: dash 50s linear infinite; + stroke-linecap: round; + } + & .edge-animation-fast { + stroke-dasharray: 9,5 !important; + stroke-dashoffset: 900; + animation: dash 20s linear infinite; + stroke-linecap: round; + } + /* Classes common for multiple diagrams */ + + & .error-icon { + fill: ${r.errorBkgColor}; + } + & .error-text { + fill: ${r.errorTextColor}; + stroke: ${r.errorTextColor}; + } + + & .edge-thickness-normal { + stroke-width: ${r.strokeWidth??1}px; + } + & .edge-thickness-thick { + stroke-width: 3.5px + } + & .edge-pattern-solid { + stroke-dasharray: 0; + } + & .edge-thickness-invisible { + stroke-width: 0; + fill: none; + } + & .edge-pattern-dashed{ + stroke-dasharray: 3; + } + .edge-pattern-dotted { + stroke-dasharray: 2; + } + + & .marker { + fill: ${r.lineColor}; + stroke: ${r.lineColor}; + } + & .marker.cross { + stroke: ${r.lineColor}; + } + + & svg { + font-family: ${r.fontFamily}; + font-size: ${r.fontSize}; + } + & p { + margin: 0 + } + + ${n} + .node .neo-node { + stroke: ${r.nodeBorder}; + } + + [data-look="neo"].node rect, [data-look="neo"].cluster rect, [data-look="neo"].node polygon { + stroke: ${r.useGradient?"url("+i+"-gradient)":r.nodeBorder}; + filter: ${r.dropShadow?r.dropShadow.replace("url(#drop-shadow)",`url(${i}-drop-shadow)`):"none"}; + } + [data-look="neo"].swimlane.cluster rect { + filter: none; + } + + + [data-look="neo"].node path { + stroke: ${r.useGradient?"url("+i+"-gradient)":r.nodeBorder}; + stroke-width: ${r.strokeWidth??1}px; + } + + [data-look="neo"].node .outer-path { + filter: ${r.dropShadow?r.dropShadow.replace("url(#drop-shadow)",`url(${i}-drop-shadow)`):"none"}; + } + + [data-look="neo"].node .neo-line path { + stroke: ${r.nodeBorder}; + filter: none; + } + + [data-look="neo"].node circle{ + stroke: ${r.useGradient?"url("+i+"-gradient)":r.nodeBorder}; + filter: ${r.dropShadow?r.dropShadow.replace("url(#drop-shadow)",`url(${i}-drop-shadow)`):"none"}; + } + + [data-look="neo"].node circle .state-start{ + fill: #000000; + } + + [data-look="neo"].icon-shape .icon { + fill: ${r.useGradient?"url("+i+"-gradient)":r.nodeBorder}; + filter: ${r.dropShadow?r.dropShadow.replace("url(#drop-shadow)",`url(${i}-drop-shadow)`):"none"}; + } + + [data-look="neo"].icon-shape .icon-neo path { + stroke: ${r.useGradient?"url("+i+"-gradient)":r.nodeBorder}; + filter: ${r.dropShadow?r.dropShadow.replace("url(#drop-shadow)",`url(${i}-drop-shadow)`):"none"}; + } + + ${t} +`},"getStyles"),_ye=s((e,t)=>{t!==void 0&&(vT[e]=t)},"addStylesForDiagram"),Aye=Sye}),FO={};Mr(FO,{clear:()=>pn,getAccDescription:()=>Mn,getAccTitle:()=>Ln,getDiagramTitle:()=>xn,setAccDescription:()=>In,setAccTitle:()=>bn,setDiagramTitle:()=>Dn});var RS,$S,LS,IS,pn,bn,Ln,In,Mn,Dn,xn,fn=G(()=>{"use strict";mi(),Gr(),RS="",$S="",LS="",IS=s(e=>Si(e,Rr()),"sanitizeText"),pn=s(()=>{RS="",LS="",$S=""},"clear"),bn=s(e=>{RS=IS(e).replace(/^\s+/g,"")},"setAccTitle"),Ln=s(()=>RS,"getAccTitle"),In=s(e=>{LS=IS(e).replace(/\n\s+/g,` +`)},"setAccDescription"),Mn=s(()=>LS,"getAccDescription"),Dn=s(e=>{$S=IS(e)},"setDiagramTitle"),xn=s(()=>$S,"getDiagramTitle")}),zO,$ye,it,MS,qO,DS,NS,Lye,bT,xT,OS,Iye,Er=G(()=>{"use strict";var e;yy(),Nt(),Gr(),mi(),vn(),Rye(),fn(),zO=Ee,$ye=pE,it=Rr,MS=mD,qO=B0,DS=s(t=>Si(t,it()),"sanitizeText"),NS=Q0,Lye=s(()=>FO,"getCommonDb"),bT={},xT=s((t,r,i)=>{bT[t]&&zO.warn(`Diagram with id ${t} already registered. Overwriting.`),bT[t]=r,i&&BO(t,i),_ye(t,r.styles),r.injectUtils?.(zO,$ye,it,DS,NS,Lye(),()=>{})},"registerDiagram"),OS=s(t=>{if(t in bT)return bT[t];throw new Iye(t)},"getDiagram"),Iye=(e=class extends Error{constructor(r){super(`Diagram ${r} not found.`)}},s(e,"DiagramNotFoundError"),e)}),WO,Mye=G(()=>{"use strict";Er(),WO=s(e=>it()[e],"getRequiredConfig")}),wp,jO,Gl,ed,ao,Xl,Dc,vy,BS,PS,TT,kT,Dye,Nye,Oye,Bye,Pye,Fye,zye,qye,Wye,jye,Vye,Uye,Yye,Hye,Gye,Xye,Kye,Qye,VO,Zye,Jye,UO,e2e,t2e,r2e,i2e,td,n2e,a2e,s2e,o2e,l2e,FS,c2e=G(()=>{"use strict";Er(),Mye(),mi(),fn(),wp=s((e,t)=>{for(let[r,i]of Object.entries(t))if(i!==void 0)if(typeof i=="object"){let[n,a]=Object.entries(i)[0];e[n]=a}else e[r]=i},"assignAttributes"),jO=s(()=>({alias:"global",label:{text:"global"},type:{text:"global"},tags:null,link:null,parentBoundary:""}),"createGlobalBoundary"),Gl=[],ed=[""],ao="global",Xl="",Dc=[jO()],vy=[],BS="",PS=!1,TT=4,kT=2,Nye=s(function(){return Dye},"getC4Type"),Oye=s(function(e){Dye=Si(e,it())},"setC4Type"),Bye=s(function(e,t,r,i,n,a,o,l,u){if(e==null||t===void 0||t===null||r===void 0||r===null||i===void 0||i===null)return;let h={},d=vy.find(p=>p.from===t&&p.to===r);if(d?h=d:vy.push(h),h.type=e,h.from=t,h.to=r,h.label={text:i},n==null)h.techn={text:""};else if(typeof n=="object"){let[p,f]=Object.entries(n)[0];h[p]={text:f}}else h.techn={text:n};if(a==null)h.descr={text:""};else if(typeof a=="object"){let[p,f]=Object.entries(a)[0];h[p]={text:f}}else h.descr={text:a};wp(h,{sprite:o,tags:l,link:u}),h.wrap=td()},"addRel"),Pye=s(function(e,t,r,i,n,a,o){if(t===null||r===null)return;let l={},u=Gl.find(h=>h.alias===t);if(u&&t===u.alias?l=u:(l.alias=t,Gl.push(l)),r==null?l.label={text:""}:l.label={text:r},i==null)l.descr={text:""};else if(typeof i=="object"){let[h,d]=Object.entries(i)[0];l[h]={text:d}}else l.descr={text:i};wp(l,{sprite:n,tags:a,link:o}),l.typeC4Shape={text:e},l.parentBoundary=ao,l.wrap=td()},"addPersonOrSystem"),Fye=s(function(e,t,r,i,n,a,o,l){if(t===null||r===null)return;let u={},h=Gl.find(d=>d.alias===t);if(h&&t===h.alias?u=h:(u.alias=t,Gl.push(u)),r==null?u.label={text:""}:u.label={text:r},i==null)u.techn={text:""};else if(typeof i=="object"){let[d,p]=Object.entries(i)[0];u[d]={text:p}}else u.techn={text:i};if(n==null)u.descr={text:""};else if(typeof n=="object"){let[d,p]=Object.entries(n)[0];u[d]={text:p}}else u.descr={text:n};wp(u,{sprite:a,tags:o,link:l}),u.wrap=td(),u.typeC4Shape={text:e},u.parentBoundary=ao},"addContainer"),zye=s(function(e,t,r,i,n,a,o,l){if(t===null||r===null)return;let u={},h=Gl.find(d=>d.alias===t);if(h&&t===h.alias?u=h:(u.alias=t,Gl.push(u)),r==null?u.label={text:""}:u.label={text:r},i==null)u.techn={text:""};else if(typeof i=="object"){let[d,p]=Object.entries(i)[0];u[d]={text:p}}else u.techn={text:i};if(n==null)u.descr={text:""};else if(typeof n=="object"){let[d,p]=Object.entries(n)[0];u[d]={text:p}}else u.descr={text:n};wp(u,{sprite:a,tags:o,link:l}),u.wrap=td(),u.typeC4Shape={text:e},u.parentBoundary=ao},"addComponent"),qye=s(function(e,t,r,i,n){if(e===null||t===null)return;let a={},o=Dc.find(l=>l.alias===e);if(o&&e===o.alias?a=o:(a.alias=e,Dc.push(a)),t==null?a.label={text:""}:a.label={text:t},r==null)a.type={text:"system"};else if(typeof r=="object"){let[l,u]=Object.entries(r)[0];a[l]={text:u}}else a.type={text:r};wp(a,{tags:i,link:n}),a.parentBoundary=ao,a.wrap=td(),Xl=ao,ao=e,ed.push(Xl)},"addPersonOrSystemBoundary"),Wye=s(function(e,t,r,i,n){if(e===null||t===null)return;let a={},o=Dc.find(l=>l.alias===e);if(o&&e===o.alias?a=o:(a.alias=e,Dc.push(a)),t==null?a.label={text:""}:a.label={text:t},r==null)a.type={text:"container"};else if(typeof r=="object"){let[l,u]=Object.entries(r)[0];a[l]={text:u}}else a.type={text:r};wp(a,{tags:i,link:n}),a.parentBoundary=ao,a.wrap=td(),Xl=ao,ao=e,ed.push(Xl)},"addContainerBoundary"),jye=s(function(e,t,r,i,n,a,o,l){if(t===null||r===null)return;let u={},h=Dc.find(d=>d.alias===t);if(h&&t===h.alias?u=h:(u.alias=t,Dc.push(u)),r==null?u.label={text:""}:u.label={text:r},i==null)u.type={text:"node"};else if(typeof i=="object"){let[d,p]=Object.entries(i)[0];u[d]={text:p}}else u.type={text:i};if(n==null)u.descr={text:""};else if(typeof n=="object"){let[d,p]=Object.entries(n)[0];u[d]={text:p}}else u.descr={text:n};wp(u,{tags:o,link:l}),u.nodeType=e,u.parentBoundary=ao,u.wrap=td(),Xl=ao,ao=t,ed.push(Xl)},"addDeploymentNode"),Vye=s(function(){ao=Xl,ed.pop(),Xl=ed.pop(),ed.push(Xl)},"popBoundaryParseStack"),Uye=s(function(e,t,r,i,n,a,o,l,u,h,d){let p=Gl.find(f=>f.alias===t);if(!(p===void 0&&(p=Dc.find(f=>f.alias===t),p===void 0))){if(r!=null)if(typeof r=="object"){let[f,g]=Object.entries(r)[0];p[f]=g}else p.bgColor=r;if(i!=null)if(typeof i=="object"){let[f,g]=Object.entries(i)[0];p[f]=g}else p.fontColor=i;if(n!=null)if(typeof n=="object"){let[f,g]=Object.entries(n)[0];p[f]=g}else p.borderColor=n;if(a!=null)if(typeof a=="object"){let[f,g]=Object.entries(a)[0];p[f]=g}else p.shadowing=a;if(o!=null)if(typeof o=="object"){let[f,g]=Object.entries(o)[0];p[f]=g}else p.shape=o;if(l!=null)if(typeof l=="object"){let[f,g]=Object.entries(l)[0];p[f]=g}else p.sprite=l;if(u!=null)if(typeof u=="object"){let[f,g]=Object.entries(u)[0];p[f]=g}else p.techn=u;if(h!=null)if(typeof h=="object"){let[f,g]=Object.entries(h)[0];p[f]=g}else p.legendText=h;if(d!=null)if(typeof d=="object"){let[f,g]=Object.entries(d)[0];p[f]=g}else p.legendSprite=d}},"updateElStyle"),Yye=s(function(e,t,r,i,n,a,o){let l=vy.find(u=>u.from===t&&u.to===r);if(l!==void 0){if(i!=null)if(typeof i=="object"){let[u,h]=Object.entries(i)[0];l[u]=h}else l.textColor=i;if(n!=null)if(typeof n=="object"){let[u,h]=Object.entries(n)[0];l[u]=h}else l.lineColor=n;if(a!=null)if(typeof a=="object"){let[u,h]=Object.entries(a)[0];l[u]=parseInt(h)}else l.offsetX=parseInt(a);if(o!=null)if(typeof o=="object"){let[u,h]=Object.entries(o)[0];l[u]=parseInt(h)}else l.offsetY=parseInt(o)}},"updateRelStyle"),Hye=s(function(e,t,r){let i=TT,n=kT;if(typeof t=="object"){let a=Object.values(t)[0];i=parseInt(a)}else i=parseInt(t);if(typeof r=="object"){let a=Object.values(r)[0];n=parseInt(a)}else n=parseInt(r);i>=1&&(TT=i),n>=1&&(kT=n)},"updateLayoutConfig"),Gye=s(function(){return TT},"getC4ShapeInRow"),Xye=s(function(){return kT},"getC4BoundaryInRow"),Kye=s(function(){return ao},"getCurrentBoundaryParse"),Qye=s(function(){return Xl},"getParentBoundaryParse"),VO=s(function(e){return e==null?Gl:Gl.filter(t=>t.parentBoundary===e)},"getC4ShapeArray"),Zye=s(function(e){return Gl.find(t=>t.alias===e)},"getC4Shape"),Jye=s(function(e){return Object.keys(VO(e))},"getC4ShapeKeys"),UO=s(function(e){return e==null?Dc:Dc.filter(t=>t.parentBoundary===e)},"getBoundaries"),e2e=UO,t2e=s(function(){return vy},"getRels"),r2e=s(function(){return BS},"getTitle"),i2e=s(function(e){PS=e},"setWrap"),td=s(function(){return PS},"autoWrap"),n2e=s(function(){Gl=[],Dc=[jO()],Xl="",ao="global",ed=[""],vy=[],ed=[""],BS="",PS=!1,TT=4,kT=2},"clear"),a2e={SOLID:0,DOTTED:1,NOTE:2,SOLID_CROSS:3,DOTTED_CROSS:4,SOLID_OPEN:5,DOTTED_OPEN:6,LOOP_START:10,LOOP_END:11,ALT_START:12,ALT_ELSE:13,ALT_END:14,OPT_START:15,OPT_END:16,ACTIVE_START:17,ACTIVE_END:18,PAR_START:19,PAR_AND:20,PAR_END:21,RECT_START:22,RECT_END:23,SOLID_POINT:24,DOTTED_POINT:25},s2e={FILLED:0,OPEN:1},o2e={LEFTOF:0,RIGHTOF:1,OVER:2},l2e=s(function(e){BS=Si(e,it())},"setTitle"),FS={addPersonOrSystem:Pye,addPersonOrSystemBoundary:qye,addContainer:Fye,addContainerBoundary:Wye,addComponent:zye,addDeploymentNode:jye,popBoundaryParseStack:Vye,addRel:Bye,updateElStyle:Uye,updateRelStyle:Yye,updateLayoutConfig:Hye,autoWrap:td,setWrap:i2e,getC4ShapeArray:VO,getC4Shape:Zye,getC4ShapeKeys:Jye,getBoundaries:UO,getBoundarys:e2e,getCurrentBoundaryParse:Kye,getParentBoundaryParse:Qye,getRels:t2e,getTitle:r2e,getC4Type:Nye,getC4ShapeInRow:Gye,getC4BoundaryInRow:Xye,setAccTitle:bn,getAccTitle:Ln,getAccDescription:Mn,setAccDescription:In,getConfig:s(()=>WO("c4"),"getConfig"),clear:n2e,LINETYPE:a2e,ARROWTYPE:s2e,PLACEMENT:o2e,setTitle:l2e,setC4Type:Oye}}),P3t=Ro(e=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.BLANK_URL=e.relativeFirstCharacters=e.whitespaceEscapeCharsRegex=e.urlSchemeRegex=e.ctrlCharactersRegex=e.htmlCtrlEntityRegex=e.htmlEntitiesRegex=e.invalidProtocolRegex=void 0,e.invalidProtocolRegex=/^([^\w]*)(javascript|data|vbscript)/im,e.htmlEntitiesRegex=/&#(\w+)(^\w|;)?/g,e.htmlCtrlEntityRegex=/&(newline|tab);/gi,e.ctrlCharactersRegex=/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim,e.urlSchemeRegex=/^.+(:|:)/gim,e.whitespaceEscapeCharsRegex=/(\\|%5[cC])((%(6[eE]|72|74))|[nrt])/g,e.relativeFirstCharacters=[".","/"],e.BLANK_URL="about:blank"}),zS=Ro(e=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.sanitizeUrl=o;var t=P3t();function r(l){return t.relativeFirstCharacters.indexOf(l[0])>-1}s(r,"isRelativeUrlWithoutProtocol");function i(l){var u=l.replace(t.ctrlCharactersRegex,"");return u.replace(t.htmlEntitiesRegex,function(h,d){return String.fromCharCode(d)})}s(i,"decodeHtmlCharacters");function n(l){return URL.canParse(l)}s(n,"isValidUrl");function a(l){try{return decodeURIComponent(l)}catch{return l}}s(a,"decodeURI");function o(l){if(!l)return t.BLANK_URL;var u,h=a(l.trim());do h=i(h).replace(t.htmlCtrlEntityRegex,"").replace(t.ctrlCharactersRegex,"").replace(t.whitespaceEscapeCharsRegex,"").trim(),h=a(h),u=h.match(t.ctrlCharactersRegex)||h.match(t.htmlEntitiesRegex)||h.match(t.htmlCtrlEntityRegex)||h.match(t.whitespaceEscapeCharsRegex);while(u&&u.length>0);var d=h;if(!d)return t.BLANK_URL;if(r(d))return d;var p=d.trimStart(),f=p.match(t.urlSchemeRegex);if(!f)return d;var g=f[0].toLowerCase().trim();if(t.invalidProtocolRegex.test(g))return t.BLANK_URL;var m=p.replace(/\\/g,"/");if(g==="mailto:"||g.includes("://"))return m;if(g==="http:"||g==="https:"){if(!n(m))return t.BLANK_URL;var y=new URL(m);return y.protocol=y.protocol.toLowerCase(),y.hostname=y.hostname.toLowerCase(),y.toString()}return m}s(o,"sanitizeUrl")});function CT(e,t){return e==null||t==null?NaN:et?1:e>=t?0:NaN}var u2e=G(()=>{"use strict";s(CT,"ascending")});function h2e(e,t){return e==null||t==null?NaN:te?1:t>=e?0:NaN}var F3t=G(()=>{"use strict";s(h2e,"descending")});function qS(e){let t,r,i;e.length!==2?(t=CT,r=s((l,u)=>CT(e(l),u),"compare2"),i=s((l,u)=>e(l)-u,"delta")):(t=e===CT||e===h2e?e:d2e,r=e,i=e);function n(l,u,h=0,d=l.length){if(h>>1;r(l[p],u)<0?h=p+1:d=p}while(h>>1;r(l[p],u)<=0?h=p+1:d=p}while(hh&&i(l[p-1],u)>-i(l[p],u)?p-1:p}return s(o,"center"),{left:n,center:o,right:a}}function d2e(){return 0}var p2e=G(()=>{"use strict";u2e(),F3t(),s(qS,"bisector"),s(d2e,"zero")});function f2e(e){return e===null?NaN:+e}var z3t=G(()=>{"use strict";s(f2e,"number")}),YO,g2e,q3t,W3t,m2e,j3t=G(()=>{"use strict";u2e(),p2e(),z3t(),YO=qS(CT),g2e=YO.right,q3t=YO.left,W3t=qS(f2e).center,m2e=g2e});function HO({_intern:e,_key:t},r){let i=t(r);return e.has(i)?e.get(i):r}function y2e({_intern:e,_key:t},r){let i=t(r);return e.has(i)?e.get(i):(e.set(i,r),r)}function v2e({_intern:e,_key:t},r){let i=t(r);return e.has(i)&&(r=e.get(i),e.delete(i)),r}function b2e(e){return e!==null&&typeof e=="object"?e.valueOf():e}var GO,V3t=G(()=>{"use strict";var e;GO=(e=class extends Map{constructor(r,i=b2e){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:i}}),r!=null)for(let[n,a]of r)this.set(n,a)}get(r){return super.get(HO(this,r))}has(r){return super.has(HO(this,r))}set(r,i){return super.set(y2e(this,r),i)}delete(r){return super.delete(v2e(this,r))}},s(e,"InternMap"),e),s(HO,"intern_get"),s(y2e,"intern_set"),s(v2e,"intern_delete"),s(b2e,"keyof")});function wT(e,t,r){let i=(t-e)/Math.max(0,r),n=Math.floor(Math.log10(i)),a=i/Math.pow(10,n),o=a>=T2e?10:a>=k2e?5:a>=C2e?2:1,l,u,h;return n<0?(h=Math.pow(10,-n)/o,l=Math.round(e*h),u=Math.round(t*h),l/ht&&--u,h=-h):(h=Math.pow(10,n)*o,l=Math.round(e/h),u=Math.round(t/h),l*ht&&--u),u0))return[];if(e===t)return[e];let i=t=n))return[];let l=a-n+1,u=new Array(l);if(i)if(o<0)for(let h=0;h{"use strict";T2e=Math.sqrt(50),k2e=Math.sqrt(10),C2e=Math.sqrt(2),s(wT,"tickSpec"),s(x2e,"ticks"),s(WS,"tickIncrement"),s(jS,"tickStep")});function w2e(e,t){let r;if(t===void 0)for(let i of e)i!=null&&(r=i)&&(r=i);else{let i=-1;for(let n of e)(n=t(n,++i,e))!=null&&(r=n)&&(r=n)}return r}var Y3t=G(()=>{"use strict";s(w2e,"max")});function E2e(e,t){let r;if(t===void 0)for(let i of e)i!=null&&(r>i||r===void 0&&i>=i)&&(r=i);else{let i=-1;for(let n of e)(n=t(n,++i,e))!=null&&(r>n||r===void 0&&n>=n)&&(r=n)}return r}var H3t=G(()=>{"use strict";s(E2e,"min")});function S2e(e,t,r){e=+e,t=+t,r=(n=arguments.length)<2?(t=e,e=0,1):n<3?1:+r;for(var i=-1,n=Math.max(0,Math.ceil((t-e)/r))|0,a=new Array(n);++i{"use strict";s(S2e,"range")}),Z0=G(()=>{"use strict";j3t(),p2e(),Y3t(),H3t(),G3t(),U3t(),V3t()});function _2e(e){return e}var X3t=G(()=>{"use strict";s(_2e,"default")});function A2e(e){return"translate("+e+",0)"}function R2e(e){return"translate(0,"+e+")"}function $2e(e){return t=>+e(t)}function L2e(e,t){return t=Math.max(0,e.bandwidth()-t*2)/2,e.round()&&(t=Math.round(t)),r=>+e(r)+t}function I2e(){return!this.__axis}function XO(e,t){var r=[],i=null,n=null,a=6,o=6,l=3,u=typeof window<"u"&&window.devicePixelRatio>1?0:.5,h=e===ET||e===ST?-1:1,d=e===ST||e===VS?"x":"y",p=e===ET||e===US?A2e:R2e;function f(g){var m=i??(t.ticks?t.ticks.apply(t,r):t.domain()),y=n??(t.tickFormat?t.tickFormat.apply(t,r):_2e),k=Math.max(a,0)+l,C=t.range(),S=+C[0]+u,E=+C[C.length-1]+u,_=(t.bandwidth?L2e:$2e)(t.copy(),u),b=g.selection?g.selection():g,v=b.selectAll(".domain").data([null]),x=b.selectAll(".tick").data(m,t).order(),w=x.exit(),R=x.enter().append("g").attr("class","tick"),$=x.select("line"),M=x.select("text");v=v.merge(v.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),x=x.merge(R),$=$.merge(R.append("line").attr("stroke","currentColor").attr(d+"2",h*a)),M=M.merge(R.append("text").attr("fill","currentColor").attr(d,h*k).attr("dy",e===ET?"0em":e===US?"0.71em":"0.32em")),g!==b&&(v=v.transition(g),x=x.transition(g),$=$.transition(g),M=M.transition(g),w=w.transition(g).attr("opacity",KO).attr("transform",function(N){return isFinite(N=_(N))?p(N+u):this.getAttribute("transform")}),R.attr("opacity",KO).attr("transform",function(N){var j=this.parentNode.__axis;return p((j&&isFinite(j=j(N))?j:_(N))+u)})),w.remove(),v.attr("d",e===ST||e===VS?o?"M"+h*o+","+S+"H"+u+"V"+E+"H"+h*o:"M"+u+","+S+"V"+E:o?"M"+S+","+h*o+"V"+u+"H"+E+"V"+h*o:"M"+S+","+u+"H"+E),x.attr("opacity",1).attr("transform",function(N){return p(_(N)+u)}),$.attr(d+"2",h*a),M.attr(d,h*k).text(y),b.filter(I2e).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",e===VS?"start":e===ST?"end":"middle"),b.each(function(){this.__axis=_})}return s(f,"axis"),f.scale=function(g){return arguments.length?(t=g,f):t},f.ticks=function(){return r=Array.from(arguments),f},f.tickArguments=function(g){return arguments.length?(r=g==null?[]:Array.from(g),f):r.slice()},f.tickValues=function(g){return arguments.length?(i=g==null?null:Array.from(g),f):i&&i.slice()},f.tickFormat=function(g){return arguments.length?(n=g,f):n},f.tickSize=function(g){return arguments.length?(a=o=+g,f):a},f.tickSizeInner=function(g){return arguments.length?(a=+g,f):a},f.tickSizeOuter=function(g){return arguments.length?(o=+g,f):o},f.tickPadding=function(g){return arguments.length?(l=+g,f):l},f.offset=function(g){return arguments.length?(u=+g,f):u},f}function M2e(e){return XO(ET,e)}function D2e(e){return XO(US,e)}var ET,VS,US,ST,KO,K3t=G(()=>{"use strict";X3t(),ET=1,VS=2,US=3,ST=4,KO=1e-6,s(A2e,"translateX"),s(R2e,"translateY"),s($2e,"number"),s(L2e,"center"),s(I2e,"entering"),s(XO,"axis"),s(M2e,"axisTop"),s(D2e,"axisBottom")}),Q3t=G(()=>{"use strict";K3t()});function QO(){for(var e=0,t=arguments.length,r={},i;e=0&&(i=r.slice(n+1),r=r.slice(0,n)),r&&!t.hasOwnProperty(r))throw new Error("unknown type: "+r);return{type:r,name:i}})}function O2e(e,t){for(var r=0,i=e.length,n;r{"use strict";B2e={value:s(()=>{},"value")},s(QO,"dispatch"),s(_T,"Dispatch"),s(N2e,"parseTypenames"),_T.prototype=QO.prototype={constructor:_T,on:s(function(e,t){var r=this._,i=N2e(e+"",r),n,a=-1,o=i.length;if(arguments.length<2){for(;++a0)for(var r=new Array(n),i=0,n,a;i{"use strict";Z3t()}),YS,JO,z2e=G(()=>{"use strict";YS="http://www.w3.org/1999/xhtml",JO={svg:"http://www.w3.org/2000/svg",xhtml:YS,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"}});function AT(e){var t=e+="",r=t.indexOf(":");return r>=0&&(t=e.slice(0,r))!=="xmlns"&&(e=e.slice(r+1)),JO.hasOwnProperty(t)?{space:JO[t],local:e}:e}var eB=G(()=>{"use strict";z2e(),s(AT,"default")});function q2e(e){return function(){var t=this.ownerDocument,r=this.namespaceURI;return r===YS&&t.documentElement.namespaceURI===YS?t.createElement(e):t.createElementNS(r,e)}}function W2e(e){return function(){return this.ownerDocument.createElementNS(e.space,e.local)}}function tB(e){var t=AT(e);return(t.local?W2e:q2e)(t)}var j2e=G(()=>{"use strict";eB(),z2e(),s(q2e,"creatorInherit"),s(W2e,"creatorFixed"),s(tB,"default")});function V2e(){}function HS(e){return e==null?V2e:function(){return this.querySelector(e)}}var rB=G(()=>{"use strict";s(V2e,"none"),s(HS,"default")});function U2e(e){typeof e!="function"&&(e=HS(e));for(var t=this._groups,r=t.length,i=new Array(r),n=0;n{"use strict";Mu(),rB(),s(U2e,"default")});function Y2e(e){return e==null?[]:Array.isArray(e)?e:Array.from(e)}var ekt=G(()=>{"use strict";s(Y2e,"array")});function H2e(){return[]}function iB(e){return e==null?H2e:function(){return this.querySelectorAll(e)}}var G2e=G(()=>{"use strict";s(H2e,"empty"),s(iB,"default")});function X2e(e){return function(){return Y2e(e.apply(this,arguments))}}function K2e(e){typeof e=="function"?e=X2e(e):e=iB(e);for(var t=this._groups,r=t.length,i=[],n=[],a=0;a{"use strict";Mu(),ekt(),G2e(),s(X2e,"arrayAll"),s(K2e,"default")});function nB(e){return function(){return this.matches(e)}}function aB(e){return function(t){return t.matches(e)}}var GS=G(()=>{"use strict";s(nB,"default"),s(aB,"childMatcher")});function Q2e(e){return function(){return eve.call(this.children,e)}}function Z2e(){return this.firstElementChild}function J2e(e){return this.select(e==null?Z2e:Q2e(typeof e=="function"?e:aB(e)))}var eve,rkt=G(()=>{"use strict";GS(),eve=Array.prototype.find,s(Q2e,"childFind"),s(Z2e,"childFirst"),s(J2e,"default")});function tve(){return Array.from(this.children)}function rve(e){return function(){return nve.call(this.children,e)}}function ive(e){return this.selectAll(e==null?tve:rve(typeof e=="function"?e:aB(e)))}var nve,ikt=G(()=>{"use strict";GS(),nve=Array.prototype.filter,s(tve,"children"),s(rve,"childrenFilter"),s(ive,"default")});function ave(e){typeof e!="function"&&(e=nB(e));for(var t=this._groups,r=t.length,i=new Array(r),n=0;n{"use strict";Mu(),GS(),s(ave,"default")});function sB(e){return new Array(e.length)}var sve=G(()=>{"use strict";s(sB,"default")});function ove(){return new nl(this._enter||this._groups.map(sB),this._parents)}function RT(e,t){this.ownerDocument=e.ownerDocument,this.namespaceURI=e.namespaceURI,this._next=null,this._parent=e,this.__data__=t}var lve=G(()=>{"use strict";sve(),Mu(),s(ove,"default"),s(RT,"EnterNode"),RT.prototype={constructor:RT,appendChild:s(function(e){return this._parent.insertBefore(e,this._next)},"appendChild"),insertBefore:s(function(e,t){return this._parent.insertBefore(e,t)},"insertBefore"),querySelector:s(function(e){return this._parent.querySelector(e)},"querySelector"),querySelectorAll:s(function(e){return this._parent.querySelectorAll(e)},"querySelectorAll")}});function cve(e){return function(){return e}}var akt=G(()=>{"use strict";s(cve,"default")});function uve(e,t,r,i,n,a){for(var o=0,l,u=t.length,h=a.length;o=E&&(E=S+1);!(b=k[E])&&++E{"use strict";Mu(),lve(),akt(),s(uve,"bindIndex"),s(hve,"bindKey"),s(dve,"datum"),s(pve,"default"),s(fve,"arraylike")});function gve(){return new nl(this._exit||this._groups.map(sB),this._parents)}var okt=G(()=>{"use strict";sve(),Mu(),s(gve,"default")});function mve(e,t,r){var i=this.enter(),n=this,a=this.exit();return typeof e=="function"?(i=e(i),i&&(i=i.selection())):i=i.append(e+""),t!=null&&(n=t(n),n&&(n=n.selection())),r==null?a.remove():r(a),i&&n?i.merge(n).order():n}var lkt=G(()=>{"use strict";s(mve,"default")});function yve(e){for(var t=e.selection?e.selection():e,r=this._groups,i=t._groups,n=r.length,a=i.length,o=Math.min(n,a),l=new Array(n),u=0;u{"use strict";Mu(),s(yve,"default")});function vve(){for(var e=this._groups,t=-1,r=e.length;++t=0;)(o=i[n])&&(a&&o.compareDocumentPosition(a)^4&&a.parentNode.insertBefore(o,a),a=o);return this}var ukt=G(()=>{"use strict";s(vve,"default")});function bve(e){e||(e=xve);function t(p,f){return p&&f?e(p.__data__,f.__data__):!p-!f}s(t,"compareNode");for(var r=this._groups,i=r.length,n=new Array(i),a=0;at?1:e>=t?0:NaN}var hkt=G(()=>{"use strict";Mu(),s(bve,"default"),s(xve,"ascending")});function Tve(){var e=arguments[0];return arguments[0]=this,e.apply(null,arguments),this}var dkt=G(()=>{"use strict";s(Tve,"default")});function kve(){return Array.from(this)}var pkt=G(()=>{"use strict";s(kve,"default")});function Cve(){for(var e=this._groups,t=0,r=e.length;t{"use strict";s(Cve,"default")});function wve(){let e=0;for(let t of this)++e;return e}var gkt=G(()=>{"use strict";s(wve,"default")});function Eve(){return!this.node()}var mkt=G(()=>{"use strict";s(Eve,"default")});function Sve(e){for(var t=this._groups,r=0,i=t.length;r{"use strict";s(Sve,"default")});function _ve(e){return function(){this.removeAttribute(e)}}function Ave(e){return function(){this.removeAttributeNS(e.space,e.local)}}function Rve(e,t){return function(){this.setAttribute(e,t)}}function $ve(e,t){return function(){this.setAttributeNS(e.space,e.local,t)}}function Lve(e,t){return function(){var r=t.apply(this,arguments);r==null?this.removeAttribute(e):this.setAttribute(e,r)}}function Ive(e,t){return function(){var r=t.apply(this,arguments);r==null?this.removeAttributeNS(e.space,e.local):this.setAttributeNS(e.space,e.local,r)}}function Mve(e,t){var r=AT(e);if(arguments.length<2){var i=this.node();return r.local?i.getAttributeNS(r.space,r.local):i.getAttribute(r)}return this.each((t==null?r.local?Ave:_ve:typeof t=="function"?r.local?Ive:Lve:r.local?$ve:Rve)(r,t))}var vkt=G(()=>{"use strict";eB(),s(_ve,"attrRemove"),s(Ave,"attrRemoveNS"),s(Rve,"attrConstant"),s($ve,"attrConstantNS"),s(Lve,"attrFunction"),s(Ive,"attrFunctionNS"),s(Mve,"default")});function oB(e){return e.ownerDocument&&e.ownerDocument.defaultView||e.document&&e||e.defaultView}var Dve=G(()=>{"use strict";s(oB,"default")});function Nve(e){return function(){this.style.removeProperty(e)}}function Ove(e,t,r){return function(){this.style.setProperty(e,t,r)}}function Bve(e,t,r){return function(){var i=t.apply(this,arguments);i==null?this.style.removeProperty(e):this.style.setProperty(e,i,r)}}function Pve(e,t,r){return arguments.length>1?this.each((t==null?Nve:typeof t=="function"?Bve:Ove)(e,t,r??"")):J0(this.node(),e)}function J0(e,t){return e.style.getPropertyValue(t)||oB(e).getComputedStyle(e,null).getPropertyValue(t)}var Fve=G(()=>{"use strict";Dve(),s(Nve,"styleRemove"),s(Ove,"styleConstant"),s(Bve,"styleFunction"),s(Pve,"default"),s(J0,"styleValue")});function zve(e){return function(){delete this[e]}}function qve(e,t){return function(){this[e]=t}}function Wve(e,t){return function(){var r=t.apply(this,arguments);r==null?delete this[e]:this[e]=r}}function jve(e,t){return arguments.length>1?this.each((t==null?zve:typeof t=="function"?Wve:qve)(e,t)):this.node()[e]}var bkt=G(()=>{"use strict";s(zve,"propertyRemove"),s(qve,"propertyConstant"),s(Wve,"propertyFunction"),s(jve,"default")});function lB(e){return e.trim().split(/^|\s+/)}function XS(e){return e.classList||new cB(e)}function cB(e){this._node=e,this._names=lB(e.getAttribute("class")||"")}function uB(e,t){for(var r=XS(e),i=-1,n=t.length;++i{"use strict";s(lB,"classArray"),s(XS,"classList"),s(cB,"ClassList"),cB.prototype={add:s(function(e){var t=this._names.indexOf(e);t<0&&(this._names.push(e),this._node.setAttribute("class",this._names.join(" ")))},"add"),remove:s(function(e){var t=this._names.indexOf(e);t>=0&&(this._names.splice(t,1),this._node.setAttribute("class",this._names.join(" ")))},"remove"),contains:s(function(e){return this._names.indexOf(e)>=0},"contains")},s(uB,"classedAdd"),s(hB,"classedRemove"),s(Vve,"classedTrue"),s(Uve,"classedFalse"),s(Yve,"classedFunction"),s(Hve,"default")});function Gve(){this.textContent=""}function Xve(e){return function(){this.textContent=e}}function Kve(e){return function(){var t=e.apply(this,arguments);this.textContent=t??""}}function Qve(e){return arguments.length?this.each(e==null?Gve:(typeof e=="function"?Kve:Xve)(e)):this.node().textContent}var Tkt=G(()=>{"use strict";s(Gve,"textRemove"),s(Xve,"textConstant"),s(Kve,"textFunction"),s(Qve,"default")});function Zve(){this.innerHTML=""}function Jve(e){return function(){this.innerHTML=e}}function ebe(e){return function(){var t=e.apply(this,arguments);this.innerHTML=t??""}}function tbe(e){return arguments.length?this.each(e==null?Zve:(typeof e=="function"?ebe:Jve)(e)):this.node().innerHTML}var kkt=G(()=>{"use strict";s(Zve,"htmlRemove"),s(Jve,"htmlConstant"),s(ebe,"htmlFunction"),s(tbe,"default")});function rbe(){this.nextSibling&&this.parentNode.appendChild(this)}function ibe(){return this.each(rbe)}var Ckt=G(()=>{"use strict";s(rbe,"raise"),s(ibe,"default")});function nbe(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function abe(){return this.each(nbe)}var wkt=G(()=>{"use strict";s(nbe,"lower"),s(abe,"default")});function sbe(e){var t=typeof e=="function"?e:tB(e);return this.select(function(){return this.appendChild(t.apply(this,arguments))})}var Ekt=G(()=>{"use strict";j2e(),s(sbe,"default")});function obe(){return null}function lbe(e,t){var r=typeof e=="function"?e:tB(e),i=t==null?obe:typeof t=="function"?t:HS(t);return this.select(function(){return this.insertBefore(r.apply(this,arguments),i.apply(this,arguments)||null)})}var Skt=G(()=>{"use strict";j2e(),rB(),s(obe,"constantNull"),s(lbe,"default")});function cbe(){var e=this.parentNode;e&&e.removeChild(this)}function ube(){return this.each(cbe)}var _kt=G(()=>{"use strict";s(cbe,"remove"),s(ube,"default")});function hbe(){var e=this.cloneNode(!1),t=this.parentNode;return t?t.insertBefore(e,this.nextSibling):e}function dbe(){var e=this.cloneNode(!0),t=this.parentNode;return t?t.insertBefore(e,this.nextSibling):e}function pbe(e){return this.select(e?dbe:hbe)}var Akt=G(()=>{"use strict";s(hbe,"selection_cloneShallow"),s(dbe,"selection_cloneDeep"),s(pbe,"default")});function fbe(e){return arguments.length?this.property("__data__",e):this.node().__data__}var Rkt=G(()=>{"use strict";s(fbe,"default")});function gbe(e){return function(t){e.call(this,t,this.__data__)}}function mbe(e){return e.trim().split(/^|\s+/).map(function(t){var r="",i=t.indexOf(".");return i>=0&&(r=t.slice(i+1),t=t.slice(0,i)),{type:t,name:r}})}function ybe(e){return function(){var t=this.__on;if(t){for(var r=0,i=-1,n=t.length,a;r{"use strict";s(gbe,"contextListener"),s(mbe,"parseTypenames"),s(ybe,"onRemove"),s(vbe,"onAdd"),s(bbe,"default")});function dB(e,t,r){var i=oB(e),n=i.CustomEvent;typeof n=="function"?n=new n(t,r):(n=i.document.createEvent("Event"),r?(n.initEvent(t,r.bubbles,r.cancelable),n.detail=r.detail):n.initEvent(t,!1,!1)),e.dispatchEvent(n)}function xbe(e,t){return function(){return dB(this,e,t)}}function Tbe(e,t){return function(){return dB(this,e,t.apply(this,arguments))}}function kbe(e,t){return this.each((typeof t=="function"?Tbe:xbe)(e,t))}var Lkt=G(()=>{"use strict";Dve(),s(dB,"dispatchEvent"),s(xbe,"dispatchConstant"),s(Tbe,"dispatchFunction"),s(kbe,"default")});function*Cbe(){for(var e=this._groups,t=0,r=e.length;t{"use strict";s(Cbe,"default")});function nl(e,t){this._groups=e,this._parents=t}function pB(){return new nl([[document.documentElement]],fB)}function wbe(){return this}var fB,by,Mu=G(()=>{"use strict";J3t(),tkt(),rkt(),ikt(),nkt(),skt(),lve(),okt(),lkt(),ckt(),ukt(),hkt(),dkt(),pkt(),fkt(),gkt(),mkt(),ykt(),vkt(),Fve(),bkt(),xkt(),Tkt(),kkt(),Ckt(),wkt(),Ekt(),Skt(),_kt(),Akt(),Rkt(),$kt(),Lkt(),Ikt(),fB=[null],s(nl,"Selection"),s(pB,"selection"),s(wbe,"selection_selection"),nl.prototype=pB.prototype={constructor:nl,select:U2e,selectAll:K2e,selectChild:J2e,selectChildren:ive,filter:ave,data:pve,enter:ove,exit:gve,join:mve,merge:yve,selection:wbe,order:vve,sort:bve,call:Tve,nodes:kve,node:Cve,size:wve,empty:Eve,each:Sve,attr:Mve,style:Pve,property:jve,classed:Hve,text:Qve,html:tbe,raise:ibe,lower:abe,append:sbe,insert:lbe,remove:ube,clone:pbe,datum:fbe,on:bbe,dispatch:kbe,[Symbol.iterator]:Cbe},by=pB});function zt(e){return typeof e=="string"?new nl([[document.querySelector(e)]],[document.documentElement]):new nl([[e]],fB)}var Mkt=G(()=>{"use strict";Mu(),s(zt,"default")}),Du=G(()=>{"use strict";GS(),eB(),Mkt(),Mu(),rB(),G2e(),Fve()}),Dkt=G(()=>{"use strict"});function xy(e,t,r){e.prototype=t.prototype=r,r.constructor=e}function $T(e,t){var r=Object.create(e.prototype);for(var i in t)r[i]=t[i];return r}var Ebe=G(()=>{"use strict";s(xy,"default"),s($T,"extend")});function Ep(){}function gB(){return this.rgb().formatHex()}function Sbe(){return this.rgb().formatHex8()}function _be(){return kB(this).formatHsl()}function mB(){return this.rgb().formatRgb()}function Sp(e){var t,r;return e=(e+"").trim().toLowerCase(),(t=$be.exec(e))?(r=t[1].length,t=parseInt(t[1],16),r===6?yB(t):r===3?new zs(t>>8&15|t>>4&240,t>>4&15|t&240,(t&15)<<4|t&15,1):r===8?LT(t>>24&255,t>>16&255,t>>8&255,(t&255)/255):r===4?LT(t>>12&15|t>>8&240,t>>8&15|t>>4&240,t>>4&15|t&240,((t&15)<<4|t&15)/255):null):(t=Lbe.exec(e))?new zs(t[1],t[2],t[3],1):(t=Ibe.exec(e))?new zs(t[1]*255/100,t[2]*255/100,t[3]*255/100,1):(t=Mbe.exec(e))?LT(t[1],t[2],t[3],t[4]):(t=Dbe.exec(e))?LT(t[1]*255/100,t[2]*255/100,t[3]*255/100,t[4]):(t=Nbe.exec(e))?TB(t[1],t[2]/100,t[3]/100,1):(t=Obe.exec(e))?TB(t[1],t[2]/100,t[3]/100,t[4]):wB.hasOwnProperty(e)?yB(wB[e]):e==="transparent"?new zs(NaN,NaN,NaN,0):null}function yB(e){return new zs(e>>16&255,e>>8&255,e&255,1)}function LT(e,t,r,i){return i<=0&&(e=t=r=NaN),new zs(e,t,r,i)}function vB(e){return e instanceof Ep||(e=Sp(e)),e?(e=e.rgb(),new zs(e.r,e.g,e.b,e.opacity)):new zs}function IT(e,t,r,i){return arguments.length===1?vB(e):new zs(e,t,r,i??1)}function zs(e,t,r,i){this.r=+e,this.g=+t,this.b=+r,this.opacity=+i}function bB(){return`#${Ap(this.r)}${Ap(this.g)}${Ap(this.b)}`}function Abe(){return`#${Ap(this.r)}${Ap(this.g)}${Ap(this.b)}${Ap((isNaN(this.opacity)?1:this.opacity)*255)}`}function xB(){let e=MT(this.opacity);return`${e===1?"rgb(":"rgba("}${_p(this.r)}, ${_p(this.g)}, ${_p(this.b)}${e===1?")":`, ${e})`}`}function MT(e){return isNaN(e)?1:Math.max(0,Math.min(1,e))}function _p(e){return Math.max(0,Math.min(255,Math.round(e)||0))}function Ap(e){return e=_p(e),(e<16?"0":"")+e.toString(16)}function TB(e,t,r,i){return i<=0?e=t=r=NaN:r<=0||r>=1?e=t=NaN:t<=0&&(e=NaN),new Kl(e,t,r,i)}function kB(e){if(e instanceof Kl)return new Kl(e.h,e.s,e.l,e.opacity);if(e instanceof Ep||(e=Sp(e)),!e)return new Kl;if(e instanceof Kl)return e;e=e.rgb();var t=e.r/255,r=e.g/255,i=e.b/255,n=Math.min(t,r,i),a=Math.max(t,r,i),o=NaN,l=a-n,u=(a+n)/2;return l?(t===a?o=(r-i)/l+(r0&&u<1?0:o,new Kl(o,l,u,e.opacity)}function Rbe(e,t,r,i){return arguments.length===1?kB(e):new Kl(e,t,r,i??1)}function Kl(e,t,r,i){this.h=+e,this.s=+t,this.l=+r,this.opacity=+i}function CB(e){return e=(e||0)%360,e<0?e+360:e}function DT(e){return Math.max(0,Math.min(1,e||0))}function KS(e,t,r){return(e<60?t+(r-t)*e/60:e<180?r:e<240?t+(r-t)*(240-e)/60:t)*255}var Ty,NT,eg,ky,Nc,$be,Lbe,Ibe,Mbe,Dbe,Nbe,Obe,wB,Bbe=G(()=>{"use strict";Ebe(),s(Ep,"Color"),Ty=.7,NT=1/Ty,eg="\\s*([+-]?\\d+)\\s*",ky="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*",Nc="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*",$be=/^#([0-9a-f]{3,8})$/,Lbe=new RegExp(`^rgb\\(${eg},${eg},${eg}\\)$`),Ibe=new RegExp(`^rgb\\(${Nc},${Nc},${Nc}\\)$`),Mbe=new RegExp(`^rgba\\(${eg},${eg},${eg},${ky}\\)$`),Dbe=new RegExp(`^rgba\\(${Nc},${Nc},${Nc},${ky}\\)$`),Nbe=new RegExp(`^hsl\\(${ky},${Nc},${Nc}\\)$`),Obe=new RegExp(`^hsla\\(${ky},${Nc},${Nc},${ky}\\)$`),wB={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},xy(Ep,Sp,{copy(e){return Object.assign(new this.constructor,this,e)},displayable(){return this.rgb().displayable()},hex:gB,formatHex:gB,formatHex8:Sbe,formatHsl:_be,formatRgb:mB,toString:mB}),s(gB,"color_formatHex"),s(Sbe,"color_formatHex8"),s(_be,"color_formatHsl"),s(mB,"color_formatRgb"),s(Sp,"color"),s(yB,"rgbn"),s(LT,"rgba"),s(vB,"rgbConvert"),s(IT,"rgb"),s(zs,"Rgb"),xy(zs,IT,$T(Ep,{brighter(e){return e=e==null?NT:Math.pow(NT,e),new zs(this.r*e,this.g*e,this.b*e,this.opacity)},darker(e){return e=e==null?Ty:Math.pow(Ty,e),new zs(this.r*e,this.g*e,this.b*e,this.opacity)},rgb(){return this},clamp(){return new zs(_p(this.r),_p(this.g),_p(this.b),MT(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:bB,formatHex:bB,formatHex8:Abe,formatRgb:xB,toString:xB})),s(bB,"rgb_formatHex"),s(Abe,"rgb_formatHex8"),s(xB,"rgb_formatRgb"),s(MT,"clampa"),s(_p,"clampi"),s(Ap,"hex"),s(TB,"hsla"),s(kB,"hslConvert"),s(Rbe,"hsl"),s(Kl,"Hsl"),xy(Kl,Rbe,$T(Ep,{brighter(e){return e=e==null?NT:Math.pow(NT,e),new Kl(this.h,this.s,this.l*e,this.opacity)},darker(e){return e=e==null?Ty:Math.pow(Ty,e),new Kl(this.h,this.s,this.l*e,this.opacity)},rgb(){var e=this.h%360+(this.h<0)*360,t=isNaN(e)||isNaN(this.s)?0:this.s,r=this.l,i=r+(r<.5?r:1-r)*t,n=2*r-i;return new zs(KS(e>=240?e-240:e+120,n,i),KS(e,n,i),KS(e<120?e+240:e-120,n,i),this.opacity)},clamp(){return new Kl(CB(this.h),DT(this.s),DT(this.l),MT(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){let e=MT(this.opacity);return`${e===1?"hsl(":"hsla("}${CB(this.h)}, ${DT(this.s)*100}%, ${DT(this.l)*100}%${e===1?")":`, ${e})`}`}})),s(CB,"clamph"),s(DT,"clampt"),s(KS,"hsl2rgb")}),Pbe,Fbe,Nkt=G(()=>{"use strict";Pbe=Math.PI/180,Fbe=180/Math.PI});function EB(e){if(e instanceof Oc)return new Oc(e.l,e.a,e.b,e.opacity);if(e instanceof Nu)return SB(e);e instanceof zs||(e=vB(e));var t=e6(e.r),r=e6(e.g),i=e6(e.b),n=QS((.2225045*t+.7168786*r+.0606169*i)/AB),a,o;return t===r&&r===i?a=o=n:(a=QS((.4360747*t+.3850649*r+.1430804*i)/_B),o=QS((.0139322*t+.0971045*r+.7141733*i)/RB)),new Oc(116*n-16,500*(a-n),200*(n-o),e.opacity)}function zbe(e,t,r,i){return arguments.length===1?EB(e):new Oc(e,t,r,i??1)}function Oc(e,t,r,i){this.l=+e,this.a=+t,this.b=+r,this.opacity=+i}function QS(e){return e>Wbe?Math.pow(e,1/3):e/LB+$B}function ZS(e){return e>tg?e*e*e:LB*(e-$B)}function JS(e){return 255*(e<=.0031308?12.92*e:1.055*Math.pow(e,1/2.4)-.055)}function e6(e){return(e/=255)<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4)}function qbe(e){if(e instanceof Nu)return new Nu(e.h,e.c,e.l,e.opacity);if(e instanceof Oc||(e=EB(e)),e.a===0&&e.b===0)return new Nu(NaN,0{"use strict";Ebe(),Bbe(),Nkt(),OT=18,_B=.96422,AB=1,RB=.82521,$B=4/29,tg=6/29,LB=3*tg*tg,Wbe=tg*tg*tg,s(EB,"labConvert"),s(zbe,"lab"),s(Oc,"Lab"),xy(Oc,zbe,$T(Ep,{brighter(e){return new Oc(this.l+OT*(e??1),this.a,this.b,this.opacity)},darker(e){return new Oc(this.l-OT*(e??1),this.a,this.b,this.opacity)},rgb(){var e=(this.l+16)/116,t=isNaN(this.a)?e:e+this.a/500,r=isNaN(this.b)?e:e-this.b/200;return t=_B*ZS(t),e=AB*ZS(e),r=RB*ZS(r),new zs(JS(3.1338561*t-1.6168667*e-.4906146*r),JS(-.9787684*t+1.9161415*e+.033454*r),JS(.0719453*t-.2289914*e+1.4052427*r),this.opacity)}})),s(QS,"xyz2lab"),s(ZS,"lab2xyz"),s(JS,"lrgb2rgb"),s(e6,"rgb2lrgb"),s(qbe,"hclConvert"),s(t6,"hcl"),s(Nu,"Hcl"),s(SB,"hcl2lab"),xy(Nu,t6,$T(Ep,{brighter(e){return new Nu(this.h,this.c,this.l+OT*(e??1),this.opacity)},darker(e){return new Nu(this.h,this.c,this.l-OT*(e??1),this.opacity)},rgb(){return SB(this).rgb()}}))}),BT=G(()=>{"use strict";Bbe(),Okt()});function IB(e,t,r,i,n){var a=e*e,o=a*e;return((1-3*e+3*a-o)*t+(4-6*a+3*o)*r+(1+3*e+3*a-3*o)*i+o*n)/6}function jbe(e){var t=e.length-1;return function(r){var i=r<=0?r=0:r>=1?(r=1,t-1):Math.floor(r*t),n=e[i],a=e[i+1],o=i>0?e[i-1]:2*n-a,l=i{"use strict";s(IB,"basis"),s(jbe,"default")});function Ube(e){var t=e.length;return function(r){var i=Math.floor(((r%=1)<0?++r:r)*t),n=e[(i+t-1)%t],a=e[i%t],o=e[(i+1)%t],l=e[(i+2)%t];return IB((r-i/t)*t,n,a,o,l)}}var Bkt=G(()=>{"use strict";Vbe(),s(Ube,"default")}),PT,Ybe=G(()=>{"use strict";PT=s(e=>()=>e,"default")});function MB(e,t){return function(r){return e+r*t}}function Hbe(e,t,r){return e=Math.pow(e,r),t=Math.pow(t,r)-e,r=1/r,function(i){return Math.pow(e+i*t,r)}}function Gbe(e,t){var r=t-e;return r?MB(e,r>180||r<-180?r-360*Math.round(r/360):r):PT(isNaN(e)?t:e)}function Xbe(e){return(e=+e)==1?rg:function(t,r){return r-t?Hbe(t,r,e):PT(isNaN(t)?r:t)}}function rg(e,t){var r=t-e;return r?MB(e,r):PT(isNaN(e)?t:e)}var Kbe=G(()=>{"use strict";Ybe(),s(MB,"linear"),s(Hbe,"exponential"),s(Gbe,"hue"),s(Xbe,"gamma"),s(rg,"nogamma")});function DB(e){return function(t){var r=t.length,i=new Array(r),n=new Array(r),a=new Array(r),o,l;for(o=0;o{"use strict";BT(),Vbe(),Bkt(),Kbe(),FT=s((function e(t){var r=Xbe(t);function i(n,a){var o=r((n=IT(n)).r,(a=IT(a)).r),l=r(n.g,a.g),u=r(n.b,a.b),h=rg(n.opacity,a.opacity);return function(d){return n.r=o(d),n.g=l(d),n.b=u(d),n.opacity=h(d),n+""}}return s(i,"rgb"),i.gamma=e,i}),"rgbGamma")(1),s(DB,"rgbSpline"),Pkt=DB(jbe),Fkt=DB(Ube)});function Zbe(e,t){t||(t=[]);var r=e?Math.min(t.length,e.length):0,i=t.slice(),n;return function(a){for(n=0;n{"use strict";s(Zbe,"default"),s(Jbe,"isNumberArray")});function exe(e,t){var r=t?t.length:0,i=e?Math.min(r,e.length):0,n=new Array(i),a=new Array(r),o;for(o=0;o{"use strict";OB(),s(exe,"genericArray")});function txe(e,t){var r=new Date;return e=+e,t=+t,function(i){return r.setTime(e*(1-i)+t*i),r}}var Wkt=G(()=>{"use strict";s(txe,"default")});function Ql(e,t){return e=+e,t=+t,function(r){return e*(1-r)+t*r}}var r6=G(()=>{"use strict";s(Ql,"default")});function rxe(e,t){var r={},i={},n;(e===null||typeof e!="object")&&(e={}),(t===null||typeof t!="object")&&(t={});for(n in t)n in e?r[n]=a6(e[n],t[n]):i[n]=t[n];return function(a){for(n in r)i[n]=r[n](a);return i}}var jkt=G(()=>{"use strict";OB(),s(rxe,"default")});function ixe(e){return function(){return e}}function nxe(e){return function(t){return e(t)+""}}function NB(e,t){var r=i6.lastIndex=n6.lastIndex=0,i,n,a,o=-1,l=[],u=[];for(e=e+"",t=t+"";(i=i6.exec(e))&&(n=n6.exec(t));)(a=n.index)>r&&(a=t.slice(r,a),l[o]?l[o]+=a:l[++o]=a),(i=i[0])===(n=n[0])?l[o]?l[o]+=n:l[++o]=n:(l[++o]=null,u.push({i:o,x:Ql(i,n)})),r=n6.lastIndex;return r{"use strict";r6(),i6=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,n6=new RegExp(i6.source,"g"),s(ixe,"zero"),s(nxe,"one"),s(NB,"default")});function a6(e,t){var r=typeof t,i;return t==null||r==="boolean"?PT(t):(r==="number"?Ql:r==="string"?(i=Sp(t))?(t=i,FT):NB:t instanceof Sp?FT:t instanceof Date?txe:Jbe(t)?Zbe:Array.isArray(t)?exe:typeof t.valueOf!="function"&&typeof t.toString!="function"||isNaN(t)?rxe:Ql)(e,t)}var OB=G(()=>{"use strict";BT(),Qbe(),qkt(),Wkt(),r6(),jkt(),axe(),Ybe(),zkt(),s(a6,"default")});function sxe(e,t){return e=+e,t=+t,function(r){return Math.round(e*(1-r)+t*r)}}var Vkt=G(()=>{"use strict";s(sxe,"default")});function BB(e,t,r,i,n,a){var o,l,u;return(o=Math.sqrt(e*e+t*t))&&(e/=o,t/=o),(u=e*r+t*i)&&(r-=e*u,i-=t*u),(l=Math.sqrt(r*r+i*i))&&(r/=l,i/=l,u/=l),e*i{"use strict";PB=180/Math.PI,s6={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1},s(BB,"default")});function oxe(e){let t=new(typeof DOMMatrix=="function"?DOMMatrix:WebKitCSSMatrix)(e+"");return t.isIdentity?s6:BB(t.a,t.b,t.c,t.d,t.e,t.f)}function lxe(e){return e==null?s6:(o6||(o6=document.createElementNS("http://www.w3.org/2000/svg","g")),o6.setAttribute("transform",e),(e=o6.transform.baseVal.consolidate())?(e=e.matrix,BB(e.a,e.b,e.c,e.d,e.e,e.f)):s6)}var o6,Ykt=G(()=>{"use strict";Ukt(),s(oxe,"parseCss"),s(lxe,"parseSvg")});function FB(e,t,r,i){function n(h){return h.length?h.pop()+" ":""}s(n,"pop");function a(h,d,p,f,g,m){if(h!==p||d!==f){var y=g.push("translate(",null,t,null,r);m.push({i:y-4,x:Ql(h,p)},{i:y-2,x:Ql(d,f)})}else(p||f)&&g.push("translate("+p+t+f+r)}s(a,"translate");function o(h,d,p,f){h!==d?(h-d>180?d+=360:d-h>180&&(h+=360),f.push({i:p.push(n(p)+"rotate(",null,i)-2,x:Ql(h,d)})):d&&p.push(n(p)+"rotate("+d+i)}s(o,"rotate");function l(h,d,p,f){h!==d?f.push({i:p.push(n(p)+"skewX(",null,i)-2,x:Ql(h,d)}):d&&p.push(n(p)+"skewX("+d+i)}s(l,"skewX");function u(h,d,p,f,g,m){if(h!==p||d!==f){var y=g.push(n(g)+"scale(",null,",",null,")");m.push({i:y-4,x:Ql(h,p)},{i:y-2,x:Ql(d,f)})}else(p!==1||f!==1)&&g.push(n(g)+"scale("+p+","+f+")")}return s(u,"scale"),function(h,d){var p=[],f=[];return h=e(h),d=e(d),a(h.translateX,h.translateY,d.translateX,d.translateY,p,f),o(h.rotate,d.rotate,p,f),l(h.skewX,d.skewX,p,f),u(h.scaleX,h.scaleY,d.scaleX,d.scaleY,p,f),h=d=null,function(g){for(var m=-1,y=f.length,k;++m{"use strict";r6(),Ykt(),s(FB,"interpolateTransform"),cxe=FB(oxe,"px, ","px)","deg)"),uxe=FB(lxe,", ",")",")")});function zB(e){return function(t,r){var i=e((t=t6(t)).h,(r=t6(r)).h),n=rg(t.c,r.c),a=rg(t.l,r.l),o=rg(t.opacity,r.opacity);return function(l){return t.h=i(l),t.c=n(l),t.l=a(l),t.opacity=o(l),t+""}}}var hxe,Gkt,Xkt=G(()=>{"use strict";BT(),Kbe(),s(zB,"hcl"),hxe=zB(Gbe),Gkt=zB(rg)}),zT=G(()=>{"use strict";OB(),r6(),Vkt(),axe(),Hkt(),Qbe(),Xkt()});function l6(){return Rp||(VB(dxe),Rp=Ey.now()+VT)}function dxe(){Rp=0}function qT(){this._call=this._time=this._next=null}function qB(e,t,r){var i=new qT;return i.restart(e,t,r),i}function pxe(){l6(),++ig;for(var e=u6,t;e;)(t=Rp-e._time)>=0&&e._call.call(void 0,t),e=e._next;--ig}function WB(){Rp=(jT=Ey.now())+VT,ig=Cy=0;try{pxe()}finally{ig=0,gxe(),Rp=0}}function fxe(){var e=Ey.now(),t=e-jT;t>jB&&(VT-=t,jT=e)}function gxe(){for(var e,t=u6,r,i=1/0;t;)t._call?(i>t._time&&(i=t._time),e=t,t=t._next):(r=t._next,t._next=null,t=e?e._next=r:u6=r);WT=e,c6(i)}function c6(e){if(!ig){Cy&&(Cy=clearTimeout(Cy));var t=e-Rp;t>24?(e<1/0&&(Cy=setTimeout(WB,e-Ey.now()-VT)),wy&&(wy=clearInterval(wy))):(wy||(jT=Ey.now(),wy=setInterval(fxe,jB)),ig=1,VB(WB))}}var ig,Cy,wy,jB,u6,WT,jT,Rp,VT,Ey,VB,mxe=G(()=>{"use strict";ig=0,Cy=0,wy=0,jB=1e3,jT=0,Rp=0,VT=0,Ey=typeof performance=="object"&&performance.now?performance:Date,VB=typeof window=="object"&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(e){setTimeout(e,17)},s(l6,"now"),s(dxe,"clearNow"),s(qT,"Timer"),qT.prototype=qB.prototype={constructor:qT,restart:s(function(e,t,r){if(typeof e!="function")throw new TypeError("callback is not a function");r=(r==null?l6():+r)+(t==null?0:+t),!this._next&&WT!==this&&(WT?WT._next=this:u6=this,WT=this),this._call=e,this._time=r,c6()},"restart"),stop:s(function(){this._call&&(this._call=null,this._time=1/0,c6())},"stop")},s(qB,"timer"),s(pxe,"timerFlush"),s(WB,"wake"),s(fxe,"poke"),s(gxe,"nap"),s(c6,"sleep")});function UB(e,t,r){var i=new qT;return t=t==null?0:+t,i.restart(n=>{i.stop(),e(n+t)},t,r),i}var Kkt=G(()=>{"use strict";mxe(),s(UB,"default")}),YB=G(()=>{"use strict";mxe(),Kkt()});function UT(e,t,r,i,n,a){var o=e.__transition;if(!o)e.__transition={};else if(r in o)return;yxe(e,r,{name:t,index:i,group:n,on:vxe,tween:bxe,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:HB})}function h6(e,t){var r=Zl(e,t);if(r.state>HB)throw new Error("too late; already scheduled");return r}function Bc(e,t){var r=Zl(e,t);if(r.state>YT)throw new Error("too late; already running");return r}function Zl(e,t){var r=e.__transition;if(!r||!(r=r[t]))throw new Error("transition not found");return r}function yxe(e,t,r){var i=e.__transition,n;i[t]=r,r.timer=qB(a,0,r.time);function a(h){r.state=GB,r.timer.restart(o,r.delay,r.time),r.delay<=h&&o(h-r.delay)}s(a,"schedule");function o(h){var d,p,f,g;if(r.state!==GB)return u();for(d in i)if(g=i[d],g.name===r.name){if(g.state===YT)return UB(o);g.state===XB?(g.state=HT,g.timer.stop(),g.on.call("interrupt",e,e.__data__,g.index,g.group),delete i[d]):+d{"use strict";F2e(),YB(),vxe=P2e("start","end","cancel","interrupt"),bxe=[],HB=0,GB=1,d6=2,YT=3,XB=4,p6=5,HT=6,s(UT,"default"),s(h6,"init"),s(Bc,"set"),s(Zl,"get"),s(yxe,"create")});function xxe(e,t){var r=e.__transition,i,n,a=!0,o;if(r){t=t==null?null:t+"";for(o in r){if((i=r[o]).name!==t){a=!1;continue}n=i.state>d6&&i.state{"use strict";wl(),s(xxe,"default")});function Txe(e){return this.each(function(){xxe(this,e)})}var Zkt=G(()=>{"use strict";Qkt(),s(Txe,"default")});function kxe(e,t){var r,i;return function(){var n=Bc(this,e),a=n.tween;if(a!==r){i=r=a;for(var o=0,l=i.length;o{"use strict";wl(),s(kxe,"tweenRemove"),s(Cxe,"tweenFunction"),s(wxe,"default"),s(f6,"tweenValue")});function KB(e,t){var r;return(typeof t=="number"?Ql:t instanceof Sp?FT:(r=Sp(t))?(t=r,FT):NB)(e,t)}var Exe=G(()=>{"use strict";BT(),zT(),s(KB,"default")});function Sxe(e){return function(){this.removeAttribute(e)}}function _xe(e){return function(){this.removeAttributeNS(e.space,e.local)}}function Axe(e,t,r){var i,n=r+"",a;return function(){var o=this.getAttribute(e);return o===n?null:o===i?a:a=t(i=o,r)}}function Rxe(e,t,r){var i,n=r+"",a;return function(){var o=this.getAttributeNS(e.space,e.local);return o===n?null:o===i?a:a=t(i=o,r)}}function $xe(e,t,r){var i,n,a;return function(){var o,l=r(this),u;return l==null?void this.removeAttribute(e):(o=this.getAttribute(e),u=l+"",o===u?null:o===i&&u===n?a:(n=u,a=t(i=o,l)))}}function Lxe(e,t,r){var i,n,a;return function(){var o,l=r(this),u;return l==null?void this.removeAttributeNS(e.space,e.local):(o=this.getAttributeNS(e.space,e.local),u=l+"",o===u?null:o===i&&u===n?a:(n=u,a=t(i=o,l)))}}function Ixe(e,t){var r=AT(e),i=r==="transform"?uxe:KB;return this.attrTween(e,typeof t=="function"?(r.local?Lxe:$xe)(r,i,f6(this,"attr."+e,t)):t==null?(r.local?_xe:Sxe)(r):(r.local?Rxe:Axe)(r,i,t))}var Jkt=G(()=>{"use strict";zT(),Du(),g6(),Exe(),s(Sxe,"attrRemove"),s(_xe,"attrRemoveNS"),s(Axe,"attrConstant"),s(Rxe,"attrConstantNS"),s($xe,"attrFunction"),s(Lxe,"attrFunctionNS"),s(Ixe,"default")});function Mxe(e,t){return function(r){this.setAttribute(e,t.call(this,r))}}function Dxe(e,t){return function(r){this.setAttributeNS(e.space,e.local,t.call(this,r))}}function Nxe(e,t){var r,i;function n(){var a=t.apply(this,arguments);return a!==i&&(r=(i=a)&&Dxe(e,a)),r}return s(n,"tween"),n._value=t,n}function Oxe(e,t){var r,i;function n(){var a=t.apply(this,arguments);return a!==i&&(r=(i=a)&&Mxe(e,a)),r}return s(n,"tween"),n._value=t,n}function Bxe(e,t){var r="attr."+e;if(arguments.length<2)return(r=this.tween(r))&&r._value;if(t==null)return this.tween(r,null);if(typeof t!="function")throw new Error;var i=AT(e);return this.tween(r,(i.local?Nxe:Oxe)(i,t))}var eCt=G(()=>{"use strict";Du(),s(Mxe,"attrInterpolate"),s(Dxe,"attrInterpolateNS"),s(Nxe,"attrTweenNS"),s(Oxe,"attrTween"),s(Bxe,"default")});function Pxe(e,t){return function(){h6(this,e).delay=+t.apply(this,arguments)}}function Fxe(e,t){return t=+t,function(){h6(this,e).delay=t}}function zxe(e){var t=this._id;return arguments.length?this.each((typeof e=="function"?Pxe:Fxe)(t,e)):Zl(this.node(),t).delay}var tCt=G(()=>{"use strict";wl(),s(Pxe,"delayFunction"),s(Fxe,"delayConstant"),s(zxe,"default")});function qxe(e,t){return function(){Bc(this,e).duration=+t.apply(this,arguments)}}function Wxe(e,t){return t=+t,function(){Bc(this,e).duration=t}}function jxe(e){var t=this._id;return arguments.length?this.each((typeof e=="function"?qxe:Wxe)(t,e)):Zl(this.node(),t).duration}var rCt=G(()=>{"use strict";wl(),s(qxe,"durationFunction"),s(Wxe,"durationConstant"),s(jxe,"default")});function Vxe(e,t){if(typeof t!="function")throw new Error;return function(){Bc(this,e).ease=t}}function Uxe(e){var t=this._id;return arguments.length?this.each(Vxe(t,e)):Zl(this.node(),t).ease}var iCt=G(()=>{"use strict";wl(),s(Vxe,"easeConstant"),s(Uxe,"default")});function Yxe(e,t){return function(){var r=t.apply(this,arguments);if(typeof r!="function")throw new Error;Bc(this,e).ease=r}}function Hxe(e){if(typeof e!="function")throw new Error;return this.each(Yxe(this._id,e))}var nCt=G(()=>{"use strict";wl(),s(Yxe,"easeVarying"),s(Hxe,"default")});function Gxe(e){typeof e!="function"&&(e=nB(e));for(var t=this._groups,r=t.length,i=new Array(r),n=0;n{"use strict";Du(),Sy(),s(Gxe,"default")});function Xxe(e){if(e._id!==this._id)throw new Error;for(var t=this._groups,r=e._groups,i=t.length,n=r.length,a=Math.min(i,n),o=new Array(i),l=0;l{"use strict";Sy(),s(Xxe,"default")});function Kxe(e){return(e+"").trim().split(/^|\s+/).every(function(t){var r=t.indexOf(".");return r>=0&&(t=t.slice(0,r)),!t||t==="start"})}function Qxe(e,t,r){var i,n,a=Kxe(t)?h6:Bc;return function(){var o=a(this,e),l=o.on;l!==i&&(n=(i=l).copy()).on(t,r),o.on=n}}function Zxe(e,t){var r=this._id;return arguments.length<2?Zl(this.node(),r).on.on(e):this.each(Qxe(r,e,t))}var oCt=G(()=>{"use strict";wl(),s(Kxe,"start"),s(Qxe,"onFunction"),s(Zxe,"default")});function Jxe(e){return function(){var t=this.parentNode;for(var r in this.__transition)if(+r!==e)return;t&&t.removeChild(this)}}function eTe(){return this.on("end.remove",Jxe(this._id))}var lCt=G(()=>{"use strict";s(Jxe,"removeFunction"),s(eTe,"default")});function tTe(e){var t=this._name,r=this._id;typeof e!="function"&&(e=HS(e));for(var i=this._groups,n=i.length,a=new Array(n),o=0;o{"use strict";Du(),Sy(),wl(),s(tTe,"default")});function rTe(e){var t=this._name,r=this._id;typeof e!="function"&&(e=iB(e));for(var i=this._groups,n=i.length,a=[],o=[],l=0;l{"use strict";Du(),Sy(),wl(),s(rTe,"default")});function iTe(){return new nTe(this._groups,this._parents)}var nTe,hCt=G(()=>{"use strict";Du(),nTe=by.prototype.constructor,s(iTe,"default")});function aTe(e,t){var r,i,n;return function(){var a=J0(this,e),o=(this.style.removeProperty(e),J0(this,e));return a===o?null:a===r&&o===i?n:n=t(r=a,i=o)}}function QB(e){return function(){this.style.removeProperty(e)}}function sTe(e,t,r){var i,n=r+"",a;return function(){var o=J0(this,e);return o===n?null:o===i?a:a=t(i=o,r)}}function oTe(e,t,r){var i,n,a;return function(){var o=J0(this,e),l=r(this),u=l+"";return l==null&&(u=l=(this.style.removeProperty(e),J0(this,e))),o===u?null:o===i&&u===n?a:(n=u,a=t(i=o,l))}}function lTe(e,t){var r,i,n,a="style."+t,o="end."+a,l;return function(){var u=Bc(this,e),h=u.on,d=u.value[a]==null?l||(l=QB(t)):void 0;(h!==r||n!==d)&&(i=(r=h).copy()).on(o,n=d),u.on=i}}function cTe(e,t,r){var i=(e+="")=="transform"?cxe:KB;return t==null?this.styleTween(e,aTe(e,i)).on("end.style."+e,QB(e)):typeof t=="function"?this.styleTween(e,oTe(e,i,f6(this,"style."+e,t))).each(lTe(this._id,e)):this.styleTween(e,sTe(e,i,t),r).on("end.style."+e,null)}var dCt=G(()=>{"use strict";zT(),Du(),wl(),g6(),Exe(),s(aTe,"styleNull"),s(QB,"styleRemove"),s(sTe,"styleConstant"),s(oTe,"styleFunction"),s(lTe,"styleMaybeRemove"),s(cTe,"default")});function uTe(e,t,r){return function(i){this.style.setProperty(e,t.call(this,i),r)}}function hTe(e,t,r){var i,n;function a(){var o=t.apply(this,arguments);return o!==n&&(i=(n=o)&&uTe(e,o,r)),i}return s(a,"tween"),a._value=t,a}function dTe(e,t,r){var i="style."+(e+="");if(arguments.length<2)return(i=this.tween(i))&&i._value;if(t==null)return this.tween(i,null);if(typeof t!="function")throw new Error;return this.tween(i,hTe(e,t,r??""))}var pCt=G(()=>{"use strict";s(uTe,"styleInterpolate"),s(hTe,"styleTween"),s(dTe,"default")});function pTe(e){return function(){this.textContent=e}}function fTe(e){return function(){var t=e(this);this.textContent=t??""}}function gTe(e){return this.tween("text",typeof e=="function"?fTe(f6(this,"text",e)):pTe(e==null?"":e+""))}var fCt=G(()=>{"use strict";g6(),s(pTe,"textConstant"),s(fTe,"textFunction"),s(gTe,"default")});function mTe(e){return function(t){this.textContent=e.call(this,t)}}function yTe(e){var t,r;function i(){var n=e.apply(this,arguments);return n!==r&&(t=(r=n)&&mTe(n)),t}return s(i,"tween"),i._value=e,i}function vTe(e){var t="text";if(arguments.length<1)return(t=this.tween(t))&&t._value;if(e==null)return this.tween(t,null);if(typeof e!="function")throw new Error;return this.tween(t,yTe(e))}var gCt=G(()=>{"use strict";s(mTe,"textInterpolate"),s(yTe,"textTween"),s(vTe,"default")});function bTe(){for(var e=this._name,t=this._id,r=ZB(),i=this._groups,n=i.length,a=0;a{"use strict";Sy(),wl(),s(bTe,"default")});function xTe(){var e,t,r=this,i=r._id,n=r.size();return new Promise(function(a,o){var l={value:o},u={value:s(function(){--n===0&&a()},"value")};r.each(function(){var h=Bc(this,i),d=h.on;d!==e&&(t=(e=d).copy(),t._.cancel.push(l),t._.interrupt.push(l),t._.end.push(u)),h.on=t}),n===0&&a()})}var yCt=G(()=>{"use strict";wl(),s(xTe,"default")});function Ou(e,t,r,i){this._groups=e,this._parents=t,this._name=r,this._id=i}function TTe(e){return by().transition(e)}function ZB(){return++kTe}var kTe,Bu,Sy=G(()=>{"use strict";Du(),Jkt(),eCt(),tCt(),rCt(),iCt(),nCt(),aCt(),sCt(),oCt(),lCt(),cCt(),uCt(),hCt(),dCt(),pCt(),fCt(),gCt(),mCt(),g6(),yCt(),kTe=0,s(Ou,"Transition"),s(TTe,"transition"),s(ZB,"newId"),Bu=by.prototype,Ou.prototype=TTe.prototype={constructor:Ou,select:tTe,selectAll:rTe,selectChild:Bu.selectChild,selectChildren:Bu.selectChildren,filter:Gxe,merge:Xxe,selection:iTe,transition:bTe,call:Bu.call,nodes:Bu.nodes,node:Bu.node,size:Bu.size,empty:Bu.empty,each:Bu.each,on:Zxe,attr:Ixe,attrTween:Bxe,style:cTe,styleTween:dTe,text:gTe,textTween:vTe,remove:eTe,tween:wxe,delay:zxe,duration:jxe,ease:Uxe,easeVarying:Hxe,end:xTe,[Symbol.iterator]:Bu[Symbol.iterator]}});function CTe(e){return((e*=2)<=1?e*e*e:(e-=2)*e*e+2)/2}var vCt=G(()=>{"use strict";s(CTe,"cubicInOut")}),wTe=G(()=>{"use strict";vCt()});function ETe(e,t){for(var r;!(r=e.__transition)||!(r=r[t]);)if(!(e=e.parentNode))throw new Error(`transition ${t} not found`);return r}function STe(e){var t,r;e instanceof Ou?(t=e._id,e=e._name):(t=ZB(),(r=_Te).time=l6(),e=e==null?null:e+"");for(var i=this._groups,n=i.length,a=0;a{"use strict";Sy(),wl(),wTe(),YB(),_Te={time:null,delay:0,duration:250,ease:CTe},s(ETe,"inherit"),s(STe,"default")}),xCt=G(()=>{"use strict";Du(),Zkt(),bCt(),by.prototype.interrupt=Txe,by.prototype.transition=STe}),JB=G(()=>{"use strict";xCt()}),TCt=G(()=>{"use strict"}),kCt=G(()=>{"use strict"}),CCt=G(()=>{"use strict"});function eP(e){return[+e[0],+e[1]]}function ATe(e){return[eP(e[0]),eP(e[1])]}function m6(e){return{type:e}}var wCt,ECt,SCt,_Ct,ACt,RCt,$Ct=G(()=>{"use strict";JB(),TCt(),kCt(),CCt(),{abs:wCt,max:ECt,min:SCt}=Math,s(eP,"number1"),s(ATe,"number2"),_Ct={name:"x",handles:["w","e"].map(m6),input:s(function(e,t){return e==null?null:[[+e[0],t[0][1]],[+e[1],t[1][1]]]},"input"),output:s(function(e){return e&&[e[0][0],e[1][0]]},"output")},ACt={name:"y",handles:["n","s"].map(m6),input:s(function(e,t){return e==null?null:[[t[0][0],+e[0]],[t[1][0],+e[1]]]},"input"),output:s(function(e){return e&&[e[0][1],e[1][1]]},"output")},RCt={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(m6),input:s(function(e){return e==null?null:ATe(e)},"input"),output:s(function(e){return e},"output")},s(m6,"type")}),LCt=G(()=>{"use strict";$Ct()});function tP(e){this._+=e[0];for(let t=1,r=e.length;t=0))throw new Error(`invalid digits: ${e}`);if(t>15)return tP;let r=10**t;return function(i){this._+=i[0];for(let n=1,a=i.length;n{"use strict";var e;y6=Math.PI,v6=2*y6,$p=1e-6,LTe=v6-$p,s(tP,"append"),s(RTe,"appendRound"),b6=(e=class{constructor(r){this._x0=this._y0=this._x1=this._y1=null,this._="",this._append=r==null?tP:RTe(r)}moveTo(r,i){this._append`M${this._x0=this._x1=+r},${this._y0=this._y1=+i}`}closePath(){this._x1!==null&&(this._x1=this._x0,this._y1=this._y0,this._append`Z`)}lineTo(r,i){this._append`L${this._x1=+r},${this._y1=+i}`}quadraticCurveTo(r,i,n,a){this._append`Q${+r},${+i},${this._x1=+n},${this._y1=+a}`}bezierCurveTo(r,i,n,a,o,l){this._append`C${+r},${+i},${+n},${+a},${this._x1=+o},${this._y1=+l}`}arcTo(r,i,n,a,o){if(r=+r,i=+i,n=+n,a=+a,o=+o,o<0)throw new Error(`negative radius: ${o}`);let l=this._x1,u=this._y1,h=n-r,d=a-i,p=l-r,f=u-i,g=p*p+f*f;if(this._x1===null)this._append`M${this._x1=r},${this._y1=i}`;else if(g>$p)if(!(Math.abs(f*h-d*p)>$p)||!o)this._append`L${this._x1=r},${this._y1=i}`;else{let m=n-l,y=a-u,k=h*h+d*d,C=m*m+y*y,S=Math.sqrt(k),E=Math.sqrt(g),_=o*Math.tan((y6-Math.acos((k+g-C)/(2*S*E)))/2),b=_/E,v=_/S;Math.abs(b-1)>$p&&this._append`L${r+b*p},${i+b*f}`,this._append`A${o},${o},0,0,${+(f*m>p*y)},${this._x1=r+v*h},${this._y1=i+v*d}`}}arc(r,i,n,a,o,l){if(r=+r,i=+i,n=+n,l=!!l,n<0)throw new Error(`negative radius: ${n}`);let u=n*Math.cos(a),h=n*Math.sin(a),d=r+u,p=i+h,f=1^l,g=l?a-o:o-a;this._x1===null?this._append`M${d},${p}`:(Math.abs(this._x1-d)>$p||Math.abs(this._y1-p)>$p)&&this._append`L${d},${p}`,n&&(g<0&&(g=g%v6+v6),g>LTe?this._append`A${n},${n},0,1,${f},${r-u},${i-h}A${n},${n},0,1,${f},${this._x1=d},${this._y1=p}`:g>$p&&this._append`A${n},${n},0,${+(g>=y6)},${f},${this._x1=r+n*Math.cos(o)},${this._y1=i+n*Math.sin(o)}`)}rect(r,i,n,a){this._append`M${this._x0=this._x1=+r},${this._y0=this._y1=+i}h${n=+n}v${+a}h${-n}Z`}toString(){return this._}},s(e,"Path"),e),s($Te,"path"),$Te.prototype=b6.prototype}),ITe=G(()=>{"use strict";ICt()}),MCt=G(()=>{"use strict"}),DCt=G(()=>{"use strict"}),NCt=G(()=>{"use strict"}),OCt=G(()=>{"use strict"}),BCt=G(()=>{"use strict"}),PCt=G(()=>{"use strict"}),FCt=G(()=>{"use strict"});function MTe(e){return Math.abs(e=Math.round(e))>=1e21?e.toLocaleString("en").replace(/,/g,""):e.toString(10)}function GT(e,t){if((r=(e=t?e.toExponential(t-1):e.toExponential()).indexOf("e"))<0)return null;var r,i=e.slice(0,r);return[i.length>1?i[0]+i.slice(2):i,+e.slice(r+1)]}var x6=G(()=>{"use strict";s(MTe,"default"),s(GT,"formatDecimalParts")});function ng(e){return e=GT(Math.abs(e)),e?e[1]:NaN}var T6=G(()=>{"use strict";x6(),s(ng,"default")});function DTe(e,t){return function(r,i){for(var n=r.length,a=[],o=0,l=e[0],u=0;n>0&&l>0&&(u+l+1>i&&(l=Math.max(1,i-u)),a.push(r.substring(n-=l,n+l)),!((u+=l+1)>i));)l=e[o=(o+1)%e.length];return a.reverse().join(t)}}var zCt=G(()=>{"use strict";s(DTe,"default")});function NTe(e){return function(t){return t.replace(/[0-9]/g,function(r){return e[+r]})}}var qCt=G(()=>{"use strict";s(NTe,"default")});function XT(e){if(!(t=OTe.exec(e)))throw new Error("invalid format: "+e);var t;return new k6({fill:t[1],align:t[2],sign:t[3],symbol:t[4],zero:t[5],width:t[6],comma:t[7],precision:t[8]&&t[8].slice(1),trim:t[9],type:t[10]})}function k6(e){this.fill=e.fill===void 0?" ":e.fill+"",this.align=e.align===void 0?">":e.align+"",this.sign=e.sign===void 0?"-":e.sign+"",this.symbol=e.symbol===void 0?"":e.symbol+"",this.zero=!!e.zero,this.width=e.width===void 0?void 0:+e.width,this.comma=!!e.comma,this.precision=e.precision===void 0?void 0:+e.precision,this.trim=!!e.trim,this.type=e.type===void 0?"":e.type+""}var OTe,BTe=G(()=>{"use strict";OTe=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i,s(XT,"formatSpecifier"),XT.prototype=k6.prototype,s(k6,"FormatSpecifier"),k6.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type}});function PTe(e){e:for(var t=e.length,r=1,i=-1,n;r0&&(i=0);break}return i>0?e.slice(0,i)+e.slice(n+1):e}var WCt=G(()=>{"use strict";s(PTe,"default")});function FTe(e,t){var r=GT(e,t);if(!r)return e+"";var i=r[0],n=r[1],a=n-(zTe=Math.max(-8,Math.min(8,Math.floor(n/3)))*3)+1,o=i.length;return a===o?i:a>o?i+new Array(a-o+1).join("0"):a>0?i.slice(0,a)+"."+i.slice(a):"0."+new Array(1-a).join("0")+GT(e,Math.max(0,t+a-1))[0]}var zTe,qTe=G(()=>{"use strict";x6(),s(FTe,"default")});function rP(e,t){var r=GT(e,t);if(!r)return e+"";var i=r[0],n=r[1];return n<0?"0."+new Array(-n).join("0")+i:i.length>n+1?i.slice(0,n+1)+"."+i.slice(n+1):i+new Array(n-i.length+2).join("0")}var jCt=G(()=>{"use strict";x6(),s(rP,"default")}),iP,VCt=G(()=>{"use strict";x6(),qTe(),jCt(),iP={"%":s((e,t)=>(e*100).toFixed(t),"%"),b:s(e=>Math.round(e).toString(2),"b"),c:s(e=>e+"","c"),d:MTe,e:s((e,t)=>e.toExponential(t),"e"),f:s((e,t)=>e.toFixed(t),"f"),g:s((e,t)=>e.toPrecision(t),"g"),o:s(e=>Math.round(e).toString(8),"o"),p:s((e,t)=>rP(e*100,t),"p"),r:rP,s:FTe,X:s(e=>Math.round(e).toString(16).toUpperCase(),"X"),x:s(e=>Math.round(e).toString(16),"x")}});function nP(e){return e}var UCt=G(()=>{"use strict";s(nP,"default")});function WTe(e){var t=e.grouping===void 0||e.thousands===void 0?nP:DTe(aP.call(e.grouping,Number),e.thousands+""),r=e.currency===void 0?"":e.currency[0]+"",i=e.currency===void 0?"":e.currency[1]+"",n=e.decimal===void 0?".":e.decimal+"",a=e.numerals===void 0?nP:NTe(aP.call(e.numerals,String)),o=e.percent===void 0?"%":e.percent+"",l=e.minus===void 0?"−":e.minus+"",u=e.nan===void 0?"NaN":e.nan+"";function h(p){p=XT(p);var f=p.fill,g=p.align,m=p.sign,y=p.symbol,k=p.zero,C=p.width,S=p.comma,E=p.precision,_=p.trim,b=p.type;b==="n"?(S=!0,b="g"):iP[b]||(E===void 0&&(E=12),_=!0,b="g"),(k||f==="0"&&g==="=")&&(k=!0,f="0",g="=");var v=y==="$"?r:y==="#"&&/[boxX]/.test(b)?"0"+b.toLowerCase():"",x=y==="$"?i:/[%p]/.test(b)?o:"",w=iP[b],R=/[defgprs%]/.test(b);E=E===void 0?6:/[gprs]/.test(b)?Math.max(1,Math.min(21,E)):Math.max(0,Math.min(20,E));function $(M){var N=v,j=x,I,z,F;if(b==="c")j=w(M)+j,M="";else{M=+M;var O=M<0||1/M<0;if(M=isNaN(M)?u:w(Math.abs(M),E),_&&(M=PTe(M)),O&&+M==0&&m!=="+"&&(O=!1),N=(O?m==="("?m:l:m==="-"||m==="("?"":m)+N,j=(b==="s"?sP[8+zTe/3]:"")+j+(O&&m==="("?")":""),R){for(I=-1,z=M.length;++IF||F>57){j=(F===46?n+M.slice(I+1):M.slice(I))+j,M=M.slice(0,I);break}}}S&&!k&&(M=t(M,1/0));var B=N.length+M.length+j.length,Y=B>1)+N+M+j+Y.slice(B);break;default:M=Y+N+M+j;break}return a(M)}return s($,"format"),$.toString=function(){return p+""},$}s(h,"newFormat");function d(p,f){var g=h((p=XT(p),p.type="f",p)),m=Math.max(-8,Math.min(8,Math.floor(ng(f)/3)))*3,y=Math.pow(10,-m),k=sP[8+m/3];return function(C){return g(y*C)+k}}return s(d,"formatPrefix"),{format:h,formatPrefix:d}}var aP,sP,YCt=G(()=>{"use strict";T6(),zCt(),qCt(),BTe(),WCt(),VCt(),qTe(),UCt(),aP=Array.prototype.map,sP=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"],s(WTe,"default")});function jTe(e){return C6=WTe(e),ag=C6.format,VTe=C6.formatPrefix,C6}var C6,ag,VTe,HCt=G(()=>{"use strict";YCt(),jTe({thousands:",",grouping:[3],currency:["$",""]}),s(jTe,"defaultLocale")});function UTe(e){return Math.max(0,-ng(Math.abs(e)))}var GCt=G(()=>{"use strict";T6(),s(UTe,"default")});function YTe(e,t){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(ng(t)/3)))*3-ng(Math.abs(e)))}var XCt=G(()=>{"use strict";T6(),s(YTe,"default")});function HTe(e,t){return e=Math.abs(e),t=Math.abs(t)-e,Math.max(0,ng(t)-ng(e))+1}var KCt=G(()=>{"use strict";T6(),s(HTe,"default")}),GTe=G(()=>{"use strict";HCt(),BTe(),GCt(),XCt(),KCt()}),QCt=G(()=>{"use strict"});function XTe(e){var t=0,r=e.children,i=r&&r.length;if(!i)t=1;else for(;--i>=0;)t+=r[i].value;e.value=t}function KTe(){return this.eachAfter(XTe)}var ZCt=G(()=>{"use strict";s(XTe,"count"),s(KTe,"default")});function QTe(e,t){let r=-1;for(let i of this)e.call(t,i,++r,this);return this}var JCt=G(()=>{"use strict";s(QTe,"default")});function ZTe(e,t){for(var r=this,i=[r],n,a,o=-1;r=i.pop();)if(e.call(t,r,++o,this),n=r.children)for(a=n.length-1;a>=0;--a)i.push(n[a]);return this}var ewt=G(()=>{"use strict";s(ZTe,"default")});function JTe(e,t){for(var r=this,i=[r],n=[],a,o,l,u=-1;r=i.pop();)if(n.push(r),a=r.children)for(o=0,l=a.length;o{"use strict";s(JTe,"default")});function e4e(e,t){let r=-1;for(let i of this)if(e.call(t,i,++r,this))return i}var rwt=G(()=>{"use strict";s(e4e,"default")});function t4e(e){return this.eachAfter(function(t){for(var r=+e(t.data)||0,i=t.children,n=i&&i.length;--n>=0;)r+=i[n].value;t.value=r})}var iwt=G(()=>{"use strict";s(t4e,"default")});function r4e(e){return this.eachBefore(function(t){t.children&&t.children.sort(e)})}var nwt=G(()=>{"use strict";s(r4e,"default")});function i4e(e){for(var t=this,r=n4e(t,e),i=[t];t!==r;)t=t.parent,i.push(t);for(var n=i.length;e!==r;)i.splice(n,0,e),e=e.parent;return i}function n4e(e,t){if(e===t)return e;var r=e.ancestors(),i=t.ancestors(),n=null;for(e=r.pop(),t=i.pop();e===t;)n=e,e=r.pop(),t=i.pop();return n}var awt=G(()=>{"use strict";s(i4e,"default"),s(n4e,"leastCommonAncestor")});function a4e(){for(var e=this,t=[e];e=e.parent;)t.push(e);return t}var swt=G(()=>{"use strict";s(a4e,"default")});function s4e(){return Array.from(this)}var owt=G(()=>{"use strict";s(s4e,"default")});function o4e(){var e=[];return this.eachBefore(function(t){t.children||e.push(t)}),e}var lwt=G(()=>{"use strict";s(o4e,"default")});function l4e(){var e=this,t=[];return e.each(function(r){r!==e&&t.push({source:r.parent,target:r})}),t}var cwt=G(()=>{"use strict";s(l4e,"default")});function*c4e(){var e=this,t,r=[e],i,n,a;do for(t=r.reverse(),r=[];e=t.pop();)if(yield e,i=e.children)for(n=0,a=i.length;n{"use strict";s(c4e,"default")});function w6(e,t){e instanceof Map?(e=[void 0,e],t===void 0&&(t=d4e)):t===void 0&&(t=h4e);for(var r=new KT(e),i,n=[r],a,o,l,u;i=n.pop();)if((o=t(i.data))&&(u=(o=Array.from(o)).length))for(i.children=o,l=u-1;l>=0;--l)n.push(a=o[l]=new KT(o[l])),a.parent=i,a.depth=i.depth+1;return r.eachBefore(f4e)}function u4e(){return w6(this).eachBefore(p4e)}function h4e(e){return e.children}function d4e(e){return Array.isArray(e)?e[1]:null}function p4e(e){e.data.value!==void 0&&(e.value=e.data.value),e.data=e.data.data}function f4e(e){var t=0;do e.height=t;while((e=e.parent)&&e.height<++t)}function KT(e){this.data=e,this.depth=this.height=0,this.parent=null}var hwt=G(()=>{"use strict";ZCt(),JCt(),ewt(),twt(),rwt(),iwt(),nwt(),awt(),swt(),owt(),lwt(),cwt(),uwt(),s(w6,"hierarchy"),s(u4e,"node_copy"),s(h4e,"objectChildren"),s(d4e,"mapChildren"),s(p4e,"copyData"),s(f4e,"computeHeight"),s(KT,"Node"),KT.prototype=w6.prototype={constructor:KT,count:KTe,each:QTe,eachAfter:JTe,eachBefore:ZTe,find:e4e,sum:t4e,sort:r4e,path:i4e,ancestors:a4e,descendants:s4e,leaves:o4e,links:l4e,copy:u4e,[Symbol.iterator]:c4e}});function g4e(e){if(typeof e!="function")throw new Error;return e}var dwt=G(()=>{"use strict";s(g4e,"required")});function _y(){return 0}function Ay(e){return function(){return e}}var pwt=G(()=>{"use strict";s(_y,"constantZero"),s(Ay,"default")});function m4e(e){e.x0=Math.round(e.x0),e.y0=Math.round(e.y0),e.x1=Math.round(e.x1),e.y1=Math.round(e.y1)}var fwt=G(()=>{"use strict";s(m4e,"default")});function y4e(e,t,r,i,n){for(var a=e.children,o,l=-1,u=a.length,h=e.value&&(i-t)/e.value;++l{"use strict";s(y4e,"default")});function v4e(e,t,r,i,n){for(var a=e.children,o,l=-1,u=a.length,h=e.value&&(n-r)/e.value;++l{"use strict";s(v4e,"default")});function b4e(e,t,r,i,n,a){for(var o=[],l=t.children,u,h,d=0,p=0,f=l.length,g,m,y=t.value,k,C,S,E,_,b,v;dS&&(S=h),v=k*k*b,E=Math.max(S/v,v/C),E>_){k-=h;break}_=E}o.push(u={value:k,dice:g{"use strict";gwt(),mwt(),x4e=(1+Math.sqrt(5))/2,s(b4e,"squarifyRatio"),T4e=s((function e(t){function r(i,n,a,o,l){b4e(t,i,n,a,o,l)}return s(r,"squarify"),r.ratio=function(i){return e((i=+i)>1?i:1)},r}),"custom")(x4e)});function k4e(){var e=T4e,t=!1,r=1,i=1,n=[0],a=_y,o=_y,l=_y,u=_y,h=_y;function d(f){return f.x0=f.y0=0,f.x1=r,f.y1=i,f.eachBefore(p),n=[0],t&&f.eachBefore(m4e),f}s(d,"treemap");function p(f){var g=n[f.depth],m=f.x0+g,y=f.y0+g,k=f.x1-g,C=f.y1-g;k{"use strict";fwt(),ywt(),dwt(),pwt(),s(k4e,"default")}),bwt=G(()=>{"use strict";hwt(),vwt()}),xwt=G(()=>{"use strict"}),Twt=G(()=>{"use strict"});function QT(e,t){switch(arguments.length){case 0:break;case 1:this.range(e);break;default:this.range(t).domain(e);break}return this}var E6=G(()=>{"use strict";s(QT,"initRange")});function Lp(){var e=new GO,t=[],r=[],i=oP;function n(a){let o=e.get(a);if(o===void 0){if(i!==oP)return i;e.set(a,o=t.push(a)-1)}return r[o%r.length]}return s(n,"scale"),n.domain=function(a){if(!arguments.length)return t.slice();t=[],e=new GO;for(let o of a)e.has(o)||e.set(o,t.push(o)-1);return n},n.range=function(a){return arguments.length?(r=Array.from(a),n):r.slice()},n.unknown=function(a){return arguments.length?(i=a,n):i},n.copy=function(){return Lp(t,r).unknown(i)},QT.apply(n,arguments),n}var oP,C4e=G(()=>{"use strict";Z0(),E6(),oP=Symbol("implicit"),s(Lp,"ordinal")});function S6(){var e=Lp().unknown(void 0),t=e.domain,r=e.range,i=0,n=1,a,o,l=!1,u=0,h=0,d=.5;delete e.unknown;function p(){var f=t().length,g=n{"use strict";Z0(),E6(),C4e(),s(S6,"band")});function w4e(e){return function(){return e}}var Cwt=G(()=>{"use strict";s(w4e,"constants")});function E4e(e){return+e}var wwt=G(()=>{"use strict";s(E4e,"number")});function sg(e){return e}function _6(e,t){return(t-=e=+e)?function(r){return(r-e)/t}:w4e(isNaN(t)?NaN:.5)}function S4e(e,t){var r;return e>t&&(r=e,e=t,t=r),function(i){return Math.max(e,Math.min(t,i))}}function _4e(e,t,r){var i=e[0],n=e[1],a=t[0],o=t[1];return n2?A4e:_4e,u=h=null,p}s(d,"rescale");function p(f){return f==null||isNaN(f=+f)?a:(u||(u=l(e.map(i),t,r)))(i(o(f)))}return s(p,"scale"),p.invert=function(f){return o(n((h||(h=l(t,e.map(i),Ql)))(f)))},p.domain=function(f){return arguments.length?(e=Array.from(f,E4e),d()):e.slice()},p.range=function(f){return arguments.length?(t=Array.from(f),d()):t.slice()},p.rangeRound=function(f){return t=Array.from(f),r=sxe,d()},p.clamp=function(f){return arguments.length?(o=f?!0:sg,d()):o!==sg},p.interpolate=function(f){return arguments.length?(r=f,d()):r},p.unknown=function(f){return arguments.length?(a=f,p):a},function(f,g){return i=f,n=g,d()}}function cP(){return R4e()(sg,sg)}var uP,$4e=G(()=>{"use strict";Z0(),zT(),Cwt(),wwt(),uP=[0,1],s(sg,"identity"),s(_6,"normalize"),s(S4e,"clamper"),s(_4e,"bimap"),s(A4e,"polymap"),s(lP,"copy"),s(R4e,"transformer"),s(cP,"continuous")});function L4e(e,t,r,i){var n=jS(e,t,r),a;switch(i=XT(i??",f"),i.type){case"s":{var o=Math.max(Math.abs(e),Math.abs(t));return i.precision==null&&!isNaN(a=YTe(n,o))&&(i.precision=a),VTe(i,o)}case"":case"e":case"g":case"p":case"r":{i.precision==null&&!isNaN(a=HTe(n,Math.max(Math.abs(e),Math.abs(t))))&&(i.precision=a-(i.type==="e"));break}case"f":case"%":{i.precision==null&&!isNaN(a=UTe(n))&&(i.precision=a-(i.type==="%")*2);break}}return ag(i)}var Ewt=G(()=>{"use strict";Z0(),GTe(),s(L4e,"tickFormat")});function I4e(e){var t=e.domain;return e.ticks=function(r){var i=t();return x2e(i[0],i[i.length-1],r??10)},e.tickFormat=function(r,i){var n=t();return L4e(n[0],n[n.length-1],r??10,i)},e.nice=function(r){r==null&&(r=10);var i=t(),n=0,a=i.length-1,o=i[n],l=i[a],u,h,d=10;for(l0;){if(h=WS(o,l,r),h===u)return i[n]=o,i[a]=l,t(i);if(h>0)o=Math.floor(o/h)*h,l=Math.ceil(l/h)*h;else if(h<0)o=Math.ceil(o*h)/h,l=Math.floor(l*h)/h;else break;u=h}return e},e}function og(){var e=cP();return e.copy=function(){return lP(e,og())},QT.apply(e,arguments),I4e(e)}var Swt=G(()=>{"use strict";Z0(),$4e(),E6(),Ewt(),s(I4e,"linearish"),s(og,"linear")});function M4e(e,t){e=e.slice();var r=0,i=e.length-1,n=e[r],a=e[i],o;return a{"use strict";s(M4e,"nice")});function za(e,t,r,i){function n(a){return e(a=arguments.length===0?new Date:new Date(+a)),a}return s(n,"interval"),n.floor=a=>(e(a=new Date(+a)),a),n.ceil=a=>(e(a=new Date(a-1)),t(a,1),e(a),a),n.round=a=>{let o=n(a),l=n.ceil(a);return a-o(t(a=new Date(+a),o==null?1:Math.floor(o)),a),n.range=(a,o,l)=>{let u=[];if(a=n.ceil(a),l=l==null?1:Math.floor(l),!(a0))return u;let h;do u.push(h=new Date(+a)),t(a,l),e(a);while(hza(o=>{if(o>=o)for(;e(o),!a(o);)o.setTime(o-1)},(o,l)=>{if(o>=o)if(l<0)for(;++l<=0;)for(;t(o,-1),!a(o););else for(;--l>=0;)for(;t(o,1),!a(o););}),r&&(n.count=(a,o)=>(A6.setTime(+a),R6.setTime(+o),e(A6),e(R6),Math.floor(r(A6,R6))),n.every=a=>(a=Math.floor(a),!isFinite(a)||!(a>0)?null:a>1?n.filter(i?o=>i(o)%a===0:o=>n.count(0,o)%a===0):n)),n}var A6,R6,Ip=G(()=>{"use strict";A6=new Date,R6=new Date,s(za,"timeInterval")}),lg,Awt,D4e=G(()=>{"use strict";Ip(),lg=za(()=>{},(e,t)=>{e.setTime(+e+t)},(e,t)=>t-e),lg.every=e=>(e=Math.floor(e),!isFinite(e)||!(e>0)?null:e>1?za(t=>{t.setTime(Math.floor(t/e)*e)},(t,r)=>{t.setTime(+t+r*e)},(t,r)=>(r-t)/e):lg),Awt=lg.range}),rd,Rwt,N4e=G(()=>{"use strict";Ip(),rd=za(e=>{e.setTime(e-e.getMilliseconds())},(e,t)=>{e.setTime(+e+t*1e3)},(e,t)=>(t-e)/1e3,e=>e.getUTCSeconds()),Rwt=rd.range}),Ry,$wt,hP,Lwt,O4e=G(()=>{"use strict";Ip(),Ry=za(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*1e3)},(e,t)=>{e.setTime(+e+t*6e4)},(e,t)=>(t-e)/6e4,e=>e.getMinutes()),$wt=Ry.range,hP=za(e=>{e.setUTCSeconds(0,0)},(e,t)=>{e.setTime(+e+t*6e4)},(e,t)=>(t-e)/6e4,e=>e.getUTCMinutes()),Lwt=hP.range}),$y,Iwt,dP,Mwt,B4e=G(()=>{"use strict";Ip(),$y=za(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*1e3-e.getMinutes()*6e4)},(e,t)=>{e.setTime(+e+t*36e5)},(e,t)=>(t-e)/36e5,e=>e.getHours()),Iwt=$y.range,dP=za(e=>{e.setUTCMinutes(0,0,0)},(e,t)=>{e.setTime(+e+t*36e5)},(e,t)=>(t-e)/36e5,e=>e.getUTCHours()),Mwt=dP.range}),Mp,Dwt,$6,Nwt,pP,Owt,P4e=G(()=>{"use strict";Ip(),Mp=za(e=>e.setHours(0,0,0,0),(e,t)=>e.setDate(e.getDate()+t),(e,t)=>(t-e-(t.getTimezoneOffset()-e.getTimezoneOffset())*6e4)/864e5,e=>e.getDate()-1),Dwt=Mp.range,$6=za(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/864e5,e=>e.getUTCDate()-1),Nwt=$6.range,pP=za(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/864e5,e=>Math.floor(e/864e5)),Owt=pP.range});function Dp(e){return za(t=>{t.setDate(t.getDate()-(t.getDay()+7-e)%7),t.setHours(0,0,0,0)},(t,r)=>{t.setDate(t.getDate()+r*7)},(t,r)=>(r-t-(r.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/6048e5)}function Np(e){return za(t=>{t.setUTCDate(t.getUTCDate()-(t.getUTCDay()+7-e)%7),t.setUTCHours(0,0,0,0)},(t,r)=>{t.setUTCDate(t.getUTCDate()+r*7)},(t,r)=>(r-t)/6048e5)}var Ly,Iy,fP,gP,Op,mP,yP,Bwt,Pwt,Fwt,zwt,qwt,Wwt,jwt,L6,ZT,F4e,z4e,cg,q4e,W4e,Vwt,Uwt,Ywt,Hwt,Gwt,Xwt,Kwt,j4e=G(()=>{"use strict";Ip(),s(Dp,"timeWeekday"),Ly=Dp(0),Iy=Dp(1),fP=Dp(2),gP=Dp(3),Op=Dp(4),mP=Dp(5),yP=Dp(6),Bwt=Ly.range,Pwt=Iy.range,Fwt=fP.range,zwt=gP.range,qwt=Op.range,Wwt=mP.range,jwt=yP.range,s(Np,"utcWeekday"),L6=Np(0),ZT=Np(1),F4e=Np(2),z4e=Np(3),cg=Np(4),q4e=Np(5),W4e=Np(6),Vwt=L6.range,Uwt=ZT.range,Ywt=F4e.range,Hwt=z4e.range,Gwt=cg.range,Xwt=q4e.range,Kwt=W4e.range}),My,Qwt,vP,Zwt,V4e=G(()=>{"use strict";Ip(),My=za(e=>{e.setDate(1),e.setHours(0,0,0,0)},(e,t)=>{e.setMonth(e.getMonth()+t)},(e,t)=>t.getMonth()-e.getMonth()+(t.getFullYear()-e.getFullYear())*12,e=>e.getMonth()),Qwt=My.range,vP=za(e=>{e.setUTCDate(1),e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCMonth(e.getUTCMonth()+t)},(e,t)=>t.getUTCMonth()-e.getUTCMonth()+(t.getUTCFullYear()-e.getUTCFullYear())*12,e=>e.getUTCMonth()),Zwt=vP.range}),Pu,Jwt,id,e5t,U4e=G(()=>{"use strict";Ip(),Pu=za(e=>{e.setMonth(0,1),e.setHours(0,0,0,0)},(e,t)=>{e.setFullYear(e.getFullYear()+t)},(e,t)=>t.getFullYear()-e.getFullYear(),e=>e.getFullYear()),Pu.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:za(t=>{t.setFullYear(Math.floor(t.getFullYear()/e)*e),t.setMonth(0,1),t.setHours(0,0,0,0)},(t,r)=>{t.setFullYear(t.getFullYear()+r*e)}),Jwt=Pu.range,id=za(e=>{e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCFullYear(e.getUTCFullYear()+t)},(e,t)=>t.getUTCFullYear()-e.getUTCFullYear(),e=>e.getUTCFullYear()),id.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:za(t=>{t.setUTCFullYear(Math.floor(t.getUTCFullYear()/e)*e),t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,r)=>{t.setUTCFullYear(t.getUTCFullYear()+r*e)}),e5t=id.range});function bP(e,t,r,i,n,a){let o=[[rd,1,1e3],[rd,5,5e3],[rd,15,15e3],[rd,30,3e4],[a,1,6e4],[a,5,3e5],[a,15,9e5],[a,30,18e5],[n,1,36e5],[n,3,108e5],[n,6,216e5],[n,12,432e5],[i,1,864e5],[i,2,1728e5],[r,1,6048e5],[t,1,2592e6],[t,3,7776e6],[e,1,31536e6]];function l(h,d,p){let f=dk).right(o,f);if(g===o.length)return e.every(jS(h/31536e6,d/31536e6,p));if(g===0)return lg.every(Math.max(jS(h,d,p),1));let[m,y]=o[f/o[g-1][2]{"use strict";Z0(),D4e(),N4e(),O4e(),B4e(),P4e(),j4e(),V4e(),U4e(),s(bP,"ticker"),[t5t,r5t]=bP(id,vP,L6,pP,dP,hP),[Y4e,H4e]=bP(Pu,My,Ly,Mp,$y,Ry)}),xP=G(()=>{"use strict";D4e(),N4e(),O4e(),B4e(),P4e(),j4e(),V4e(),U4e(),i5t()});function I6(e){if(0<=e.y&&e.y<100){var t=new Date(-1,e.m,e.d,e.H,e.M,e.S,e.L);return t.setFullYear(e.y),t}return new Date(e.y,e.m,e.d,e.H,e.M,e.S,e.L)}function M6(e){if(0<=e.y&&e.y<100){var t=new Date(Date.UTC(-1,e.m,e.d,e.H,e.M,e.S,e.L));return t.setUTCFullYear(e.y),t}return new Date(Date.UTC(e.y,e.m,e.d,e.H,e.M,e.S,e.L))}function Dy(e,t,r){return{y:e,m:t,d:r,H:0,M:0,S:0,L:0}}function G4e(e){var t=e.dateTime,r=e.date,i=e.time,n=e.periods,a=e.days,o=e.shortDays,l=e.months,u=e.shortMonths,h=Ny(n),d=Oy(n),p=Ny(a),f=Oy(a),g=Ny(o),m=Oy(o),y=Ny(l),k=Oy(l),C=Ny(u),S=Oy(u),E={a:O,A:B,b:Y,B:U,c:null,d:EP,e:EP,f:g3e,g:E3e,G:_3e,H:d3e,I:p3e,j:f3e,L:SP,m:m3e,M:y3e,p:H,q:J,Q:IP,s:MP,S:v3e,u:b3e,U:x3e,V:T3e,w:k3e,W:C3e,x:null,X:null,y:w3e,Y:S3e,Z:A3e,"%":LP},_={a:te,A:ie,b:ue,B:se,c:null,d:AP,e:AP,f:I3e,g:W3e,G:V3e,H:R3e,I:$3e,j:L3e,L:RP,m:M3e,M:D3e,p:le,q:Te,Q:IP,s:MP,S:N3e,u:O3e,U:B3e,V:P3e,w:F3e,W:z3e,x:null,X:null,y:q3e,Y:j3e,Z:U3e,"%":LP},b={a:$,A:M,b:N,B:j,c:I,d:CP,e:CP,f:l3e,g:kP,G:TP,H:wP,I:wP,j:n3e,L:o3e,m:i3e,M:a3e,p:R,q:r3e,Q:u3e,s:h3e,S:s3e,u:Q4e,U:Z4e,V:J4e,w:K4e,W:e3e,x:z,X:F,y:kP,Y:TP,Z:t3e,"%":c3e};E.x=v(r,E),E.X=v(i,E),E.c=v(t,E),_.x=v(r,_),_.X=v(i,_),_.c=v(t,_);function v(oe,fe){return function(ge){var ke=[],Ne=-1,xe=0,$e=oe.length,Me,He,Ge;for(ge instanceof Date||(ge=new Date(+ge));++Ne<$e;)oe.charCodeAt(Ne)===37&&(ke.push(oe.slice(xe,Ne)),(He=DP[Me=oe.charAt(++Ne)])!=null?Me=oe.charAt(++Ne):He=Me==="e"?" ":"0",(Ge=fe[Me])&&(Me=Ge(ge,He)),ke.push(Me),xe=Ne+1);return ke.push(oe.slice(xe,Ne)),ke.join("")}}s(v,"newFormat");function x(oe,fe){return function(ge){var ke=Dy(1900,void 0,1),Ne=w(ke,oe,ge+="",0),xe,$e;if(Ne!=ge.length)return null;if("Q"in ke)return new Date(ke.Q);if("s"in ke)return new Date(ke.s*1e3+("L"in ke?ke.L:0));if(fe&&!("Z"in ke)&&(ke.Z=0),"p"in ke&&(ke.H=ke.H%12+ke.p*12),ke.m===void 0&&(ke.m="q"in ke?ke.q:0),"V"in ke){if(ke.V<1||ke.V>53)return null;"w"in ke||(ke.w=1),"Z"in ke?(xe=M6(Dy(ke.y,0,1)),$e=xe.getUTCDay(),xe=$e>4||$e===0?ZT.ceil(xe):ZT(xe),xe=$6.offset(xe,(ke.V-1)*7),ke.y=xe.getUTCFullYear(),ke.m=xe.getUTCMonth(),ke.d=xe.getUTCDate()+(ke.w+6)%7):(xe=I6(Dy(ke.y,0,1)),$e=xe.getDay(),xe=$e>4||$e===0?Iy.ceil(xe):Iy(xe),xe=Mp.offset(xe,(ke.V-1)*7),ke.y=xe.getFullYear(),ke.m=xe.getMonth(),ke.d=xe.getDate()+(ke.w+6)%7)}else("W"in ke||"U"in ke)&&("w"in ke||(ke.w="u"in ke?ke.u%7:"W"in ke?1:0),$e="Z"in ke?M6(Dy(ke.y,0,1)).getUTCDay():I6(Dy(ke.y,0,1)).getDay(),ke.m=0,ke.d="W"in ke?(ke.w+6)%7+ke.W*7-($e+5)%7:ke.w+ke.U*7-($e+6)%7);return"Z"in ke?(ke.H+=ke.Z/100|0,ke.M+=ke.Z%100,M6(ke)):I6(ke)}}s(x,"newParse");function w(oe,fe,ge,ke){for(var Ne=0,xe=fe.length,$e=ge.length,Me,He;Ne=$e)return-1;if(Me=fe.charCodeAt(Ne++),Me===37){if(Me=fe.charAt(Ne++),He=b[Me in DP?fe.charAt(Ne++):Me],!He||(ke=He(oe,ge,ke))<0)return-1}else if(Me!=ge.charCodeAt(ke++))return-1}return ke}s(w,"parseSpecifier");function R(oe,fe,ge){var ke=h.exec(fe.slice(ge));return ke?(oe.p=d.get(ke[0].toLowerCase()),ge+ke[0].length):-1}s(R,"parsePeriod");function $(oe,fe,ge){var ke=g.exec(fe.slice(ge));return ke?(oe.w=m.get(ke[0].toLowerCase()),ge+ke[0].length):-1}s($,"parseShortWeekday");function M(oe,fe,ge){var ke=p.exec(fe.slice(ge));return ke?(oe.w=f.get(ke[0].toLowerCase()),ge+ke[0].length):-1}s(M,"parseWeekday");function N(oe,fe,ge){var ke=C.exec(fe.slice(ge));return ke?(oe.m=S.get(ke[0].toLowerCase()),ge+ke[0].length):-1}s(N,"parseShortMonth");function j(oe,fe,ge){var ke=y.exec(fe.slice(ge));return ke?(oe.m=k.get(ke[0].toLowerCase()),ge+ke[0].length):-1}s(j,"parseMonth");function I(oe,fe,ge){return w(oe,t,fe,ge)}s(I,"parseLocaleDateTime");function z(oe,fe,ge){return w(oe,r,fe,ge)}s(z,"parseLocaleDate");function F(oe,fe,ge){return w(oe,i,fe,ge)}s(F,"parseLocaleTime");function O(oe){return o[oe.getDay()]}s(O,"formatShortWeekday");function B(oe){return a[oe.getDay()]}s(B,"formatWeekday");function Y(oe){return u[oe.getMonth()]}s(Y,"formatShortMonth");function U(oe){return l[oe.getMonth()]}s(U,"formatMonth");function H(oe){return n[+(oe.getHours()>=12)]}s(H,"formatPeriod");function J(oe){return 1+~~(oe.getMonth()/3)}s(J,"formatQuarter");function te(oe){return o[oe.getUTCDay()]}s(te,"formatUTCShortWeekday");function ie(oe){return a[oe.getUTCDay()]}s(ie,"formatUTCWeekday");function ue(oe){return u[oe.getUTCMonth()]}s(ue,"formatUTCShortMonth");function se(oe){return l[oe.getUTCMonth()]}s(se,"formatUTCMonth");function le(oe){return n[+(oe.getUTCHours()>=12)]}s(le,"formatUTCPeriod");function Te(oe){return 1+~~(oe.getUTCMonth()/3)}return s(Te,"formatUTCQuarter"),{format:s(function(oe){var fe=v(oe+="",E);return fe.toString=function(){return oe},fe},"format"),parse:s(function(oe){var fe=x(oe+="",!1);return fe.toString=function(){return oe},fe},"parse"),utcFormat:s(function(oe){var fe=v(oe+="",_);return fe.toString=function(){return oe},fe},"utcFormat"),utcParse:s(function(oe){var fe=x(oe+="",!0);return fe.toString=function(){return oe},fe},"utcParse")}}function Vi(e,t,r){var i=e<0?"-":"",n=(i?-e:e)+"",a=n.length;return i+(a[t.toLowerCase(),r]))}function K4e(e,t,r){var i=ts.exec(t.slice(r,r+1));return i?(e.w=+i[0],r+i[0].length):-1}function Q4e(e,t,r){var i=ts.exec(t.slice(r,r+1));return i?(e.u=+i[0],r+i[0].length):-1}function Z4e(e,t,r){var i=ts.exec(t.slice(r,r+2));return i?(e.U=+i[0],r+i[0].length):-1}function J4e(e,t,r){var i=ts.exec(t.slice(r,r+2));return i?(e.V=+i[0],r+i[0].length):-1}function e3e(e,t,r){var i=ts.exec(t.slice(r,r+2));return i?(e.W=+i[0],r+i[0].length):-1}function TP(e,t,r){var i=ts.exec(t.slice(r,r+4));return i?(e.y=+i[0],r+i[0].length):-1}function kP(e,t,r){var i=ts.exec(t.slice(r,r+2));return i?(e.y=+i[0]+(+i[0]>68?1900:2e3),r+i[0].length):-1}function t3e(e,t,r){var i=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(t.slice(r,r+6));return i?(e.Z=i[1]?0:-(i[2]+(i[3]||"00")),r+i[0].length):-1}function r3e(e,t,r){var i=ts.exec(t.slice(r,r+1));return i?(e.q=i[0]*3-3,r+i[0].length):-1}function i3e(e,t,r){var i=ts.exec(t.slice(r,r+2));return i?(e.m=i[0]-1,r+i[0].length):-1}function CP(e,t,r){var i=ts.exec(t.slice(r,r+2));return i?(e.d=+i[0],r+i[0].length):-1}function n3e(e,t,r){var i=ts.exec(t.slice(r,r+3));return i?(e.m=0,e.d=+i[0],r+i[0].length):-1}function wP(e,t,r){var i=ts.exec(t.slice(r,r+2));return i?(e.H=+i[0],r+i[0].length):-1}function a3e(e,t,r){var i=ts.exec(t.slice(r,r+2));return i?(e.M=+i[0],r+i[0].length):-1}function s3e(e,t,r){var i=ts.exec(t.slice(r,r+2));return i?(e.S=+i[0],r+i[0].length):-1}function o3e(e,t,r){var i=ts.exec(t.slice(r,r+3));return i?(e.L=+i[0],r+i[0].length):-1}function l3e(e,t,r){var i=ts.exec(t.slice(r,r+6));return i?(e.L=Math.floor(i[0]/1e3),r+i[0].length):-1}function c3e(e,t,r){var i=Y3e.exec(t.slice(r,r+1));return i?r+i[0].length:-1}function u3e(e,t,r){var i=ts.exec(t.slice(r));return i?(e.Q=+i[0],r+i[0].length):-1}function h3e(e,t,r){var i=ts.exec(t.slice(r));return i?(e.s=+i[0],r+i[0].length):-1}function EP(e,t){return Vi(e.getDate(),t,2)}function d3e(e,t){return Vi(e.getHours(),t,2)}function p3e(e,t){return Vi(e.getHours()%12||12,t,2)}function f3e(e,t){return Vi(1+Mp.count(Pu(e),e),t,3)}function SP(e,t){return Vi(e.getMilliseconds(),t,3)}function g3e(e,t){return SP(e,t)+"000"}function m3e(e,t){return Vi(e.getMonth()+1,t,2)}function y3e(e,t){return Vi(e.getMinutes(),t,2)}function v3e(e,t){return Vi(e.getSeconds(),t,2)}function b3e(e){var t=e.getDay();return t===0?7:t}function x3e(e,t){return Vi(Ly.count(Pu(e)-1,e),t,2)}function _P(e){var t=e.getDay();return t>=4||t===0?Op(e):Op.ceil(e)}function T3e(e,t){return e=_P(e),Vi(Op.count(Pu(e),e)+(Pu(e).getDay()===4),t,2)}function k3e(e){return e.getDay()}function C3e(e,t){return Vi(Iy.count(Pu(e)-1,e),t,2)}function w3e(e,t){return Vi(e.getFullYear()%100,t,2)}function E3e(e,t){return e=_P(e),Vi(e.getFullYear()%100,t,2)}function S3e(e,t){return Vi(e.getFullYear()%1e4,t,4)}function _3e(e,t){var r=e.getDay();return e=r>=4||r===0?Op(e):Op.ceil(e),Vi(e.getFullYear()%1e4,t,4)}function A3e(e){var t=e.getTimezoneOffset();return(t>0?"-":(t*=-1,"+"))+Vi(t/60|0,"0",2)+Vi(t%60,"0",2)}function AP(e,t){return Vi(e.getUTCDate(),t,2)}function R3e(e,t){return Vi(e.getUTCHours(),t,2)}function $3e(e,t){return Vi(e.getUTCHours()%12||12,t,2)}function L3e(e,t){return Vi(1+$6.count(id(e),e),t,3)}function RP(e,t){return Vi(e.getUTCMilliseconds(),t,3)}function I3e(e,t){return RP(e,t)+"000"}function M3e(e,t){return Vi(e.getUTCMonth()+1,t,2)}function D3e(e,t){return Vi(e.getUTCMinutes(),t,2)}function N3e(e,t){return Vi(e.getUTCSeconds(),t,2)}function O3e(e){var t=e.getUTCDay();return t===0?7:t}function B3e(e,t){return Vi(L6.count(id(e)-1,e),t,2)}function $P(e){var t=e.getUTCDay();return t>=4||t===0?cg(e):cg.ceil(e)}function P3e(e,t){return e=$P(e),Vi(cg.count(id(e),e)+(id(e).getUTCDay()===4),t,2)}function F3e(e){return e.getUTCDay()}function z3e(e,t){return Vi(ZT.count(id(e)-1,e),t,2)}function q3e(e,t){return Vi(e.getUTCFullYear()%100,t,2)}function W3e(e,t){return e=$P(e),Vi(e.getUTCFullYear()%100,t,2)}function j3e(e,t){return Vi(e.getUTCFullYear()%1e4,t,4)}function V3e(e,t){var r=e.getUTCDay();return e=r>=4||r===0?cg(e):cg.ceil(e),Vi(e.getUTCFullYear()%1e4,t,4)}function U3e(){return"+0000"}function LP(){return"%"}function IP(e){return+e}function MP(e){return Math.floor(+e/1e3)}var DP,ts,Y3e,H3e,n5t=G(()=>{"use strict";xP(),s(I6,"localDate"),s(M6,"utcDate"),s(Dy,"newDate"),s(G4e,"formatLocale"),DP={"-":"",_:" ",0:"0"},ts=/^\s*\d+/,Y3e=/^%/,H3e=/[\\^$*+?|[\]().{}]/g,s(Vi,"pad"),s(X4e,"requote"),s(Ny,"formatRe"),s(Oy,"formatLookup"),s(K4e,"parseWeekdayNumberSunday"),s(Q4e,"parseWeekdayNumberMonday"),s(Z4e,"parseWeekNumberSunday"),s(J4e,"parseWeekNumberISO"),s(e3e,"parseWeekNumberMonday"),s(TP,"parseFullYear"),s(kP,"parseYear"),s(t3e,"parseZone"),s(r3e,"parseQuarter"),s(i3e,"parseMonthNumber"),s(CP,"parseDayOfMonth"),s(n3e,"parseDayOfYear"),s(wP,"parseHour24"),s(a3e,"parseMinutes"),s(s3e,"parseSeconds"),s(o3e,"parseMilliseconds"),s(l3e,"parseMicroseconds"),s(c3e,"parseLiteralPercent"),s(u3e,"parseUnixTimestamp"),s(h3e,"parseUnixTimestampSeconds"),s(EP,"formatDayOfMonth"),s(d3e,"formatHour24"),s(p3e,"formatHour12"),s(f3e,"formatDayOfYear"),s(SP,"formatMilliseconds"),s(g3e,"formatMicroseconds"),s(m3e,"formatMonthNumber"),s(y3e,"formatMinutes"),s(v3e,"formatSeconds"),s(b3e,"formatWeekdayNumberMonday"),s(x3e,"formatWeekNumberSunday"),s(_P,"dISO"),s(T3e,"formatWeekNumberISO"),s(k3e,"formatWeekdayNumberSunday"),s(C3e,"formatWeekNumberMonday"),s(w3e,"formatYear"),s(E3e,"formatYearISO"),s(S3e,"formatFullYear"),s(_3e,"formatFullYearISO"),s(A3e,"formatZone"),s(AP,"formatUTCDayOfMonth"),s(R3e,"formatUTCHour24"),s($3e,"formatUTCHour12"),s(L3e,"formatUTCDayOfYear"),s(RP,"formatUTCMilliseconds"),s(I3e,"formatUTCMicroseconds"),s(M3e,"formatUTCMonthNumber"),s(D3e,"formatUTCMinutes"),s(N3e,"formatUTCSeconds"),s(O3e,"formatUTCWeekdayNumberMonday"),s(B3e,"formatUTCWeekNumberSunday"),s($P,"UTCdISO"),s(P3e,"formatUTCWeekNumberISO"),s(F3e,"formatUTCWeekdayNumberSunday"),s(z3e,"formatUTCWeekNumberMonday"),s(q3e,"formatUTCYear"),s(W3e,"formatUTCYearISO"),s(j3e,"formatUTCFullYear"),s(V3e,"formatUTCFullYearISO"),s(U3e,"formatUTCZone"),s(LP,"formatLiteralPercent"),s(IP,"formatUnixTimestamp"),s(MP,"formatUnixTimestampSeconds")});function G3e(e){return By=G4e(e),D6=By.format,a5t=By.parse,s5t=By.utcFormat,o5t=By.utcParse,By}var By,D6,a5t,s5t,o5t,l5t=G(()=>{"use strict";n5t(),G3e({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}),s(G3e,"defaultLocale")}),X3e=G(()=>{"use strict";l5t()});function K3e(e){return new Date(e)}function Q3e(e){return e instanceof Date?+e:+new Date(+e)}function NP(e,t,r,i,n,a,o,l,u,h){var d=cP(),p=d.invert,f=d.domain,g=h(".%L"),m=h(":%S"),y=h("%I:%M"),k=h("%I %p"),C=h("%a %d"),S=h("%b %d"),E=h("%B"),_=h("%Y");function b(v){return(u(v){"use strict";xP(),X3e(),$4e(),E6(),_wt(),s(K3e,"date"),s(Q3e,"number"),s(NP,"calendar"),s(Z3e,"time")}),u5t=G(()=>{"use strict";kwt(),Swt(),C4e(),c5t()});function J3e(e){for(var t=e.length/6|0,r=new Array(t),i=0;i{"use strict";s(J3e,"default")}),eke,d5t=G(()=>{"use strict";h5t(),eke=J3e("4e79a7f28e2ce1575976b7b259a14fedc949af7aa1ff9da79c755fbab0ab")}),p5t=G(()=>{"use strict";d5t()});function fa(e){return s(function(){return e},"constant")}var OP=G(()=>{"use strict";s(fa,"default")});function tke(e){return e>1?0:e<-1?Py:Math.acos(e)}function BP(e){return e>=1?JT:e<=-1?-JT:Math.asin(e)}var PP,qs,Bp,rke,N6,Pc,ug,Ws,Py,JT,e4,FP=G(()=>{"use strict";PP=Math.abs,qs=Math.atan2,Bp=Math.cos,rke=Math.max,N6=Math.min,Pc=Math.sin,ug=Math.sqrt,Ws=1e-12,Py=Math.PI,JT=Py/2,e4=2*Py,s(tke,"acos"),s(BP,"asin")});function zP(e){let t=3;return e.digits=function(r){if(!arguments.length)return t;if(r==null)t=null;else{let i=Math.floor(r);if(!(i>=0))throw new RangeError(`invalid digits: ${r}`);t=i}return e},()=>new b6(t)}var ike=G(()=>{"use strict";ITe(),s(zP,"withPath")});function nke(e){return e.innerRadius}function ake(e){return e.outerRadius}function ske(e){return e.startAngle}function oke(e){return e.endAngle}function lke(e){return e&&e.padAngle}function cke(e,t,r,i,n,a,o,l){var u=r-e,h=i-t,d=o-n,p=l-a,f=p*u-d*h;if(!(f*fI*I+z*z&&(w=$,R=M),{cx:w,cy:R,x01:-d,y01:-p,x11:w*(n/b-1),y11:R*(n/b-1)}}function hg(){var e=nke,t=ake,r=fa(0),i=null,n=ske,a=oke,o=lke,l=null,u=zP(h);function h(){var d,p,f=+e.apply(this,arguments),g=+t.apply(this,arguments),m=n.apply(this,arguments)-JT,y=a.apply(this,arguments)-JT,k=PP(y-m),C=y>m;if(l||(l=d=u()),gWs))l.moveTo(0,0);else if(k>e4-Ws)l.moveTo(g*Bp(m),g*Pc(m)),l.arc(0,0,g,m,y,!C),f>Ws&&(l.moveTo(f*Bp(y),f*Pc(y)),l.arc(0,0,f,y,m,C));else{var S=m,E=y,_=m,b=y,v=k,x=k,w=o.apply(this,arguments)/2,R=w>Ws&&(i?+i.apply(this,arguments):ug(f*f+g*g)),$=N6(PP(g-f)/2,+r.apply(this,arguments)),M=$,N=$,j,I;if(R>Ws){var z=BP(R/f*Pc(w)),F=BP(R/g*Pc(w));(v-=z*2)>Ws?(z*=C?1:-1,_+=z,b-=z):(v=0,_=b=(m+y)/2),(x-=F*2)>Ws?(F*=C?1:-1,S+=F,E-=F):(x=0,S=E=(m+y)/2)}var O=g*Bp(S),B=g*Pc(S),Y=f*Bp(b),U=f*Pc(b);if($>Ws){var H=g*Bp(E),J=g*Pc(E),te=f*Bp(_),ie=f*Pc(_),ue;if(kWs?N>Ws?(j=t4(te,ie,O,B,g,N,C),I=t4(H,J,Y,U,g,N,C),l.moveTo(j.cx+j.x01,j.cy+j.y01),N<$?l.arc(j.cx,j.cy,N,qs(j.y01,j.x01),qs(I.y01,I.x01),!C):(l.arc(j.cx,j.cy,N,qs(j.y01,j.x01),qs(j.y11,j.x11),!C),l.arc(0,0,g,qs(j.cy+j.y11,j.cx+j.x11),qs(I.cy+I.y11,I.cx+I.x11),!C),l.arc(I.cx,I.cy,N,qs(I.y11,I.x11),qs(I.y01,I.x01),!C))):(l.moveTo(O,B),l.arc(0,0,g,S,E,!C)):l.moveTo(O,B),!(f>Ws)||!(v>Ws)?l.lineTo(Y,U):M>Ws?(j=t4(Y,U,H,J,f,-M,C),I=t4(O,B,te,ie,f,-M,C),l.lineTo(j.cx+j.x01,j.cy+j.y01),M<$?l.arc(j.cx,j.cy,M,qs(j.y01,j.x01),qs(I.y01,I.x01),!C):(l.arc(j.cx,j.cy,M,qs(j.y01,j.x01),qs(j.y11,j.x11),!C),l.arc(0,0,f,qs(j.cy+j.y11,j.cx+j.x11),qs(I.cy+I.y11,I.cx+I.x11),C),l.arc(I.cx,I.cy,M,qs(I.y11,I.x11),qs(I.y01,I.x01),!C))):l.arc(0,0,f,b,_,C)}if(l.closePath(),d)return l=null,d+""||null}return s(h,"arc"),h.centroid=function(){var d=(+e.apply(this,arguments)+ +t.apply(this,arguments))/2,p=(+n.apply(this,arguments)+ +a.apply(this,arguments))/2-Py/2;return[Bp(p)*d,Pc(p)*d]},h.innerRadius=function(d){return arguments.length?(e=typeof d=="function"?d:fa(+d),h):e},h.outerRadius=function(d){return arguments.length?(t=typeof d=="function"?d:fa(+d),h):t},h.cornerRadius=function(d){return arguments.length?(r=typeof d=="function"?d:fa(+d),h):r},h.padRadius=function(d){return arguments.length?(i=d==null?null:typeof d=="function"?d:fa(+d),h):i},h.startAngle=function(d){return arguments.length?(n=typeof d=="function"?d:fa(+d),h):n},h.endAngle=function(d){return arguments.length?(a=typeof d=="function"?d:fa(+d),h):a},h.padAngle=function(d){return arguments.length?(o=typeof d=="function"?d:fa(+d),h):o},h.context=function(d){return arguments.length?(l=d??null,h):l},h}var f5t=G(()=>{"use strict";OP(),FP(),ike(),s(nke,"arcInnerRadius"),s(ake,"arcOuterRadius"),s(ske,"arcStartAngle"),s(oke,"arcEndAngle"),s(lke,"arcPadAngle"),s(cke,"intersect"),s(t4,"cornerTangents"),s(hg,"default")});function qP(e){return typeof e=="object"&&"length"in e?e:Array.from(e)}var g5t,uke=G(()=>{"use strict";g5t=Array.prototype.slice,s(qP,"default")});function WP(e){this._context=e}function Fy(e){return new WP(e)}var hke=G(()=>{"use strict";s(WP,"Linear"),WP.prototype={areaStart:s(function(){this._line=0},"areaStart"),areaEnd:s(function(){this._line=NaN},"areaEnd"),lineStart:s(function(){this._point=0},"lineStart"),lineEnd:s(function(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:s(function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:this._context.lineTo(e,t);break}},"point")},s(Fy,"default")});function dke(e){return e[0]}function pke(e){return e[1]}var m5t=G(()=>{"use strict";s(dke,"x"),s(pke,"y")});function r4(e,t){var r=fa(!0),i=null,n=Fy,a=null,o=zP(l);e=typeof e=="function"?e:e===void 0?dke:fa(e),t=typeof t=="function"?t:t===void 0?pke:fa(t);function l(u){var h,d=(u=qP(u)).length,p,f=!1,g;for(i==null&&(a=n(g=o())),h=0;h<=d;++h)!(h{"use strict";uke(),OP(),hke(),ike(),m5t(),s(r4,"default")});function fke(e,t){return te?1:t>=e?0:NaN}var v5t=G(()=>{"use strict";s(fke,"default")});function gke(e){return e}var b5t=G(()=>{"use strict";s(gke,"default")});function mke(){var e=gke,t=fke,r=null,i=fa(0),n=fa(e4),a=fa(0);function o(l){var u,h=(l=qP(l)).length,d,p,f=0,g=new Array(h),m=new Array(h),y=+i.apply(this,arguments),k=Math.min(e4,Math.max(-e4,n.apply(this,arguments)-y)),C,S=Math.min(Math.abs(k)/h,a.apply(this,arguments)),E=S*(k<0?-1:1),_;for(u=0;u0&&(f+=_);for(t!=null?g.sort(function(b,v){return t(m[b],m[v])}):r!=null&&g.sort(function(b,v){return r(l[b],l[v])}),u=0,p=f?(k-h*E)/f:0;u0?_*p:0)+E,m[d]={data:l[d],index:u,value:_,startAngle:y,endAngle:C,padAngle:S};return m}return s(o,"pie"),o.value=function(l){return arguments.length?(e=typeof l=="function"?l:fa(+l),o):e},o.sortValues=function(l){return arguments.length?(t=l,r=null,o):t},o.sort=function(l){return arguments.length?(r=l,t=null,o):r},o.startAngle=function(l){return arguments.length?(i=typeof l=="function"?l:fa(+l),o):i},o.endAngle=function(l){return arguments.length?(n=typeof l=="function"?l:fa(+l),o):n},o.padAngle=function(l){return arguments.length?(a=typeof l=="function"?l:fa(+l),o):a},o}var x5t=G(()=>{"use strict";uke(),OP(),v5t(),b5t(),FP(),s(mke,"default")});function jP(e){return new UP(e,!0)}function VP(e){return new UP(e,!1)}var UP,T5t=G(()=>{"use strict";var e;UP=(e=class{constructor(r,i){this._context=r,this._x=i}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line}point(r,i){switch(r=+r,i=+i,this._point){case 0:{this._point=1,this._line?this._context.lineTo(r,i):this._context.moveTo(r,i);break}case 1:this._point=2;default:{this._x?this._context.bezierCurveTo(this._x0=(this._x0+r)/2,this._y0,this._x0,i,r,i):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+i)/2,r,this._y0,r,i);break}}this._x0=r,this._y0=i}},s(e,"Bump"),e),s(jP,"bumpX"),s(VP,"bumpY")});function nd(){}var O6=G(()=>{"use strict";s(nd,"default")});function i4(e,t,r){e._context.bezierCurveTo((2*e._x0+e._x1)/3,(2*e._y0+e._y1)/3,(e._x0+2*e._x1)/3,(e._y0+2*e._y1)/3,(e._x0+4*e._x1+t)/6,(e._y0+4*e._y1+r)/6)}function n4(e){this._context=e}function a4(e){return new n4(e)}var B6=G(()=>{"use strict";s(i4,"point"),s(n4,"Basis"),n4.prototype={areaStart:s(function(){this._line=0},"areaStart"),areaEnd:s(function(){this._line=NaN},"areaEnd"),lineStart:s(function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},"lineStart"),lineEnd:s(function(){switch(this._point){case 3:i4(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:s(function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:i4(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t},"point")},s(a4,"default")});function YP(e){this._context=e}function yke(e){return new YP(e)}var k5t=G(()=>{"use strict";O6(),B6(),s(YP,"BasisClosed"),YP.prototype={areaStart:nd,areaEnd:nd,lineStart:s(function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},"lineStart"),lineEnd:s(function(){switch(this._point){case 1:{this._context.moveTo(this._x2,this._y2),this._context.closePath();break}case 2:{this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break}case 3:{this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4);break}}},"lineEnd"),point:s(function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x2=e,this._y2=t;break;case 1:this._point=2,this._x3=e,this._y3=t;break;case 2:this._point=3,this._x4=e,this._y4=t,this._context.moveTo((this._x0+4*this._x1+e)/6,(this._y0+4*this._y1+t)/6);break;default:i4(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t},"point")},s(yke,"default")});function HP(e){this._context=e}function vke(e){return new HP(e)}var C5t=G(()=>{"use strict";B6(),s(HP,"BasisOpen"),HP.prototype={areaStart:s(function(){this._line=0},"areaStart"),areaEnd:s(function(){this._line=NaN},"areaEnd"),lineStart:s(function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},"lineStart"),lineEnd:s(function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:s(function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var r=(this._x0+4*this._x1+e)/6,i=(this._y0+4*this._y1+t)/6;this._line?this._context.lineTo(r,i):this._context.moveTo(r,i);break;case 3:this._point=4;default:i4(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t},"point")},s(vke,"default")});function GP(e,t){this._basis=new n4(e),this._beta=t}var bke,w5t=G(()=>{"use strict";B6(),s(GP,"Bundle"),GP.prototype={lineStart:s(function(){this._x=[],this._y=[],this._basis.lineStart()},"lineStart"),lineEnd:s(function(){var e=this._x,t=this._y,r=e.length-1;if(r>0)for(var i=e[0],n=t[0],a=e[r]-i,o=t[r]-n,l=-1,u;++l<=r;)u=l/r,this._basis.point(this._beta*e[l]+(1-this._beta)*(i+u*a),this._beta*t[l]+(1-this._beta)*(n+u*o));this._x=this._y=null,this._basis.lineEnd()},"lineEnd"),point:s(function(e,t){this._x.push(+e),this._y.push(+t)},"point")},bke=s((function e(t){function r(i){return t===1?new n4(i):new GP(i,t)}return s(r,"bundle"),r.beta=function(i){return e(+i)},r}),"custom")(.85)});function s4(e,t,r){e._context.bezierCurveTo(e._x1+e._k*(e._x2-e._x0),e._y1+e._k*(e._y2-e._y0),e._x2+e._k*(e._x1-t),e._y2+e._k*(e._y1-r),e._x2,e._y2)}function P6(e,t){this._context=e,this._k=(1-t)/6}var XP,F6=G(()=>{"use strict";s(s4,"point"),s(P6,"Cardinal"),P6.prototype={areaStart:s(function(){this._line=0},"areaStart"),areaEnd:s(function(){this._line=NaN},"areaEnd"),lineStart:s(function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},"lineStart"),lineEnd:s(function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:s4(this,this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:s(function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2,this._x1=e,this._y1=t;break;case 2:this._point=3;default:s4(this,e,t);break}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t},"point")},XP=s((function e(t){function r(i){return new P6(i,t)}return s(r,"cardinal"),r.tension=function(i){return e(+i)},r}),"custom")(0)});function z6(e,t){this._context=e,this._k=(1-t)/6}var xke,Tke=G(()=>{"use strict";O6(),F6(),s(z6,"CardinalClosed"),z6.prototype={areaStart:nd,areaEnd:nd,lineStart:s(function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},"lineStart"),lineEnd:s(function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},"lineEnd"),point:s(function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x3=e,this._y3=t;break;case 1:this._point=2,this._context.moveTo(this._x4=e,this._y4=t);break;case 2:this._point=3,this._x5=e,this._y5=t;break;default:s4(this,e,t);break}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t},"point")},xke=s((function e(t){function r(i){return new z6(i,t)}return s(r,"cardinal"),r.tension=function(i){return e(+i)},r}),"custom")(0)});function q6(e,t){this._context=e,this._k=(1-t)/6}var kke,Cke=G(()=>{"use strict";F6(),s(q6,"CardinalOpen"),q6.prototype={areaStart:s(function(){this._line=0},"areaStart"),areaEnd:s(function(){this._line=NaN},"areaEnd"),lineStart:s(function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},"lineStart"),lineEnd:s(function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:s(function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:s4(this,e,t);break}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t},"point")},kke=s((function e(t){function r(i){return new q6(i,t)}return s(r,"cardinal"),r.tension=function(i){return e(+i)},r}),"custom")(0)});function W6(e,t,r){var i=e._x1,n=e._y1,a=e._x2,o=e._y2;if(e._l01_a>Ws){var l=2*e._l01_2a+3*e._l01_a*e._l12_a+e._l12_2a,u=3*e._l01_a*(e._l01_a+e._l12_a);i=(i*l-e._x0*e._l12_2a+e._x2*e._l01_2a)/u,n=(n*l-e._y0*e._l12_2a+e._y2*e._l01_2a)/u}if(e._l23_a>Ws){var h=2*e._l23_2a+3*e._l23_a*e._l12_a+e._l12_2a,d=3*e._l23_a*(e._l23_a+e._l12_a);a=(a*h+e._x1*e._l23_2a-t*e._l12_2a)/d,o=(o*h+e._y1*e._l23_2a-r*e._l12_2a)/d}e._context.bezierCurveTo(i,n,a,o,e._x2,e._y2)}function KP(e,t){this._context=e,this._alpha=t}var QP,ZP=G(()=>{"use strict";FP(),F6(),s(W6,"point"),s(KP,"CatmullRom"),KP.prototype={areaStart:s(function(){this._line=0},"areaStart"),areaEnd:s(function(){this._line=NaN},"areaEnd"),lineStart:s(function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},"lineStart"),lineEnd:s(function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:s(function(e,t){if(e=+e,t=+t,this._point){var r=this._x2-e,i=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(r*r+i*i,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3;default:W6(this,e,t);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t},"point")},QP=s((function e(t){function r(i){return t?new KP(i,t):new P6(i,0)}return s(r,"catmullRom"),r.alpha=function(i){return e(+i)},r}),"custom")(.5)});function JP(e,t){this._context=e,this._alpha=t}var wke,E5t=G(()=>{"use strict";Tke(),O6(),ZP(),s(JP,"CatmullRomClosed"),JP.prototype={areaStart:nd,areaEnd:nd,lineStart:s(function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},"lineStart"),lineEnd:s(function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},"lineEnd"),point:s(function(e,t){if(e=+e,t=+t,this._point){var r=this._x2-e,i=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(r*r+i*i,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=e,this._y3=t;break;case 1:this._point=2,this._context.moveTo(this._x4=e,this._y4=t);break;case 2:this._point=3,this._x5=e,this._y5=t;break;default:W6(this,e,t);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t},"point")},wke=s((function e(t){function r(i){return t?new JP(i,t):new z6(i,0)}return s(r,"catmullRom"),r.alpha=function(i){return e(+i)},r}),"custom")(.5)});function eF(e,t){this._context=e,this._alpha=t}var Eke,S5t=G(()=>{"use strict";Cke(),ZP(),s(eF,"CatmullRomOpen"),eF.prototype={areaStart:s(function(){this._line=0},"areaStart"),areaEnd:s(function(){this._line=NaN},"areaEnd"),lineStart:s(function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},"lineStart"),lineEnd:s(function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:s(function(e,t){if(e=+e,t=+t,this._point){var r=this._x2-e,i=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(r*r+i*i,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:W6(this,e,t);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t},"point")},Eke=s((function e(t){function r(i){return t?new eF(i,t):new q6(i,0)}return s(r,"catmullRom"),r.alpha=function(i){return e(+i)},r}),"custom")(.5)});function tF(e){this._context=e}function Ske(e){return new tF(e)}var _5t=G(()=>{"use strict";O6(),s(tF,"LinearClosed"),tF.prototype={areaStart:nd,areaEnd:nd,lineStart:s(function(){this._point=0},"lineStart"),lineEnd:s(function(){this._point&&this._context.closePath()},"lineEnd"),point:s(function(e,t){e=+e,t=+t,this._point?this._context.lineTo(e,t):(this._point=1,this._context.moveTo(e,t))},"point")},s(Ske,"default")});function rF(e){return e<0?-1:1}function iF(e,t,r){var i=e._x1-e._x0,n=t-e._x1,a=(e._y1-e._y0)/(i||n<0&&-0),o=(r-e._y1)/(n||i<0&&-0),l=(a*n+o*i)/(i+n);return(rF(a)+rF(o))*Math.min(Math.abs(a),Math.abs(o),.5*Math.abs(l))||0}function nF(e,t){var r=e._x1-e._x0;return r?(3*(e._y1-e._y0)/r-t)/2:t}function j6(e,t,r){var i=e._x0,n=e._y0,a=e._x1,o=e._y1,l=(a-i)/3;e._context.bezierCurveTo(i+l,n+l*t,a-l,o-l*r,a,o)}function o4(e){this._context=e}function aF(e){this._context=new sF(e)}function sF(e){this._context=e}function oF(e){return new o4(e)}function lF(e){return new aF(e)}var A5t=G(()=>{"use strict";s(rF,"sign"),s(iF,"slope3"),s(nF,"slope2"),s(j6,"point"),s(o4,"MonotoneX"),o4.prototype={areaStart:s(function(){this._line=0},"areaStart"),areaEnd:s(function(){this._line=NaN},"areaEnd"),lineStart:s(function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},"lineStart"),lineEnd:s(function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:j6(this,this._t0,nF(this,this._t0));break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:s(function(e,t){var r=NaN;if(e=+e,t=+t,!(e===this._x1&&t===this._y1)){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,j6(this,nF(this,r=iF(this,e,t)),r);break;default:j6(this,this._t0,r=iF(this,e,t));break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t,this._t0=r}},"point")},s(aF,"MonotoneY"),(aF.prototype=Object.create(o4.prototype)).point=function(e,t){o4.prototype.point.call(this,t,e)},s(sF,"ReflectContext"),sF.prototype={moveTo:s(function(e,t){this._context.moveTo(t,e)},"moveTo"),closePath:s(function(){this._context.closePath()},"closePath"),lineTo:s(function(e,t){this._context.lineTo(t,e)},"lineTo"),bezierCurveTo:s(function(e,t,r,i,n,a){this._context.bezierCurveTo(t,e,i,r,a,n)},"bezierCurveTo")},s(oF,"monotoneX"),s(lF,"monotoneY")});function cF(e){this._context=e}function uF(e){var t,r=e.length-1,i,n=new Array(r),a=new Array(r),o=new Array(r);for(n[0]=0,a[0]=2,o[0]=e[0]+2*e[1],t=1;t=0;--t)n[t]=(o[t]-n[t+1])/a[t];for(a[r-1]=(e[r]+n[r-1])/2,t=0;t{"use strict";s(cF,"Natural"),cF.prototype={areaStart:s(function(){this._line=0},"areaStart"),areaEnd:s(function(){this._line=NaN},"areaEnd"),lineStart:s(function(){this._x=[],this._y=[]},"lineStart"),lineEnd:s(function(){var e=this._x,t=this._y,r=e.length;if(r)if(this._line?this._context.lineTo(e[0],t[0]):this._context.moveTo(e[0],t[0]),r===2)this._context.lineTo(e[1],t[1]);else for(var i=uF(e),n=uF(t),a=0,o=1;o{"use strict";s(l4,"Step"),l4.prototype={areaStart:s(function(){this._line=0},"areaStart"),areaEnd:s(function(){this._line=NaN},"areaEnd"),lineStart:s(function(){this._x=this._y=NaN,this._point=0},"lineStart"),lineEnd:s(function(){0=0&&(this._t=1-this._t,this._line=1-this._line)},"lineEnd"),point:s(function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:{if(this._t<=0)this._context.lineTo(this._x,t),this._context.lineTo(e,t);else{var r=this._x*(1-this._t)+e*this._t;this._context.lineTo(r,this._y),this._context.lineTo(r,t)}break}}this._x=e,this._y=t},"point")},s(dF,"default"),s(pF,"stepBefore"),s(fF,"stepAfter")}),L5t=G(()=>{"use strict";f5t(),y5t(),x5t(),k5t(),C5t(),B6(),T5t(),w5t(),Tke(),Cke(),F6(),E5t(),S5t(),ZP(),_5t(),hke(),A5t(),R5t(),$5t()}),I5t=G(()=>{"use strict"}),M5t=G(()=>{"use strict"});function dg(e,t,r){this.k=e,this.x=t,this.y=r}function _ke(e){for(;!e.__zoom;)if(!(e=e.parentNode))return Ake;return e.__zoom}var Ake,Rke=G(()=>{"use strict";s(dg,"Transform"),dg.prototype={constructor:dg,scale:s(function(e){return e===1?this:new dg(this.k*e,this.x,this.y)},"scale"),translate:s(function(e,t){return e===0&t===0?this:new dg(this.k,this.x+this.k*e,this.y+this.k*t)},"translate"),apply:s(function(e){return[e[0]*this.k+this.x,e[1]*this.k+this.y]},"apply"),applyX:s(function(e){return e*this.k+this.x},"applyX"),applyY:s(function(e){return e*this.k+this.y},"applyY"),invert:s(function(e){return[(e[0]-this.x)/this.k,(e[1]-this.y)/this.k]},"invert"),invertX:s(function(e){return(e-this.x)/this.k},"invertX"),invertY:s(function(e){return(e-this.y)/this.k},"invertY"),rescaleX:s(function(e){return e.copy().domain(e.range().map(this.invertX,this).map(e.invert,e))},"rescaleX"),rescaleY:s(function(e){return e.copy().domain(e.range().map(this.invertY,this).map(e.invert,e))},"rescaleY"),toString:s(function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"},"toString")},Ake=new dg(1,0,0),_ke.prototype=dg.prototype,s(_ke,"transform")}),D5t=G(()=>{"use strict"}),N5t=G(()=>{"use strict";JB(),I5t(),M5t(),Rke(),D5t()}),O5t=G(()=>{"use strict";N5t(),Rke()}),pi=G(()=>{"use strict";Z0(),Q3t(),LCt(),MCt(),BT(),DCt(),NCt(),F2e(),Dkt(),OCt(),wTe(),BCt(),FCt(),GTe(),QCt(),bwt(),zT(),ITe(),xwt(),PCt(),Twt(),u5t(),p5t(),Du(),L5t(),xP(),X3e(),YB(),JB(),O5t()}),gF,c4,mF,$ke,V6,U6,El,Y6,H6,pg=G(()=>{"use strict";gF=$o(zS(),1),pi(),mi(),c4=s((e,t)=>{let r=e.append("rect");if(r.attr("x",t.x),r.attr("y",t.y),r.attr("fill",t.fill),r.attr("stroke",t.stroke),r.attr("width",t.width),r.attr("height",t.height),t.name&&r.attr("name",t.name),t.rx&&r.attr("rx",t.rx),t.ry&&r.attr("ry",t.ry),t.attrs!==void 0)for(let i in t.attrs)r.attr(i,t.attrs[i]);return t.class&&r.attr("class",t.class),r},"drawRect"),mF=s((e,t)=>{let r={x:t.startx,y:t.starty,width:t.stopx-t.startx,height:t.stopy-t.starty,fill:t.fill,stroke:t.stroke,class:"rect"};c4(e,r).lower()},"drawBackgroundRect"),$ke=s((e,t)=>{let r=t.text.replace(K0," "),i=e.append("text");i.attr("x",t.x),i.attr("y",t.y),i.attr("class","legend"),i.style("text-anchor",t.anchor),t.class&&i.attr("class",t.class);let n=i.append("tspan");return n.attr("x",t.x+t.textMargin*2),n.text(r),i},"drawText"),V6=s((e,t,r,i)=>{let n=e.append("image");n.attr("x",t),n.attr("y",r);let a=(0,gF.sanitizeUrl)(i);n.attr("xlink:href",a)},"drawImage"),U6=s((e,t,r,i)=>{let n=e.append("use");n.attr("x",t),n.attr("y",r);let a=(0,gF.sanitizeUrl)(i);n.attr("xlink:href",`#${a}`)},"drawEmbeddedImage"),El=s(()=>({x:0,y:0,width:100,height:100,fill:"#EDF2AE",stroke:"#666",anchor:"start",rx:0,ry:0}),"getNoteRect"),Y6=s(()=>({x:0,y:0,width:100,height:100,"text-anchor":"start",style:"#666",textMargin:0,rx:0,ry:0,tspan:!0}),"getTextObj"),H6=s(()=>{let e=zt(".mermaidTooltip");return e.empty()&&(e=zt("body").append("div").attr("class","mermaidTooltip").style("opacity",0).style("position","absolute").style("text-align","center").style("max-width","200px").style("padding","2px").style("font-size","12px").style("background","#ffffde").style("border","1px solid #333").style("border-radius","2px").style("pointer-events","none").style("z-index","100")),e},"createTooltip")}),yF,Lke,Ike,Mke,Dke,Nke,Oke,Bke,Pke,Fke,zy,Fu,B5t=G(()=>{"use strict";mi(),pg(),yF=s(function(e,t){return c4(e,t)},"drawRect"),Lke=s((e,t,r,i)=>{let n=e.append("g"),a=0;for(let o of t){let l=o.textColor?o.textColor:"#444444",u=o.lineColor?o.lineColor:"#444444",h=o.offsetX?parseInt(String(o.offsetX)):0,d=o.offsetY?parseInt(String(o.offsetY)):0,p="";if(a===0){let m=n.append("line");m.attr("x1",o.startPoint.x),m.attr("y1",o.startPoint.y),m.attr("x2",o.endPoint.x),m.attr("y2",o.endPoint.y),m.attr("stroke-width","1"),m.attr("stroke",u),m.style("fill","none"),o.type!=="rel_b"&&m.attr("marker-end","url("+p+"#"+i+"-arrowhead)"),(o.type==="birel"||o.type==="rel_b")&&m.attr("marker-start","url("+p+"#"+i+"-arrowend)"),a=-1}else{let m=n.append("path");m.attr("fill","none").attr("stroke-width","1").attr("stroke",u).attr("d","Mstartx,starty Qcontrolx,controly stopx,stopy ".replaceAll("startx",o.startPoint.x).replaceAll("starty",o.startPoint.y).replaceAll("controlx",o.startPoint.x+(o.endPoint.x-o.startPoint.x)/2-(o.endPoint.x-o.startPoint.x)/4).replaceAll("controly",o.startPoint.y+(o.endPoint.y-o.startPoint.y)/2).replaceAll("stopx",o.endPoint.x).replaceAll("stopy",o.endPoint.y)),o.type!=="rel_b"&&m.attr("marker-end","url("+p+"#"+i+"-arrowhead)"),(o.type==="birel"||o.type==="rel_b")&&m.attr("marker-start","url("+p+"#"+i+"-arrowend)")}let f=o.label.width,g=r.messageFont();zy(r)(o.label.text,n,Math.min(o.startPoint.x,o.endPoint.x)+Math.abs(o.endPoint.x-o.startPoint.x)/2+h,Math.min(o.startPoint.y,o.endPoint.y)+Math.abs(o.endPoint.y-o.startPoint.y)/2+d,f,o.label.height,{fill:l},g),o.techn&&o.techn.text!==""&&(g=r.messageFont(),zy(r)("["+o.techn.text+"]",n,Math.min(o.startPoint.x,o.endPoint.x)+Math.abs(o.endPoint.x-o.startPoint.x)/2+h,Math.min(o.startPoint.y,o.endPoint.y)+Math.abs(o.endPoint.y-o.startPoint.y)/2+r.messageFontSize+5+d,Math.max(f,o.techn.width),o.techn.height,{fill:l,"font-style":"italic"},g))}},"drawRels"),Ike=s(function(e,t,r){let i=e.append("g"),n=t.bgColor?t.bgColor:"none",a=t.borderColor?t.borderColor:"#444444",o=t.fontColor?t.fontColor:"black",l={"stroke-width":1,"stroke-dasharray":"7.0,7.0"};t.nodeType&&(l={"stroke-width":1});let u={x:t.x,y:t.y,fill:n,stroke:a,width:t.width,height:t.height,rx:2.5,ry:2.5,attrs:l};yF(i,u);let h=r.boundaryFont();h.fontWeight="bold",h.fontSize=h.fontSize+2,h.fontColor=o,zy(r)(t.label.text,i,t.x,t.y+t.label.Y,t.width,t.height,{fill:"#444444"},h),t.type&&t.type.text!==""&&(h=r.boundaryFont(),h.fontColor=o,zy(r)(t.type.text,i,t.x,t.y+t.type.Y,t.width,t.height,{fill:"#444444"},h)),t.descr&&t.descr.text!==""&&(h=r.boundaryFont(),h.fontSize=h.fontSize-2,h.fontColor=o,zy(r)(t.descr.text,i,t.x,t.y+t.descr.Y,t.width,t.height,{fill:"#444444"},h))},"drawBoundary"),Mke=s(function(e,t){e.append("defs").append("symbol").attr("id",t+"-database").attr("fill-rule","evenodd").attr("clip-rule","evenodd").append("path").attr("transform","scale(.5)").attr("d","M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z")},"insertDatabaseIcon"),Dke=s(function(e,t){e.append("defs").append("symbol").attr("id",t+"-computer").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z")},"insertComputerIcon"),Nke=s(function(e,t){e.append("defs").append("symbol").attr("id",t+"-clock").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z")},"insertClockIcon"),Oke=s(function(e,t){e.append("defs").append("marker").attr("id",t+"-arrowhead").attr("refX",9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z")},"insertArrowHead"),Bke=s(function(e,t){e.append("defs").append("marker").attr("id",t+"-arrowend").attr("refX",1).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 10 0 L 0 5 L 10 10 z")},"insertArrowEnd"),Pke=s(function(e,t){e.append("defs").append("marker").attr("id",t+"-filled-head").attr("refX",18).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},"insertArrowFilledHead"),Fke=s(function(e,t){let r=e.append("defs").append("marker").attr("id",t+"-crosshead").attr("markerWidth",15).attr("markerHeight",8).attr("orient","auto").attr("refX",16).attr("refY",4);r.append("path").attr("fill","black").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 9,2 V 6 L16,4 Z"),r.append("path").attr("fill","none").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 0,1 L 6,7 M 6,1 L 0,7")},"insertArrowCrossHead"),zy=(function(){function e(n,a,o,l,u,h,d){let p=a.append("text").attr("x",o+u/2).attr("y",l+h/2+5).style("text-anchor","middle").text(n);i(p,d)}s(e,"byText");function t(n,a,o,l,u,h,d,p){let{fontSize:f,fontFamily:g,fontWeight:m}=p,y=n.split(Vt.lineBreakRegex);for(let k=0;k=0}var P5t=G(()=>{"use strict";s(zke,"isLength")});function vF(e){return e!=null&&typeof e!="function"&&zke(e.length)}var qke=G(()=>{"use strict";P5t(),s(vF,"isArrayLike")});function Wke(e){return e==="__proto__"}var F5t=G(()=>{"use strict";s(Wke,"isUnsafeProperty")});function u4(e){return e==null||typeof e!="object"&&typeof e!="function"}var G6=G(()=>{"use strict";s(u4,"isPrimitive")});function bF(e){return Object.getOwnPropertySymbols(e).filter(t=>Object.prototype.propertyIsEnumerable.call(e,t))}var jke=G(()=>{"use strict";s(bF,"getSymbols")});function qy(e){return e==null?e===void 0?"[object Undefined]":"[object Null]":Object.prototype.toString.call(e)}var X6=G(()=>{"use strict";s(qy,"getTag")}),K6,Wy,h4,d4,p4,Q6,Z6,J6,e_,xF,t_,r_,i_,TF,kF,CF,wF,EF,SF,_F,AF,RF,$F=G(()=>{"use strict";K6="[object RegExp]",Wy="[object String]",h4="[object Number]",d4="[object Boolean]",p4="[object Arguments]",Q6="[object Symbol]",Z6="[object Date]",J6="[object Map]",e_="[object Set]",xF="[object Array]",t_="[object ArrayBuffer]",r_="[object Object]",i_="[object DataView]",TF="[object Uint8Array]",kF="[object Uint8ClampedArray]",CF="[object Uint16Array]",wF="[object Uint32Array]",EF="[object Int8Array]",SF="[object Int16Array]",_F="[object Int32Array]",AF="[object Float32Array]",RF="[object Float64Array]"});function n_(e){return ArrayBuffer.isView(e)&&!(e instanceof DataView)}var LF=G(()=>{"use strict";s(n_,"isTypedArray")});function Vke(e,t){return fg(e,void 0,e,new Map,t)}function fg(e,t,r,i=new Map,n=void 0){let a=n?.(e,t,r,i);if(a!==void 0)return a;if(u4(e))return e;if(i.has(e))return i.get(e);if(Array.isArray(e)){let o=new Array(e.length);i.set(e,o);for(let l=0;l{"use strict";jke(),X6(),$F(),G6(),LF(),s(Vke,"cloneDeepWith"),s(fg,"cloneDeepWithImpl"),s(Jl,"copyProperties"),s(Uke,"isCloneableObject")});function Yke(e,t){return Vke(e,(r,i,n,a)=>{let o=t?.(r,i,n,a);if(o!==void 0)return o;if(typeof e=="object"){if(qy(e)===r_&&typeof e.constructor!="function"){let l={};return a.set(e,l),Jl(l,e,n,a),l}switch(Object.prototype.toString.call(e)){case h4:case Wy:case d4:{let l=new e.constructor(e?.valueOf());return Jl(l,e),l}case p4:{let l={};return Jl(l,e),l.length=e.length,l[Symbol.iterator]=e[Symbol.iterator],l}default:return}}})}var q5t=G(()=>{"use strict";z5t(),X6(),$F(),s(Yke,"cloneDeepWith")});function IF(e){return Yke(e)}var W5t=G(()=>{"use strict";q5t(),s(IF,"cloneDeep")});function a_(e){return e!==null&&typeof e=="object"&&qy(e)==="[object Arguments]"}var Hke=G(()=>{"use strict";X6(),s(a_,"isArguments")});function s_(e){return typeof e=="object"&&e!==null}var Gke=G(()=>{"use strict";s(s_,"isObjectLike")});function Xke(e){return s_(e)&&vF(e)}var j5t=G(()=>{"use strict";qke(),Gke(),s(Xke,"isArrayLikeObject")});function Kke(e){return Array.isArray(e)}var V5t=G(()=>{"use strict";s(Kke,"isArray")});function jy(e,t){if(typeof e!="function"||t!=null&&typeof t!="function")throw new TypeError("Expected a function");let r=s(function(...n){let a=t?t.apply(this,n):n[0],o=r.cache;if(o.has(a))return o.get(a);let l=e.apply(this,n);return r.cache=o.set(a,l)||o,l},"memoized"),i=jy.Cache||Map;return r.cache=new i,r}var U5t=G(()=>{"use strict";s(jy,"memoize"),jy.Cache=Map});function Qke(){}var Y5t=G(()=>{"use strict";s(Qke,"noop")});function Zke(e){let t=e?.constructor,r=typeof t=="function"?t.prototype:Object.prototype;return e===r}var H5t=G(()=>{"use strict";s(Zke,"isPrototype")});function Vy(e){return n_(e)}var MF=G(()=>{"use strict";LF(),s(Vy,"isTypedArray")});function Jke(e){if(u4(e))return e;let t=qy(e);if(!eCe(e))return{};if(Kke(e)){let i=Array.from(e);return e.length>0&&typeof e[0]=="string"&&Object.hasOwn(e,"index")&&(i.index=e.index,i.input=e.input),i}if(Vy(e)){let i=e,n=i.constructor;return new n(i.buffer,i.byteOffset,i.length)}if(t===t_)return new ArrayBuffer(e.byteLength);if(t===i_){let i=e,n=i.buffer,a=i.byteOffset,o=i.byteLength,l=new ArrayBuffer(o),u=new Uint8Array(n,a,o);return new Uint8Array(l).set(u),new DataView(l)}if(t===d4||t===h4||t===Wy){let i=e.constructor,n=new i(e.valueOf());return t===Wy?rCe(n,e):o_(n,e),n}if(t===Z6)return new Date(Number(e));if(t===K6){let i=e,n=new RegExp(i.source,i.flags);return n.lastIndex=i.lastIndex,n}if(t===Q6)return Object(Symbol.prototype.valueOf.call(e));if(t===J6){let i=e,n=new Map;return i.forEach((a,o)=>{n.set(o,a)}),n}if(t===e_){let i=e,n=new Set;return i.forEach(a=>{n.add(a)}),n}if(t===p4){let i=e,n={};return o_(n,i),n.length=i.length,n[Symbol.iterator]=i[Symbol.iterator],n}let r={};return iCe(r,e),o_(r,e),tCe(r,e),r}function eCe(e){switch(qy(e)){case p4:case xF:case t_:case i_:case d4:case Z6:case AF:case RF:case EF:case SF:case _F:case J6:case h4:case r_:case K6:case e_:case Wy:case Q6:case TF:case kF:case CF:case wF:return!0;default:return!1}}function o_(e,t){for(let r in t)Object.hasOwn(t,r)&&(e[r]=t[r])}function tCe(e,t){let r=Object.getOwnPropertySymbols(t);for(let i=0;i=r)&&(e[i]=t[i])}function iCe(e,t){let r=Object.getPrototypeOf(t);r!==null&&typeof t.constructor=="function"&&Object.setPrototypeOf(e,r)}var G5t=G(()=>{"use strict";G6(),X6(),$F(),V5t(),MF(),s(Jke,"clone"),s(eCe,"isCloneableObject"),s(o_,"copyOwnProperties"),s(tCe,"copySymbolProperties"),s(rCe,"cloneStringObjectProperties"),s(iCe,"copyPrototype")});function l_(e){if(typeof e!="object"||e==null)return!1;if(Object.getPrototypeOf(e)===null)return!0;if(Object.prototype.toString.call(e)!=="[object Object]"){let r=e[Symbol.toStringTag];return r==null||!Object.getOwnPropertyDescriptor(e,Symbol.toStringTag)?.writable?!1:e.toString()===`[object ${r}]`}let t=e;for(;Object.getPrototypeOf(t)!==null;)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}var X5t=G(()=>{"use strict";s(l_,"isPlainObject")});function nCe(e){if(u4(e))return e;if(Array.isArray(e)||n_(e)||e instanceof ArrayBuffer||typeof SharedArrayBuffer<"u"&&e instanceof SharedArrayBuffer)return e.slice(0);let t=Object.getPrototypeOf(e);if(t==null)return Object.assign(Object.create(t),e);let r=t.constructor;if(e instanceof Date||e instanceof Map||e instanceof Set)return new r(e);if(e instanceof RegExp){let i=new r(e);return i.lastIndex=e.lastIndex,i}if(e instanceof DataView)return new r(e.buffer.slice(0));if(e instanceof Error){let i;return e instanceof AggregateError?i=new r(e.errors,e.message,{cause:e.cause}):i=new r(e.message,{cause:e.cause}),i.stack=e.stack,Object.assign(i,e),i}if(typeof File<"u"&&e instanceof File)return new r([e],e.name,{type:e.type,lastModified:e.lastModified});if(typeof e=="object"){let i=Object.create(t);return Object.assign(i,e)}return e}var K5t=G(()=>{"use strict";G6(),LF(),s(nCe,"clone")});function aCe(e,...t){let r=t.slice(0,-1),i=t[t.length-1],n=e;for(let a=0;a{"use strict";W5t(),F5t(),K5t(),G6(),jke(),Hke(),j5t(),Gke(),X5t(),MF(),s(aCe,"mergeWith"),s(c_,"mergeWithDeep")});function sCe(e,...t){return aCe(e,...t,Qke)}var Z5t=G(()=>{"use strict";Q5t(),Y5t(),s(sCe,"merge")});function DF(e){if(e==null)return!0;if(vF(e))return typeof e.splice!="function"&&typeof e!="string"&&(typeof Buffer>"u"||!Buffer.isBuffer(e))&&!Vy(e)&&!a_(e)?!1:e.length===0;if(typeof e=="object"){if(e instanceof Map||e instanceof Set)return e.size===0;let t=Object.keys(e);return Zke(e)?t.filter(r=>r!=="constructor").length===0:t.length===0}return!0}var J5t=G(()=>{"use strict";Hke(),qke(),MF(),H5t(),s(DF,"isEmpty")}),NF=G(()=>{"use strict";U5t(),G5t(),Z5t(),J5t()});function OF(e,t){if(!e)return t;let r=`curve${e.charAt(0).toUpperCase()+e.slice(1)}`;return fCe[r]??t}function oCe(e,t){let r=e.trim();if(r)return t.securityLevel!=="loose"?(0,pCe.sanitizeUrl)(r):r}function BF(e,t){return!e||!t?0:Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2))}function lCe(e){let t,r=0;e.forEach(n=>{r+=BF(n,t),t=n});let i=r/2;return h_(e,i)}function cCe(e){return e.length===1?e[0]:lCe(e)}function uCe(e,t,r){let i=structuredClone(r);Ee.info("our points",i),t!=="start_left"&&t!=="start_right"&&i.reverse();let n=25+e,a=h_(i,n),o=10+e*.5,l=Math.atan2(i[0].y-a.y,i[0].x-a.x),u={x:0,y:0};return t==="start_left"?(u.x=Math.sin(l+Math.PI)*o+(i[0].x+a.x)/2,u.y=-Math.cos(l+Math.PI)*o+(i[0].y+a.y)/2):t==="end_right"?(u.x=Math.sin(l-Math.PI)*o+(i[0].x+a.x)/2-5,u.y=-Math.cos(l-Math.PI)*o+(i[0].y+a.y)/2-5):t==="end_left"?(u.x=Math.sin(l)*o+(i[0].x+a.x)/2-5,u.y=-Math.cos(l)*o+(i[0].y+a.y)/2-5):(u.x=Math.sin(l)*o+(i[0].x+a.x)/2,u.y=-Math.cos(l)*o+(i[0].y+a.y)/2),u}function PF(e){let t="",r="";for(let i of e)i!==void 0&&(i.startsWith("color:")||i.startsWith("text-align:")?r=r+i+";":t=t+i+";");return{style:t,labelStyle:r}}function hCe(e){let t="",r="0123456789abcdef",i=r.length;for(let n=0;nMath.round(parseFloat(a)).toString());return n.includes(r.toString())||n.includes(i.toString())}var pCe,zF,fCe,gCe,mCe,qF,yCe,vCe,bCe,WF,h_,xCe,jF,VF,UF,TCe,kCe,Uy,CCe,f4,wCe,d_,ECe,SCe,zu,ai,_Ce,ad,gg,Ar=G(()=>{"use strict";var e;pCe=$o(zS(),1),pi(),mi(),fD(),Nt(),yy(),$x(),NF(),NO(),zF="​",fCe={curveBasis:a4,curveBasisClosed:yke,curveBasisOpen:vke,curveBumpX:jP,curveBumpY:VP,curveBundle:bke,curveCardinalClosed:xke,curveCardinalOpen:kke,curveCardinal:XP,curveCatmullRomClosed:wke,curveCatmullRomOpen:Eke,curveCatmullRom:QP,curveLinear:Fy,curveLinearClosed:Ske,curveMonotoneX:oF,curveMonotoneY:lF,curveNatural:hF,curveStep:dF,curveStepAfter:fF,curveStepBefore:pF},gCe=/\s*(?:(\w+)(?=:):|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi,mCe=s(function(t,r){let i=qF(t,/(?:init\b)|(?:initialize\b)/),n={};if(Array.isArray(i)){let l=i.map(u=>u.args);Bx(l),n=Sa(n,[...l])}else n=i.args;if(!n)return;let a=_S(t,r),o="config";return n[o]!==void 0&&(a==="flowchart-v2"&&(a="flowchart"),n[a]=n[o],delete n[o]),n},"detectInit"),qF=s(function(t,r=null){try{let i=new RegExp(`[%]{2}(?![{]${gCe.source})(?=[}][%]{2}).* +`,"ig");t=t.trim().replace(i,"").replace(/'/gm,'"'),Ee.debug(`Detecting diagram directive${r!==null?" type:"+r:""} based on the text:${t}`);let n,a=[];for(;(n=my.exec(t))!==null;)if(n.index===my.lastIndex&&my.lastIndex++,n&&!r||r&&n[1]?.match(r)||r&&n[2]?.match(r)){let o=n[1]?n[1]:n[2],l=n[3]?n[3].trim():n[4]?JSON.parse(n[4].trim()):null;a.push({type:o,args:l})}return a.length===0?{type:t,args:null}:a.length===1?a[0]:a}catch(i){return Ee.error(`ERROR: ${i.message} - Unable to parse directive type: '${r}' based on the text: '${t}'`),{type:void 0,args:null}}},"detectDirective"),yCe=s(function(t){return t.replace(my,"")},"removeDirectives"),vCe=s(function(t,r){for(let[i,n]of r.entries())if(n.match(t))return i;return-1},"isSubstringInArray"),s(OF,"interpolateToCurve"),s(oCe,"formatUrl"),bCe=s((t,...r)=>{let i=t.split("."),n=i.length-1,a=i[n],o=window;for(let l=0;l{let i=Math.pow(10,r);return Math.round(t*i)/i},"roundNumber"),h_=s((t,r)=>{let i,n=r;for(let a of t){if(i){let o=BF(a,i);if(o===0)return i;if(o=1)return{x:a.x,y:a.y};if(l>0&&l<1)return{x:WF((1-l)*i.x+l*a.x,5),y:WF((1-l)*i.y+l*a.y,5)}}}i=a}throw new Error("Could not find a suitable point for the given distance")},"calculatePoint"),xCe=s((t,r,i)=>{Ee.info(`our points ${JSON.stringify(r)}`),r[0]!==i&&(r=r.reverse());let n=h_(r,25),a=t?10:5,o=Math.atan2(r[0].y-n.y,r[0].x-n.x),l={x:0,y:0};return l.x=Math.sin(o)*a+(r[0].x+n.x)/2,l.y=-Math.cos(o)*a+(r[0].y+n.y)/2,l},"calcCardinalityPosition"),s(uCe,"calcTerminalLabelPosition"),s(PF,"getStylesFromArray"),jF=0,VF=s(()=>(jF++,"id-"+Math.random().toString(36).substr(2,12)+"-"+jF),"generateId"),s(hCe,"makeRandomHex"),UF=s(t=>hCe(t.length),"random"),TCe=s(function(){return{x:0,y:0,fill:void 0,anchor:"start",style:"#666",width:100,height:100,textMargin:0,rx:0,ry:0,valign:void 0,text:""}},"getTextObj"),kCe=s(function(t,r){let i=r.text.replace(Vt.lineBreakRegex," "),[,n]=zu(r.fontSize),a=t.append("text");a.attr("x",r.x),a.attr("y",r.y),a.style("text-anchor",r.anchor),a.style("font-family",r.fontFamily),a.style("font-size",n),a.style("font-weight",r.fontWeight),a.attr("fill",r.fill),r.class!==void 0&&a.attr("class",r.class);let o=a.append("tspan");return o.attr("x",r.x+r.textMargin*2),o.attr("fill",r.fill),o.text(i),a},"drawSimpleText"),Uy=jy((t,r,i)=>{if(!t||(i=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",joinWith:"
"},i),Vt.lineBreakRegex.test(t)))return t;let n=t.split(" ").filter(Boolean),a=[],o="";return n.forEach((l,u)=>{let h=al(`${l} `,i),d=al(o,i);if(h>r){let{hyphenatedStrings:p,remainingWord:f}=CCe(l,r,"-",i);a.push(o,...p),o=f}else d+h>=r?(a.push(o),o=l):o=[o,l].filter(Boolean).join(" ");u+1===n.length&&a.push(o)}),a.filter(l=>l!=="").join(i.joinWith)},(t,r,i)=>`${t}${r}${i.fontSize}${i.fontWeight}${i.fontFamily}${i.joinWith}`),CCe=jy((t,r,i="-",n)=>{n=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:0},n);let a=[...t],o=[],l="";return a.forEach((u,h)=>{let d=`${l}${u}`;if(al(d,n)>=r){let p=h+1,f=a.length===p,g=`${d}${i}`;o.push(f?d:g),l=""}else l=d}),{hyphenatedStrings:o,remainingWord:l}},(t,r,i="-",n)=>`${t}${r}${i}${n.fontSize}${n.fontWeight}${n.fontFamily}`),s(u_,"calculateTextHeight"),s(al,"calculateTextWidth"),f4=jy((t,r)=>{let{fontSize:i=12,fontFamily:n="Arial",fontWeight:a=400}=r;if(!t)return{width:0,height:0};let[,o]=zu(i),l=["sans-serif",n],u=t.split(Vt.lineBreakRegex),h=[],d=zt("body");if(!d.remove)return{width:0,height:0,lineHeight:0};let p=d.append("svg");for(let g of l){let m=0,y={width:0,height:0,lineHeight:0};for(let k of u){let C=TCe();C.text=k||zF;let S=kCe(p,C).style("font-size",o).style("font-weight",a).style("font-family",g),E=(S._groups||S)[0][0].getBBox();if(E.width===0&&E.height===0)throw new Error("svg element not in render tree");y.width=Math.round(Math.max(y.width,E.width)),m=Math.round(E.height),y.height+=m,y.lineHeight=Math.round(Math.max(y.lineHeight,m))}h.push(y)}p.remove();let f=isNaN(h[1].height)||isNaN(h[1].width)||isNaN(h[1].lineHeight)||h[0].height>h[1].height&&h[0].width>h[1].width&&h[0].lineHeight>h[1].lineHeight?0:1;return h[f]},(t,r)=>`${t}${r.fontSize}${r.fontWeight}${r.fontFamily}`),wCe=(e=class{constructor(r=!1,i){this.count=0,this.count=i?i.length:0,this.next=r?()=>this.count++:()=>Date.now()}},s(e,"InitIDGenerator"),e),ECe=s(function(t){return d_=d_||document.createElement("div"),t=escape(t).replace(/%26/g,"&").replace(/%23/g,"#").replace(/%3B/g,";"),d_.innerHTML=t,unescape(d_.textContent)},"entityDecode"),s(FF,"isDetailedError"),SCe=s((t,r,i,n)=>{if(!n)return;let a=t.node()?.getBBox();a&&t.append("text").text(n).attr("text-anchor","middle").attr("x",a.x+a.width/2).attr("y",-i).attr("class",r)},"insertTitle"),zu=s(t=>{if(typeof t=="number")return[t,t+"px"];let r=parseInt(t??"",10);return Number.isNaN(r)?[void 0,void 0]:t===String(r)?[r,t+"px"]:[r,t]},"parseFontSize"),s(Gn,"cleanAndMerge"),ai={assignWithDepth:Sa,wrapLabel:Uy,calculateTextHeight:u_,calculateTextWidth:al,calculateTextDimensions:f4,cleanAndMerge:Gn,detectInit:mCe,detectDirective:qF,isSubstringInArray:vCe,interpolateToCurve:OF,calcLabelPosition:cCe,calcCardinalityPosition:xCe,calcTerminalLabelPosition:uCe,formatUrl:oCe,getStylesFromArray:PF,generateId:VF,random:UF,runFunc:bCe,entityDecode:ECe,insertTitle:SCe,isLabelCoordinateInPath:dCe,parseFontSize:zu,InitIDGenerator:wCe},_Ce=s(function(t){let r=t;return r=r.replace(/style.*:\S*#.*;/g,function(i){return i.substring(0,i.length-1)}),r=r.replace(/classDef.*:\S*#.*;/g,function(i){return i.substring(0,i.length-1)}),r=r.replace(/#\w+;/g,function(i){let n=i.substring(1,i.length-1);return/^\+?\d+$/.test(n)?"fl°°"+n+"¶ß":"fl°"+n+"¶ß"}),r},"encodeEntities"),ad=s(function(t){return t.replace(/fl°°/g,"&#").replace(/fl°/g,"&").replace(/¶ß/g,";")},"decodeEntities"),gg=s((t,r,{counter:i=0,prefix:n,suffix:a},o)=>o||`${n?`${n}_`:""}${t}_${r}_${i}${a?`_${a}`:""}`,"getEdgeId"),s(qa,"handleUndefinedAttr"),s(dCe,"isLabelCoordinateInPath")}),ACe,eEt=G(()=>{"use strict";pi(),ACe=s((e,t)=>{if(t==="sandbox"){let r=zt("#i"+e).node()?.contentDocument;if(!r)throw new Error(`Sandbox iframe #i${e} is missing its content document`);return{root:zt(r.body),doc:r}}return{root:zt("body"),doc:document}},"getDiagramRoot")}),tEt,g4=G(()=>{"use strict";tEt=typeof performance<"u"&&typeof performance.now=="function",globalThis.injected??={includeLargeFeatures:!0,profiling:!1,version:"0.0.0"}});function p_(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function YF(e){Pp=e}function Qi(e,t=""){let r=typeof e=="string"?e:e.source,i={replace:s((n,a)=>{let o=typeof a=="string"?a:a.source;return o=o.replace(so.caret,"$1"),r=r.replace(n,o),i},"replace"),getRegex:s(()=>new RegExp(r,t),"getRegex")};return i}function Fc(e,t){if(t){if(so.escapeTest.test(e))return e.replace(so.escapeReplace,oz)}else if(so.escapeTestNoEncode.test(e))return e.replace(so.escapeReplaceNoEncode,oz);return e}function HF(e){try{e=encodeURI(e).replace(so.percentDecode,"%")}catch{return null}return e}function GF(e,t){let r=e.replace(so.findPipe,(a,o,l)=>{let u=!1,h=o;for(;--h>=0&&l[h]==="\\";)u=!u;return u?"|":" |"}),i=r.split(so.splitPipe),n=0;if(i[0].trim()||i.shift(),i.length>0&&!i.at(-1)?.trim()&&i.pop(),t)if(i.length>t)i.splice(t);else for(;i.length0?-2:-1}function XF(e,t,r,i,n){let a=t.href,o=t.title||null,l=e[1].replace(n.other.outputLinkReplace,"$1");i.state.inLink=!0;let u={type:e[0].charAt(0)==="!"?"image":"link",raw:r,href:a,title:o,text:l,tokens:i.inlineTokens(l)};return i.state.inLink=!1,u}function $Ce(e,t,r){let i=e.match(r.other.indentCodeCompensation);if(i===null)return t;let n=i[1];return t.split(` +`).map(a=>{let o=a.match(r.other.beginningSpace);if(o===null)return a;let[l]=o;return l.length>=n.length?a.slice(n.length):a}).join(` +`)}function Zi(e,t){return Fp.parse(e,t)}var Pp,Hy,so,LCe,ICe,MCe,Gy,DCe,f_,KF,QF,NCe,g_,OCe,m_,BCe,PCe,m4,y_,FCe,ZF,zCe,v_,JF,qCe,WCe,jCe,VCe,ez,UCe,y4,b_,tz,YCe,rz,HCe,GCe,XCe,iz,KCe,QCe,nz,ZCe,JCe,ewe,twe,rwe,iwe,nwe,v4,awe,az,sz,swe,x_,owe,T_,lwe,b4,Xy,cwe,oz,x4,qu,T4,k_,Wu,Ky,uwe,Fp,rEt,iEt,nEt,aEt,sEt,oEt,lEt,cEt=G(()=>{"use strict";var e,t,r,i,n,a,o;s(p_,"L"),Pp=p_(),s(YF,"G"),Hy={exec:s(()=>null,"exec")},s(Qi,"h"),so={codeRemoveIndent:/^(?: {1,4}| {0,3}\t)/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:s(l=>new RegExp(`^( {0,3}${l})((?:[ ][^\\n]*)?(?:\\n|$))`),"listItemRegex"),nextBulletRegex:s(l=>new RegExp(`^ {0,${Math.min(3,l-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),"nextBulletRegex"),hrRegex:s(l=>new RegExp(`^ {0,${Math.min(3,l-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),"hrRegex"),fencesBeginRegex:s(l=>new RegExp(`^ {0,${Math.min(3,l-1)}}(?:\`\`\`|~~~)`),"fencesBeginRegex"),headingBeginRegex:s(l=>new RegExp(`^ {0,${Math.min(3,l-1)}}#`),"headingBeginRegex"),htmlBeginRegex:s(l=>new RegExp(`^ {0,${Math.min(3,l-1)}}<(?:[a-z].*>|!--)`,"i"),"htmlBeginRegex")},LCe=/^(?:[ \t]*(?:\n|$))+/,ICe=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,MCe=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,Gy=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,DCe=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,f_=/(?:[*+-]|\d{1,9}[.)])/,KF=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,QF=Qi(KF).replace(/bull/g,f_).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),NCe=Qi(KF).replace(/bull/g,f_).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),g_=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,OCe=/^[^\n]+/,m_=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,BCe=Qi(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",m_).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),PCe=Qi(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,f_).getRegex(),m4="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",y_=/|$))/,FCe=Qi("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",y_).replace("tag",m4).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),ZF=Qi(g_).replace("hr",Gy).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",m4).getRegex(),zCe=Qi(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",ZF).getRegex(),v_={blockquote:zCe,code:ICe,def:BCe,fences:MCe,heading:DCe,hr:Gy,html:FCe,lheading:QF,list:PCe,newline:LCe,paragraph:ZF,table:Hy,text:OCe},JF=Qi("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",Gy).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",m4).getRegex(),qCe={...v_,lheading:NCe,table:JF,paragraph:Qi(g_).replace("hr",Gy).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",JF).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",m4).getRegex()},WCe={...v_,html:Qi(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",y_).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:Hy,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:Qi(g_).replace("hr",Gy).replace("heading",` *#{1,6} *[^ +]`).replace("lheading",QF).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},jCe=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,VCe=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,ez=/^( {2,}|\\)\n(?!\s*$)/,UCe=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\]*?>/g,iz=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,KCe=Qi(iz,"u").replace(/punct/g,y4).getRegex(),QCe=Qi(iz,"u").replace(/punct/g,rz).getRegex(),nz="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",ZCe=Qi(nz,"gu").replace(/notPunctSpace/g,tz).replace(/punctSpace/g,b_).replace(/punct/g,y4).getRegex(),JCe=Qi(nz,"gu").replace(/notPunctSpace/g,GCe).replace(/punctSpace/g,HCe).replace(/punct/g,rz).getRegex(),ewe=Qi("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,tz).replace(/punctSpace/g,b_).replace(/punct/g,y4).getRegex(),twe=Qi(/\\(punct)/,"gu").replace(/punct/g,y4).getRegex(),rwe=Qi(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),iwe=Qi(y_).replace("(?:-->|$)","-->").getRegex(),nwe=Qi("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",iwe).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),v4=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`[^`]*`|[^\[\]\\`])*?/,awe=Qi(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]*(?:\n[ \t]*)?)(title))?\s*\)/).replace("label",v4).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),az=Qi(/^!?\[(label)\]\[(ref)\]/).replace("label",v4).replace("ref",m_).getRegex(),sz=Qi(/^!?\[(ref)\](?:\[\])?/).replace("ref",m_).getRegex(),swe=Qi("reflink|nolink(?!\\()","g").replace("reflink",az).replace("nolink",sz).getRegex(),x_={_backpedal:Hy,anyPunctuation:twe,autolink:rwe,blockSkip:XCe,br:ez,code:VCe,del:Hy,emStrongLDelim:KCe,emStrongRDelimAst:ZCe,emStrongRDelimUnd:ewe,escape:jCe,link:awe,nolink:sz,punctuation:YCe,reflink:az,reflinkSearch:swe,tag:nwe,text:UCe,url:Hy},owe={...x_,link:Qi(/^!?\[(label)\]\((.*?)\)/).replace("label",v4).getRegex(),reflink:Qi(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",v4).getRegex()},T_={...x_,emStrongRDelimAst:JCe,emStrongLDelim:QCe,url:Qi(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,"i").replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},oz=s(l=>cwe[l],"ke"),s(Fc,"w"),s(HF,"J"),s(GF,"V"),s(Yy,"z"),s(RCe,"ge"),s(XF,"fe"),s($Ce,"Je"),x4=(e=class{options;rules;lexer;constructor(u){this.options=u||Pp}space(u){let h=this.rules.block.newline.exec(u);if(h&&h[0].length>0)return{type:"space",raw:h[0]}}code(u){let h=this.rules.block.code.exec(u);if(h){let d=h[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:h[0],codeBlockStyle:"indented",text:this.options.pedantic?d:Yy(d,` +`)}}}fences(u){let h=this.rules.block.fences.exec(u);if(h){let d=h[0],p=$Ce(d,h[3]||"",this.rules);return{type:"code",raw:d,lang:h[2]?h[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):h[2],text:p}}}heading(u){let h=this.rules.block.heading.exec(u);if(h){let d=h[2].trim();if(this.rules.other.endingHash.test(d)){let p=Yy(d,"#");(this.options.pedantic||!p||this.rules.other.endingSpaceChar.test(p))&&(d=p.trim())}return{type:"heading",raw:h[0],depth:h[1].length,text:d,tokens:this.lexer.inline(d)}}}hr(u){let h=this.rules.block.hr.exec(u);if(h)return{type:"hr",raw:Yy(h[0],` +`)}}blockquote(u){let h=this.rules.block.blockquote.exec(u);if(h){let d=Yy(h[0],` +`).split(` +`),p="",f="",g=[];for(;d.length>0;){let m=!1,y=[],k;for(k=0;k1,f={type:"list",raw:"",ordered:p,start:p?+d.slice(0,-1):"",loose:!1,items:[]};d=p?`\\d{1,9}\\${d.slice(-1)}`:`\\${d}`,this.options.pedantic&&(d=p?d:"[*+-]");let g=this.rules.other.listItemRegex(d),m=!1;for(;u;){let k=!1,C="",S="";if(!(h=g.exec(u))||this.rules.block.hr.test(u))break;C=h[0],u=u.substring(C.length);let E=h[2].split(` +`,1)[0].replace(this.rules.other.listReplaceTabs,R=>" ".repeat(3*R.length)),_=u.split(` +`,1)[0],b=!E.trim(),v=0;if(this.options.pedantic?(v=2,S=E.trimStart()):b?v=h[1].length+1:(v=h[2].search(this.rules.other.nonSpaceChar),v=v>4?1:v,S=E.slice(v),v+=h[1].length),b&&this.rules.other.blankLine.test(_)&&(C+=_+` +`,u=u.substring(_.length+1),k=!0),!k){let R=this.rules.other.nextBulletRegex(v),$=this.rules.other.hrRegex(v),M=this.rules.other.fencesBeginRegex(v),N=this.rules.other.headingBeginRegex(v),j=this.rules.other.htmlBeginRegex(v);for(;u;){let I=u.split(` +`,1)[0],z;if(_=I,this.options.pedantic?(_=_.replace(this.rules.other.listReplaceNesting," "),z=_):z=_.replace(this.rules.other.tabCharGlobal," "),M.test(_)||N.test(_)||j.test(_)||R.test(_)||$.test(_))break;if(z.search(this.rules.other.nonSpaceChar)>=v||!_.trim())S+=` +`+z.slice(v);else{if(b||E.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||M.test(E)||N.test(E)||$.test(E))break;S+=` +`+_}!b&&!_.trim()&&(b=!0),C+=I+` +`,u=u.substring(I.length+1),E=z.slice(v)}}f.loose||(m?f.loose=!0:this.rules.other.doubleBlankLine.test(C)&&(m=!0));let x=null,w;this.options.gfm&&(x=this.rules.other.listIsTask.exec(S),x&&(w=x[0]!=="[ ] ",S=S.replace(this.rules.other.listReplaceTask,""))),f.items.push({type:"list_item",raw:C,task:!!x,checked:w,loose:!1,text:S,tokens:[]}),f.raw+=C}let y=f.items.at(-1);if(y)y.raw=y.raw.trimEnd(),y.text=y.text.trimEnd();else return;f.raw=f.raw.trimEnd();for(let k=0;kE.type==="space"),S=C.length>0&&C.some(E=>this.rules.other.anyLine.test(E.raw));f.loose=S}if(f.loose)for(let k=0;k({text:y,tokens:this.lexer.inline(y),header:!1,align:g.align[k]})));return g}}lheading(u){let h=this.rules.block.lheading.exec(u);if(h)return{type:"heading",raw:h[0],depth:h[2].charAt(0)==="="?1:2,text:h[1],tokens:this.lexer.inline(h[1])}}paragraph(u){let h=this.rules.block.paragraph.exec(u);if(h){let d=h[1].charAt(h[1].length-1)===` +`?h[1].slice(0,-1):h[1];return{type:"paragraph",raw:h[0],text:d,tokens:this.lexer.inline(d)}}}text(u){let h=this.rules.block.text.exec(u);if(h)return{type:"text",raw:h[0],text:h[0],tokens:this.lexer.inline(h[0])}}escape(u){let h=this.rules.inline.escape.exec(u);if(h)return{type:"escape",raw:h[0],text:h[1]}}tag(u){let h=this.rules.inline.tag.exec(u);if(h)return!this.lexer.state.inLink&&this.rules.other.startATag.test(h[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(h[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(h[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(h[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:h[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:h[0]}}link(u){let h=this.rules.inline.link.exec(u);if(h){let d=h[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(d)){if(!this.rules.other.endAngleBracket.test(d))return;let g=Yy(d.slice(0,-1),"\\");if((d.length-g.length)%2===0)return}else{let g=RCe(h[2],"()");if(g===-2)return;if(g>-1){let m=(h[0].indexOf("!")===0?5:4)+h[1].length+g;h[2]=h[2].substring(0,g),h[0]=h[0].substring(0,m).trim(),h[3]=""}}let p=h[2],f="";if(this.options.pedantic){let g=this.rules.other.pedanticHrefTitle.exec(p);g&&(p=g[1],f=g[3])}else f=h[3]?h[3].slice(1,-1):"";return p=p.trim(),this.rules.other.startAngleBracket.test(p)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(d)?p=p.slice(1):p=p.slice(1,-1)),XF(h,{href:p&&p.replace(this.rules.inline.anyPunctuation,"$1"),title:f&&f.replace(this.rules.inline.anyPunctuation,"$1")},h[0],this.lexer,this.rules)}}reflink(u,h){let d;if((d=this.rules.inline.reflink.exec(u))||(d=this.rules.inline.nolink.exec(u))){let p=(d[2]||d[1]).replace(this.rules.other.multipleSpaceGlobal," "),f=h[p.toLowerCase()];if(!f){let g=d[0].charAt(0);return{type:"text",raw:g,text:g}}return XF(d,f,d[0],this.lexer,this.rules)}}emStrong(u,h,d=""){let p=this.rules.inline.emStrongLDelim.exec(u);if(!(!p||p[3]&&d.match(this.rules.other.unicodeAlphaNumeric))&&(!(p[1]||p[2])||!d||this.rules.inline.punctuation.exec(d))){let f=[...p[0]].length-1,g,m,y=f,k=0,C=p[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(C.lastIndex=0,h=h.slice(-1*u.length+f);(p=C.exec(h))!=null;){if(g=p[1]||p[2]||p[3]||p[4]||p[5]||p[6],!g)continue;if(m=[...g].length,p[3]||p[4]){y+=m;continue}else if((p[5]||p[6])&&f%3&&!((f+m)%3)){k+=m;continue}if(y-=m,y>0)continue;m=Math.min(m,m+y+k);let S=[...p[0]][0].length,E=u.slice(0,f+p.index+S+m);if(Math.min(f,m)%2){let b=E.slice(1,-1);return{type:"em",raw:E,text:b,tokens:this.lexer.inlineTokens(b)}}let _=E.slice(2,-2);return{type:"strong",raw:E,text:_,tokens:this.lexer.inlineTokens(_)}}}}codespan(u){let h=this.rules.inline.code.exec(u);if(h){let d=h[2].replace(this.rules.other.newLineCharGlobal," "),p=this.rules.other.nonSpaceChar.test(d),f=this.rules.other.startingSpaceChar.test(d)&&this.rules.other.endingSpaceChar.test(d);return p&&f&&(d=d.substring(1,d.length-1)),{type:"codespan",raw:h[0],text:d}}}br(u){let h=this.rules.inline.br.exec(u);if(h)return{type:"br",raw:h[0]}}del(u){let h=this.rules.inline.del.exec(u);if(h)return{type:"del",raw:h[0],text:h[2],tokens:this.lexer.inlineTokens(h[2])}}autolink(u){let h=this.rules.inline.autolink.exec(u);if(h){let d,p;return h[2]==="@"?(d=h[1],p="mailto:"+d):(d=h[1],p=d),{type:"link",raw:h[0],text:d,href:p,tokens:[{type:"text",raw:d,text:d}]}}}url(u){let h;if(h=this.rules.inline.url.exec(u)){let d,p;if(h[2]==="@")d=h[0],p="mailto:"+d;else{let f;do f=h[0],h[0]=this.rules.inline._backpedal.exec(h[0])?.[0]??"";while(f!==h[0]);d=h[0],h[1]==="www."?p="http://"+h[0]:p=h[0]}return{type:"link",raw:h[0],text:d,href:p,tokens:[{type:"text",raw:d,text:d}]}}}inlineText(u){let h=this.rules.inline.text.exec(u);if(h){let d=this.lexer.state.inRawBlock;return{type:"text",raw:h[0],text:h[0],escaped:d}}}},s(e,"y"),e),qu=(t=class{tokens;options;state;tokenizer;inlineQueue;constructor(u){this.tokens=[],this.tokens.links=Object.create(null),this.options=u||Pp,this.options.tokenizer=this.options.tokenizer||new x4,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let h={other:so,block:b4.normal,inline:Xy.normal};this.options.pedantic?(h.block=b4.pedantic,h.inline=Xy.pedantic):this.options.gfm&&(h.block=b4.gfm,this.options.breaks?h.inline=Xy.breaks:h.inline=Xy.gfm),this.tokenizer.rules=h}static get rules(){return{block:b4,inline:Xy}}static lex(u,h){return new t(h).lex(u)}static lexInline(u,h){return new t(h).inlineTokens(u)}lex(u){u=u.replace(so.carriageReturn,` +`),this.blockTokens(u,this.tokens);for(let h=0;h(p=g.call({lexer:this},u,h))?(u=u.substring(p.raw.length),h.push(p),!0):!1))continue;if(p=this.tokenizer.space(u)){u=u.substring(p.raw.length);let g=h.at(-1);p.raw.length===1&&g!==void 0?g.raw+=` +`:h.push(p);continue}if(p=this.tokenizer.code(u)){u=u.substring(p.raw.length);let g=h.at(-1);g?.type==="paragraph"||g?.type==="text"?(g.raw+=(g.raw.endsWith(` +`)?"":` +`)+p.raw,g.text+=` +`+p.text,this.inlineQueue.at(-1).src=g.text):h.push(p);continue}if(p=this.tokenizer.fences(u)){u=u.substring(p.raw.length),h.push(p);continue}if(p=this.tokenizer.heading(u)){u=u.substring(p.raw.length),h.push(p);continue}if(p=this.tokenizer.hr(u)){u=u.substring(p.raw.length),h.push(p);continue}if(p=this.tokenizer.blockquote(u)){u=u.substring(p.raw.length),h.push(p);continue}if(p=this.tokenizer.list(u)){u=u.substring(p.raw.length),h.push(p);continue}if(p=this.tokenizer.html(u)){u=u.substring(p.raw.length),h.push(p);continue}if(p=this.tokenizer.def(u)){u=u.substring(p.raw.length);let g=h.at(-1);g?.type==="paragraph"||g?.type==="text"?(g.raw+=(g.raw.endsWith(` +`)?"":` +`)+p.raw,g.text+=` +`+p.raw,this.inlineQueue.at(-1).src=g.text):this.tokens.links[p.tag]||(this.tokens.links[p.tag]={href:p.href,title:p.title},h.push(p));continue}if(p=this.tokenizer.table(u)){u=u.substring(p.raw.length),h.push(p);continue}if(p=this.tokenizer.lheading(u)){u=u.substring(p.raw.length),h.push(p);continue}let f=u;if(this.options.extensions?.startBlock){let g=1/0,m=u.slice(1),y;this.options.extensions.startBlock.forEach(k=>{y=k.call({lexer:this},m),typeof y=="number"&&y>=0&&(g=Math.min(g,y))}),g<1/0&&g>=0&&(f=u.substring(0,g+1))}if(this.state.top&&(p=this.tokenizer.paragraph(f))){let g=h.at(-1);d&&g?.type==="paragraph"?(g.raw+=(g.raw.endsWith(` +`)?"":` +`)+p.raw,g.text+=` +`+p.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=g.text):h.push(p),d=f.length!==u.length,u=u.substring(p.raw.length);continue}if(p=this.tokenizer.text(u)){u=u.substring(p.raw.length);let g=h.at(-1);g?.type==="text"?(g.raw+=(g.raw.endsWith(` +`)?"":` +`)+p.raw,g.text+=` +`+p.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=g.text):h.push(p);continue}if(u){let g="Infinite loop on byte: "+u.charCodeAt(0);if(this.options.silent){console.error(g);break}else throw new Error(g)}}return this.state.top=!0,h}inline(u,h=[]){return this.inlineQueue.push({src:u,tokens:h}),h}inlineTokens(u,h=[]){let d=u,p=null;if(this.tokens.links){let m=Object.keys(this.tokens.links);if(m.length>0)for(;(p=this.tokenizer.rules.inline.reflinkSearch.exec(d))!=null;)m.includes(p[0].slice(p[0].lastIndexOf("[")+1,-1))&&(d=d.slice(0,p.index)+"["+"a".repeat(p[0].length-2)+"]"+d.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(p=this.tokenizer.rules.inline.anyPunctuation.exec(d))!=null;)d=d.slice(0,p.index)+"++"+d.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;(p=this.tokenizer.rules.inline.blockSkip.exec(d))!=null;)d=d.slice(0,p.index)+"["+"a".repeat(p[0].length-2)+"]"+d.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);d=this.options.hooks?.emStrongMask?.call({lexer:this},d)??d;let f=!1,g="";for(;u;){f||(g=""),f=!1;let m;if(this.options.extensions?.inline?.some(k=>(m=k.call({lexer:this},u,h))?(u=u.substring(m.raw.length),h.push(m),!0):!1))continue;if(m=this.tokenizer.escape(u)){u=u.substring(m.raw.length),h.push(m);continue}if(m=this.tokenizer.tag(u)){u=u.substring(m.raw.length),h.push(m);continue}if(m=this.tokenizer.link(u)){u=u.substring(m.raw.length),h.push(m);continue}if(m=this.tokenizer.reflink(u,this.tokens.links)){u=u.substring(m.raw.length);let k=h.at(-1);m.type==="text"&&k?.type==="text"?(k.raw+=m.raw,k.text+=m.text):h.push(m);continue}if(m=this.tokenizer.emStrong(u,d,g)){u=u.substring(m.raw.length),h.push(m);continue}if(m=this.tokenizer.codespan(u)){u=u.substring(m.raw.length),h.push(m);continue}if(m=this.tokenizer.br(u)){u=u.substring(m.raw.length),h.push(m);continue}if(m=this.tokenizer.del(u)){u=u.substring(m.raw.length),h.push(m);continue}if(m=this.tokenizer.autolink(u)){u=u.substring(m.raw.length),h.push(m);continue}if(!this.state.inLink&&(m=this.tokenizer.url(u))){u=u.substring(m.raw.length),h.push(m);continue}let y=u;if(this.options.extensions?.startInline){let k=1/0,C=u.slice(1),S;this.options.extensions.startInline.forEach(E=>{S=E.call({lexer:this},C),typeof S=="number"&&S>=0&&(k=Math.min(k,S))}),k<1/0&&k>=0&&(y=u.substring(0,k+1))}if(m=this.tokenizer.inlineText(y)){u=u.substring(m.raw.length),m.raw.slice(-1)!=="_"&&(g=m.raw.slice(-1)),f=!0;let k=h.at(-1);k?.type==="text"?(k.raw+=m.raw,k.text+=m.text):h.push(m);continue}if(u){let k="Infinite loop on byte: "+u.charCodeAt(0);if(this.options.silent){console.error(k);break}else throw new Error(k)}}return h}},s(t,"l"),t),T4=(r=class{options;parser;constructor(u){this.options=u||Pp}space(u){return""}code({text:u,lang:h,escaped:d}){let p=(h||"").match(so.notSpaceStart)?.[0],f=u.replace(so.endingNewline,"")+` +`;return p?'
'+(d?f:Fc(f,!0))+`
+`:"
"+(d?f:Fc(f,!0))+`
+`}blockquote({tokens:u}){return`
+${this.parser.parse(u)}
+`}html({text:u}){return u}def(u){return""}heading({tokens:u,depth:h}){return`${this.parser.parseInline(u)} +`}hr(u){return`
+`}list(u){let h=u.ordered,d=u.start,p="";for(let m=0;m +`+p+" +`}listitem(u){let h="";if(u.task){let d=this.checkbox({checked:!!u.checked});u.loose?u.tokens[0]?.type==="paragraph"?(u.tokens[0].text=d+" "+u.tokens[0].text,u.tokens[0].tokens&&u.tokens[0].tokens.length>0&&u.tokens[0].tokens[0].type==="text"&&(u.tokens[0].tokens[0].text=d+" "+Fc(u.tokens[0].tokens[0].text),u.tokens[0].tokens[0].escaped=!0)):u.tokens.unshift({type:"text",raw:d+" ",text:d+" ",escaped:!0}):h+=d+" "}return h+=this.parser.parse(u.tokens,!!u.loose),`
  • ${h}
  • +`}checkbox({checked:u}){return"'}paragraph({tokens:u}){return`

    ${this.parser.parseInline(u)}

    +`}table(u){let h="",d="";for(let f=0;f${p}`),` + +`+h+` +`+p+`
    +`}tablerow({text:u}){return` +${u} +`}tablecell(u){let h=this.parser.parseInline(u.tokens),d=u.header?"th":"td";return(u.align?`<${d} align="${u.align}">`:`<${d}>`)+h+` +`}strong({tokens:u}){return`${this.parser.parseInline(u)}`}em({tokens:u}){return`${this.parser.parseInline(u)}`}codespan({text:u}){return`${Fc(u,!0)}`}br(u){return"
    "}del({tokens:u}){return`${this.parser.parseInline(u)}`}link({href:u,title:h,tokens:d}){let p=this.parser.parseInline(d),f=HF(u);if(f===null)return p;u=f;let g='
    ",g}image({href:u,title:h,text:d,tokens:p}){p&&(d=this.parser.parseInline(p,this.parser.textRenderer));let f=HF(u);if(f===null)return Fc(d);u=f;let g=`${d}{let m=f[g].flat(1/0);d=d.concat(this.walkTokens(m,h))}):f.tokens&&(d=d.concat(this.walkTokens(f.tokens,h)))}}return d}use(...u){let h=this.defaults.extensions||{renderers:{},childTokens:{}};return u.forEach(d=>{let p={...d};if(p.async=this.defaults.async||p.async||!1,d.extensions&&(d.extensions.forEach(f=>{if(!f.name)throw new Error("extension name required");if("renderer"in f){let g=h.renderers[f.name];g?h.renderers[f.name]=function(...m){let y=f.renderer.apply(this,m);return y===!1&&(y=g.apply(this,m)),y}:h.renderers[f.name]=f.renderer}if("tokenizer"in f){if(!f.level||f.level!=="block"&&f.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let g=h[f.level];g?g.unshift(f.tokenizer):h[f.level]=[f.tokenizer],f.start&&(f.level==="block"?h.startBlock?h.startBlock.push(f.start):h.startBlock=[f.start]:f.level==="inline"&&(h.startInline?h.startInline.push(f.start):h.startInline=[f.start]))}"childTokens"in f&&f.childTokens&&(h.childTokens[f.name]=f.childTokens)}),p.extensions=h),d.renderer){let f=this.defaults.renderer||new T4(this.defaults);for(let g in d.renderer){if(!(g in f))throw new Error(`renderer '${g}' does not exist`);if(["options","parser"].includes(g))continue;let m=g,y=d.renderer[m],k=f[m];f[m]=(...C)=>{let S=y.apply(f,C);return S===!1&&(S=k.apply(f,C)),S||""}}p.renderer=f}if(d.tokenizer){let f=this.defaults.tokenizer||new x4(this.defaults);for(let g in d.tokenizer){if(!(g in f))throw new Error(`tokenizer '${g}' does not exist`);if(["options","rules","lexer"].includes(g))continue;let m=g,y=d.tokenizer[m],k=f[m];f[m]=(...C)=>{let S=y.apply(f,C);return S===!1&&(S=k.apply(f,C)),S}}p.tokenizer=f}if(d.hooks){let f=this.defaults.hooks||new Ky;for(let g in d.hooks){if(!(g in f))throw new Error(`hook '${g}' does not exist`);if(["options","block"].includes(g))continue;let m=g,y=d.hooks[m],k=f[m];Ky.passThroughHooks.has(g)?f[m]=C=>{if(this.defaults.async&&Ky.passThroughHooksRespectAsync.has(g))return Promise.resolve(y.call(f,C)).then(E=>k.call(f,E));let S=y.call(f,C);return k.call(f,S)}:f[m]=(...C)=>{let S=y.apply(f,C);return S===!1&&(S=k.apply(f,C)),S}}p.hooks=f}if(d.walkTokens){let f=this.defaults.walkTokens,g=d.walkTokens;p.walkTokens=function(m){let y=[];return y.push(g.call(this,m)),f&&(y=y.concat(f.call(this,m))),y}}this.defaults={...this.defaults,...p}}),this}setOptions(u){return this.defaults={...this.defaults,...u},this}lexer(u,h){return qu.lex(u,h??this.defaults)}parser(u,h){return Wu.parse(u,h??this.defaults)}parseMarkdown(u){return(h,d)=>{let p={...d},f={...this.defaults,...p},g=this.onError(!!f.silent,!!f.async);if(this.defaults.async===!0&&p.async===!1)return g(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof h>"u"||h===null)return g(new Error("marked(): input parameter is undefined or null"));if(typeof h!="string")return g(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(h)+", string expected"));f.hooks&&(f.hooks.options=f,f.hooks.block=u);let m=f.hooks?f.hooks.provideLexer():u?qu.lex:qu.lexInline,y=f.hooks?f.hooks.provideParser():u?Wu.parse:Wu.parseInline;if(f.async)return Promise.resolve(f.hooks?f.hooks.preprocess(h):h).then(k=>m(k,f)).then(k=>f.hooks?f.hooks.processAllTokens(k):k).then(k=>f.walkTokens?Promise.all(this.walkTokens(k,f.walkTokens)).then(()=>k):k).then(k=>y(k,f)).then(k=>f.hooks?f.hooks.postprocess(k):k).catch(g);try{f.hooks&&(h=f.hooks.preprocess(h));let k=m(h,f);f.hooks&&(k=f.hooks.processAllTokens(k)),f.walkTokens&&this.walkTokens(k,f.walkTokens);let C=y(k,f);return f.hooks&&(C=f.hooks.postprocess(C)),C}catch(k){return g(k)}}}onError(u,h){return d=>{if(d.message+=` +Please report this to https://github.com/markedjs/marked.`,u){let p="

    An error occurred:

    "+Fc(d.message+"",!0)+"
    ";return h?Promise.resolve(p):p}if(h)return Promise.reject(d);throw d}}},s(o,"B"),o),Fp=new uwe,s(Zi,"d"),Zi.options=Zi.setOptions=function(l){return Fp.setOptions(l),Zi.defaults=Fp.defaults,YF(Zi.defaults),Zi},Zi.getDefaults=p_,Zi.defaults=Pp,Zi.use=function(...l){return Fp.use(...l),Zi.defaults=Fp.defaults,YF(Zi.defaults),Zi},Zi.walkTokens=function(l,u){return Fp.walkTokens(l,u)},Zi.parseInline=Fp.parseInline,Zi.Parser=Wu,Zi.parser=Wu.parse,Zi.Renderer=T4,Zi.TextRenderer=k_,Zi.Lexer=qu,Zi.lexer=qu.lex,Zi.Tokenizer=x4,Zi.Hooks=Ky,Zi.parse=Zi,rEt=Zi.options,iEt=Zi.setOptions,nEt=Zi.use,aEt=Zi.walkTokens,sEt=Zi.parseInline,oEt=Wu.parse,lEt=qu.lex});function hwe(e,{markdownAutoWrap:t}){let r=e.replace(//g,` +`).replace(/\n{2,}/g,` +`);return MO(r)}function dwe(e){return e.split(/\\n|\n|/gi).map(t=>t.trim().match(/<[^>]+>|[^\s<>]+/g)?.map(r=>({content:r,type:"normal"}))??[])}function pwe(e,t={}){let r=hwe(e,t),i=Zi.lexer(r),n=[[]],a=0;function o(l,u="normal"){l.type==="text"?l.text.split(` +`).forEach((h,d)=>{d!==0&&(a++,n.push([])),h.split(" ").forEach(p=>{p=p.replace(/'/g,"'"),p&&n[a].push({content:p,type:u})})}):l.type==="strong"||l.type==="em"?l.tokens.forEach(h=>{o(h,l.type)}):l.type==="html"&&n[a].push({content:l.text,type:"normal"})}return s(o,"processNode"),i.forEach(l=>{l.type==="paragraph"?l.tokens?.forEach(u=>{o(u)}):l.type==="html"?n[a].push({content:l.text,type:"normal"}):n[a].push({content:l.raw,type:"normal"})}),n}function fwe(e){return e?`

    ${e.replace(/\\n|\n/g,"
    ")}

    `:""}function gwe(e,{markdownAutoWrap:t}={}){let r=Zi.lexer(e);function i(n){return n.type==="text"?t===!1?n.text.replace(/\n */g,"
    ").replace(/ /g," "):n.text.replace(/\n */g,"
    "):n.type==="strong"?`${n.tokens?.map(i).join("")}`:n.type==="em"?`${n.tokens?.map(i).join("")}`:n.type==="paragraph"?`

    ${n.tokens?.map(i).join("")}

    `:n.type==="space"?"":n.type==="html"?`${n.text}`:n.type==="escape"?n.text:(Ee.warn(`Unsupported markdown: ${n.type}`),n.raw)}return s(i,"output"),r.map(i).join("")}var uEt=G(()=>{"use strict";cEt(),vye(),Nt(),s(hwe,"preprocessMarkdown"),s(dwe,"nonMarkdownToLines"),s(pwe,"markdownToLines"),s(fwe,"nonMarkdownToHTML"),s(gwe,"markdownToHTML")}),hEt=Ro((e,t)=>{"use strict";(function(r){"use strict";var i=function(){},n=r.requestAnimationFrame||r.webkitRequestAnimationFrame||r.mozRequestAnimationFrame||r.msRequestAnimationFrame||function(p){return setTimeout(p,16)};function a(){var p=this;p.reads=[],p.writes=[],p.raf=n.bind(r),i("initialized",p)}s(a,"FastDom"),a.prototype={constructor:a,runTasks:s(function(p){i("run tasks");for(var f;f=p.shift();)f()},"runTasks"),measure:s(function(p,f){i("measure");var g=f?p.bind(f):p;return this.reads.push(g),o(this),g},"measure"),mutate:s(function(p,f){i("mutate");var g=f?p.bind(f):p;return this.writes.push(g),o(this),g},"mutate"),clear:s(function(p){return i("clear",p),u(this.reads,p)||u(this.writes,p)},"clear"),extend:s(function(p){if(i("extend",p),typeof p!="object")throw new Error("expected object");var f=Object.create(this);return h(f,p),f.fastdom=this,f.initialize&&f.initialize(),f},"extend"),catch:null};function o(p){p.scheduled||(p.scheduled=!0,p.raf(l.bind(null,p)),i("flush scheduled"))}s(o,"scheduleFlush");function l(p){i("flush");var f=p.writes,g=p.reads,m;try{i("flushing reads",g.length),p.runTasks(g),i("flushing writes",f.length),p.runTasks(f)}catch(y){m=y}if(p.scheduled=!1,(g.length||f.length)&&o(p),m)if(i("task errored",m.message),p.catch)p.catch(m);else throw m}s(l,"flush");function u(p,f){var g=p.indexOf(f);return!!~g&&!!p.splice(g,1)}s(u,"remove");function h(p,f){for(var g in f)f.hasOwnProperty(g)&&(p[g]=f[g])}s(h,"mixin");var d=r.fastdom=r.fastdom||new a;typeof define=="function"?define(function(){return d}):typeof t=="object"&&(t.exports=d)})(typeof window<"u"?window:typeof e<"u"?e:globalThis)}),dEt=Ro((e,t)=>{"use strict";(function(){var r={initialize:s(function(){this._tasks=new Map},"initialize"),mutate:s(function(n,a){return i(this,"mutate",n,a)},"mutate"),measure:s(function(n,a){return i(this,"measure",n,a)},"measure"),clear:s(function(n){var a=this._tasks,o=a.get(n);this.fastdom.clear(o),a.delete(n)},"clear")};function i(n,a,o,l){var u=n._tasks,h=n.fastdom,d,p=new Promise(function(f,g){d=h[a](function(){u.delete(p);try{f(l?o.call(l):o())}catch(m){g(m)}},l)});return u.set(p,d),p}s(i,"create"),(typeof define)[0]=="f"?define(function(){return r}):(typeof t)[0]=="o"?t.exports=r:window.fastdomPromised=r})()}),mwe,ywe,vwe,zp,lz=G(()=>{"use strict";mwe=$o(hEt(),1),ywe=$o(dEt(),1),vwe=mwe.default.extend({raf(e){typeof queueMicrotask=="function"?queueMicrotask(e):setTimeout(e,0)}}).extend(ywe.default),zp=vwe});function bwe(e){return Intl.Segmenter?[...new Intl.Segmenter().segment(e)].map(t=>t.segment):[...e]}function xwe(e,t){let r=bwe(t.content);return cz(e,[],r,t.type)}function cz(e,t,r,i){if(r.length===0)return[{content:t.join(""),type:i},{content:"",type:i}];let[n,...a]=r,o=[...t,n];return e([{content:o.join(""),type:i}])?cz(e,o,a,i):(t.length===0&&n&&(t.push(n),r.shift()),[{content:t.join(""),type:i},{content:r.join(""),type:i}])}function Twe(e,t){if(e.some(({content:r})=>r.includes(` +`)))throw new Error("splitLineToFitWidth does not support newlines in the line");return C_(e,t)}function C_(e,t,r=[],i=[]){if(e.length===0)return i.length>0&&r.push(i),r.length>0?r:[];let n="";e[0].content===" "&&(n=" ",e.shift());let a=e.shift()??{content:" ",type:"normal"},o=[...i];if(n!==""&&o.push({content:n,type:"normal"}),o.push(a),t(o))return C_(e,t,r,o);if(i.length>0)r.push(i),e.unshift(a);else if(a.content){let[l,u]=xwe(t,a);r.push([l]),u.content&&e.unshift(u)}return C_(e,t,r)}var pEt=G(()=>{"use strict";s(bwe,"splitTextToChars"),s(xwe,"splitWordToFitWidth"),s(cz,"splitWordToFitWidthRecursion"),s(Twe,"splitLineToFitWidth"),s(C_,"splitLineToFitWidthRecursion")});function uz(e,t){t&&e.attr("style",t)}async function kwe(e,t,r,i,n=!1,a=Rr()){let o=e.append("foreignObject");o.attr("width",`${Math.min(10*r,dz)}px`),o.attr("height",`${Math.min(10*r,dz)}px`);let l=o.append("xhtml:div"),u=pa(t.label)?await mT(t.label.replace(Vt.lineBreakRegex,` +`),a):Si(t.label,a),h=t.isNode?"nodeLabel":"edgeLabel",d=l.append("span");return d.html(u),uz(d,t.labelStyle),d.attr("class",`${h} ${i}`),uz(l,t.labelStyle),l.style("display","table-cell"),l.style("white-space","nowrap"),l.style("line-height","1.5"),r!==Number.POSITIVE_INFINITY&&(l.style("max-width",r+"px"),l.style("text-align","center")),l.attr("xmlns","http://www.w3.org/1999/xhtml"),n&&l.attr("class","labelBkg"),(await zp.measure(()=>l.node().getBoundingClientRect())).width===r&&(l.style("display","table"),l.style("white-space","break-spaces"),l.style("width",r+"px")),o.node()}function w_(e,t,r,i=!1){let n=e.append("tspan").attr("class","text-outer-tspan").attr("x",0).attr("y",t*r-.1+"em").attr("dy",r+"em");return i&&n.attr("text-anchor","middle"),n}function Cwe(e,t,r){let i=e.append("text"),n=w_(i,1,t);E_(n,r);let a=n.node().getComputedTextLength();return i.remove(),a}function wwe(e,t,r){let i=e.append("text"),n=w_(i,1,t);E_(n,[{content:r,type:"normal"}]);let a=n.node()?.getBoundingClientRect();return a&&i.remove(),a}function Ewe(e,t,r,i=!1,n=!1){let a=t.append("g"),o=a.insert("rect").attr("class","background").attr("style","stroke: none"),l=a.append("text").attr("y","-10.1");n&&l.attr("text-anchor","middle");let u=0;for(let h of r){let d=s(f=>Cwe(a,1.1,f)<=e,"checkWidth"),p=d(h)?[h]:Twe(h,d);for(let f of p){let g=w_(l,u,1.1,n);E_(g,f),u++}}if(i){let h=l.node().getBBox(),d=2;return o.attr("x",h.x-d).attr("y",h.y-d).attr("width",h.width+2*d).attr("height",h.height+2*d),a.node()}else return l.node()}function hz(e){let t=/&(amp|lt|gt);/g;return e.replace(t,(r,i)=>{switch(i){case"amp":return"&";case"lt":return"<";case"gt":return">";default:return r}})}function E_(e,t){e.text(""),t.forEach((r,i)=>{let n=e.append("tspan").attr("font-style",r.type==="em"?"italic":"normal").attr("class","text-inner-tspan").attr("font-weight",r.type==="strong"?"bold":"normal");i===0?n.text(hz(r.content)):n.text(" "+hz(r.content))})}async function Swe(e,t={}){let r=[];e.replace(/(fa[bklrs]?):fa-([\w-]+)/g,(n,a,o)=>(r.push((async()=>{let l=`${a}:${o}`;return await yye(l)?await Lu(l,void 0,{class:"label-icon"}):``})()),n));let i=await Promise.all(r);return e.replace(/(fa[bklrs]?):fa-([\w-]+)/g,()=>i.shift()??"")}var dz,Io,ec=G(()=>{"use strict";pi(),mi(),Nt(),g4(),uEt(),Ar(),lz(),Iu(),pEt(),Gr(),s(uz,"applyStyle"),dz=16384,s(kwe,"addHtmlSpan"),s(w_,"createTspan"),s(Cwe,"computeWidthOfText"),s(wwe,"computeDimensionOfText"),s(Ewe,"createFormattedText"),s(hz,"decodeHTMLEntities"),s(E_,"updateTextContentAndStyles"),s(Swe,"replaceIconSubstring"),Io=s(async(e,t="",{style:r="",isTitle:i=!1,classes:n="",useHtmlLabels:a=!0,markdown:o=!0,isNode:l=!0,width:u=200,addSvgBackground:h=!1}={},d)=>{if(Ee.debug("XYZ createText",t,r,i,n,a,l,"addSvgBackground: ",h),a){let p=o?gwe(t,d):fwe(t),f=await Swe(ad(p),d),g=t.replace(/\\\\/g,"\\"),m={isNode:l,label:pa(t)?g:f,labelStyle:r.replace("fill:","color:")};return await kwe(e,m,u,n,h,d)}else{let p=ad(t.replace(//g,"
    ")),f=o?pwe(p.replace("
    ","
    "),d):dwe(p),g=Ewe(u,e,f,t?h:!1,!l);if(l){/stroke:/.exec(r)&&(r=r.replace("stroke:","lineColor:"));let m=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/color:/g,"fill:");zt(g).attr("style",m)}else{let m=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/background:/g,"fill:");zt(g).select("rect").attr("style",m.replace(/background:/g,"fill:"));let y=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/color:/g,"fill:");zt(g).select("text").attr("style",y)}return i?zt(g).selectAll("tspan.text-outer-tspan").classed("title-row",!0):zt(g).selectAll("tspan.text-outer-tspan").classed("row",!0),g}},"createText")});async function pz(e){let t=e.getElementsByTagName("img");if(!t||t.length===0)return;let r=!fz(e);await Promise.all([...t].map(i=>new Promise(n=>{function a(){if(i.style.display="flex",i.style.flexDirection="column",r){let o=it().fontSize?it().fontSize:window.getComputedStyle(document.body).fontSize,l=5,[u=vi.fontSize]=zu(o),h=u*l+"px";i.style.minWidth=h,i.style.maxWidth=h}else i.style.width="100%";n(i)}s(a,"setupImage"),setTimeout(()=>{i.complete&&a()}),i.addEventListener("error",a),i.addEventListener("load",a)})))}function fz(e){return e.nodeType===Awe?e.textContent?.trim()!=="":e.nodeType!==_we||e.tagName.toLowerCase()==="img"?!1:[...e.childNodes].some(fz)}var _we,Awe,Rwe=G(()=>{"use strict";Er(),Za(),Ar(),_we=1,Awe=3,s(pz,"configureLabelImages"),s(fz,"hasTextBesidesImages")}),gz,$we,Lwe,fEt=G(()=>{"use strict";pi(),ec(),Er(),mi(),Ar(),gz=3,$we=32,Lwe=s(async(e,t,r)=>{let i=it(),n=e.insert("g").attr("class",r??"node default").attr("id",t.domId||t.id),a=n.insert("g").attr("class","label").attr("style",qa(t.labelStyle)),o=[{text:typeof t.label=="string"?t.label:t.label?.[0]??"",cssClass:"c4-name"},{text:t.stereotype,cssClass:"c4-type"},...(t.description??[]).map(y=>({text:y,cssClass:"c4-descr"}))].filter(y=>y.text),l=t.width?Math.max(t.width-2*(t.padding??0),$we):it().flowchart?.wrappingWidth??200,u=i.c4?.wrap??!0?l:Number.POSITIVE_INFINITY,h=await Promise.all(o.map(async y=>{let k=a.append("g").attr("class",y.cssClass),C=await Io(k,Si(ad(y.text??""),i),{useHtmlLabels:!1,markdown:!1,isNode:!0,width:u,style:t.labelStyle},i);return zt(C).selectAll("tspan.text-outer-tspan").attr("text-anchor","middle"),zt(C).selectAll("tspan.text-inner-tspan").attr("font-weight",null).attr("font-style",null),{el:k,box:k.node().getBBox()}})),d=Math.max(...h.map(({box:y})=>y.width),0),p=0;for(let{el:y,box:k}of h)y.attr("transform",`translate(${d/2-k.x-k.width/2}, ${p-k.y})`),p+=k.height+gz;let f=h.length>0?p-gz:0;a.insert("rect",":first-child"),a.attr("transform",`translate(${-d/2}, ${-f/2})`);let g=a.node().getBBox(),m=(t.padding??0)/2;return{shapeSvg:n,bbox:g,halfPadding:m,label:a}},"c4LabelHelper")});function Ni(e){let t=e.map((r,i)=>`${i===0?"M":"L"}${r.x},${r.y}`);return t.push("Z"),t.join(" ")}function qp(e,t,r,i,n,a){let o=[],l=r-e,u=i-t,h=l/a,d=2*Math.PI/h,p=t+u/2;for(let f=0;f<=50;f++){let g=f/50,m=e+g*l,y=p+n*Math.sin(d*(m-e));o.push({x:m,y})}return o}function ju(e,t,r,i,n,a){let o=[],l=n*Math.PI/180,u=(a*Math.PI/180-l)/(i-1);for(let h=0;hu.tagName==="path"),r=document.createElementNS("http://www.w3.org/2000/svg","path"),i=t.map(u=>u.getAttribute("d")).filter(u=>u!==null).join(" ");r.setAttribute("d",i);let n=t.find(u=>u.getAttribute("fill")!=="none"),a=t.find(u=>u.getAttribute("stroke")!=="none"),o=s((u,h)=>u?.getAttribute(h)??void 0,"getAttr");if(n){let u={fill:o(n,"fill"),"fill-opacity":o(n,"fill-opacity")??"1"};Object.entries(u).forEach(([h,d])=>{d&&r.setAttribute(h,d)})}if(a){let u={stroke:o(a,"stroke"),"stroke-width":o(a,"stroke-width")??"1","stroke-opacity":o(a,"stroke-opacity")??"1"};Object.entries(u).forEach(([h,d])=>{d&&r.setAttribute(h,d)})}let l=document.createElementNS("http://www.w3.org/2000/svg","g");return l.appendChild(r),l}var Nr,S_,fr,Or,lr=G(()=>{"use strict";ec(),lz(),Er(),Gr(),pi(),mi(),Ar(),Rwe(),g4(),fEt(),Nr=s(async(e,t,r)=>{if(t.stereotype!==void 0)return Lwe(e,t,r);let i,n=t.useHtmlLabels||_c(it()?.htmlLabels);r?i=r:i="node default";let a=e.insert("g").attr("class",i).attr("id",t.domId||t.id),o=a.insert("g").attr("class","label").attr("style",qa(t.labelStyle)),l;t.label===void 0?l="":l=typeof t.label=="string"?t.label:t.label[0];let u=!!t.icon||!!t.img,h=t.labelType==="markdown",d=await Io(o,Si(ad(l),it()),{useHtmlLabels:n,width:t.width||t.wrappingWidth||it().flowchart?.wrappingWidth,classes:h?"markdown-node-label":"",style:t.labelStyle,addSvgBackground:u,markdown:h},it()),p=(t?.padding??0)/2,f;if(n){let g=d.children[0],m=zt(d);await pz(g),f=await zp.measure(()=>g.getBoundingClientRect()),m.attr("width",f.width),m.attr("height",f.height)}else f=await zp.measure(()=>d.getBBox());return n?o.attr("transform","translate("+-f.width/2+", "+-f.height/2+")"):o.attr("transform","translate(0, "+-f.height/2+")"),t.centerLabel&&o.attr("transform","translate("+-f.width/2+", "+-f.height/2+")"),o.insert("rect",":first-child"),{shapeSvg:a,bbox:f,halfPadding:p,label:o}},"labelHelper"),S_=s(async(e,t,r)=>{let i=r.useHtmlLabels??Ps(it()),n=e.insert("g").attr("class","label").attr("style",r.labelStyle||""),a=await Io(n,Si(ad(t),it()),{useHtmlLabels:i,width:r.width||it()?.flowchart?.wrappingWidth,style:r.labelStyle,addSvgBackground:!!r.icon||!!r.img}),o=r.padding/2,l;if(Ps(it())){let u=a.children[0],h=zt(a);l=await zp.measure(()=>u.getBoundingClientRect()),h.attr("width",l.width),h.attr("height",l.height)}else l=await zp.measure(()=>a.getBBox());return i?n.attr("transform","translate("+-l.width/2+", "+-l.height/2+")"):n.attr("transform","translate(0, "+-l.height/2+")"),r.centerLabel&&n.attr("transform","translate("+-l.width/2+", "+-l.height/2+")"),n.insert("rect",":first-child"),{shapeSvg:e,bbox:l,halfPadding:o,label:n}},"insertLabel"),fr=s((e,t,r)=>{if(r){e.width=r.width,e.height=r.height;return}let i=t.node().getBBox();e.width=i.width,e.height=i.height},"updateNodeBounds"),Or=s((e,t)=>(e.look==="handDrawn"?"rough-node":"node")+" "+e.cssClasses+" "+(t||""),"getNodeClasses"),s(Ni,"createPathFromPoints"),s(qp,"generateFullSineWavePoints"),s(ju,"generateCirclePoints"),s(mz,"mergePaths")});function Iwe(e,t){return e.intersect(t)}var Mwe,gEt=G(()=>{"use strict";s(Iwe,"intersectNode"),Mwe=Iwe});function Dwe(e,t,r,i){var n=e.x,a=e.y,o=n-i.x,l=a-i.y,u=Math.sqrt(t*t*l*l+r*r*o*o),h=Math.abs(t*r*o/u);i.x{"use strict";s(Dwe,"intersectEllipse"),yz=Dwe});function Owe(e,t,r){return yz(e,t,t,r)}var Bwe,mEt=G(()=>{"use strict";Nwe(),s(Owe,"intersectCircle"),Bwe=Owe});function Pwe(e,t,r,i){{let n=t.y-e.y,a=e.x-t.x,o=t.x*e.y-e.x*t.y,l=n*r.x+a*r.y+o,u=n*i.x+a*i.y+o,h=1e-6;if(l!==0&&u!==0&&vz(l,u))return;let d=i.y-r.y,p=r.x-i.x,f=i.x*r.y-r.x*i.y,g=d*e.x+p*e.y+f,m=d*t.x+p*t.y+f;if(Math.abs(g)0}var Fwe,yEt=G(()=>{"use strict";s(Pwe,"intersectLine"),s(vz,"sameSign"),Fwe=Pwe});function zwe(e,t,r){let i=e.x,n=e.y,a=[],o=Number.POSITIVE_INFINITY,l=Number.POSITIVE_INFINITY;typeof t.forEach=="function"?t.forEach(function(d){o=Math.min(o,d.x),l=Math.min(l,d.y)}):(o=Math.min(o,t.x),l=Math.min(l,t.y));let u=i-e.width/2-o,h=n-e.height/2-l;for(let d=0;d1&&a.sort(function(d,p){let f=d.x-r.x,g=d.y-r.y,m=Math.sqrt(f*f+g*g),y=p.x-r.x,k=p.y-r.y,C=Math.sqrt(y*y+k*k);return m{"use strict";yEt(),s(zwe,"intersectPolygon"),qwe=zwe}),Wwe,Wp,bz=G(()=>{"use strict";Wwe=s((e,t)=>{var r=e.x,i=e.y,n=t.x-r,a=t.y-i,o=e.width/2,l=e.height/2,u,h;return Math.abs(a)*o>Math.abs(n)*l?(a<0&&(l=-l),u=a===0?0:l*n/a,h=l):(n<0&&(o=-o),u=o,h=n===0?0:o*a/n),{x:r+u,y:i+h}},"intersectRect"),Wp=Wwe}),nr,Tr=G(()=>{"use strict";gEt(),mEt(),Nwe(),vEt(),bz(),nr={node:Mwe,circle:Bwe,ellipse:yz,polygon:qwe,rect:Wp}}),jwe,Vwe,mg,Uwe,__,cr,pr,Ywe,gr=G(()=>{"use strict";Er(),jwe=s(e=>{let{handDrawnSeed:t}=it();return{fill:e,hachureAngle:120,hachureGap:4,fillWeight:2,roughness:.7,stroke:e,seed:t}},"solidStateFill"),Vwe=s(e=>Array.isArray(e)?e:e?e.split(";").map(t=>t.trim()).filter(Boolean):[],"normalizeStyleList"),mg=s(e=>{let t=Uwe([...e.cssCompiledStyles||[],...e.cssStyles||[],...Vwe(e.labelStyle)]);return{stylesMap:t,stylesArray:[...t]}},"compileStyles"),Uwe=s(e=>{let t=new Map;return e.forEach(r=>{let[i,n]=r.split(":");t.set(i.trim(),n?.trim())}),t},"styles2Map"),__=s(e=>e==="color"||e==="font-size"||e==="font-family"||e==="font-weight"||e==="font-style"||e==="text-decoration"||e==="text-align"||e==="text-transform"||e==="line-height"||e==="letter-spacing"||e==="word-spacing"||e==="text-shadow"||e==="text-overflow"||e==="white-space"||e==="word-wrap"||e==="word-break"||e==="overflow-wrap"||e==="hyphens","isLabelStyle"),cr=s(e=>{let{stylesArray:t}=mg(e),r=[],i=[],n=[],a=[];return t.forEach(o=>{let l=o[0];__(l)?r.push(o.join(":")+" !important"):(i.push(o.join(":")+" !important"),l.includes("stroke")&&n.push(o.join(":")+" !important"),l==="fill"&&a.push(o.join(":")+" !important"))}),{labelStyles:r.join(";"),nodeStyles:i.join(";"),stylesArray:t,borderStyles:n,backgroundStyles:a}},"styles2String"),pr=s((e,t)=>{let{themeVariables:r,handDrawnSeed:i}=it(),{nodeBorder:n,mainBkg:a}=r,{stylesMap:o}=mg(e);return Object.assign({roughness:.7,fill:o.get("fill")||a,fillStyle:"hachure",fillWeight:4,hachureGap:5.2,stroke:o.get("stroke")||n,seed:i,strokeWidth:o.get("stroke-width")?.replace("px","")||1.3,fillLineDash:[0,0],strokeLineDash:Ywe(o.get("stroke-dasharray"))},t)},"userNodeOverrides"),Ywe=s(e=>{if(!e)return[0,0];let t=e.trim().split(/\s+/).map(Number);if(t.length===1){let n=isNaN(t[0])?0:t[0];return[n,n]}let r=isNaN(t[0])?0:t[0],i=isNaN(t[1])?0:t[1];return[r,i]},"getStrokeDashArray")});function A_(e,t,r){if(e&&e.length){let[i,n]=t,a=Math.PI/180*r,o=Math.cos(a),l=Math.sin(a);for(let u of e){let[h,d]=u;u[0]=(h-i)*o-(d-n)*l+i,u[1]=(h-i)*l+(d-n)*o+n}}}function Hwe(e,t){return e[0]===t[0]&&e[1]===t[1]}function Gwe(e,t,r,i=1){let n=r,a=Math.max(t,.1),o=e[0]&&e[0][0]&&typeof e[0][0]=="number"?[e]:e,l=[0,0];if(n)for(let h of o)A_(h,l,n);let u=(function(h,d,p){let f=[];for(let S of h){let E=[...S];Hwe(E[0],E[E.length-1])||E.push([E[0][0],E[0][1]]),E.length>2&&f.push(E)}let g=[];d=Math.max(d,.1);let m=[];for(let S of f)for(let E=0;ES.yminE.ymin?1:S.xE.x?1:S.ymax===E.ymax?0:(S.ymax-E.ymax)/Math.abs(S.ymax-E.ymax))),!m.length)return g;let y=[],k=m[0].ymin,C=0;for(;y.length||m.length;){if(m.length){let S=-1;for(let E=0;Ek);E++)S=E;m.splice(0,S+1).forEach((E=>{y.push({s:k,edge:E})}))}if(y=y.filter((S=>!(S.edge.ymax<=k))),y.sort(((S,E)=>S.edge.x===E.edge.x?0:(S.edge.x-E.edge.x)/Math.abs(S.edge.x-E.edge.x))),(p!==1||C%d==0)&&y.length>1)for(let S=0;S=y.length)break;let _=y[S].edge,b=y[E].edge;g.push([[Math.round(_.x),k],[Math.round(b.x),k]])}k+=p,y.forEach((S=>{S.edge.x=S.edge.x+p*S.edge.islope})),C++}return g})(o,a,i);if(n){for(let h of o)A_(h,l,-n);(function(h,d,p){let f=[];h.forEach((g=>f.push(...g))),A_(f,d,p)})(u,l,-n)}return u}function Qy(e,t){var r;let i=t.hachureAngle+90,n=t.hachureGap;n<0&&(n=4*t.strokeWidth),n=Math.round(Math.max(n,.1));let a=1;return t.roughness>=1&&(((r=t.randomizer)===null||r===void 0?void 0:r.next())||Math.random())>.7&&(a=n),Gwe(e,n,i,a||1)}function k4(e){let t=e[0],r=e[1];return Math.sqrt(Math.pow(t[0]-r[0],2)+Math.pow(t[1]-r[1],2))}function R_(e,t){return e.type===t}function $_(e){let t=[],r=(function(o){let l=new Array;for(;o!=="";)if(o.match(/^([ \t\r\n,]+)/))o=o.substr(RegExp.$1.length);else if(o.match(/^([aAcChHlLmMqQsStTvVzZ])/))l[l.length]={type:a5e,text:RegExp.$1},o=o.substr(RegExp.$1.length);else{if(!o.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))return[];l[l.length]={type:B_,text:`${parseFloat(RegExp.$1)}`},o=o.substr(RegExp.$1.length)}return l[l.length]={type:Mz,text:""},l})(e),i="BOD",n=0,a=r[n];for(;!R_(a,Mz);){let o=0,l=[];if(i==="BOD"){if(a.text!=="M"&&a.text!=="m")return $_("M0,0"+e);n++,o=R4[a.text],i=a.text}else R_(a,B_)?o=R4[i]:(n++,o=R4[a.text],i=a.text);if(!(n+od%2?h+r:h+t));a.push({key:"C",data:u}),t=u[4],r=u[5];break}case"Q":a.push({key:"Q",data:[...l]}),t=l[2],r=l[3];break;case"q":{let u=l.map(((h,d)=>d%2?h+r:h+t));a.push({key:"Q",data:u}),t=u[2],r=u[3];break}case"A":a.push({key:"A",data:[...l]}),t=l[5],r=l[6];break;case"a":t+=l[5],r+=l[6],a.push({key:"A",data:[l[0],l[1],l[2],l[3],l[4],t,r]});break;case"H":a.push({key:"H",data:[...l]}),t=l[0];break;case"h":t+=l[0],a.push({key:"H",data:[t]});break;case"V":a.push({key:"V",data:[...l]}),r=l[0];break;case"v":r+=l[0],a.push({key:"V",data:[r]});break;case"S":a.push({key:"S",data:[...l]}),t=l[2],r=l[3];break;case"s":{let u=l.map(((h,d)=>d%2?h+r:h+t));a.push({key:"S",data:u}),t=u[2],r=u[3];break}case"T":a.push({key:"T",data:[...l]}),t=l[0],r=l[1];break;case"t":t+=l[0],r+=l[1],a.push({key:"T",data:[t,r]});break;case"Z":case"z":a.push({key:"Z",data:[]}),t=i,r=n}return a}function Tz(e){let t=[],r="",i=0,n=0,a=0,o=0,l=0,u=0;for(let{key:h,data:d}of e){switch(h){case"M":t.push({key:"M",data:[...d]}),[i,n]=d,[a,o]=d;break;case"C":t.push({key:"C",data:[...d]}),i=d[4],n=d[5],l=d[2],u=d[3];break;case"L":t.push({key:"L",data:[...d]}),[i,n]=d;break;case"H":i=d[0],t.push({key:"L",data:[i,n]});break;case"V":n=d[0],t.push({key:"L",data:[i,n]});break;case"S":{let p=0,f=0;r==="C"||r==="S"?(p=i+(i-l),f=n+(n-u)):(p=i,f=n),t.push({key:"C",data:[p,f,...d]}),l=d[0],u=d[1],i=d[2],n=d[3];break}case"T":{let[p,f]=d,g=0,m=0;r==="Q"||r==="T"?(g=i+(i-l),m=n+(n-u)):(g=i,m=n);let y=i+2*(g-i)/3,k=n+2*(m-n)/3,C=p+2*(g-p)/3,S=f+2*(m-f)/3;t.push({key:"C",data:[y,k,C,S,p,f]}),l=g,u=m,i=p,n=f;break}case"Q":{let[p,f,g,m]=d,y=i+2*(p-i)/3,k=n+2*(f-n)/3,C=g+2*(p-g)/3,S=m+2*(f-m)/3;t.push({key:"C",data:[y,k,C,S,g,m]}),l=p,u=f,i=g,n=m;break}case"A":{let p=Math.abs(d[0]),f=Math.abs(d[1]),g=d[2],m=d[3],y=d[4],k=d[5],C=d[6];p===0||f===0?(t.push({key:"C",data:[i,n,k,C,k,C]}),i=k,n=C):(i!==k||n!==C)&&(kz(i,n,k,C,p,f,g,m,y).forEach((function(S){t.push({key:"C",data:S})})),i=k,n=C);break}case"Z":t.push({key:"Z",data:[]}),i=a,n=o}r=h}return t}function Zy(e,t,r){return[e*Math.cos(r)-t*Math.sin(r),e*Math.sin(r)+t*Math.cos(r)]}function kz(e,t,r,i,n,a,o,l,u,h){let d=(p=o,Math.PI*p/180);var p;let f=[],g=0,m=0,y=0,k=0;if(h)[g,m,y,k]=h;else{[e,t]=Zy(e,t,-d),[r,i]=Zy(r,i,-d);let j=(e-r)/2,I=(t-i)/2,z=j*j/(n*n)+I*I/(a*a);z>1&&(z=Math.sqrt(z),n*=z,a*=z);let F=n*n,O=a*a,B=F*O-F*I*I-O*j*j,Y=F*I*I+O*j*j,U=(l===u?-1:1)*Math.sqrt(Math.abs(B/Y));y=U*n*I/a+(e+r)/2,k=U*-a*j/n+(t+i)/2,g=Math.asin(parseFloat(((t-k)/a).toFixed(9))),m=Math.asin(parseFloat(((i-k)/a).toFixed(9))),em&&(g-=2*Math.PI),!u&&m>g&&(m-=2*Math.PI)}let C=m-g;if(Math.abs(C)>120*Math.PI/180){let j=m,I=r,z=i;m=u&&m>g?g+120*Math.PI/180*1:g+120*Math.PI/180*-1,f=kz(r=y+n*Math.cos(m),i=k+a*Math.sin(m),I,z,n,a,o,0,u,[m,j,y,k])}C=m-g;let S=Math.cos(g),E=Math.sin(g),_=Math.cos(m),b=Math.sin(m),v=Math.tan(C/4),x=4/3*n*v,w=4/3*a*v,R=[e,t],$=[e+x*E,t-w*S],M=[r+x*b,i-w*_],N=[r,i];if($[0]=2*R[0]-$[0],$[1]=2*R[1]-$[1],h)return[$,M,N].concat(f);{f=[$,M,N].concat(f);let j=[];for(let I=0;I2){let n=[];for(let a=0;a2*Math.PI&&(g=0,m=2*Math.PI);let y=2*Math.PI/u.curveStepCount,k=Math.min(y/2,(m-g)/2),C=Lz(k,h,d,p,f,g,m,1,u);if(!u.disableMultiStroke){let S=Lz(k,h,d,p,f,g,m,1.5,u);C.push(...S)}return o&&(l?C.push(...sd(h,d,h+p*Math.cos(g),d+f*Math.sin(g),u),...sd(h,d,h+p*Math.cos(m),d+f*Math.sin(m),u)):C.push({op:"lineTo",data:[h,d]},{op:"lineTo",data:[h+p*Math.cos(g),d+f*Math.sin(g)]})),{type:"path",ops:C}}function _z(e,t){let r=Tz(xz($_(e))),i=[],n=[0,0],a=[0,0];for(let{key:o,data:l}of r)switch(o){case"M":a=[l[0],l[1]],n=[l[0],l[1]];break;case"L":i.push(...sd(a[0],a[1],l[0],l[1],t)),a=[l[0],l[1]];break;case"C":{let[u,h,d,p,f,g]=l;i.push(...Kwe(u,h,d,p,f,g,a,t)),a=[f,g];break}case"Z":i.push(...sd(a[0],a[1],n[0],n[1],t)),a=[n[0],n[1]]}return{type:"path",ops:i}}function I_(e,t){let r=[];for(let i of e)if(i.length){let n=t.maxRandomnessOffset||0,a=i.length;if(a>2){r.push({op:"move",data:[i[0][0]+Xr(n,t),i[0][1]+Xr(n,t)]});for(let o=1;o500?.4:-.0016668*u+1.233334;let d=n.maxRandomnessOffset||0;d*d*100>l&&(d=u/10);let p=d/2,f=.2+.2*Rz(n),g=n.bowing*n.maxRandomnessOffset*(i-t)/200,m=n.bowing*n.maxRandomnessOffset*(e-r)/200;g=Xr(g,n,h),m=Xr(m,n,h);let y=[],k=s(()=>Xr(p,n,h),"M"),C=s(()=>Xr(d,n,h),"k"),S=n.preserveVertices;return a&&(o?y.push({op:"move",data:[e+(S?0:k()),t+(S?0:k())]}):y.push({op:"move",data:[e+(S?0:Xr(d,n,h)),t+(S?0:Xr(d,n,h))]})),o?y.push({op:"bcurveTo",data:[g+e+(r-e)*f+k(),m+t+(i-t)*f+k(),g+e+2*(r-e)*f+k(),m+t+2*(i-t)*f+k(),r+(S?0:k()),i+(S?0:k())]}):y.push({op:"bcurveTo",data:[g+e+(r-e)*f+C(),m+t+(i-t)*f+C(),g+e+2*(r-e)*f+C(),m+t+2*(i-t)*f+C(),r+(S?0:C()),i+(S?0:C())]}),y}function E4(e,t,r){if(!e.length)return[];let i=[];i.push([e[0][0]+Xr(t,r),e[0][1]+Xr(t,r)]),i.push([e[0][0]+Xr(t,r),e[0][1]+Xr(t,r)]);for(let n=1;n3){let a=[],o=1-r.curveTightness;n.push({op:"move",data:[e[1][0],e[1][1]]});for(let l=1;l+21&&n.push(l)):n.push(l),n.push(e[t+3])}else{let l=e[t+0],u=e[t+1],h=e[t+2],d=e[t+3],p=jp(l,u,.5),f=jp(u,h,.5),g=jp(h,d,.5),m=jp(p,f,.5),y=jp(f,g,.5),k=jp(m,y,.5);D_([l,p,m,k],0,r,n),D_([k,y,g,d],0,r,n)}var a,o;return n}function Zwe(e,t){return A4(e,0,e.length,t)}function A4(e,t,r,i,n){let a=n||[],o=e[t],l=e[r-1],u=0,h=1;for(let d=t+1;du&&(u=p,h=d)}return Math.sqrt(u)>i?(A4(e,t,h+1,i,a),A4(e,h,r,i,a)):(a.length||a.push(o),a.push(l)),a}function N_(e,t=.15,r){let i=[],n=(e.length-1)/3;for(let a=0;a0?A4(i,0,i.length,r):i}var O_,Jwe,e5e,t5e,r5e,i5e,Mo,n5e,a5e,B_,Mz,R4,s5e,sl,$4,o5e,L4,l5e,ur,vr=G(()=>{"use strict";var e,t,r,i,n,a,o,l,u,h;s(A_,"t"),s(Hwe,"e"),s(Gwe,"s"),s(Qy,"n"),O_=(e=class{constructor(p){this.helper=p}fillPolygons(p,f){return this._fillPolygons(p,f)}_fillPolygons(p,f){let g=Qy(p,f);return{type:"fillSketch",ops:this.renderLines(g,f)}}renderLines(p,f){let g=[];for(let m of p)g.push(...this.helper.doubleLineOps(m[0][0],m[0][1],m[1][0],m[1][1],f));return g}},s(e,"o"),e),s(k4,"a"),Jwe=(t=class extends O_{fillPolygons(p,f){let g=f.hachureGap;g<0&&(g=4*f.strokeWidth),g=Math.max(g,.1);let m=Qy(p,Object.assign({},f,{hachureGap:g})),y=Math.PI/180*f.hachureAngle,k=[],C=.5*g*Math.cos(y),S=.5*g*Math.sin(y);for(let[E,_]of m)k4([E,_])&&k.push([[E[0]-C,E[1]+S],[..._]],[[E[0]+C,E[1]-S],[..._]]);return{type:"fillSketch",ops:this.renderLines(k,f)}}},s(t,"h"),t),e5e=(r=class extends O_{fillPolygons(p,f){let g=this._fillPolygons(p,f),m=Object.assign({},f,{hachureAngle:f.hachureAngle+90}),y=this._fillPolygons(p,m);return g.ops=g.ops.concat(y.ops),g}},s(r,"r"),r),t5e=(i=class{constructor(p){this.helper=p}fillPolygons(p,f){let g=Qy(p,f=Object.assign({},f,{hachureAngle:0}));return this.dotsOnLines(g,f)}dotsOnLines(p,f){let g=[],m=f.hachureGap;m<0&&(m=4*f.strokeWidth),m=Math.max(m,.1);let y=f.fillWeight;y<0&&(y=f.strokeWidth/2);let k=m/4;for(let C of p){let S=k4(C),E=S/m,_=Math.ceil(E)-1,b=S-_*m,v=(C[0][0]+C[1][0])/2-m/4,x=Math.min(C[0][1],C[1][1]);for(let w=0;w<_;w++){let R=x+b+w*m,$=v-k+2*Math.random()*k,M=R-k+2*Math.random()*k,N=this.helper.ellipse($,M,y,y,f);g.push(...N.ops)}}return{type:"fillSketch",ops:g}}},s(i,"i"),i),r5e=(n=class{constructor(p){this.helper=p}fillPolygons(p,f){let g=Qy(p,f);return{type:"fillSketch",ops:this.dashedLine(g,f)}}dashedLine(p,f){let g=f.dashOffset<0?f.hachureGap<0?4*f.strokeWidth:f.hachureGap:f.dashOffset,m=f.dashGap<0?f.hachureGap<0?4*f.strokeWidth:f.hachureGap:f.dashGap,y=[];return p.forEach((k=>{let C=k4(k),S=Math.floor(C/(g+m)),E=(C+m-S*(g+m))/2,_=k[0],b=k[1];_[0]>b[0]&&(_=k[1],b=k[0]);let v=Math.atan((b[1]-_[1])/(b[0]-_[0]));for(let x=0;x{let k=k4(y),C=Math.round(k/(2*f)),S=y[0],E=y[1];S[0]>E[0]&&(S=y[1],E=y[0]);let _=Math.atan((E[1]-S[1])/(E[0]-S[0]));for(let b=0;b2*Math.PI&&(O=0,B=2*Math.PI);let Y=(B-O)/N.curveStepCount,U=[];for(let H=O;H<=B;H+=Y)U.push([j+z*Math.cos(H),I+F*Math.sin(H)]);return U.push([j+z*Math.cos(B),I+F*Math.sin(B)]),U.push([j,I]),yg([U],N)})(p,f,g,m,y,k,E));return E.stroke!==sl&&_.push(b),this._d("arc",_,E)}curve(p,f){let g=this._o(f),m=[],y=wz(p,g);if(g.fill&&g.fill!==sl)if(g.fillStyle==="solid"){let k=wz(p,Object.assign(Object.assign({},g),{disableMultiStroke:!0,roughness:g.roughness?g.roughness+g.fillShapeRoughnessGain:0}));m.push({type:"fillPath",ops:this._mergedShape(k.ops)})}else{let k=[],C=p;if(C.length){let S=typeof C[0][0]=="number"?[C]:C;for(let E of S)E.length<3?k.push(...E):E.length===3?k.push(...N_(Iz([E[0],E[0],E[1],E[2]]),10,(1+g.roughness)/2)):k.push(...N_(Iz(E),10,(1+g.roughness)/2))}k.length&&m.push(yg([k],g))}return g.stroke!==sl&&m.push(y),this._d("curve",m,g)}polygon(p,f){let g=this._o(f),m=[],y=C4(p,!0,g);return g.fill&&(g.fillStyle==="solid"?m.push(I_([p],g)):m.push(yg([p],g))),g.stroke!==sl&&m.push(y),this._d("polygon",m,g)}path(p,f){let g=this._o(f),m=[];if(!p)return this._d("path",m,g);p=(p||"").replace(/\n/g," ").replace(/(-\s)/g,"-").replace("/(ss)/g"," ");let y=g.fill&&g.fill!=="transparent"&&g.fill!==sl,k=g.stroke!==sl,C=!!(g.simplification&&g.simplification<1),S=(function(_,b,v){let x=Tz(xz($_(_))),w=[],R=[],$=[0,0],M=[],N=s(()=>{M.length>=4&&R.push(...N_(M,b)),M=[]},"i"),j=s(()=>{N(),R.length&&(w.push(R),R=[])},"c");for(let{key:z,data:F}of x)switch(z){case"M":j(),$=[F[0],F[1]],R.push($);break;case"L":N(),R.push([F[0],F[1]]);break;case"C":if(!M.length){let O=R.length?R[R.length-1]:$;M.push([O[0],O[1]])}M.push([F[0],F[1]]),M.push([F[2],F[3]]),M.push([F[4],F[5]]);break;case"Z":N(),R.push([$[0],$[1]])}if(j(),!v)return w;let I=[];for(let z of w){let F=Zwe(z,v);F.length&&I.push(F)}return I})(p,1,C?4-4*(g.simplification||1):(1+g.roughness)/2),E=_z(p,g);if(y)if(g.fillStyle==="solid")if(S.length===1){let _=_z(p,Object.assign(Object.assign({},g),{disableMultiStroke:!0,roughness:g.roughness?g.roughness+g.fillShapeRoughnessGain:0}));m.push({type:"fillPath",ops:this._mergedShape(_.ops)})}else m.push(I_(S,g));else m.push(yg(S,g));return k&&(C?S.forEach((_=>{m.push(C4(_,!1,g))})):m.push(E)),this._d("path",m,g)}opsToPath(p,f){let g="";for(let m of p.ops){let y=typeof f=="number"&&f>=0?m.data.map((k=>+k.toFixed(f))):m.data;switch(m.op){case"move":g+=`M${y[0]} ${y[1]} `;break;case"bcurveTo":g+=`C${y[0]} ${y[1]}, ${y[2]} ${y[3]}, ${y[4]} ${y[5]} `;break;case"lineTo":g+=`L${y[0]} ${y[1]} `}}return g.trim()}toPaths(p){let f=p.sets||[],g=p.options||this.defaultOptions,m=[];for(let y of f){let k=null;switch(y.type){case"path":k={d:this.opsToPath(y),stroke:g.stroke,strokeWidth:g.strokeWidth,fill:sl};break;case"fillPath":k={d:this.opsToPath(y),stroke:sl,strokeWidth:0,fill:g.fill||sl};break;case"fillSketch":k=this.fillSketch(y,g)}k&&m.push(k)}return m}fillSketch(p,f){let g=f.fillWeight;return g<0&&(g=f.strokeWidth/2),{d:this.opsToPath(p),stroke:f.fill||sl,strokeWidth:g,fill:sl}}_mergedShape(p){return p.filter(((f,g)=>g===0||f.op!=="move"))}},s(l,"et"),l),o5e=(u=class{constructor(p,f){this.canvas=p,this.ctx=this.canvas.getContext("2d"),this.gen=new $4(f)}draw(p){let f=p.sets||[],g=p.options||this.getDefaultOptions(),m=this.ctx,y=p.options.fixedDecimalPlaceDigits;for(let k of f)switch(k.type){case"path":m.save(),m.strokeStyle=g.stroke==="none"?"transparent":g.stroke,m.lineWidth=g.strokeWidth,g.strokeLineDash&&m.setLineDash(g.strokeLineDash),g.strokeLineDashOffset&&(m.lineDashOffset=g.strokeLineDashOffset),this._drawToContext(m,k,y),m.restore();break;case"fillPath":{m.save(),m.fillStyle=g.fill||"";let C=p.shape==="curve"||p.shape==="polygon"||p.shape==="path"?"evenodd":"nonzero";this._drawToContext(m,k,y,C),m.restore();break}case"fillSketch":this.fillSketch(m,k,g)}}fillSketch(p,f,g){let m=g.fillWeight;m<0&&(m=g.strokeWidth/2),p.save(),g.fillLineDash&&p.setLineDash(g.fillLineDash),g.fillLineDashOffset&&(p.lineDashOffset=g.fillLineDashOffset),p.strokeStyle=g.fill||"",p.lineWidth=m,this._drawToContext(p,f,g.fixedDecimalPlaceDigits),p.restore()}_drawToContext(p,f,g,m="nonzero"){p.beginPath();for(let y of f.ops){let k=typeof g=="number"&&g>=0?y.data.map((C=>+C.toFixed(g))):y.data;switch(y.op){case"move":p.moveTo(k[0],k[1]);break;case"bcurveTo":p.bezierCurveTo(k[0],k[1],k[2],k[3],k[4],k[5]);break;case"lineTo":p.lineTo(k[0],k[1])}}f.type==="fillPath"?p.fill(m):p.stroke()}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}line(p,f,g,m,y){let k=this.gen.line(p,f,g,m,y);return this.draw(k),k}rectangle(p,f,g,m,y){let k=this.gen.rectangle(p,f,g,m,y);return this.draw(k),k}ellipse(p,f,g,m,y){let k=this.gen.ellipse(p,f,g,m,y);return this.draw(k),k}circle(p,f,g,m){let y=this.gen.circle(p,f,g,m);return this.draw(y),y}linearPath(p,f){let g=this.gen.linearPath(p,f);return this.draw(g),g}polygon(p,f){let g=this.gen.polygon(p,f);return this.draw(g),g}arc(p,f,g,m,y,k,C=!1,S){let E=this.gen.arc(p,f,g,m,y,k,C,S);return this.draw(E),E}curve(p,f){let g=this.gen.curve(p,f);return this.draw(g),g}path(p,f){let g=this.gen.path(p,f);return this.draw(g),g}},s(u,"st"),u),L4="http://www.w3.org/2000/svg",l5e=(h=class{constructor(p,f){this.svg=p,this.gen=new $4(f)}draw(p){let f=p.sets||[],g=p.options||this.getDefaultOptions(),m=this.svg.ownerDocument||window.document,y=m.createElementNS(L4,"g"),k=p.options.fixedDecimalPlaceDigits;for(let C of f){let S=null;switch(C.type){case"path":S=m.createElementNS(L4,"path"),S.setAttribute("d",this.opsToPath(C,k)),S.setAttribute("stroke",g.stroke),S.setAttribute("stroke-width",g.strokeWidth+""),S.setAttribute("fill","none"),g.strokeLineDash&&S.setAttribute("stroke-dasharray",g.strokeLineDash.join(" ").trim()),g.strokeLineDashOffset&&S.setAttribute("stroke-dashoffset",`${g.strokeLineDashOffset}`);break;case"fillPath":S=m.createElementNS(L4,"path"),S.setAttribute("d",this.opsToPath(C,k)),S.setAttribute("stroke","none"),S.setAttribute("stroke-width","0"),S.setAttribute("fill",g.fill||""),p.shape!=="curve"&&p.shape!=="polygon"||S.setAttribute("fill-rule","evenodd");break;case"fillSketch":S=this.fillSketch(m,C,g)}S&&y.appendChild(S)}return y}fillSketch(p,f,g){let m=g.fillWeight;m<0&&(m=g.strokeWidth/2);let y=p.createElementNS(L4,"path");return y.setAttribute("d",this.opsToPath(f,g.fixedDecimalPlaceDigits)),y.setAttribute("stroke",g.fill||""),y.setAttribute("stroke-width",m+""),y.setAttribute("fill","none"),g.fillLineDash&&y.setAttribute("stroke-dasharray",g.fillLineDash.join(" ").trim()),g.fillLineDashOffset&&y.setAttribute("stroke-dashoffset",`${g.fillLineDashOffset}`),y}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}opsToPath(p,f){return this.gen.opsToPath(p,f)}line(p,f,g,m,y){let k=this.gen.line(p,f,g,m,y);return this.draw(k)}rectangle(p,f,g,m,y){let k=this.gen.rectangle(p,f,g,m,y);return this.draw(k)}ellipse(p,f,g,m,y){let k=this.gen.ellipse(p,f,g,m,y);return this.draw(k)}circle(p,f,g,m){let y=this.gen.circle(p,f,g,m);return this.draw(y)}linearPath(p,f){let g=this.gen.linearPath(p,f);return this.draw(g)}polygon(p,f){let g=this.gen.polygon(p,f);return this.draw(g)}arc(p,f,g,m,y,k,C=!1,S){let E=this.gen.arc(p,f,g,m,y,k,C,S);return this.draw(E)}curve(p,f){let g=this.gen.curve(p,f);return this.draw(g)}path(p,f){let g=this.gen.path(p,f);return this.draw(g)}},s(h,"ot"),h),ur={canvas:s((d,p)=>new o5e(d,p),"canvas"),svg:s((d,p)=>new l5e(d,p),"svg"),generator:s(d=>new $4(d),"generator"),newSeed:s(()=>$4.newSeed(),"newSeed")}});function c5e(e,t){let{labelStyles:r}=cr(t);t.labelStyle=r;let i=Or(t),n=i;i||(n="anchor");let a=e.insert("g").attr("class",n).attr("id",t.domId||t.id),o=1,{cssStyles:l}=t,u=ur.svg(a),h=pr(t,{fill:"black",stroke:"none",fillStyle:"solid"});t.look!=="handDrawn"&&(h.roughness=0);let d=u.circle(0,0,o*2,h),p=a.insert(()=>d,":first-child");return p.attr("class","anchor").attr("style",qa(l)),fr(t,p),t.intersect=function(f){return Ee.info("Circle intersect",t,o,f),nr.circle(t,o,f)},a}var bEt=G(()=>{"use strict";Nt(),lr(),Tr(),gr(),vr(),Ar(),s(c5e,"anchor")});function Dz(e,t,r,i,n,a,o){let l=(e+r)/2,u=(t+i)/2,h=Math.atan2(i-t,r-e),d=(r-e)/2,p=(i-t)/2,f=d/n,g=p/a,m=Math.sqrt(f**2+g**2);if(m>1)throw new Error("The given radii are too small to create an arc between the points.");let y=Math.sqrt(1-m**2),k=l+y*a*Math.sin(h)*(o?-1:1),C=u-y*n*Math.cos(h)*(o?-1:1),S=Math.atan2((t-C)/a,(e-k)/n),E=Math.atan2((i-C)/a,(r-k)/n)-S;o&&E<0&&(E+=2*Math.PI),!o&&E>0&&(E-=2*Math.PI);let _=[];for(let b=0;b<20;b++){let v=b/19,x=S+v*E,w=k+n*Math.cos(x),R=C+a*Math.sin(x);_.push({x:w,y:R})}return _}function u5e(e,t,r){let[i,n]=[t,r].sort((a,o)=>o-a);return n*(1-Math.sqrt(1-(e/i/2)**2))}async function h5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?16:n,o=t.look==="neo"?12:n,l=s(w=>w+o,"calcTotalHeight"),u=s(w=>{let R=w/2;return[R/(2.5+w/50),R]},"calcEllipseRadius"),{shapeSvg:h,bbox:d}=await Nr(e,t,Or(t)),p=l(t?.height?t?.height:d.height),[f,g]=u(p),m=u5e(p,f,g),y=(t?.width?t?.width:d.width)+a*2+m-m,k=p,{cssStyles:C}=t,S=[{x:y/2,y:-k/2},{x:-y/2,y:-k/2},...Dz(-y/2,-k/2,-y/2,k/2,f,g,!1),{x:y/2,y:k/2},...Dz(y/2,k/2,y/2,-k/2,f,g,!0)],E=ur.svg(h),_=pr(t,{});t.look!=="handDrawn"&&(_.roughness=0,_.fillStyle="solid");let b=Ni(S),v=E.path(b,_),x=h.insert(()=>v,":first-child");return x.attr("class","basic label-container outer-path"),C&&t.look!=="handDrawn"&&x.selectAll("path").attr("style",C),i&&t.look!=="handDrawn"&&x.selectAll("path").attr("style",i),x.attr("transform",`translate(${f/2}, 0)`),fr(t,x),t.intersect=function(w){return nr.polygon(t,S,w)},h}var xEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(Dz,"generateArcPoints"),s(u5e,"calculateArcSagitta"),s(h5e,"bowTieRect")});async function d5e(e,t,{config:{themeVariables:r}}){let{labelStyles:i,nodeStyles:n}=cr(t);t.labelStyle=i;let{shapeSvg:a,bbox:o,label:l}=await Nr(e,t,Or(t)),u=r?.nodeBorder??r?.lineColor??"currentColor",h=t.padding??12,d=Math.max(o.width+h*2,t.width??0,80),p=Math.max(Math.min(d*.08,12),5),f=Math.max(o.height+h*2+p,t.height??0),g=-f/2+p,m=f/2,y=d*.72,k=[`M${-d/2},${g}`,`L${-y/2},${m}`,`A${y/2},${p} 0 0 0 ${y/2},${m}`,`L${d/2},${g}`,`A${d/2},${p} 0 0 0 ${-d/2},${g}`,"Z"].join(" "),{cssStyles:C}=t,S=a.insert("g",":first-child").attr("class","basic label-container");if(t.look==="handDrawn"){let x=ur.svg(a).path(k,pr(t,{}));S.node()?.appendChild(x),C&&S.attr("style",C)}else S.append("path").attr("d",k).attr("style",n);S.append("ellipse").attr("cx",0).attr("cy",g).attr("rx",d/2).attr("ry",p).attr("style",`fill:none;stroke:${u};stroke-width:1px`),fr(t,S);let E=g+(m-g)/2;l.attr("transform",`translate(${-(o.width/2)-(o.x-(o.left??0))}, ${E-o.height/2-(o.y-(o.top??0))})`);let _=12,b=s((x,w,R)=>Array.from({length:_+1},($,M)=>{let N=Math.PI-M*Math.PI/_;return{x:x*Math.cos(N),y:w+R*p*Math.sin(N)}}),"arc"),v=[...b(d/2,g,-1),...b(y/2,m,1).reverse()];return t.intersect=function(x){return nr.polygon(t,v,x)},a}var TEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(d5e,"bucket")}),Sl,Vu=G(()=>{"use strict";Sl=s((e,t,r,i,n)=>["M",e+n,t,"H",e+r-n,"A",n,n,0,0,1,e+r,t+n,"V",t+i-n,"A",n,n,0,0,1,e+r-n,t+i,"H",e+n,"A",n,n,0,0,1,e,t+i-n,"V",t+n,"A",n,n,0,0,1,e+n,t,"Z"].join(" "),"createRoundedRectPathD")});async function p5e(e,t){let{themeVariables:r}=it(),i=r.clusterBkg,n=r.clusterBorder,{nodeStyles:a}=cr(t),{shapeSvg:o,bbox:l}=await Nr(e,t,Or(t)),u=t.padding??8,h=l.height,d=Math.max(l.width+u*2,f5e,t?.width??0),p=Math.max(h+F_+P_+u*2,t?.height??0),f=-d/2,g=-p/2,m=-(F_+P_)/2,y=o.select(".label");y&&(t.useHtmlLabels??Ps(it())?y.attr("transform",`translate(${-l.width/2}, ${-l.height/2+m})`):y.attr("transform",`translate(0, ${-l.height/2+m})`));let k;if(t.look==="handDrawn"){let b=ur.svg(o),v=pr(t,{fill:i,stroke:n,fillStyle:"solid"}),x=b.path(Sl(f,g,d,p,z_),v);k=o.insert(()=>x,":first-child"),k.attr("class","basic label-container collapsed-group").attr("style",qa(t.cssStyles))}else k=o.insert("rect",":first-child"),k.attr("class","basic label-container collapsed-group").attr("style",a).attr("rx",z_).attr("ry",z_).attr("x",f).attr("y",g).attr("width",d).attr("height",p).attr("fill",i).attr("stroke",n);let C=g+u+h+F_;o.append("line").attr("class","collapsed-separator").attr("x1",f+8).attr("y1",C).attr("x2",f+d-8).attr("y2",C).attr("stroke",n).attr("stroke-dasharray","3, 3");let S=C+P_/2,E=2.5,_=10;for(let b=-1;b<=1;b++)o.append("circle").attr("class","collapsed-indicator").attr("cx",b*_).attr("cy",S).attr("r",E).attr("fill",n);return fr(t,k),t.calcIntersect=function(b,v){return nr.rect(b,v)},t.intersect=function(b){return nr.rect(t,b)},o}var P_,F_,f5e,z_,kEt=G(()=>{"use strict";Er(),Gr(),lr(),Tr(),Vu(),gr(),vr(),Ar(),P_=20,F_=8,f5e=80,z_=8,s(p5e,"collapsedGroup")});function Uu(e,t,r,i){return e.insert("polygon",":first-child").attr("points",i.map(function(n){return n.x+","+n.y}).join(" ")).attr("class","label-container").attr("transform","translate("+-t/2+","+r/2+")")}var od=G(()=>{"use strict";s(Uu,"insertPolygonShape")});async function g5e(e,t){let r=t,{shapeSvg:i,bbox:n}=await Nr(e,r,Or(r)),a=r.padding??0,o=n.height+2*a,l=o/2,u=n.width+2*l+a,h=r.width??0,d=r.positioned&&(r.widthInColumns??1)>1&&h>u?h:u,p=b5e(r.directions??[],n,r,d),f=Uu(i,d,o,p);return f.attr("style",r.style??null),fr(r,f),r.intersect=function(g){return nr.polygon(r,p,g)},i}var m5e,q_,y5e,v5e,Nz,b5e,CEt=G(()=>{"use strict";Tr(),od(),lr(),m5e=["right","left","up","down"],q_="point",y5e=s(e=>{let t=new Set;for(let r of e)switch(r){case"x":t.add("right"),t.add("left");break;case"y":t.add("up"),t.add("down");break;default:t.add(r);break}return t},"expandAndDeduplicateDirections"),v5e=s(e=>m5e.filter(t=>e.has(t)).join("|")||q_,"getDirectionKey"),Nz={"right|left|up|down":s(({height:e,midpoint:t,padding:r,width:i})=>[{x:0,y:0},{x:t,y:0},{x:i/2,y:2*r},{x:i-t,y:0},{x:i,y:0},{x:i,y:-e/3},{x:i+2*r,y:-e/2},{x:i,y:-2*e/3},{x:i,y:-e},{x:i-t,y:-e},{x:i/2,y:-e-2*r},{x:t,y:-e},{x:0,y:-e},{x:0,y:-2*e/3},{x:-2*r,y:-e/2},{x:0,y:-e/3}],"right|left|up|down"),"right|left|up":s(({height:e,midpoint:t,width:r})=>[{x:t,y:0},{x:r-t,y:0},{x:r,y:-e/2},{x:r-t,y:-e},{x:t,y:-e},{x:0,y:-e/2}],"right|left|up"),"right|left|down":s(({height:e,midpoint:t,width:r})=>[{x:0,y:0},{x:t,y:-e},{x:r-t,y:-e},{x:r,y:0}],"right|left|down"),"right|up|down":s(({height:e,midpoint:t,width:r})=>[{x:0,y:0},{x:r,y:-t},{x:r,y:-e+t},{x:0,y:-e}],"right|up|down"),"left|up|down":s(({height:e,midpoint:t,width:r})=>[{x:r,y:0},{x:0,y:-t},{x:0,y:-e+t},{x:r,y:-e}],"left|up|down"),"right|left":s(({height:e,midpoint:t,padding:r,width:i})=>[{x:t,y:0},{x:t,y:-r},{x:i-t,y:-r},{x:i-t,y:0},{x:i,y:-e/2},{x:i-t,y:-e},{x:i-t,y:-e+r},{x:t,y:-e+r},{x:t,y:-e},{x:0,y:-e/2}],"right|left"),"up|down":s(({height:e,midpoint:t,padding:r,width:i})=>[{x:i/2,y:0},{x:0,y:-r},{x:t,y:-r},{x:t,y:-e+r},{x:0,y:-e+r},{x:i/2,y:-e},{x:i,y:-e+r},{x:i-t,y:-e+r},{x:i-t,y:-r},{x:i,y:-r}],"up|down"),"right|up":s(({height:e,midpoint:t,width:r})=>[{x:0,y:0},{x:r,y:-t},{x:0,y:-e}],"right|up"),"right|down":s(({height:e,width:t})=>[{x:0,y:0},{x:t,y:0},{x:0,y:-e}],"right|down"),"left|up":s(({height:e,midpoint:t,width:r})=>[{x:r,y:0},{x:0,y:-t},{x:r,y:-e}],"left|up"),"left|down":s(({height:e,width:t})=>[{x:t,y:0},{x:0,y:0},{x:t,y:-e}],"left|down"),right:s(({height:e,midpoint:t,padding:r,width:i})=>[{x:t,y:-r},{x:t,y:-r},{x:i-t,y:-r},{x:i-t,y:0},{x:i,y:-e/2},{x:i-t,y:-e},{x:i-t,y:-e+r},{x:t,y:-e+r},{x:t,y:-e+r}],"right"),left:s(({height:e,midpoint:t,padding:r,width:i})=>[{x:t,y:0},{x:t,y:-r},{x:i-t,y:-r},{x:i-t,y:-e+r},{x:t,y:-e+r},{x:t,y:-e},{x:0,y:-e/2}],"left"),up:s(({height:e,midpoint:t,padding:r,width:i})=>[{x:t,y:-r},{x:t,y:-e+r},{x:0,y:-e+r},{x:i/2,y:-e},{x:i,y:-e+r},{x:i-t,y:-e+r},{x:i-t,y:-r}],"up"),down:s(({height:e,midpoint:t,padding:r,width:i})=>[{x:i/2,y:0},{x:0,y:-r},{x:t,y:-r},{x:t,y:-e+r},{x:i-t,y:-e+r},{x:i-t,y:-r},{x:i,y:-r}],"down"),[q_]:()=>[{x:0,y:0}]},b5e=s((e,t,r,i)=>{let n=y5e(e),a=(r.padding??0)/2,o=t.height+4*a,l=o/2,u=i??t.width+2*l+2*a,h=v5e(n);return(Nz[h]??Nz[q_])({height:o,midpoint:l,padding:a,width:u})},"getArrowPoints"),s(g5e,"block_arrow")});async function x5e(e,t,{config:{themeVariables:r}}){let{labelStyles:i,nodeStyles:n}=cr(t);t.labelStyle=i;let{shapeSvg:a,bbox:o,label:l}=await Nr(e,t,Or(t)),u=r?.nodeBorder??r?.lineColor??"currentColor",h=t.padding??12,d=18,p=12,f=Math.max(o.width+h*2,t.width??0,90),g=Math.max(o.height+h*2+d,t.height??0),m=-g/2,{cssStyles:y}=t,k=a.insert("g",":first-child").attr("class","basic label-container");if(t.look==="handDrawn"){let S=ur.svg(a).path(Sl(-f/2,m,f,g,p),pr(t,{}));k.node()?.appendChild(S),y&&k.attr("style",y)}else k.append("rect").attr("x",-f/2).attr("y",m).attr("width",f).attr("height",g).attr("rx",p).attr("ry",p).attr("style",n);k.append("line").attr("x1",-f/2).attr("y1",m+d).attr("x2",f/2).attr("y2",m+d).attr("style",`stroke:${u};stroke-width:1px`);for(let S=0;S<3;S++)k.append("circle").attr("cx",-f/2+12+S*9).attr("cy",m+d/2).attr("r",2.5).attr("style",`fill:${u};stroke:none`);k.append("rect").attr("class","browser-address-bar").attr("x",-f/2+44).attr("y",m+4).attr("width",Math.max(f-56,10)).attr("height",d-8).attr("rx",3).attr("ry",3).attr("style",`fill:none;stroke:${u};stroke-width:1px;opacity:0.6`),fr(t,k);let C=m+d+(g-d)/2;return l.attr("transform",`translate(${-(o.width/2)-(o.x-(o.left??0))}, ${C-o.height/2-(o.y-(o.top??0))})`),t.intersect=function(S){return nr.rect(t,S)},a}var wEt=G(()=>{"use strict";lr(),Tr(),Vu(),gr(),vr(),s(x5e,"browser")});async function T5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?28:n,o=t.look==="neo"?24:n,{shapeSvg:l,bbox:u}=await Nr(e,t,Or(t)),h=(t?.width??u.width)+(t.look==="neo"?a*2:a+I4),d=(t?.height??u.height)+(t.look==="neo"?o*2:o),p=0,f=h,g=-d,m=0,y=[{x:p+I4,y:g},{x:f,y:g},{x:f,y:m},{x:p,y:m},{x:p,y:g+I4},{x:p+I4,y:g}],k,{cssStyles:C}=t;if(t.look==="handDrawn"){let S=ur.svg(l),E=pr(t,{}),_=Ni(y),b=S.path(_,E);k=l.insert(()=>b,":first-child").attr("transform",`translate(${-h/2}, ${d/2})`),C&&k.attr("style",C)}else k=Uu(l,h,d,y);return i&&k.attr("style",i),fr(t,k),t.intersect=function(S){return nr.polygon(t,y,S)},l}var I4,EEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),od(),lr(),I4=12,s(T5e,"card")});function k5e(e,t){let{nodeStyles:r}=cr(t);t.label="";let i=e.insert("g").attr("class",Or(t)).attr("id",t.domId??t.id),{cssStyles:n}=t,a=Math.max(28,t.width??0),o=[{x:0,y:a/2},{x:a/2,y:0},{x:0,y:-a/2},{x:-a/2,y:0}],l=ur.svg(i),u=pr(t,{});t.look!=="handDrawn"&&(u.roughness=0,u.fillStyle="solid");let h=Ni(o),d=l.path(h,u),p=i.insert(()=>d,":first-child");return n&&t.look!=="handDrawn"&&p.selectAll("path").attr("style",n),r&&t.look!=="handDrawn"&&p.selectAll("path").attr("style",r),t.width=28,t.height=28,t.intersect=function(f){return nr.polygon(t,o,f)},i}var SEt=G(()=>{"use strict";Tr(),vr(),gr(),lr(),s(k5e,"choice")});async function Oz(e,t,r){let{labelStyles:i,nodeStyles:n}=cr(t);t.labelStyle=i;let{shapeSvg:a,bbox:o,halfPadding:l}=await Nr(e,t,Or(t)),u=16,h=r?.padding??l,d=t.look==="neo"?o.width/2+u*2:o.width/2+h,p,{cssStyles:f}=t;if(t.look==="handDrawn"){let g=ur.svg(a),m=pr(t,{}),y=g.circle(0,0,d*2,m);p=a.insert(()=>y,":first-child"),p.attr("class","basic label-container").attr("style",qa(f))}else p=a.insert("circle",":first-child").attr("class","basic label-container").attr("style",n).attr("r",d).attr("cx",0).attr("cy",0);return fr(t,p),t.calcIntersect=function(g,m){let y=g.width/2;return nr.circle(g,y,m)},t.intersect=function(g){return Ee.info("Circle intersect",t,d,g),nr.circle(t,d,g)},a}var C5e=G(()=>{"use strict";vr(),Nt(),Ar(),Tr(),gr(),lr(),s(Oz,"circle")});async function w5e(e,t){let r=t,i=["node",r.cssClasses,r.class].filter(Boolean).join(" "),{shapeSvg:n,bbox:a,halfPadding:o}=await Nr(e,r,i),l=n.insert("rect",":first-child"),u=r.padding??0,h=r.positioned?r.width??0:a.width+u,d=r.positioned?r.height??0:a.height+u,p=r.positioned?-h/2:-a.width/2-o,f=r.positioned?-d/2:-a.height/2-o;return l.attr("class","basic cluster composite label-container").attr("style",r.style??null).attr("rx",r.rx??null).attr("ry",r.ry??null).attr("x",p).attr("y",f).attr("width",h).attr("height",d),fr(r,l),r.intersect=function(g){return nr.rect(r,g)},n}var _Et=G(()=>{"use strict";Tr(),lr(),s(w5e,"composite")});async function E5e(e,t,{config:{themeVariables:r}}){let{labelStyles:i,nodeStyles:n}=cr(t);t.labelStyle=i;let{shapeSvg:a,bbox:o,label:l}=await Nr(e,t,Or(t)),u=r?.nodeBorder??r?.lineColor??"currentColor",h=t.padding??12,d=20,p=12,f=Math.max(o.width+h*2,t.width??0,90),g=Math.max(o.height+h*2+d,t.height??0),m=-g/2,{cssStyles:y}=t,k=a.insert("g",":first-child").attr("class","basic label-container");if(t.look==="handDrawn"){let S=ur.svg(a).path(Sl(-f/2,m,f,g,p),pr(t,{}));k.node()?.appendChild(S),y&&k.attr("style",y)}else k.append("rect").attr("x",-f/2).attr("y",m).attr("width",f).attr("height",g).attr("rx",p).attr("ry",p).attr("style",n);k.append("text").attr("x",-f/2+12).attr("y",m+16).attr("class","console-glyph").attr("style",`font-family:monospace;font-weight:bold;font-size:14px;fill:${u}`).text(">_"),fr(t,k);let C=m+d+(g-d)/2;return l.attr("transform",`translate(${-(o.width/2)-(o.x-(o.left??0))}, ${C-o.height/2-(o.y-(o.top??0))})`),t.intersect=function(S){return nr.rect(t,S)},a}var AEt=G(()=>{"use strict";lr(),Tr(),Vu(),gr(),vr(),s(E5e,"consoleWindow")});function S5e(e){let t=Math.cos(Math.PI/4),r=Math.sin(Math.PI/4),i=e*2,n={x:i/2*t,y:i/2*r},a={x:-(i/2)*t,y:i/2*r},o={x:-(i/2)*t,y:-(i/2)*r},l={x:i/2*t,y:-(i/2)*r};return`M ${a.x},${a.y} L ${l.x},${l.y} + M ${n.x},${n.y} L ${o.x},${o.y}`}function _5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r,t.label="";let n=e.insert("g").attr("class",Or(t)).attr("id",t.domId??t.id),a=Math.max(30,t?.width??0),{cssStyles:o}=t,l=ur.svg(n),u=pr(t,{});t.look!=="handDrawn"&&(u.roughness=0,u.fillStyle="solid");let h=l.circle(0,0,a*2,u),d=S5e(a),p=l.path(d,u),f=n.insert(()=>h,":first-child");return f.insert(()=>p),f.attr("class","outer-path"),o&&t.look!=="handDrawn"&&f.selectAll("path").attr("style",o),i&&t.look!=="handDrawn"&&f.selectAll("path").attr("style",i),fr(t,f),t.intersect=function(g){return Ee.info("crossedCircle intersect",t,{radius:a,point:g}),nr.circle(t,a,g)},n}var REt=G(()=>{"use strict";Nt(),lr(),gr(),vr(),Tr(),s(S5e,"createLine"),s(_5e,"crossedCircle")});function ld(e,t,r,i=100,n=0,a=180){let o=[],l=n*Math.PI/180,u=(a*Math.PI/180-l)/(i-1);for(let h=0;h_,":first-child").attr("stroke-opacity",0),b.insert(()=>S,":first-child"),b.attr("class","text"),f&&t.look!=="handDrawn"&&b.selectAll("path").attr("style",f),i&&t.look!=="handDrawn"&&b.selectAll("path").attr("style",i),b.attr("transform",`translate(${p}, 0)`),o.attr("transform",`translate(${-h/2+p-(a.x-(a.left??0))},${-d/2+(t.padding??0)/2-(a.y-(a.top??0))})`),fr(t,b),t.intersect=function(v){return nr.polygon(t,m,v)},n}var $Et=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(ld,"generateCirclePoints"),s(A5e,"curlyBraceLeft")});function cd(e,t,r,i=100,n=0,a=180){let o=[],l=n*Math.PI/180,u=(a*Math.PI/180-l)/(i-1);for(let h=0;h_,":first-child").attr("stroke-opacity",0),b.insert(()=>S,":first-child"),b.attr("class","text"),f&&t.look!=="handDrawn"&&b.selectAll("path").attr("style",f),i&&t.look!=="handDrawn"&&b.selectAll("path").attr("style",i),b.attr("transform",`translate(${-p}, 0)`),o.attr("transform",`translate(${-h/2+(t.padding??0)/2-(a.x-(a.left??0))},${-d/2+(t.padding??0)/2-(a.y-(a.top??0))})`),fr(t,b),t.intersect=function(v){return nr.polygon(t,m,v)},n}var LEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(cd,"generateCirclePoints"),s(R5e,"curlyBraceRight")});function js(e,t,r,i=100,n=0,a=180){let o=[],l=n*Math.PI/180,u=(a*Math.PI/180-l)/(i-1);for(let h=0;hx,":first-child").attr("stroke-opacity",0),w.insert(()=>E,":first-child"),w.insert(()=>b,":first-child"),w.attr("class","text"),f&&t.look!=="handDrawn"&&w.selectAll("path").attr("style",f),i&&t.look!=="handDrawn"&&w.selectAll("path").attr("style",i),w.attr("transform",`translate(${p-p/4}, 0)`),o.attr("transform",`translate(${-h/2+(t.padding??0)/2-(a.x-(a.left??0))},${-d/2+(t.padding??0)/2-(a.y-(a.top??0))})`),fr(t,w),t.intersect=function(R){return nr.polygon(t,y,R)},n}var IEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(js,"generateCirclePoints"),s($5e,"curlyBraces")});async function L5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?16:n,o=t.look==="neo"?12:n,l=20,u=5,{shapeSvg:h,bbox:d}=await Nr(e,t,Or(t)),p=Math.max(l,(d.width+a*2)*1.25,t?.width??0),f=Math.max(u,d.height+o*2,t?.height??0),g=f/2,{cssStyles:m}=t,y=ur.svg(h),k=pr(t,{});t.look!=="handDrawn"&&(k.roughness=0,k.fillStyle="solid");let C=p,S=f,E=C-g,_=S/4,b=[{x:E,y:0},{x:_,y:0},{x:0,y:S/2},{x:_,y:S},{x:E,y:S},...ju(-E,-S/2,g,50,270,90)],v=Ni(b),x=y.path(v,k),w=h.insert(()=>x,":first-child");return w.attr("class","basic label-container outer-path"),m&&t.look!=="handDrawn"&&w.selectChildren("path").attr("style",m),i&&t.look!=="handDrawn"&&w.selectChildren("path").attr("style",i),w.attr("transform",`translate(${-p/2}, ${-f/2})`),fr(t,w),t.intersect=function(R){return nr.polygon(t,b,R)},h}var MEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(L5e,"curvedTrapezoid")});async function I5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let{shapeSvg:n,bbox:a,label:o}=await Nr(e,t,Or(t)),l=t.padding??20,u=Math.max(a.width+l*2,t.width??0,100),h=Math.min(Math.max(u*.23,16),56),d=h*.27,p=Math.max(a.height+l*2,t.height?t.height-(2*h-d):0),f=Math.min(u*.177,p*.45),g=p+2*h-d,m=-g/2,y=m+2*h-d,k=n.insert("g",":first-child").attr("class","basic label-container"),{cssStyles:C}=t;if(t.look==="handDrawn"){let v=ur.svg(n),x=pr(t,{}),w=v.path(Sl(-u/2,y,u,p,f),x),R=v.circle(0,m+h,h*2,x);k.insert(()=>R,":first-child"),k.insert(()=>w,":first-child"),C&&k.attr("style",C)}else k.append("rect").attr("x",-u/2).attr("y",y).attr("width",u).attr("height",p).attr("rx",f).attr("ry",f).attr("style",i),k.append("circle").attr("cx",0).attr("cy",m+h).attr("r",h).attr("style",i);fr(t,k);let S=y+p/2;o.attr("transform",`translate(${-(a.width/2)-(a.x-(a.left??0))}, ${S-a.height/2-(a.y-(a.top??0))})`);let E=m+h,_=Math.asin(Math.min(1,(y-E)/h))*180/Math.PI,b=[...ju(0,-E,h,24,180+_,-_),...ju(-(-u/2+f),-(y+f),f,12,90,0),...ju(-(-u/2+f),-(g/2-f),f,12,360,270),...ju(-(u/2-f),-(g/2-f),f,12,270,180),...ju(-(u/2-f),-(y+f),f,12,180,90)];return t.intersect=function(v){return nr.polygon(t,b,v)},n}var DEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),Vu(),s(I5e,"person")});async function M5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?24:n,o=t.look==="neo"?24:n,l=t.width??0;if(t.width&&(t.width=t.width-o,t.widthv,":first-child"),y=u.insert(()=>b,":first-child"),y.attr("class","basic label-container"),k&&y.attr("style",k)}else{let C=D5e(0,0,p,m,f,g);y=u.insert("path",":first-child").attr("d",C).attr("class","basic label-container outer-path").attr("style",qa(k)).attr("style",i)}return y.attr("label-offset-y",g),y.attr("transform",`translate(${-p/2}, ${-(m/2+g)})`),fr(t,y),d.attr("transform",`translate(${-(h.width/2)-(h.x-(h.left??0))}, ${-(h.height/2)+(t.padding??0)/1.5-(h.y-(h.top??0))})`),t.intersect=function(C){let S=nr.rect(t,C),E=S.x-(t.x??0);if(f!=0&&(Math.abs(E)<(t.width??0)/2||Math.abs(E)==(t.width??0)/2&&Math.abs(S.y-(t.y??0))>(t.height??0)/2-g)){let _=g*g*(1-E*E/(f*f));_>0&&(_=Math.sqrt(_)),_=g-_,C.y-(t.y??0)>0&&(_=-_),S.y+=_}return S},u}var D5e,N5e,O5e,Bz,Pz,NEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),Ar(),D5e=s((e,t,r,i,n,a)=>[`M${e},${t+a}`,`a${n},${a} 0,0,0 ${r},0`,`a${n},${a} 0,0,0 ${-r},0`,`l0,${i}`,`a${n},${a} 0,0,0 ${r},0`,`l0,${-i}`].join(" "),"createCylinderPathD"),N5e=s((e,t,r,i,n,a)=>[`M${e},${t+a}`,`M${e+r},${t+a}`,`a${n},${a} 0,0,0 ${-r},0`,`l0,${i}`,`a${n},${a} 0,0,0 ${r},0`,`l0,${-i}`].join(" "),"createOuterCylinderPathD"),O5e=s((e,t,r,i,n,a)=>[`M${e-r/2},${-i/2}`,`a${n},${a} 0,0,0 ${r},0`].join(" "),"createInnerCylinderPathD"),Bz=8,Pz=8,s(M5e,"cylinder")});async function e2(e,t,r){let{labelStyles:i,nodeStyles:n}=cr(t);t.labelStyle=i;let{shapeSvg:a,bbox:o}=await Nr(e,t,Or(t)),l=Math.max(o.width+r.labelPaddingX*2,t?.width||0),u=Math.max(o.height+r.labelPaddingY*2,t?.height||0),h=-l/2,d=-u/2,p,{rx:f,ry:g}=t,{cssStyles:m}=t;if(r?.rx&&r.ry&&(f=r.rx,g=r.ry),t.look==="handDrawn"){let y=ur.svg(a),k=pr(t,{}),C=f||g?y.path(Sl(h,d,l,u,f||0),k):y.rectangle(h,d,l,u,k);p=a.insert(()=>C,":first-child"),p.attr("class","basic label-container").attr("style",qa(m))}else p=a.insert("rect",":first-child"),p.attr("class","basic label-container").attr("style",n).attr("rx",qa(f)).attr("ry",qa(g)).attr("x",h).attr("y",d).attr("width",l).attr("height",u);return fr(t,p,t.look==="handDrawn"?void 0:{width:l,height:u}),t.calcIntersect=function(y,k){return nr.rect(y,k)},t.intersect=function(y){return nr.rect(t,y)},a}var t2=G(()=>{"use strict";lr(),Tr(),Vu(),gr(),vr(),Ar(),s(e2,"drawRect")});async function B5e(e,t){let{cssClasses:r,labelPaddingX:i,labelPaddingY:n,padding:a,width:o,height:l}=t,u={rx:0,ry:0,classes:r??"",labelPaddingX:i??(a??0)*2,labelPaddingY:n??a??0},h=await e2(e,t,u);if(t.look==="handDrawn"){let g=ur.svg(h),m=pr(t,{}),y=h.select(".basic.label-container > path:nth-child(2)"),k=y.node();if(!k)return h;let C=null;if(k instanceof SVGGraphicsElement)C=k.getBBox();else return h;return h.insert(()=>g.line(C.x,C.y,C.x+C.width,C.y,m),".basic.label-container g.label"),h.insert(()=>g.line(C.x,C.y+C.height,C.x+C.width,C.y+C.height,m),".basic.label-container g.label"),y.remove(),h}let d=h.select(".basic.label-container"),p=(Number(d.attr("width"))||o)??0,f=(Number(d.attr("height"))||l)??0;return p>0&&f>0&&d.attr("stroke-dasharray",`${p} ${f}`),h}var OEt=G(()=>{"use strict";t2(),gr(),vr(),s(B5e,"datastore")});async function P5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.look==="neo"?16:t.padding??0,a=t.look==="neo"?16:t.padding??0,{shapeSvg:o,bbox:l,label:u}=await Nr(e,t,Or(t)),h=l.width+n,d=l.height+a,p=d*.2,f=-h/2,g=-d/2-p/2,{cssStyles:m}=t,y=ur.svg(o),k=pr(t,{});t.look!=="handDrawn"&&(k.roughness=0,k.fillStyle="solid");let C=[{x:f,y:g+p},{x:-f,y:g+p},{x:-f,y:-g},{x:f,y:-g},{x:f,y:g},{x:-f,y:g},{x:-f,y:g+p}],S=y.polygon(C.map(_=>[_.x,_.y]),k),E=o.insert(()=>S,":first-child");return E.attr("class","basic label-container outer-path"),m&&t.look!=="handDrawn"&&E.selectAll("path").attr("style",m),i&&t.look!=="handDrawn"&&E.selectAll("path").attr("style",i),u.attr("transform",`translate(${f+(t.padding??0)/2-(l.x-(l.left??0))}, ${g+p+(t.padding??0)/2-(l.y-(l.top??0))})`),fr(t,E),t.intersect=function(_){return nr.rect(t,_)},o}var BEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(P5e,"dividedRectangle")});async function F5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t),n=t.look==="neo"?12:5;t.labelStyle=r;let a=t.padding??0,o=t.look==="neo"?16:a,{shapeSvg:l,bbox:u}=await Nr(e,t,Or(t)),h=(t?.width?t?.width/2:u.width/2)+(o??0),d=h-n,p,{cssStyles:f}=t;if(t.look==="handDrawn"){let g=ur.svg(l),m=pr(t,{roughness:.2,strokeWidth:2.5}),y=pr(t,{roughness:.2,strokeWidth:1.5}),k=g.circle(0,0,h*2,m),C=g.circle(0,0,d*2,y);p=l.insert("g",":first-child"),p.attr("class",qa(t.cssClasses)).attr("style",qa(f)),p.node()?.appendChild(k),p.node()?.appendChild(C)}else{p=l.insert("g",":first-child");let g=p.insert("circle",":first-child"),m=p.insert("circle");p.attr("class","basic label-container").attr("style",i),g.attr("class","outer-circle").attr("style",i).attr("r",h).attr("cx",0).attr("cy",0),m.attr("class","inner-circle").attr("style",i).attr("r",d).attr("cx",0).attr("cy",0)}return fr(t,p),t.intersect=function(g){return Ee.info("DoubleCircle intersect",t,h,g),nr.circle(t,h,g)},l}var PEt=G(()=>{"use strict";Nt(),lr(),Tr(),gr(),vr(),Ar(),s(F5e,"doublecircle")});function z5e(e,t,{config:{themeVariables:r}}){let{labelStyles:i,nodeStyles:n}=cr(t);t.label="",t.labelStyle=i;let a=e.insert("g").attr("class",Or(t)).attr("id",t.domId??t.id),o=7,{cssStyles:l}=t,u=ur.svg(a),{nodeBorder:h}=r,d=pr(t,{fillStyle:"solid"});t.look!=="handDrawn"&&(d.roughness=0);let p=u.circle(0,0,o*2,d),f=a.insert(()=>p,":first-child");return f.selectAll("path").attr("style",`fill: ${h} !important;`),l&&l.length>0&&t.look!=="handDrawn"&&f.selectAll("path").attr("style",l),n&&t.look!=="handDrawn"&&f.selectAll("path").attr("style",n),fr(t,f),t.intersect=function(g){return Ee.info("filledCircle intersect",t,{radius:o,point:g}),nr.circle(t,o,g)},a}var FEt=G(()=>{"use strict";vr(),Nt(),Tr(),gr(),lr(),s(z5e,"filledCircle")});async function q5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?n*2:n;(t.width||t.height)&&(t.height=t?.height??0,t.heightC,":first-child").attr("transform",`translate(${-d/2}, ${d/2})`).attr("class","outer-path");return g&&t.look!=="handDrawn"&&S.selectChildren("path").attr("style",g),i&&t.look!=="handDrawn"&&S.selectChildren("path").attr("style",i),t.width=h,t.height=d,fr(t,S),u.attr("transform",`translate(${-l.width/2-(l.x-(l.left??0))}, ${-d/2+(t.padding??0)/2+(l.y-(l.top??0))})`),t.intersect=function(E){return Ee.info("Triangle intersect",t,f,E),nr.polygon(t,f,E)},o}var Fz,zz,zEt=G(()=>{"use strict";Nt(),lr(),Tr(),gr(),vr(),lr(),Fz=10,zz=10,s(q5e,"flippedTriangle")});async function W5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let{shapeSvg:n,bbox:a,label:o}=await Nr(e,t,Or(t)),l=t.padding??12,u=Math.max(a.width+l*2,t.width??0,90),h=a.height+l*2,d=Math.max(Math.min(h*.16,14),8),p=Math.max(h+d,t.height??0),f=p-d,g=Math.max(u*.38,28),m=-p/2,y=[{x:-u/2,y:m},{x:-u/2+g,y:m},{x:-u/2+g,y:m+d},{x:u/2,y:m+d},{x:u/2,y:p/2},{x:-u/2,y:p/2}],k=[`M${y[0].x},${y[0].y}`,...y.slice(1).map(_=>`L${_.x},${_.y}`),"Z"].join(" "),{cssStyles:C}=t,S;if(t.look==="handDrawn"){let _=ur.svg(n).path(k,pr(t,{}));S=n.insert(()=>_,":first-child").attr("class","basic label-container"),C&&S.attr("style",C)}else S=n.insert("path",":first-child").attr("d",k).attr("class","basic label-container").attr("style",i);t.look==="handDrawn"?fr(t,S):fr(t,S,{width:u,height:p});let E=m+d+f/2;return o.attr("transform",`translate(${-(a.width/2)-(a.x-(a.left??0))}, ${E-a.height/2-(a.y-(a.top??0))})`),t.intersect=function(_){return nr.polygon(t,y,_)},n}var qEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(W5e,"folder")});function j5e(e,t,{dir:r,config:{state:i,themeVariables:n}}){let{nodeStyles:a}=cr(t);t.label="";let o=e.insert("g").attr("class",Or(t)).attr("id",t.domId??t.id),{cssStyles:l}=t,u=Math.max(70,t?.width??0),h=Math.max(10,t?.height??0);r==="LR"&&(u=Math.max(10,t?.width??0),h=Math.max(70,t?.height??0));let d=-1*u/2,p=-1*h/2,f=ur.svg(o),g=pr(t,{stroke:n.lineColor,fill:n.lineColor});t.look!=="handDrawn"&&(g.roughness=0,g.fillStyle="solid");let m=f.rectangle(d,p,u,h,g),y=o.insert(()=>m,":first-child");l&&t.look!=="handDrawn"&&y.selectAll("path").attr("style",l),a&&t.look!=="handDrawn"&&y.selectAll("path").attr("style",a),fr(t,y);let k=i?.padding??0;return t.width&&t.height&&(t.width+=k/2||0,t.height+=k/2||0),t.intersect=function(C){return nr.rect(t,C)},o}var WEt=G(()=>{"use strict";vr(),Tr(),gr(),lr(),s(j5e,"forkJoin")});async function V5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=15,a=10,o=t.look==="neo"?16:t.padding??0,l=t.look==="neo"?12:t.padding??0;(t.width||t.height)&&(t.height=(t?.height??0)-l*2,t.heightS,":first-child");return E.attr("class","basic label-container outer-path"),g&&t.look!=="handDrawn"&&E.selectChildren("path").attr("style",g),i&&t.look!=="handDrawn"&&E.selectChildren("path").attr("style",i),fr(t,E),t.intersect=function(_){return Ee.info("Pill intersect",t,{radius:f,point:_}),nr.polygon(t,k,_)},u}var jEt=G(()=>{"use strict";Nt(),lr(),Tr(),gr(),vr(),s(V5e,"halfRoundedRectangle")});async function U5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t),n=t.look==="neo"?3.5:4;t.labelStyle=r;let a=t.padding??0,o=70,l=32,u=t.look==="neo"?o:a,h=t.look==="neo"?l:a;if(t.width||t.height){let S=(t.height??0)/n;t.width=(t?.width??0)-2*S-h,t.height=(t.height??0)-u}let{shapeSvg:d,bbox:p}=await Nr(e,t,Or(t)),f=(t?.height?t?.height:p.height)+u,g=f/n,m=(t?.width?t?.width:p.width)+2*g+h,y=[{x:g,y:0},{x:m-g,y:0},{x:m,y:-f/2},{x:m-g,y:-f},{x:g,y:-f},{x:0,y:-f/2}],k,{cssStyles:C}=t;if(t.look==="handDrawn"){let S=ur.svg(d),E=pr(t,{}),_=Y5e(0,0,m,f,g),b=S.path(_,E);k=d.insert(()=>b,":first-child").attr("transform",`translate(${-m/2}, ${f/2})`),C&&k.attr("style",C)}else k=Uu(d,m,f,y);return i&&k.attr("style",i),t.width=m,t.height=f,fr(t,k),t.intersect=function(S){return nr.polygon(t,y,S)},d}var Y5e,VEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),od(),Y5e=s((e,t,r,i,n)=>[`M${e+n},${t}`,`L${e+r-n},${t}`,`L${e+r},${t-i/2}`,`L${e+r-n},${t-i}`,`L${e+n},${t-i}`,`L${e},${t-i/2}`,"Z"].join(" "),"createHexagonPathD"),s(U5e,"hexagon")});async function H5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.label="",t.labelStyle=r;let{shapeSvg:n}=await Nr(e,t,Or(t)),a=Math.max(30,t?.width??0),o=Math.max(30,t?.height??0),{cssStyles:l}=t,u=ur.svg(n),h=pr(t,{});t.look!=="handDrawn"&&(h.roughness=0,h.fillStyle="solid");let d=[{x:0,y:0},{x:a,y:0},{x:0,y:o},{x:a,y:o}],p=Ni(d),f=u.path(p,h),g=n.insert(()=>f,":first-child");return g.attr("class","basic label-container outer-path"),l&&t.look!=="handDrawn"&&g.selectChildren("path").attr("style",l),i&&t.look!=="handDrawn"&&g.selectChildren("path").attr("style",i),g.attr("transform",`translate(${-a/2}, ${-o/2})`),fr(t,g),t.intersect=function(m){return Ee.info("Pill intersect",t,{points:d}),nr.polygon(t,d,m)},n}var UEt=G(()=>{"use strict";Nt(),lr(),Tr(),gr(),vr(),s(H5e,"hourglass")});async function G5e(e,t,{config:{themeVariables:r,flowchart:i}}){let{labelStyles:n}=cr(t);t.labelStyle=n;let a=t.assetHeight??48,o=t.assetWidth??48,l=Math.max(a,o),u=i?.wrappingWidth;t.width=Math.max(l,u??0);let{shapeSvg:h,bbox:d,label:p}=await Nr(e,t,"icon-shape default"),f=t.pos==="t",g=l,m=l,{nodeBorder:y}=r,{stylesMap:k}=mg(t),C=-m/2,S=-g/2,E=t.label?8:0,_=ur.svg(h),b=pr(t,{stroke:"none",fill:"none"});t.look!=="handDrawn"&&(b.roughness=0,b.fillStyle="solid");let v=_.rectangle(C,S,m,g,b),x=Math.max(m,d.width),w=g+d.height+E,R=_.rectangle(-x/2,-w/2,x,w,{...b,fill:"transparent",stroke:"none"}),$=h.insert(()=>v,":first-child"),M=h.insert(()=>R);if(t.icon){let N=h.append("g");N.html(`${await Lu(t.icon,{height:l,width:l,fallbackPrefix:""})}`);let j=N.node().getBBox(),I=j.width,z=j.height,F=j.x,O=j.y;N.attr("transform",`translate(${-I/2-F},${f?d.height/2+E/2-z/2-O:-d.height/2-E/2-z/2-O})`),N.attr("style",`color: ${k.get("stroke")??y};`)}return p.attr("transform",`translate(${-d.width/2-(d.x-(d.left??0))},${f?-w/2:w/2-d.height})`),$.attr("transform",`translate(0,${f?d.height/2+E/2:-d.height/2-E/2})`),fr(t,M),t.intersect=function(N){if(Ee.info("iconSquare intersect",t,N),!t.label)return nr.rect(t,N);let j=t.x??0,I=t.y??0,z=t.height??0,F=[];return f?F=[{x:j-d.width/2,y:I-z/2},{x:j+d.width/2,y:I-z/2},{x:j+d.width/2,y:I-z/2+d.height+E},{x:j+m/2,y:I-z/2+d.height+E},{x:j+m/2,y:I+z/2},{x:j-m/2,y:I+z/2},{x:j-m/2,y:I-z/2+d.height+E},{x:j-d.width/2,y:I-z/2+d.height+E}]:F=[{x:j-m/2,y:I-z/2},{x:j+m/2,y:I-z/2},{x:j+m/2,y:I-z/2+g},{x:j+d.width/2,y:I-z/2+g},{x:j+d.width/2/2,y:I+z/2},{x:j-d.width/2,y:I+z/2},{x:j-d.width/2,y:I-z/2+g},{x:j-m/2,y:I-z/2+g}],nr.polygon(t,F,N)},h}var YEt=G(()=>{"use strict";vr(),Nt(),Iu(),Tr(),gr(),lr(),s(G5e,"icon")});async function X5e(e,t,{config:{themeVariables:r,flowchart:i}}){let{labelStyles:n}=cr(t);t.labelStyle=n;let a=t.assetHeight??48,o=t.assetWidth??48,l=Math.max(a,o),u=i?.wrappingWidth;t.width=Math.max(l,u??0);let{shapeSvg:h,bbox:d,label:p}=await Nr(e,t,"icon-shape default"),f=20,g=t.label?8:0,m=t.pos==="t",{nodeBorder:y,mainBkg:k}=r,{stylesMap:C}=mg(t),S=ur.svg(h),E=pr(t,{});t.look!=="handDrawn"&&(E.roughness=0,E.fillStyle="solid");let _=C.get("fill");E.stroke=_??k;let b=h.append("g");t.icon&&b.html(`${await Lu(t.icon,{height:l,width:l,fallbackPrefix:""})}`);let v=b.node().getBBox(),x=v.width,w=v.height,R=v.x,$=v.y,M=Math.max(x,w)*Math.SQRT2+f*2,N=S.circle(0,0,M,E),j=Math.max(M,d.width),I=M+d.height+g,z=S.rectangle(-j/2,-I/2,j,I,{...E,fill:"transparent",stroke:"none"}),F=h.insert(()=>N,":first-child"),O=h.insert(()=>z);return b.attr("transform",`translate(${-x/2-R},${m?d.height/2+g/2-w/2-$:-d.height/2-g/2-w/2-$})`),b.attr("style",`color: ${C.get("stroke")??y};`),p.attr("transform",`translate(${-d.width/2-(d.x-(d.left??0))},${m?-I/2:I/2-d.height})`),F.attr("transform",`translate(0,${m?d.height/2+g/2:-d.height/2-g/2})`),fr(t,O),t.intersect=function(B){return Ee.info("iconSquare intersect",t,B),nr.rect(t,B)},h}var HEt=G(()=>{"use strict";vr(),Nt(),Iu(),Tr(),gr(),lr(),s(X5e,"iconCircle")});async function K5e(e,t,{config:{themeVariables:r,flowchart:i}}){let{labelStyles:n}=cr(t);t.labelStyle=n;let a=t.assetHeight??48,o=t.assetWidth??48,l=Math.max(a,o),u=i?.wrappingWidth;t.width=Math.max(l,u??0);let{shapeSvg:h,bbox:d,halfPadding:p,label:f}=await Nr(e,t,"icon-shape default"),g=t.pos==="t",m=l+p*2,y=l+p*2,{nodeBorder:k,mainBkg:C}=r,{stylesMap:S}=mg(t),E=-y/2,_=-m/2,b=t.label?8:0,v=ur.svg(h),x=pr(t,{});t.look!=="handDrawn"&&(x.roughness=0,x.fillStyle="solid");let w=S.get("fill");x.stroke=w??C;let R=v.path(Sl(E,_,y,m,5),x),$=Math.max(y,d.width),M=m+d.height+b,N=v.rectangle(-$/2,-M/2,$,M,{...x,fill:"transparent",stroke:"none"}),j=h.insert(()=>R,":first-child").attr("class","icon-shape2"),I=h.insert(()=>N);if(t.icon){let z=h.append("g");z.html(`${await Lu(t.icon,{height:l,width:l,fallbackPrefix:""})}`);let F=z.node().getBBox(),O=F.width,B=F.height,Y=F.x,U=F.y;z.attr("transform",`translate(${-O/2-Y},${g?d.height/2+b/2-B/2-U:-d.height/2-b/2-B/2-U})`),z.attr("style",`color: ${S.get("stroke")??k};`)}return f.attr("transform",`translate(${-d.width/2-(d.x-(d.left??0))},${g?-M/2:M/2-d.height})`),j.attr("transform",`translate(0,${g?d.height/2+b/2:-d.height/2-b/2})`),fr(t,I),t.intersect=function(z){if(Ee.info("iconSquare intersect",t,z),!t.label)return nr.rect(t,z);let F=t.x??0,O=t.y??0,B=t.height??0,Y=[];return g?Y=[{x:F-d.width/2,y:O-B/2},{x:F+d.width/2,y:O-B/2},{x:F+d.width/2,y:O-B/2+d.height+b},{x:F+y/2,y:O-B/2+d.height+b},{x:F+y/2,y:O+B/2},{x:F-y/2,y:O+B/2},{x:F-y/2,y:O-B/2+d.height+b},{x:F-d.width/2,y:O-B/2+d.height+b}]:Y=[{x:F-y/2,y:O-B/2},{x:F+y/2,y:O-B/2},{x:F+y/2,y:O-B/2+m},{x:F+d.width/2,y:O-B/2+m},{x:F+d.width/2/2,y:O+B/2},{x:F-d.width/2,y:O+B/2},{x:F-d.width/2,y:O-B/2+m},{x:F-y/2,y:O-B/2+m}],nr.polygon(t,Y,z)},h}var GEt=G(()=>{"use strict";vr(),Nt(),Iu(),Tr(),gr(),Vu(),lr(),s(K5e,"iconRounded")});async function Q5e(e,t,{config:{themeVariables:r,flowchart:i}}){let{labelStyles:n}=cr(t);t.labelStyle=n;let a=t.assetHeight??48,o=t.assetWidth??48,l=Math.max(a,o),u=i?.wrappingWidth;t.width=Math.max(l,u??0);let{shapeSvg:h,bbox:d,halfPadding:p,label:f}=await Nr(e,t,"icon-shape default"),g=t.pos==="t",m=l+p*2,y=l+p*2,{nodeBorder:k,mainBkg:C}=r,{stylesMap:S}=mg(t),E=-y/2,_=-m/2,b=t.label?8:0,v=ur.svg(h),x=pr(t,{});t.look!=="handDrawn"&&(x.roughness=0,x.fillStyle="solid");let w=S.get("fill");x.stroke=w??C;let R=v.path(Sl(E,_,y,m,.1),x),$=Math.max(y,d.width),M=m+d.height+b,N=v.rectangle(-$/2,-M/2,$,M,{...x,fill:"transparent",stroke:"none"}),j=h.insert(()=>R,":first-child"),I=h.insert(()=>N);if(t.icon){let z=h.append("g");z.html(`${await Lu(t.icon,{height:l,width:l,fallbackPrefix:""})}`);let F=z.node().getBBox(),O=F.width,B=F.height,Y=F.x,U=F.y;z.attr("transform",`translate(${-O/2-Y},${g?d.height/2+b/2-B/2-U:-d.height/2-b/2-B/2-U})`),z.attr("style",`color: ${S.get("stroke")??k};`)}return f.attr("transform",`translate(${-d.width/2-(d.x-(d.left??0))},${g?-M/2:M/2-d.height})`),j.attr("transform",`translate(0,${g?d.height/2+b/2:-d.height/2-b/2})`),fr(t,I),t.intersect=function(z){if(Ee.info("iconSquare intersect",t,z),!t.label)return nr.rect(t,z);let F=t.x??0,O=t.y??0,B=t.height??0,Y=[];return g?Y=[{x:F-d.width/2,y:O-B/2},{x:F+d.width/2,y:O-B/2},{x:F+d.width/2,y:O-B/2+d.height+b},{x:F+y/2,y:O-B/2+d.height+b},{x:F+y/2,y:O+B/2},{x:F-y/2,y:O+B/2},{x:F-y/2,y:O-B/2+d.height+b},{x:F-d.width/2,y:O-B/2+d.height+b}]:Y=[{x:F-y/2,y:O-B/2},{x:F+y/2,y:O-B/2},{x:F+y/2,y:O-B/2+m},{x:F+d.width/2,y:O-B/2+m},{x:F+d.width/2/2,y:O+B/2},{x:F-d.width/2,y:O+B/2},{x:F-d.width/2,y:O-B/2+m},{x:F-y/2,y:O-B/2+m}],nr.polygon(t,Y,z)},h}var XEt=G(()=>{"use strict";vr(),Nt(),Iu(),Tr(),Vu(),gr(),lr(),s(Q5e,"iconSquare")});async function Z5e(e,t,{config:{flowchart:r}}){let i=new Image;i.src=t?.img??"",await i.decode();let n=Number(i.naturalWidth.toString().replace("px","")),a=Number(i.naturalHeight.toString().replace("px",""));t.imageAspectRatio=n/a;let{labelStyles:o}=cr(t);t.labelStyle=o;let l=r?.wrappingWidth;t.defaultWidth=r?.wrappingWidth;let u=Math.max(t.label?l??0:0,t?.assetWidth??n),h=t.constraint==="on"&&t?.assetHeight?t.assetHeight*t.imageAspectRatio:u,d=t.constraint==="on"?h/t.imageAspectRatio:t?.assetHeight??a;t.width=Math.max(h,l??0);let{shapeSvg:p,bbox:f,label:g}=await Nr(e,t,"image-shape default"),m=t.pos==="t",y=-h/2,k=-d/2,C=t.label?8:0,S=ur.svg(p),E=pr(t,{});t.look!=="handDrawn"&&(E.roughness=0,E.fillStyle="solid");let _=S.rectangle(y,k,h,d,E),b=Math.max(h,f.width),v=d+f.height+C,x=S.rectangle(-b/2,-v/2,b,v,{...E,fill:"none",stroke:"none"}),w=p.insert(()=>_,":first-child"),R=p.insert(()=>x);if(t.img){let $=p.append("image");$.attr("href",t.img),$.attr("width",h),$.attr("height",d),$.attr("preserveAspectRatio","none"),$.attr("transform",`translate(${-h/2},${m?v/2-d:-v/2})`)}return g.attr("transform",`translate(${-f.width/2-(f.x-(f.left??0))},${m?-d/2-f.height/2-C/2:d/2-f.height/2+C/2})`),w.attr("transform",`translate(0,${m?f.height/2+C/2:-f.height/2-C/2})`),fr(t,R),t.intersect=function($){if(Ee.info("iconSquare intersect",t,$),!t.label)return nr.rect(t,$);let M=t.x??0,N=t.y??0,j=t.height??0,I=[];return m?I=[{x:M-f.width/2,y:N-j/2},{x:M+f.width/2,y:N-j/2},{x:M+f.width/2,y:N-j/2+f.height+C},{x:M+h/2,y:N-j/2+f.height+C},{x:M+h/2,y:N+j/2},{x:M-h/2,y:N+j/2},{x:M-h/2,y:N-j/2+f.height+C},{x:M-f.width/2,y:N-j/2+f.height+C}]:I=[{x:M-h/2,y:N-j/2},{x:M+h/2,y:N-j/2},{x:M+h/2,y:N-j/2+d},{x:M+f.width/2,y:N-j/2+d},{x:M+f.width/2/2,y:N+j/2},{x:M-f.width/2,y:N+j/2},{x:M-f.width/2,y:N-j/2+d},{x:M-h/2,y:N-j/2+d}],nr.polygon(t,I,$)},p}var KEt=G(()=>{"use strict";vr(),Nt(),Tr(),gr(),lr(),s(Z5e,"imageSquare")});async function J5e(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=n,o=t.look==="neo"?n*2:n,{shapeSvg:l,bbox:u}=await Nr(e,t,Or(t)),h=Math.max(u.height+a*2,t.height??0),d=Math.max(u.width+o*2,(t.width??0)-h),p=[{x:0,y:0},{x:d,y:0},{x:d+3*h/6,y:-h},{x:-3*h/6,y:-h}],f,{cssStyles:g}=t;if(t.look==="handDrawn"){let m=ur.svg(l),y=pr(t,{}),k=Ni(p),C=m.path(k,y);f=l.insert(()=>C,":first-child").attr("transform",`translate(${-d/2}, ${h/2})`),g&&f.attr("style",g)}else f=Uu(l,d,h,p);return i&&f.attr("style",i),t.width=d,t.height=h,fr(t,f),t.intersect=function(m){return nr.polygon(t,p,m)},l}var QEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),od(),s(J5e,"inv_trapezoid")});async function eEe(e,t){let{shapeSvg:r,bbox:i,label:n}=await Nr(e,t,"label"),a=r.insert("rect",":first-child");return a.attr("width",.1).attr("height",.1),r.attr("class","label edgeLabel"),n.attr("transform",`translate(${-(i.width/2)-(i.x-(i.left??0))}, ${-(i.height/2)-(i.y-(i.top??0))})`),fr(t,a),t.intersect=function(o){return nr.rect(t,o)},r}var ZEt=G(()=>{"use strict";t2(),lr(),Tr(),s(eEe,"labelRect")});async function tEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=n,o=t.look==="neo"?n*2:n,{shapeSvg:l,bbox:u}=await Nr(e,t,Or(t)),h=Math.max(u.height+a,t.height??0),d=Math.max(u.width+o,(t.width??0)-h),p=[{x:0,y:0},{x:d+3*h/6,y:0},{x:d,y:-h},{x:-(3*h)/6,y:-h}],f,{cssStyles:g}=t;if(t.look==="handDrawn"){let m=ur.svg(l),y=pr(t,{}),k=Ni(p),C=m.path(k,y);f=l.insert(()=>C,":first-child").attr("transform",`translate(${-d/2}, ${h/2})`),g&&f.attr("style",g)}else f=Uu(l,d,h,p);return i&&f.attr("style",i),t.width=d,t.height=h,fr(t,f),t.intersect=function(m){return nr.polygon(t,p,m)},l}var JEt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),od(),s(tEe,"lean_left")});async function rEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=n,o=t.look==="neo"?n*2:n,{shapeSvg:l,bbox:u}=await Nr(e,t,Or(t)),h=Math.max(u.height+a,t.height??0),d=Math.max(u.width+o,(t.width??0)-h),p=[{x:-3*h/6,y:0},{x:d,y:0},{x:d+3*h/6,y:-h},{x:0,y:-h}],f,{cssStyles:g}=t;if(t.look==="handDrawn"){let m=ur.svg(l),y=pr(t,{}),k=Ni(p),C=m.path(k,y);f=l.insert(()=>C,":first-child").attr("transform",`translate(${-d/2}, ${h/2})`),g&&f.attr("style",g)}else f=Uu(l,d,h,p);return i&&f.attr("style",i),t.width=d,t.height=h,fr(t,f),t.intersect=function(m){return nr.polygon(t,p,m)},l}var eSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),od(),s(rEe,"lean_right")});function iEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.label="",t.labelStyle=r;let n=e.insert("g").attr("class",Or(t)).attr("id",t.domId??t.id),{cssStyles:a}=t,o=Math.max(35,t?.width??0),l=Math.max(35,t?.height??0),u=7,h=[{x:o,y:0},{x:0,y:l+u/2},{x:o-2*u,y:l+u/2},{x:0,y:2*l},{x:o,y:l-u/2},{x:2*u,y:l-u/2}],d=ur.svg(n),p=pr(t,{});t.look!=="handDrawn"&&(p.roughness=0,p.fillStyle="solid");let f=Ni(h),g=d.path(f,p),m=n.insert(()=>g,":first-child");return m.attr("class","outer-path"),a&&t.look!=="handDrawn"&&m.selectAll("path").attr("style",a),i&&t.look!=="handDrawn"&&m.selectAll("path").attr("style",i),m.attr("transform",`translate(-${o/2},${-l})`),fr(t,m),t.intersect=function(y){return Ee.info("lightningBolt intersect",t,y),nr.polygon(t,h,y)},n}var tSt=G(()=>{"use strict";Nt(),lr(),gr(),vr(),Tr(),lr(),s(iEe,"lightningBolt")});async function nEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?16:n,o=t.look==="neo"?24:n;if(t.width||t.height){let C=t.width??0;t.width=(t.width??0)-a,t.widthv,":first-child").attr("class","line"),y=l.insert(()=>b,":first-child"),y.attr("class","basic label-container"),k&&y.attr("style",k)}else{let C=aEe(0,0,d,g,p,f,m);y=l.insert("path",":first-child").attr("d",C).attr("class","basic label-container outer-path").attr("style",qa(k)).attr("style",i)}return y.attr("label-offset-y",f),y.attr("transform",`translate(${-d/2}, ${-(g/2+f)})`),fr(t,y),h.attr("transform",`translate(${-(u.width/2)-(u.x-(u.left??0))}, ${-(u.height/2)+f-(u.y-(u.top??0))})`),t.intersect=function(C){let S=nr.rect(t,C),E=S.x-(t.x??0);if(p!=0&&(Math.abs(E)<(t.width??0)/2||Math.abs(E)==(t.width??0)/2&&Math.abs(S.y-(t.y??0))>(t.height??0)/2-f)){let _=f*f*(1-E*E/(p*p));_>0&&(_=Math.sqrt(_)),_=f-_,C.y-(t.y??0)>0&&(_=-_),S.y+=_}return S},l}var aEe,sEe,oEe,qz,Wz,rSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),Ar(),aEe=s((e,t,r,i,n,a,o)=>[`M${e},${t+a}`,`a${n},${a} 0,0,0 ${r},0`,`a${n},${a} 0,0,0 ${-r},0`,`l0,${i}`,`a${n},${a} 0,0,0 ${r},0`,`l0,${-i}`,`M${e},${t+a+o}`,`a${n},${a} 0,0,0 ${r},0`].join(" "),"createCylinderPathD"),sEe=s((e,t,r,i,n,a,o)=>[`M${e},${t+a}`,`M${e+r},${t+a}`,`a${n},${a} 0,0,0 ${-r},0`,`l0,${i}`,`a${n},${a} 0,0,0 ${r},0`,`l0,${-i}`,`M${e},${t+a+o}`,`a${n},${a} 0,0,0 ${r},0`].join(" "),"createOuterCylinderPathD"),oEe=s((e,t,r,i,n,a)=>[`M${e-r/2},${-i/2}`,`a${n},${a} 0,0,0 ${r},0`].join(" "),"createInnerCylinderPathD"),qz=10,Wz=10,s(nEe,"linedCylinder")});async function lEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?16:n,o=t.look==="neo"?12:n;if(t.width||t.height){let _=t.width;t.width=(_??0)*10/11-a*2,t.width<10&&(t.width=10),t.height=(t?.height??0)-o*2,t.height<10&&(t.height=10)}let{shapeSvg:l,bbox:u,label:h}=await Nr(e,t,Or(t)),d=(t?.width?t?.width:u.width)+(a??0)*2,p=(t?.height?t?.height:u.height)+(o??0)*2,f=t.look==="neo"?p/4:p/8,g=p+f,{cssStyles:m}=t,y=ur.svg(l),k=pr(t,{});t.look!=="handDrawn"&&(k.roughness=0,k.fillStyle="solid");let C=[{x:-d/2-d/2*.1,y:-g/2},{x:-d/2-d/2*.1,y:g/2},...qp(-d/2-d/2*.1,g/2,d/2+d/2*.1,g/2,f,.8),{x:d/2+d/2*.1,y:-g/2},{x:-d/2-d/2*.1,y:-g/2},{x:-d/2,y:-g/2},{x:-d/2,y:g/2*1.1},{x:-d/2,y:-g/2}],S=y.polygon(C.map(_=>[_.x,_.y]),k),E=l.insert(()=>S,":first-child");return E.attr("class","basic label-container outer-path"),m&&t.look!=="handDrawn"&&E.selectAll("path").attr("style",m),i&&t.look!=="handDrawn"&&E.selectAll("path").attr("style",i),E.attr("transform",`translate(0,${-f/2})`),h.attr("transform",`translate(${-d/2+(t.padding??0)+d/2*.1/2-(u.x-(u.left??0))},${-p/2+(t.padding??0)-f/2-(u.y-(u.top??0))})`),fr(t,E),t.intersect=function(_){return nr.polygon(t,C,_)},l}var iSt=G(()=>{"use strict";lr(),Tr(),vr(),gr(),s(lEe,"linedWaveEdgedRect")});async function cEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?16:n,o=t.look==="neo"?12:n,l=t.look==="neo"?10:5;(t.width||t.height)&&(t.width=Math.max((t?.width??0)-a*2-2*l,10),t.height=Math.max((t?.height??0)-o*2-2*l,10));let{shapeSvg:u,bbox:h,label:d}=await Nr(e,t,Or(t)),p=(t?.width?t?.width:h.width)+a*2+2*l,f=(t?.height?t?.height:h.height)+o*2+2*l,g=p-2*l,m=f-2*l,y=-g/2,k=-m/2,{cssStyles:C}=t,S=ur.svg(u),E=pr(t,{}),_=[{x:y-l,y:k+l},{x:y-l,y:k+m+l},{x:y+g-l,y:k+m+l},{x:y+g-l,y:k+m},{x:y+g,y:k+m},{x:y+g,y:k+m-l},{x:y+g+l,y:k+m-l},{x:y+g+l,y:k-l},{x:y+l,y:k-l},{x:y+l,y:k},{x:y,y:k},{x:y,y:k+l}],b=[{x:y,y:k+l},{x:y+g-l,y:k+l},{x:y+g-l,y:k+m},{x:y+g,y:k+m},{x:y+g,y:k},{x:y,y:k}];t.look!=="handDrawn"&&(E.roughness=0,E.fillStyle="solid");let v=Ni(_),x=S.path(v,E),w=Ni(b),R=S.path(w,E);t.look!=="handDrawn"&&(x=mz(x),R=mz(R));let $=u.insert("g",":first-child");return $.insert(()=>x),$.insert(()=>R),$.attr("class","basic label-container outer-path"),C&&t.look!=="handDrawn"&&$.selectAll("path").attr("style",C),i&&t.look!=="handDrawn"&&$.selectAll("path").attr("style",i),d.attr("transform",`translate(${-(h.width/2)-l-(h.x-(h.left??0))}, ${-(h.height/2)+l-(h.y-(h.top??0))})`),fr(t,$),t.intersect=function(M){return nr.polygon(t,_,M)},u}var nSt=G(()=>{"use strict";lr(),gr(),vr(),Tr(),s(cEe,"multiRect")});async function uEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let{shapeSvg:n,bbox:a,label:o}=await Nr(e,t,Or(t)),l=t.padding??0,u=t.look==="neo"?16:l,h=t.look==="neo"?12:l,d=!0;(t.width||t.height)&&(d=!1,t.width=(t?.width??0)-u*2,t.height=(t?.height??0)-h*3);let p=Math.max(a.width,t?.width??0)+u*2,f=Math.max(a.height,t?.height??0)+h*3,g=t.look==="neo"?f/4:f/8,m=f+(d?g/2:-g/2),y=-p/2,k=-m/2,C=10,{cssStyles:S}=t,E=qp(y-C,k+m+C,y+p-C,k+m+C,g,.8),_=E?.[E.length-1],b=[{x:y-C,y:k+C},{x:y-C,y:k+m+C},...E,{x:y+p-C,y:_.y-C},{x:y+p,y:_.y-C},{x:y+p,y:_.y-2*C},{x:y+p+C,y:_.y-2*C},{x:y+p+C,y:k-C},{x:y+C,y:k-C},{x:y+C,y:k},{x:y,y:k},{x:y,y:k+C}],v=[{x:y,y:k+C},{x:y+p-C,y:k+C},{x:y+p-C,y:_.y-C},{x:y+p,y:_.y-C},{x:y+p,y:k},{x:y,y:k}],x=ur.svg(n),w=pr(t,{});t.look!=="handDrawn"&&(w.roughness=0,w.fillStyle="solid");let R=Ni(b),$=x.path(R,w),M=Ni(v),N=x.path(M,w),j=n.insert(()=>$,":first-child");return j.insert(()=>N),j.attr("class","basic label-container outer-path"),S&&t.look!=="handDrawn"&&j.selectAll("path").attr("style",S),i&&t.look!=="handDrawn"&&j.selectAll("path").attr("style",i),j.attr("transform",`translate(0,${-g/2})`),o.attr("transform",`translate(${-(a.width/2)-C-(a.x-(a.left??0))}, ${-(a.height/2)+C-g/2-(a.y-(a.top??0))})`),fr(t,j),t.intersect=function(I){return nr.polygon(t,b,I)},n}var aSt=G(()=>{"use strict";lr(),Tr(),vr(),gr(),s(uEe,"multiWaveEdgedRectangle")});async function hEe(e,t,{config:{themeVariables:r}}){let{labelStyles:i,nodeStyles:n}=cr(t);t.labelStyle=i,t.useHtmlLabels||Ps(Rr())||(t.centerLabel=!0);let{shapeSvg:a,bbox:o,label:l}=await Nr(e,t,Or(t)),u=Math.max(o.width+(t.padding??0)*2,t?.width??0),h=Math.max(o.height+(t.padding??0)*2,t?.height??0),d=-u/2,p=-h/2,{cssStyles:f}=t,g=ur.svg(a),m=pr(t,{fill:r.noteBkgColor,stroke:r.noteBorderColor});t.look!=="handDrawn"&&(m.roughness=0,m.fillStyle="solid");let y=g.rectangle(d,p,u,h,m),k=a.insert(()=>y,":first-child");return k.attr("class","basic label-container outer-path"),l.attr("class","label noteLabel"),f&&t.look!=="handDrawn"&&k.selectAll("path").attr("style",f),n&&t.look!=="handDrawn"&&k.selectAll("path").attr("style",n),l.attr("transform",`translate(${-o.width/2-(o.x-(o.left??0))}, ${-(o.height/2)-(o.y-(o.top??0))})`),fr(t,k),t.intersect=function(C){return nr.rect(t,C)},a}var sSt=G(()=>{"use strict";vr(),Tr(),gr(),lr(),Gr(),Gr(),s(hEe,"note")});async function dEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let{shapeSvg:n,bbox:a}=await Nr(e,t,Or(t)),o=a.width+(t.padding??0),l=a.height+(t.padding??0),u=o+l,h=.5,d=[{x:u/2,y:0},{x:u,y:-u/2},{x:u/2,y:-u},{x:0,y:-u/2}],p,{cssStyles:f}=t;if(t.look==="handDrawn"){let g=ur.svg(n),m=pr(t,{}),y=pEe(0,0,u),k=g.path(y,m);p=n.insert(()=>k,":first-child").attr("transform",`translate(${-u/2+h}, ${u/2})`),f&&p.attr("style",f)}else p=Uu(n,u,u,d),p.attr("transform",`translate(${-u/2+h}, ${u/2})`);return i&&p.attr("style",i),fr(t,p),t.calcIntersect=function(g,m){let y=g.width,k=[{x:y/2,y:0},{x:y,y:-y/2},{x:y/2,y:-y},{x:0,y:-y/2}],C=nr.polygon(g,k,m);return{x:C.x-.5,y:C.y-.5}},t.intersect=function(g){return this.calcIntersect(t,g)},n}var pEe,oSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),od(),pEe=s((e,t,r)=>[`M${e+r/2},${t}`,`L${e+r},${t-r/2}`,`L${e+r/2},${t-r}`,`L${e},${t-r/2}`,"Z"].join(" "),"createDecisionBoxPathD"),s(dEe,"question")});async function fEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?21:n??0,o=t.look==="neo"?12:n??0,{shapeSvg:l,bbox:u,label:h}=await Nr(e,t,Or(t)),d=u.width+(t.look==="neo"?a*2:a),p=Math.max(u.height+(t.look==="neo"?o*2:o),t.height??0),f=p/4,g=-Math.max(d,(t.width??0)-f)/2,m=-p/2,y=m/2,k=[{x:g+y,y:m},{x:g,y:0},{x:g+y,y:-m},{x:-g,y:-m},{x:-g,y:m}],{cssStyles:C}=t,S=ur.svg(l),E=pr(t,{});t.look!=="handDrawn"&&(E.roughness=0,E.fillStyle="solid");let _=Ni(k),b=S.path(_,E),v=l.insert(()=>b,":first-child");return v.attr("class","basic label-container outer-path"),C&&t.look!=="handDrawn"&&v.selectAll("path").attr("style",C),i&&t.look!=="handDrawn"&&v.selectAll("path").attr("style",i),v.attr("transform",`translate(${-y/2},0)`),h.attr("transform",`translate(${-y/2-u.width/2-(u.x-(u.left??0))}, ${-(u.height/2)-(u.y-(u.top??0))})`),fr(t,v),t.intersect=function(x){return nr.polygon(t,k,x)},l}var lSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(fEe,"rect_left_inv_arrow")}),gEe,ud,jz=G(()=>{"use strict";Gr(),Er(),ec(),gEe=s(async(e,t,r,i=!1,n=!1)=>{let a=t||"";typeof a=="object"&&(a=a[0]);let o=it(),l=Ps(o);return await Io(e,a,{style:r,isTitle:i,useHtmlLabels:l,markdown:!1,isNode:n,width:Number.POSITIVE_INFINITY},o)},"createLabel"),ud=gEe});async function mEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n;t.cssClasses?n="node "+t.cssClasses:n="node default";let a=e.insert("g").attr("class",n).attr("id",t.domId||t.id),o=a.insert("g"),l=a.insert("g").attr("class","label").attr("style",i),u=t.description,h=t.label,d=await ud(l,h,t.labelStyle,!0,!0),p={width:0,height:0};if(Ps(it())){let w=d.children[0],R=zt(d);p=w.getBoundingClientRect(),R.attr("width",p.width),R.attr("height",p.height)}Ee.info("Text 2",u);let f=u||[],g=d.getBBox(),m=await ud(l,Array.isArray(f)?f.join("
    "):f,t.labelStyle,!0,!0),y=m.children[0],k=zt(m);p=y.getBoundingClientRect(),k.attr("width",p.width),k.attr("height",p.height);let C=(t.padding||0)/2;zt(m).attr("transform","translate( "+(p.width>g.width?0:(g.width-p.width)/2)+", "+(g.height+C+5)+")"),zt(d).attr("transform","translate( "+(p.width(Ee.debug("Rough node insert CXC",$),M),":first-child"),v=a.insert(()=>(Ee.debug("Rough node insert CXC",$),$),":first-child")}else v=o.insert("rect",":first-child"),x=o.insert("line"),v.attr("class","outer title-state").attr("style",i).attr("x",-p.width/2-C).attr("y",-p.height/2-C).attr("width",p.width+(t.padding||0)).attr("height",p.height+(t.padding||0)),x.attr("class","divider").attr("x1",-p.width/2-C).attr("x2",p.width/2+C).attr("y1",-p.height/2-C+g.height+C).attr("y2",-p.height/2-C+g.height+C);return fr(t,v),t.intersect=function(w){return nr.rect(t,w)},a}var cSt=G(()=>{"use strict";pi(),lr(),jz(),Tr(),gr(),vr(),Er(),Vu(),Nt(),Gr(),s(mEe,"rectWithTitle")});async function yEe(e,t,{config:{themeVariables:r}}){let i=r?.radius??5,n={rx:i,ry:i,classes:"",labelPaddingX:(t?.padding??0)*1,labelPaddingY:(t?.padding??0)*1};return e2(e,t,n)}var uSt=G(()=>{"use strict";t2(),s(yEe,"roundedRect")});async function vEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.look==="neo"?16:t.padding??0,a=t.look==="neo"?12:t.padding??0,{shapeSvg:o,bbox:l,label:u}=await Nr(e,t,Or(t)),h=(t?.width??l.width)+n*2+(t.look==="neo"?Vp:Vp*2),d=(t?.height??l.height)+a*2,p=h-Vp,f=d,g=Vp-h/2,m=-d/2,{cssStyles:y}=t,k=ur.svg(o),C=pr(t,{});t.look!=="handDrawn"&&(C.roughness=0,C.fillStyle="solid");let S=[{x:g,y:m},{x:g+p,y:m},{x:g+p,y:m+f},{x:g-Vp,y:m+f},{x:g-Vp,y:m},{x:g,y:m},{x:g,y:m+f}],E=k.polygon(S.map(b=>[b.x,b.y]),C),_=o.insert(()=>E,":first-child");return _.attr("class","basic label-container outer-path").attr("style",qa(y)),i&&t.look!=="handDrawn"&&_.selectAll("path").attr("style",i),y&&t.look!=="handDrawn"&&_.selectAll("path").attr("style",i),u.attr("transform",`translate(${Vp/2-l.width/2-(l.x-(l.left??0))}, ${-(l.height/2)-(l.y-(l.top??0))})`),fr(t,_),t.intersect=function(b){return nr.rect(t,b)},o}var Vp,hSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),Ar(),Vp=8,s(vEe,"shadedProcess")});async function bEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?16:n,o=t.look==="neo"?12:n;(t.width||t.height)&&(t.width=Math.max((t?.width??0)-a*2,10),t.height=Math.max((t?.height??0)/1.5-o*2,10));let{shapeSvg:l,bbox:u,label:h}=await Nr(e,t,Or(t)),d=(t?.width?t?.width:u.width)+a*2,p=((t?.height?t?.height:u.height)+o*2)*1.5,f=d,g=p/1.5,m=-f/2,y=-g/2,{cssStyles:k}=t,C=ur.svg(l),S=pr(t,{});t.look!=="handDrawn"&&(S.roughness=0,S.fillStyle="solid");let E=[{x:m,y},{x:m,y:y+g},{x:m+f,y:y+g},{x:m+f,y:y-g/2}],_=Ni(E),b=C.path(_,S),v=l.insert(()=>b,":first-child");return v.attr("class","basic label-container outer-path"),k&&t.look!=="handDrawn"&&v.selectChildren("path").attr("style",k),i&&t.look!=="handDrawn"&&v.selectChildren("path").attr("style",i),v.attr("transform",`translate(0, ${g/4})`),h.attr("transform",`translate(${-f/2+(t.padding??0)-(u.x-(u.left??0))}, ${-g/4+(t.padding??0)-(u.y-(u.top??0))})`),fr(t,v),t.intersect=function(x){return nr.polygon(t,E,x)},l}var dSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(bEe,"slopedRect")});async function xEe(e,t){let r=t.padding??0,i=t.look==="neo"?16:r*2,n=t.look==="neo"?12:r,a={rx:0,ry:0,classes:"",labelPaddingX:t.labelPaddingX??i,labelPaddingY:n};return e2(e,t,a)}var pSt=G(()=>{"use strict";t2(),s(xEe,"squareRect")});async function TEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?20:n,o=t.look==="neo"?12:n,{shapeSvg:l,bbox:u}=await Nr(e,t,Or(t)),h=u.height+(t.look==="neo"?o*2:o),d=u.width+h/4+(t.look==="neo"?a*2:a),p=h/2,{cssStyles:f}=t,g=ur.svg(l),m=pr(t,{});t.look!=="handDrawn"&&(m.roughness=0,m.fillStyle="solid");let y=[{x:-d/2+p,y:-h/2},{x:d/2-p,y:-h/2},...ju(-d/2+p,0,p,50,90,270),{x:d/2-p,y:h/2},...ju(d/2-p,0,p,50,270,450)],k=Ni(y),C=g.path(k,m),S=l.insert(()=>C,":first-child");return S.attr("class","basic label-container outer-path"),f&&t.look!=="handDrawn"&&S.selectChildren("path").attr("style",f),i&&t.look!=="handDrawn"&&S.selectChildren("path").attr("style",i),fr(t,S),t.intersect=function(E){return nr.polygon(t,y,E)},l}var fSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(TEe,"stadium")});async function kEe(e,t){let r={rx:t.look==="neo"?3:5,ry:t.look==="neo"?3:5,classes:"flowchart-node"};return e2(e,t,r)}var gSt=G(()=>{"use strict";t2(),s(kEe,"state")});function CEe(e,t,{config:{themeVariables:r}}){let{labelStyles:i,nodeStyles:n}=cr(t);t.labelStyle=i;let{cssStyles:a}=t,{lineColor:o,stateBorder:l,nodeBorder:u,nodeShadow:h}=r;(t.width||t.height)&&((t.width??0)<14&&(t.width=14),(t.height??0)<14&&(t.height=14)),t.width||(t.width=14),t.height||(t.height=14);let d=e.insert("g").attr("class","node default").attr("id",t.domId??t.id),p=ur.svg(d),f=pr(t,{});t.look!=="handDrawn"&&(f.roughness=0,f.fillStyle="solid");let g=p.circle(0,0,t.width,{...f,stroke:o,strokeWidth:2}),m=l??u,y=(t.width??0)*5/14,k=p.circle(0,0,y,{...f,fill:m,stroke:m,strokeWidth:2,fillStyle:"solid"}),C=d.insert(()=>g,":first-child");if(C.insert(()=>k),t.look!=="handDrawn"&&C.attr("class","outer-path"),a&&C.selectAll("path").attr("style",a),n&&C.selectAll("path").attr("style",n),t.width<25&&h&&t.look!=="handDrawn"){let S=e.node()?.ownerSVGElement?.id??"",E=S?`${S}-drop-shadow-small`:"drop-shadow-small";C.attr("style",`filter:url(#${E})`)}return fr(t,C),t.intersect=function(S){return nr.circle(t,(t.width??0)/2,S)},d}var mSt=G(()=>{"use strict";vr(),Tr(),gr(),lr(),s(CEe,"stateEnd")});function wEe(e,t,{config:{themeVariables:r}}){let{lineColor:i,nodeShadow:n}=r;(t.width||t.height)&&((t.width??0)<14&&(t.width=14),(t.height??0)<14&&(t.height=14)),t.width||(t.width=14),t.height||(t.height=14);let a=e.insert("g").attr("class","node default").attr("id",t.domId||t.id),o;if(t.look==="handDrawn"){let l=ur.svg(a).circle(0,0,t.width,jwe(i));o=a.insert(()=>l),o.attr("class","state-start").attr("r",(t.width??7)/2).attr("width",t.width??14).attr("height",t.height??14)}else o=a.insert("circle",":first-child"),o.attr("class","state-start").attr("r",(t.width??7)/2).attr("width",t.width??14).attr("height",t.height??14);if(t.width<25&&n&&t.look!=="handDrawn"){let l=e.node()?.ownerSVGElement?.id??"",u=l?`${l}-drop-shadow-small`:"drop-shadow-small";o.attr("style",`filter:url(#${u})`)}return fr(t,o),t.intersect=function(l){return nr.circle(t,(t.width??7)/2,l)},a}var ySt=G(()=>{"use strict";vr(),Tr(),gr(),lr(),s(wEe,"stateStart")});async function EEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t?.padding??8,a=t.look==="neo"?28:n,o=t.look==="neo"?12:n,{shapeSvg:l,bbox:u}=await Nr(e,t,Or(t)),h=Math.max(u.width+2*vg+a,t.width??0),d=Math.max(u.height+o,t.height??0),p=h-2*vg,f=d,g=-h/2,m=-d/2,y=[{x:0,y:0},{x:p,y:0},{x:p,y:-f},{x:0,y:-f},{x:0,y:0},{x:-8,y:0},{x:p+8,y:0},{x:p+8,y:-f},{x:-8,y:-f},{x:-8,y:0}];if(t.look==="handDrawn"){let k=ur.svg(l),C=pr(t,{}),S=k.rectangle(g,m,p+16,f,C),E=k.line(g+vg,m,g+vg,m+f,C),_=k.line(g+vg+p,m,g+vg+p,m+f,C);l.insert(()=>E,":first-child"),l.insert(()=>_,":first-child");let b=l.insert(()=>S,":first-child"),{cssStyles:v}=t;b.attr("class","basic label-container").attr("style",qa(v)),fr(t,b)}else{let k=Uu(l,p,f,y);i&&k.attr("style",i),fr(t,k)}return t.intersect=function(k){return nr.polygon(t,y,k)},l}var vg,vSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),od(),Ar(),vg=8,s(EEe,"subroutine")});async function SEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?16:n,o=t.look==="neo"?12:n;(t.width||t.height)&&(t.height=Math.max((t?.height??0)-o*2,10),t.width=Math.max((t?.width??0)-a*2-W_*(t.height+o*2),10));let{shapeSvg:l,bbox:u}=await Nr(e,t,Or(t)),h=(t?.height?t?.height:u.height)+o*2,d=W_*h,p=W_*h,f=(t?.width?t?.width:u.width)+a*2+d-d,g=h,m=-f/2,y=-g/2,{cssStyles:k}=t,C=ur.svg(l),S=pr(t,{}),E=[{x:m-d/2,y},{x:m+f+d/2,y},{x:m+f+d/2,y:y+g},{x:m-d/2,y:y+g}],_=[{x:m+f-d/2,y:y+g},{x:m+f+d/2,y:y+g},{x:m+f+d/2,y:y+g-p}];t.look!=="handDrawn"&&(S.roughness=0,S.fillStyle="solid");let b=Ni(E),v=C.path(b,S),x=Ni(_),w=C.path(x,{...S,fillStyle:"solid"}),R=l.insert(()=>w,":first-child");return R.insert(()=>v,":first-child"),R.attr("class","basic label-container outer-path"),k&&t.look!=="handDrawn"&&R.selectAll("path").attr("style",k),i&&t.look!=="handDrawn"&&R.selectAll("path").attr("style",i),fr(t,R),t.intersect=function($){return nr.polygon(t,E,$)},l}var W_,bSt=G(()=>{"use strict";lr(),gr(),vr(),Tr(),W_=.2,s(SEe,"taggedRect")});async function _Ee(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let{shapeSvg:n,bbox:a,label:o}=await Nr(e,t,Or(t)),l=Math.max(a.width+(t.padding??0)*2,t?.width??0),u=Math.max(a.height+(t.padding??0)*2,t?.height??0),h=u/8,d=.2*l,p=.2*u,f=u+h,{cssStyles:g}=t,m=ur.svg(n),y=pr(t,{});t.look!=="handDrawn"&&(y.roughness=0,y.fillStyle="solid");let k=[{x:-l/2-l/2*.1,y:f/2},...qp(-l/2-l/2*.1,f/2,l/2+l/2*.1,f/2,h,.8),{x:l/2+l/2*.1,y:-f/2},{x:-l/2-l/2*.1,y:-f/2}],C=-l/2+l/2*.1,S=-f/2-p*.4,E=[{x:C+l-d,y:(S+u)*1.3},{x:C+l,y:S+u-p},{x:C+l,y:(S+u)*.9},...qp(C+l,(S+u)*1.25,C+l-d,(S+u)*1.3,-u*.02,.5)],_=Ni(k),b=m.path(_,y),v=Ni(E),x=m.path(v,{...y,fillStyle:"solid"}),w=n.insert(()=>x,":first-child");return w.insert(()=>b,":first-child"),w.attr("class","basic label-container outer-path"),g&&t.look!=="handDrawn"&&w.selectAll("path").attr("style",g),i&&t.look!=="handDrawn"&&w.selectAll("path").attr("style",i),w.attr("transform",`translate(0,${-h/2})`),o.attr("transform",`translate(${-l/2+(t.padding??0)-(a.x-(a.left??0))},${-u/2+(t.padding??0)-h/2-(a.y-(a.top??0))})`),fr(t,w),t.intersect=function(R){return nr.polygon(t,k,R)},n}var xSt=G(()=>{"use strict";lr(),Tr(),vr(),gr(),s(_Ee,"taggedWaveEdgedRectangle")});async function AEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let{shapeSvg:n,bbox:a}=await Nr(e,t,Or(t)),o=Math.max(a.width+(t.padding??0),t?.width||0),l=Math.max(a.height+(t.padding??0),t?.height||0),u=-o/2,h=-l/2,d=n.insert("rect",":first-child");return d.attr("class","text").attr("style",i).attr("rx",0).attr("ry",0).attr("x",u).attr("y",h).attr("width",o).attr("height",l),fr(t,d),t.intersect=function(p){return nr.rect(t,p)},n}var TSt=G(()=>{"use strict";lr(),Tr(),gr(),s(AEe,"text")});async function REe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?12:n/2,o=t.height??0;if(t.height&&(t.height=t.height-a,t.height_,":first-child"),y=l.insert(()=>E,":first-child"),y.attr("class","basic label-container"),m&&y.attr("style",m)}else{let k=$Ee(0,0,g,d,f,p);y=l.insert("path",":first-child").attr("d",k).attr("class","basic label-container").attr("style",qa(m)).attr("style",i),y.attr("class","basic label-container outer-path"),m&&y.selectAll("path").attr("style",m),i&&y.selectAll("path").attr("style",i)}return y.attr("label-offset-x",f),y.attr("transform",`translate(${-g/2}, ${d/2} )`),h.attr("transform",`translate(${-(u.width/2)-f-(u.x-(u.left??0))}, ${-(u.height/2)-(u.y-(u.top??0))})`),fr(t,y),t.intersect=function(k){let C=nr.rect(t,k),S=C.y-(t.y??0);if(p!=0&&(Math.abs(S)<(t.height??0)/2||Math.abs(S)==(t.height??0)/2&&Math.abs(C.x-(t.x??0))>(t.width??0)/2-f)){let E=f*f*(1-S*S/(p*p));E!=0&&(E=Math.sqrt(Math.abs(E))),E=f-E,k.x-(t.x??0)>0&&(E=-E),C.x+=E}return C},l}var $Ee,LEe,IEe,Vz,Uz,kSt=G(()=>{"use strict";lr(),gr(),vr(),Tr(),Ar(),$Ee=s((e,t,r,i,n,a)=>`M${e},${t} + a${n},${a} 0,0,1 0,${-i} + l${r},0 + a${n},${a} 0,0,1 0,${i} + M${r},${-i} + a${n},${a} 0,0,0 0,${i} + l${-r},0`,"createCylinderPathD"),LEe=s((e,t,r,i,n,a)=>[`M${e},${t}`,`M${e+r},${t}`,`a${n},${a} 0,0,0 0,${-i}`,`l${-r},0`,`a${n},${a} 0,0,0 0,${i}`,`l${r},0`].join(" "),"createOuterCylinderPathD"),IEe=s((e,t,r,i,n,a)=>[`M${e+r/2},${-i/2}`,`a${n},${a} 0,0,0 0,${i}`].join(" "),"createInnerCylinderPathD"),Vz=5,Uz=10,s(REe,"tiltedCylinder")});async function MEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=(t.look,n),o=t.look==="neo"?n*2:n,{shapeSvg:l,bbox:u}=await Nr(e,t,Or(t)),h=Math.max(u.height+a,t.height??0),d=Math.max(u.width+o,(t.width??0)-h),p=[{x:-3*h/6,y:0},{x:d+3*h/6,y:0},{x:d,y:-h},{x:0,y:-h}],f,{cssStyles:g}=t;if(t.look==="handDrawn"){let m=ur.svg(l),y=pr(t,{}),k=Ni(p),C=m.path(k,y);f=l.insert(()=>C,":first-child").attr("transform",`translate(${-d/2}, ${h/2})`),g&&f.attr("style",g)}else f=Uu(l,d,h,p);return i&&f.attr("style",i),t.width=d,t.height=h,fr(t,f),t.intersect=function(m){return nr.polygon(t,p,m)},l}var CSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),od(),s(MEe,"trapezoid")});async function DEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?16:n,o=t.look==="neo"?12:n,l=15,u=5;(t.width||t.height)&&(t.height=(t.height??0)-o*2,t.heightS,":first-child");return E.attr("class","basic label-container outer-path"),g&&t.look!=="handDrawn"&&E.selectChildren("path").attr("style",g),i&&t.look!=="handDrawn"&&E.selectChildren("path").attr("style",i),fr(t,E),t.intersect=function(_){return nr.polygon(t,k,_)},h}var wSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(DEe,"trapezoidalPentagon")});async function NEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?n*2:n;(t.width||t.height)&&(t.width=((t?.width??0)-a)/2,t.widthS,":first-child").attr("transform",`translate(${-p/2}, ${p/2})`).attr("class","outer-path");return m&&t.look!=="handDrawn"&&E.selectChildren("path").attr("style",m),i&&t.look!=="handDrawn"&&E.selectChildren("path").attr("style",i),t.width=d,t.height=p,fr(t,E),u.attr("transform",`translate(${-l.width/2-(l.x-(l.left??0))}, ${p/2-(l.height+(t.padding??0)/(h?2:1)-(l.y-(l.top??0)))})`),t.intersect=function(_){return Ee.info("Triangle intersect",t,g,_),nr.polygon(t,g,_)},o}var Yz,Hz,ESt=G(()=>{"use strict";Nt(),lr(),Tr(),gr(),vr(),lr(),mi(),Er(),Yz=10,Hz=10,s(NEe,"triangle")});async function OEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?16:n,o=t.look==="neo"?12:n,l=!0;(t.width||t.height)&&(l=!1,t.width=(t?.width??0)-a*2,t.width<10&&(t.width=10),t.height=(t?.height??0)-o*2,t.height<10&&(t.height=10));let{shapeSvg:u,bbox:h,label:d}=await Nr(e,t,Or(t)),p=(t?.width?t?.width:h.width)+(a??0)*2,f=(t?.height?t?.height:h.height)+(o??0)*2,g=t.look==="neo"?f/4:f/8,m=f+(l?g:-g),{cssStyles:y}=t,k=14-p,C=k>0?k/2:0,S=ur.svg(u),E=pr(t,{});t.look!=="handDrawn"&&(E.roughness=0,E.fillStyle="solid");let _=[{x:-p/2-C,y:m/2},...qp(-p/2-C,m/2,p/2+C,m/2,g,.8),{x:p/2+C,y:-m/2},{x:-p/2-C,y:-m/2}],b=Ni(_),v=S.path(b,E),x=u.insert(()=>v,":first-child");return x.attr("class","basic label-container outer-path"),y&&t.look!=="handDrawn"&&x.selectAll("path").attr("style",y),i&&t.look!=="handDrawn"&&x.selectAll("path").attr("style",i),x.attr("transform",`translate(0,${-g/2})`),d.attr("transform",`translate(${-p/2+(t.padding??0)-(h.x-(h.left??0))},${-f/2+(t.padding??0)-g-(h.y-(h.top??0))})`),fr(t,x),t.intersect=function(w){return nr.polygon(t,_,w)},u}var SSt=G(()=>{"use strict";lr(),Tr(),vr(),gr(),s(OEe,"waveEdgedRectangle")});async function BEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.padding??0,a=t.look==="neo"?16:n,o=t.look==="neo"?20:n;if(t.width||t.height){t.width=t?.width??0,t.width<20&&(t.width=20),t.height=t?.height??0,t.height<10&&(t.height=10);let _=Math.min(t.height*.2,t.height/4);t.height=Math.ceil(t.height-o-_*(20/9)),t.width=t.width-a*2}let{shapeSvg:l,bbox:u}=await Nr(e,t,Or(t)),h=(t?.width?t?.width:u.width)+a*2,d=(t?.height?t?.height:u.height)+o,p=d/8,f=d+p*2,{cssStyles:g}=t,m=ur.svg(l),y=pr(t,{});t.look!=="handDrawn"&&(y.roughness=0,y.fillStyle="solid");let k=[{x:-h/2,y:f/2},...qp(-h/2,f/2,h/2,f/2,p,1),{x:h/2,y:-f/2},...qp(h/2,-f/2,-h/2,-f/2,p,-1)],C=Ni(k),S=m.path(C,y),E=l.insert(()=>S,":first-child");return E.attr("class","basic label-container"),g&&t.look!=="handDrawn"&&E.selectAll("path").attr("style",g),i&&t.look!=="handDrawn"&&E.selectAll("path").attr("style",i),fr(t,E),t.intersect=function(_){return nr.polygon(t,k,_)},l}var _St=G(()=>{"use strict";lr(),Tr(),gr(),vr(),s(BEe,"waveRectangle")});async function PEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t.look==="neo"?16:t.padding??0,a=t.look==="neo"?12:t.padding??0;(t.width||t.height)&&(t.width=Math.max((t?.width??0)-n*2-sa,10),t.height=Math.max((t?.height??0)-a*2-sa,10));let{shapeSvg:o,bbox:l,label:u}=await Nr(e,t,Or(t)),h=(t?.width?t?.width:l.width)+n*2+sa,d=(t?.height?t?.height:l.height)+a*2+sa,p=h-sa,f=d-sa,g=-p/2,m=-f/2,{cssStyles:y}=t,k=ur.svg(o),C=pr(t,{}),S=[{x:g-sa,y:m-sa},{x:g-sa,y:m+f},{x:g+p,y:m+f},{x:g+p,y:m-sa}],E=`M${g-sa},${m-sa} L${g+p},${m-sa} L${g+p},${m+f} L${g-sa},${m+f} L${g-sa},${m-sa} + M${g-sa},${m} L${g+p},${m} + M${g},${m-sa} L${g},${m+f}`;t.look!=="handDrawn"&&(C.roughness=0,C.fillStyle="solid");let _=k.path(E,C),b=o.insert(()=>_,":first-child");return b.attr("transform",`translate(${sa/2}, ${sa/2})`),b.attr("class","basic label-container outer-path"),y&&t.look!=="handDrawn"&&b.selectAll("path").attr("style",y),i&&t.look!=="handDrawn"&&b.selectAll("path").attr("style",i),u.attr("transform",`translate(${-(l.width/2)+sa/2-(l.x-(l.left??0))}, ${-(l.height/2)+sa/2-(l.y-(l.top??0))})`),fr(t,b),t.intersect=function(v){return nr.polygon(t,S,v)},o}var sa,ASt=G(()=>{"use strict";lr(),gr(),vr(),Tr(),sa=10,s(PEe,"windowPane")});async function Gz(e,t){let r=t;r.alias&&(t.label=r.alias);let{theme:i,themeVariables:n}=Rr(),{rowEven:a,rowOdd:o,nodeBorder:l,borderColorArray:u}=n;if(t.look==="handDrawn"){let{themeVariables:se}=Rr(),{background:le}=se,Te={...t,id:t.id+"-background",domId:(t.domId||t.id)+"-background",look:"default",cssStyles:["stroke: none",`fill: ${le}`]};await Gz(e,Te)}let h=Rr();t.useHtmlLabels=h.htmlLabels;let d=h.er?.diagramPadding??10,p=h.er?.entityPadding??6,{cssStyles:f}=t,{labelStyles:g,nodeStyles:m}=cr(t);if(r.attributes.length===0&&t.label){let se={rx:0,ry:0,labelPaddingX:d,labelPaddingY:d*1.5,classes:""};al(t.label,h)+se.labelPaddingX*20){let se=C.width+d*2-(b+v+x+w);b+=se/M,v+=se/M,x>0&&(x+=se/M),w>0&&(w+=se/M)}let j=b+v+x+w,I=ur.svg(k),z=pr(t,{});t.look!=="handDrawn"&&(z.roughness=0,z.fillStyle="solid");let F=0;_.length>0&&(F=_.reduce((se,le)=>se+(le?.rowHeight??0),0));let O=Math.max(N.width+d*2,t?.width||0,j),B=Math.max((F??0)+C.height,t?.height||0),Y=-O/2,U=-B/2;if(k.selectAll("g:not(:first-child)").each((se,le,Te)=>{let oe=zt(Te[le]),fe=oe.attr("transform"),ge=0,ke=0;if(fe){let Ne=RegExp(/translate\(([^,]+),([^)]+)\)/).exec(fe);Ne&&(ge=parseFloat(Ne[1]),ke=parseFloat(Ne[2]),oe.attr("class").includes("attribute-name")?ge+=b:oe.attr("class").includes("attribute-keys")?ge+=b+v:oe.attr("class").includes("attribute-comment")&&(ge+=b+v+x))}oe.attr("transform",`translate(${Y+d/2+ge}, ${ke+U+C.height+p/2})`)}),k.select(".name").attr("transform","translate("+-C.width/2+", "+(U+p/2)+")"),i!=null&&Xz.has(i)){let se=r.colorIndex??0;k.attr("data-color-id",`color-${se%u.length}`)}let H=I.rectangle(Y,U,O,B,z),J=k.insert(()=>H,":first-child").attr("class","outer-path").attr("style",f.join(""));E.push(0);for(let[se,le]of _.entries()){let Te=(se+1)%2===0&&le.yOffset!==0,oe=I.rectangle(Y,C.height+U+le?.yOffset,O,le?.rowHeight,{...z,fill:Te?a:o,stroke:l});k.insert(()=>oe,"g.label").attr("style",f.join("")).attr("class",`row-rect-${Te?"even":"odd"}`)}let te=1e-4,ie=i2(Y,C.height+U,O+Y,C.height+U,te),ue=I.polygon(ie.map(se=>[se.x,se.y]),z);if(k.insert(()=>ue).attr("class","divider"),ie=i2(b+Y,C.height+U,b+Y,B+U,te),ue=I.polygon(ie.map(se=>[se.x,se.y]),z),k.insert(()=>ue).attr("class","divider"),R){let se=b+v+Y;ie=i2(se,C.height+U,se,B+U,te),ue=I.polygon(ie.map(le=>[le.x,le.y]),z),k.insert(()=>ue).attr("class","divider")}if($){let se=b+v+x+Y;ie=i2(se,C.height+U,se,B+U,te),ue=I.polygon(ie.map(le=>[le.x,le.y]),z),k.insert(()=>ue).attr("class","divider")}for(let se of E){let le=C.height+U+se;ie=i2(Y,le,O+Y,le,te),ue=I.polygon(ie.map(Te=>[Te.x,Te.y]),z),k.insert(()=>ue).attr("class","divider")}if(fr(t,J),m&&t.look!=="handDrawn")if(i!=null&&FEe.has(i))k.selectAll("path").attr("style",m);else{let se=m.split(";")?.filter(le=>le.includes("stroke"))?.map(le=>`${le}`).join("; ");k.selectAll("path").attr("style",se??""),k.selectAll(".row-rect-even path").attr("style",m)}return t.intersect=function(se){return nr.rect(t,se)},k}async function r2(e,t,r,i=0,n=0,a=[],o=""){let l=e.insert("g").attr("class",`label ${a.join(" ")}`).attr("transform",`translate(${i}, ${n})`).attr("style",o);t!==Jh(t)&&(t=Jh(t),t=t.replaceAll("<","<").replaceAll(">",">"));let u=l.node().appendChild(await Io(l,t,{width:al(t,r)+100,style:o,useHtmlLabels:r.htmlLabels},r));if(t.includes("<")||t.includes(">")){let d=u.children[0];for(d.textContent=d.textContent.replaceAll("<","<").replaceAll(">",">");d.childNodes[0];)d=d.childNodes[0],d.textContent=d.textContent.replaceAll("<","<").replaceAll(">",">")}let h=u.getBBox();if(_c(r.htmlLabels)){let d=u.children[0];d.style.textAlign="start";let p=zt(u);h=d.getBoundingClientRect(),p.attr("width",h.width),p.attr("height",h.height)}return h}function i2(e,t,r,i,n){return e===r?[{x:e-n/2,y:t},{x:e+n/2,y:t},{x:r+n/2,y:i},{x:r-n/2,y:i}]:[{x:e,y:t-n/2},{x:e,y:t+n/2},{x:r,y:i+n/2},{x:r,y:i-n/2}]}var Xz,FEe,RSt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),t2(),Gr(),ec(),mi(),pi(),Ar(),Xz=new Set(["redux-color","redux-dark-color"]),FEe=new Set(["redux","redux-dark","redux-color","redux-dark-color"]),s(Gz,"erBox"),s(r2,"addText"),s(i2,"lineToPolygon")});async function zEe(e,t,r,i,n=r.class.padding??12){let a=i?0:3,o=e.insert("g").attr("class",Or(t)).attr("id",t.domId||t.id),l=null,u=null,h=null,d=null,p=0,f=0,g=0;if(l=o.insert("g").attr("class","annotation-group text"),t.annotations.length>0){let S=t.annotations[0];await M4(l,{text:`«${S}»`},0),p=l.node().getBBox().height}u=o.insert("g").attr("class","label-group text"),await M4(u,t,0,["font-weight: bolder"]);let m=u.node().getBBox();f=m.height,h=o.insert("g").attr("class","members-group text");let y=0;for(let S of t.members){let E=await M4(h,S,y,[S.parseClassifier()]);y+=E+a}g=h.node().getBBox().height,g<=0&&(g=n/2),d=o.insert("g").attr("class","methods-group text");let k=0;for(let S of t.methods){let E=await M4(d,S,k,[S.parseClassifier()]);k+=E+a}let C=o.node().getBBox();if(l!==null){let S=l.node().getBBox();l.attr("transform",`translate(${-S.width/2})`)}return u.attr("transform",`translate(${-m.width/2}, ${p})`),C=o.node().getBBox(),h.attr("transform",`translate(0, ${p+f+n*2})`),C=o.node().getBBox(),d.attr("transform",`translate(0, ${p+f+(g?g+n*4:n*2)})`),C=o.node().getBBox(),{shapeSvg:o,bbox:C}}async function M4(e,t,r,i=[]){let n=e.insert("g").attr("class","label").attr("style",i.join("; ")),a=Rr(),o="useHtmlLabels"in t?t.useHtmlLabels:_c(a.htmlLabels)??!0,l="";"text"in t?l=t.text:l=t.label,!o&&l.startsWith("\\")&&(l=l.substring(1)),pa(l)&&(o=!0);let u=await Io(n,DS(ad(l)),{width:al(l,a)+50,classes:"markdown-node-label",useHtmlLabels:o},a),h,d=1;if(o){let p=u.children[0],f=zt(u);d=p.innerHTML.split("
    ").length,p.innerHTML.includes("")&&(d+=p.innerHTML.split("").length-1),await pz(p),h=p.getBoundingClientRect(),f.attr("width",h.width),f.attr("height",h.height)}else{i.includes("font-weight: bolder")&&zt(u).selectAll("tspan").attr("font-weight",""),d=u.children.length;let p=u.children[0];(u.textContent===""||u.textContent.includes(">"))&&(p.textContent=l[0]+l.substring(1).replaceAll(">",">").replaceAll("<","<").trim(),l[1]===" "&&(p.textContent=p.textContent[0]+" "+p.textContent.substring(1))),p.textContent==="undefined"&&(p.textContent=""),h=u.getBBox()}return n.attr("transform","translate(0,"+(-h.height/(2*d)+r)+")"),h.height}var $St=G(()=>{"use strict";pi(),Gr(),lr(),Ar(),Er(),ec(),Rwe(),mi(),s(zEe,"textHelper"),s(M4,"addText")});async function qEe(e,t){let r=it(),{themeVariables:i}=r,{useGradient:n}=i,a=r.class.padding??12,o=a,l=t.useHtmlLabels??_c(r.htmlLabels)??!0,u=t;u.annotations=u.annotations??[],u.members=u.members??[],u.methods=u.methods??[];let{shapeSvg:h,bbox:d}=await zEe(e,t,r,l,o),{labelStyles:p,nodeStyles:f}=cr(t);t.labelStyle=p,t.cssStyles=u.styles||"";let g=u.styles?.join(";")||f||"";t.cssStyles||(t.cssStyles=g.replaceAll("!important","").split(";"));let m=u.members.length===0&&u.methods.length===0&&!r.class?.hideEmptyMembersBox,y=ur.svg(h),k=pr(t,{});t.look!=="handDrawn"&&(k.roughness=0,k.fillStyle="solid");let C=Math.max(t.width??0,d.width),S=Math.max(t.height??0,d.height),E=(t.height??0)>d.height;u.members.length===0&&u.methods.length===0?S+=o:u.members.length>0&&u.methods.length===0&&(S+=o*2);let _=-C/2,b=-S/2,v=m?a*2:u.members.length===0&&u.methods.length===0?-a:0;E&&(v=a*2);let x=y.rectangle(_-a,b-a-(m?a:u.members.length===0&&u.methods.length===0?-a/2:0),C+2*a,S+2*a+v,k),w=h.insert(()=>x,":first-child");w.attr("class","basic label-container outer-path");let R=w.node().getBBox(),$=h.select(".annotation-group").node().getBBox().height-(m?a/2:0)||0,M=h.select(".label-group").node().getBBox().height-(m?a/2:0)||0,N=h.select(".members-group").node().getBBox().height-(m?a/2:0)||0,j=($+M+b+a-(b-a-(m?a:u.members.length===0&&u.methods.length===0?-a/2:0)))/2;if(h.selectAll(".text").each((I,z,F)=>{let O=zt(F[z]),B=O.attr("transform"),Y=0;if(B){let J=RegExp(/translate\(([^,]+),([^)]+)\)/).exec(B);J&&(Y=parseFloat(J[2]))}let U=Y+b+a-(m?a:u.members.length===0&&u.methods.length===0?-a/2:0);if(O.attr("class").includes("methods-group")){let J=Math.max(N,o/2);E?U=Math.max(j,$+M+J+b+o*2+a)+o*2:U=$+M+J+b+o*4+a}u.members.length===0&&u.methods.length===0&&r.class?.hideEmptyMembersBox&&(u.annotations.length>0?U=Y-o:U=Y),l||(U-=4);let H=_;(O.attr("class").includes("label-group")||O.attr("class").includes("annotation-group"))&&(H=-O.node()?.getBBox().width/2||0,h.selectAll("text").each(function(J,te,ie){window.getComputedStyle(ie[te]).textAnchor==="middle"&&(H=0)})),O.attr("transform",`translate(${H}, ${U})`)}),u.members.length>0||u.methods.length>0||m){let I=$+M+b+a,z=y.line(R.x,I,R.x+R.width,I+.001,k);h.insert(()=>z).attr("class",`divider${t.look==="neo"&&!n?" neo-line":""}`).attr("style",g)}if(m||u.members.length>0||u.methods.length>0){let I=$+M+N+b+o*2+a,z=y.line(R.x,E?Math.max(j,I):I,R.x+R.width,(E?Math.max(j,I):I)+.001,k);h.insert(()=>z).attr("class",`divider${t.look==="neo"&&!n?" neo-line":""}`).attr("style",g)}if(u.look!=="handDrawn"&&h.selectAll("path").attr("style",g),w.select(":nth-child(2)").attr("style",g),h.selectAll(".divider").select("path").attr("style",g),t.labelStyle?h.selectAll("span").attr("style",t.labelStyle):h.selectAll("span").attr("style",g),!l){let I=RegExp(/color\s*:\s*([^;]*)/),z=I.exec(g);if(z){let F=z[0].replace("color","fill");h.selectAll("tspan").attr("style",F)}else if(p){let F=I.exec(p);if(F){let O=F[0].replace("color","fill");h.selectAll("tspan").attr("style",O)}}}return fr(t,w),t.intersect=function(I){return nr.rect(t,I)},h}var LSt=G(()=>{"use strict";lr(),Er(),pi(),vr(),gr(),Tr(),$St(),mi(),s(qEe,"classBox")});async function WEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let n=t,a=t,o=20,l=20,u="verifyMethod"in t,h=Or(t),d=it(),{themeVariables:p}=d,{borderColorArray:f,requirementEdgeLabelBackground:g}=p,m=d.layout==="elk"?"start":"center",y=e.insert("g").attr("class",h).attr("id",t.domId??t.id),k;u?k=await Yu(y,`<<${n.type}>>`,0,t.labelStyle):k=await Yu(y,"<<Element>>",0,t.labelStyle);let C=k,S=await Yu(y,n.name,C,t.labelStyle+"; font-weight: bold;");if(C+=S+l,u){let M=await Yu(y,`${n.requirementId?`ID: ${n.requirementId}`:""}`,C,t.labelStyle,m);C+=M;let N=await Yu(y,`${n.text?`Text: ${n.text}`:""}`,C,t.labelStyle,m);C+=N;let j=await Yu(y,`${n.risk?`Risk: ${n.risk}`:""}`,C,t.labelStyle,m);C+=j,await Yu(y,`${n.verifyMethod?`Verification: ${n.verifyMethod}`:""}`,C,t.labelStyle,m)}else{let M=await Yu(y,`${a.type?`Type: ${a.type}`:""}`,C,t.labelStyle,m);C+=M,await Yu(y,`${a.docRef?`Doc Ref: ${a.docRef}`:""}`,C,t.labelStyle,m)}let E=(y.node()?.getBBox().width??200)+o,_=(y.node()?.getBBox().height??200)+o,b=-E/2,v=-_/2,x=ur.svg(y),w=pr(t,{});t.look!=="handDrawn"&&(w.roughness=0,w.fillStyle="solid");let R=x.rectangle(b,v,E,_,w),$=y.insert(()=>R,":first-child");if($.attr("class","basic label-container outer-path").attr("style",i),f?.length){let M=t.colorIndex??0;y.attr("data-color-id",`color-${M%f.length}`)}if(y.selectAll(".label").each((M,N,j)=>{let I=zt(j[N]),z=I.attr("transform"),F=0,O=0;if(z){let U=RegExp(/translate\(([^,]+),([^)]+)\)/).exec(z);U&&(F=parseFloat(U[1]),O=parseFloat(U[2]))}let B=O-_/2,Y=b+o/2;(N===0||N===1)&&(Y=F),I.attr("transform",`translate(${Y}, ${B+o})`)}),C>k+S+l){let M=v+k+S+l,N;if(t.look==="neo"){let j=[[b,M],[b+E,M],[b+E,M+.001],[b,M+.001]];N=x.polygon(j,w)}else N=x.line(b,M,b+E,M,w);y.insert(()=>N).attr("class","divider")}return fr(t,$),t.intersect=function(M){return nr.rect(t,M)},i&&t.look!=="handDrawn"&&(g||f?.length)&&y.selectAll("path").attr("style",i),y}async function Yu(e,t,r,i="",n="center"){if(t==="")return 0;let a=e.insert("g").attr("class","label").attr("style",i),o=it(),l=o.htmlLabels??!0,u=await Io(a,DS(ad(t)),{width:al(t,o)+50,classes:"markdown-node-label",useHtmlLabels:l,style:i},o),h;if(l){let d=u.children[0],p=zt(u);n==="start"&&zt(d).style("text-align","left"),h=d.getBoundingClientRect(),p.attr("width",h.width),p.attr("height",h.height)}else{let d=u.children[0];for(let p of d.children)i&&p.setAttribute("style",i);if(n==="start"){d.setAttribute("text-anchor","start");for(let p of d.children)p.setAttribute("text-anchor","start")}h=u.getBBox(),h.height+=6}return a.attr("transform",`translate(${-h.width/2},${-h.height/2+r})`),h.height}var ISt=G(()=>{"use strict";lr(),Tr(),gr(),vr(),Ar(),Er(),ec(),pi(),s(WEe,"requirementBox"),s(Yu,"addText")});async function jEe(e,t,{config:r}){let{labelStyles:i,nodeStyles:n}=cr(t);t.labelStyle=i||"";let a=10,o=t.width;t.width=(t.width??200)-10;let{shapeSvg:l,bbox:u,label:h}=await Nr(e,t,Or(t)),d=t.padding||10,p="",f;"ticket"in t&&t.ticket&&r?.kanban?.ticketBaseUrl&&(p=r?.kanban?.ticketBaseUrl.replace("#TICKET#",t.ticket),f=l.insert("svg:a",":first-child").attr("class","kanban-ticket-link").attr("xlink:href",p).attr("target","_blank"));let g={useHtmlLabels:t.useHtmlLabels,labelStyle:t.labelStyle||"",width:t.width,img:t.img,padding:t.padding||8,centerLabel:!1},m,y;f?{label:m,bbox:y}=await S_(f,"ticket"in t&&t.ticket||"",g):{label:m,bbox:y}=await S_(l,"ticket"in t&&t.ticket||"",g);let{label:k,bbox:C}=await S_(l,"assigned"in t&&t.assigned||"",g);t.width=o;let S=10,E=t?.width||0,_=Math.max(y.height,C.height)/2,b=Math.max(u.height+S*2,t?.height||0)+_,v=-E/2,x=-b/2;h.attr("transform","translate("+(d-E/2)+", "+(-_-u.height/2)+")"),m.attr("transform","translate("+(d-E/2)+", "+(-_+u.height/2)+")"),k.attr("transform","translate("+(d+E/2-C.width-2*a)+", "+(-_+u.height/2)+")");let w,{rx:R,ry:$}=t,{cssStyles:M}=t;if(t.look==="handDrawn"){let N=ur.svg(l),j=pr(t,{}),I=R||$?N.path(Sl(v,x,E,b,R||0),j):N.rectangle(v,x,E,b,j);w=l.insert(()=>I,":first-child"),w.attr("class","basic label-container").attr("style",M||null)}else{w=l.insert("rect",":first-child"),w.attr("class","basic label-container __APA__").attr("style",n).attr("rx",R??5).attr("ry",$??5).attr("x",v).attr("y",x).attr("width",E).attr("height",b);let N="priority"in t&&t.priority;if(N){let j=l.append("line"),I=v+2,z=x+Math.floor((R??0)/2),F=x+b-Math.floor((R??0)/2);j.attr("x1",I).attr("y1",z).attr("x2",I).attr("y2",F).attr("stroke-width","4").attr("stroke",VEe(N))}}return fr(t,w),t.height=b,t.intersect=function(N){return nr.rect(t,N)},l}var VEe,MSt=G(()=>{"use strict";lr(),Tr(),Vu(),gr(),vr(),VEe=s(e=>{switch(e){case"Very High":return"red";case"High":return"orange";case"Medium":return null;case"Low":return"blue";case"Very Low":return"lightblue"}},"colorFromPriority"),s(jEe,"kanbanItem")});async function UEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let{shapeSvg:n,bbox:a,halfPadding:o,label:l}=await Nr(e,t,Or(t)),u=a.width+10*o,h=a.height+8*o,d=.15*u,{cssStyles:p}=t,f=a.width+20,g=a.height+20,m=Math.max(u,f),y=Math.max(h,g);l.attr("transform",`translate(${-a.width/2}, ${-a.height/2})`);let k,C=`M0 0 + a${d},${d} 1 0,0 ${m*.25},${-1*y*.1} + a${d},${d} 1 0,0 ${m*.25},0 + a${d},${d} 1 0,0 ${m*.25},0 + a${d},${d} 1 0,0 ${m*.25},${y*.1} + + a${d},${d} 1 0,0 ${m*.15},${y*.33} + a${d*.8},${d*.8} 1 0,0 0,${y*.34} + a${d},${d} 1 0,0 ${-1*m*.15},${y*.33} + + a${d},${d} 1 0,0 ${-1*m*.25},${y*.15} + a${d},${d} 1 0,0 ${-1*m*.25},0 + a${d},${d} 1 0,0 ${-1*m*.25},0 + a${d},${d} 1 0,0 ${-1*m*.25},${-1*y*.15} + + a${d},${d} 1 0,0 ${-1*m*.1},${-1*y*.33} + a${d*.8},${d*.8} 1 0,0 0,${-1*y*.34} + a${d},${d} 1 0,0 ${m*.1},${-1*y*.33} + H0 V0 Z`;if(t.look==="handDrawn"){let S=ur.svg(n),E=pr(t,{}),_=S.path(C,E);k=n.insert(()=>_,":first-child"),k.attr("class","basic label-container").attr("style",qa(p))}else k=n.insert("path",":first-child").attr("class","basic label-container").attr("style",i).attr("d",C);return k.attr("transform",`translate(${-m/2}, ${-y/2})`),fr(t,k),t.calcIntersect=function(S,E){return nr.rect(S,E)},t.intersect=function(S){return Ee.info("Bang intersect",t,S),nr.rect(t,S)},n}var DSt=G(()=>{"use strict";Nt(),lr(),Tr(),gr(),vr(),Ar(),s(UEe,"bang")});async function YEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let{shapeSvg:n,bbox:a,halfPadding:o,label:l}=await Nr(e,t,Or(t)),u=a.width+2*o,h=a.height+2*o,d=.15*u,p=.25*u,f=.35*u,g=.2*u,{cssStyles:m}=t,y,k=`M0 0 + a${d},${d} 0 0,1 ${u*.25},${-1*u*.1} + a${f},${f} 1 0,1 ${u*.4},${-1*u*.1} + a${p},${p} 1 0,1 ${u*.35},${u*.2} + + a${d},${d} 1 0,1 ${u*.15},${h*.35} + a${g},${g} 1 0,1 ${-1*u*.15},${h*.65} + + a${p},${d} 1 0,1 ${-1*u*.25},${u*.15} + a${f},${f} 1 0,1 ${-1*u*.5},0 + a${d},${d} 1 0,1 ${-1*u*.25},${-1*u*.15} + + a${d},${d} 1 0,1 ${-1*u*.1},${-1*h*.35} + a${g},${g} 1 0,1 ${u*.1},${-1*h*.65} + H0 V0 Z`;if(t.look==="handDrawn"){let C=ur.svg(n),S=pr(t,{}),E=C.path(k,S);y=n.insert(()=>E,":first-child"),y.attr("class","basic label-container").attr("style",qa(m))}else y=n.insert("path",":first-child").attr("class","basic label-container").attr("style",i).attr("d",k);return l.attr("transform",`translate(${-a.width/2}, ${-a.height/2})`),y.attr("transform",`translate(${-u/2}, ${-h/2})`),fr(t,y),t.calcIntersect=function(C,S){return nr.rect(C,S)},t.intersect=function(C){return Ee.info("Cloud intersect",t,C),nr.rect(t,C)},n}var NSt=G(()=>{"use strict";vr(),Nt(),Ar(),Tr(),gr(),lr(),s(YEe,"cloud")});async function HEe(e,t){let{labelStyles:r,nodeStyles:i}=cr(t);t.labelStyle=r;let{shapeSvg:n,bbox:a,halfPadding:o,label:l}=await Nr(e,t,Or(t)),u=a.width+8*o,h=a.height+2*o,d=5,p=t.look==="neo"?` + M${-u/2} ${h/2-d} + v${-h+2*d} + q0,-${d} ${d},-${d} + h${u-2*d} + q${d},0 ${d},${d} + v${h-d} + H${-u/2} + Z + `:` + M${-u/2} ${h/2-d} + v${-h+2*d} + q0,-${d} ${d},-${d} + h${u-2*d} + q${d},0 ${d},${d} + v${h-2*d} + q0,${d} ${-d},${d} + h${-(u-2*d)} + q${-d},0 ${-d},${-d} + Z + `;if(!t.domId)throw new Error(`defaultMindmapNode: node "${t.id}" is missing a domId — was render.ts domId prefixing skipped?`);let f=n.append("path").attr("id",t.domId).attr("class","node-bkg node-"+t.type).attr("style",i).attr("d",p);return n.append("line").attr("class","node-line-").attr("x1",-u/2).attr("y1",h/2).attr("x2",u/2).attr("y2",h/2),l.attr("transform",`translate(${-a.width/2}, ${-a.height/2})`),n.append(()=>l.node()),fr(t,f),t.calcIntersect=function(g,m){return nr.rect(g,m)},t.intersect=function(g){return nr.rect(t,g)},n}var OSt=G(()=>{"use strict";Tr(),gr(),lr(),s(HEe,"defaultMindmapNode")});async function GEe(e,t){let r={padding:t.padding??0};return Oz(e,t,r)}var BSt=G(()=>{"use strict";C5e(),s(GEe,"mindmapCircle")});function XEe(e){return e in j_}var KEe,QEe,j_,Kz=G(()=>{"use strict";bEt(),xEt(),TEt(),kEt(),CEt(),wEt(),EEt(),SEt(),C5e(),_Et(),AEt(),REt(),$Et(),LEt(),IEt(),MEt(),DEt(),NEt(),OEt(),BEt(),PEt(),FEt(),zEt(),qEt(),WEt(),jEt(),VEt(),UEt(),YEt(),HEt(),GEt(),XEt(),KEt(),QEt(),ZEt(),JEt(),eSt(),tSt(),rSt(),iSt(),nSt(),aSt(),sSt(),oSt(),lSt(),cSt(),uSt(),hSt(),dSt(),pSt(),fSt(),gSt(),mSt(),ySt(),vSt(),bSt(),xSt(),TSt(),kSt(),CSt(),wSt(),ESt(),SSt(),_St(),ASt(),RSt(),LSt(),ISt(),MSt(),DSt(),NSt(),OSt(),BSt(),KEe=[{semanticName:"Process",name:"Rectangle",shortName:"rect",description:"Standard process shape",aliases:["proc","process","rectangle"],internalAliases:["squareRect"],handler:xEe},{semanticName:"Event",name:"Rounded Rectangle",shortName:"rounded",description:"Represents an event",aliases:["event"],internalAliases:["roundedRect"],handler:yEe},{semanticName:"Terminal Point",name:"Stadium",shortName:"stadium",description:"Terminal point",aliases:["terminal","pill"],handler:TEe},{semanticName:"Subprocess",name:"Framed Rectangle",shortName:"fr-rect",description:"Subprocess",aliases:["subprocess","subproc","framed-rectangle","subroutine"],handler:EEe},{semanticName:"Database",name:"Cylinder",shortName:"cyl",description:"Database storage",aliases:["db","database","cylinder"],handler:M5e},{semanticName:"Data Store",name:"Data Store",shortName:"datastore",description:"Data flow diagram data store",aliases:["data-store"],handler:B5e},{semanticName:"Folder",name:"Folder",shortName:"folder",description:"Folder or directory",aliases:["directory"],handler:W5e},{semanticName:"Bucket",name:"Bucket",shortName:"bucket",description:"Object storage bucket",handler:d5e},{semanticName:"Console",name:"Console (terminal window)",shortName:"console",description:"Terminal or console window",handler:E5e},{semanticName:"Browser",name:"Browser",shortName:"browser",description:"Browser window",handler:x5e},{semanticName:"Person",name:"Person",shortName:"person",description:"Person (circular head above a rounded body)",handler:I5e},{semanticName:"Start",name:"Circle",shortName:"circle",description:"Starting point",aliases:["circ"],handler:Oz},{semanticName:"Bang",name:"Bang",shortName:"bang",description:"Bang",aliases:["bang"],handler:UEe},{semanticName:"Cloud",name:"Cloud",shortName:"cloud",description:"cloud",aliases:["cloud"],handler:YEe},{semanticName:"Decision",name:"Diamond",shortName:"diam",description:"Decision-making step",aliases:["decision","diamond","question"],handler:dEe},{semanticName:"Prepare Conditional",name:"Hexagon",shortName:"hex",description:"Preparation or condition step",aliases:["hexagon","prepare"],handler:U5e},{semanticName:"Data Input/Output",name:"Lean Right",shortName:"lean-r",description:"Represents input or output",aliases:["lean-right","in-out"],internalAliases:["lean_right"],handler:rEe},{semanticName:"Data Input/Output",name:"Lean Left",shortName:"lean-l",description:"Represents output or input",aliases:["lean-left","out-in"],internalAliases:["lean_left"],handler:tEe},{semanticName:"Priority Action",name:"Trapezoid Base Bottom",shortName:"trap-b",description:"Priority action",aliases:["priority","trapezoid-bottom","trapezoid"],handler:MEe},{semanticName:"Manual Operation",name:"Trapezoid Base Top",shortName:"trap-t",description:"Represents a manual task",aliases:["manual","trapezoid-top","inv-trapezoid"],internalAliases:["inv_trapezoid"],handler:J5e},{semanticName:"Stop",name:"Double Circle",shortName:"dbl-circ",description:"Represents a stop point",aliases:["double-circle"],internalAliases:["doublecircle"],handler:F5e},{semanticName:"Text Block",name:"Text Block",shortName:"text",description:"Text block",handler:AEe},{semanticName:"Card",name:"Notched Rectangle",shortName:"notch-rect",description:"Represents a card",aliases:["card","notched-rectangle"],handler:T5e},{semanticName:"Lined/Shaded Process",name:"Lined Rectangle",shortName:"lin-rect",description:"Lined process shape",aliases:["lined-rectangle","lined-process","lin-proc","shaded-process"],handler:vEe},{semanticName:"Start",name:"Small Circle",shortName:"sm-circ",description:"Small starting point",aliases:["start","small-circle"],internalAliases:["stateStart"],handler:wEe},{semanticName:"Stop",name:"Framed Circle",shortName:"fr-circ",description:"Stop point",aliases:["stop","framed-circle"],internalAliases:["stateEnd"],handler:CEe},{semanticName:"Fork/Join",name:"Filled Rectangle",shortName:"fork",description:"Fork or join in process flow",aliases:["join"],internalAliases:["forkJoin"],handler:j5e},{semanticName:"Collate",name:"Hourglass",shortName:"hourglass",description:"Represents a collate operation",aliases:["hourglass","collate"],handler:H5e},{semanticName:"Comment",name:"Curly Brace",shortName:"brace",description:"Adds a comment",aliases:["comment","brace-l"],handler:A5e},{semanticName:"Comment Right",name:"Curly Brace",shortName:"brace-r",description:"Adds a comment",handler:R5e},{semanticName:"Comment with braces on both sides",name:"Curly Braces",shortName:"braces",description:"Adds a comment",handler:$5e},{semanticName:"Com Link",name:"Lightning Bolt",shortName:"bolt",description:"Communication link",aliases:["com-link","lightning-bolt"],handler:iEe},{semanticName:"Document",name:"Document",shortName:"doc",description:"Represents a document",aliases:["doc","document"],handler:OEe},{semanticName:"Delay",name:"Half-Rounded Rectangle",shortName:"delay",description:"Represents a delay",aliases:["half-rounded-rectangle"],handler:V5e},{semanticName:"Direct Access Storage",name:"Horizontal Cylinder",shortName:"h-cyl",description:"Direct access storage",aliases:["das","horizontal-cylinder"],handler:REe},{semanticName:"Disk Storage",name:"Lined Cylinder",shortName:"lin-cyl",description:"Disk storage",aliases:["disk","lined-cylinder"],handler:nEe},{semanticName:"Display",name:"Curved Trapezoid",shortName:"curv-trap",description:"Represents a display",aliases:["curved-trapezoid","display"],handler:L5e},{semanticName:"Divided Process",name:"Divided Rectangle",shortName:"div-rect",description:"Divided process shape",aliases:["div-proc","divided-rectangle","divided-process"],handler:P5e},{semanticName:"Extract",name:"Triangle",shortName:"tri",description:"Extraction process",aliases:["extract","triangle"],handler:NEe},{semanticName:"Internal Storage",name:"Window Pane",shortName:"win-pane",description:"Internal storage",aliases:["internal-storage","window-pane"],handler:PEe},{semanticName:"Junction",name:"Filled Circle",shortName:"f-circ",description:"Junction point",aliases:["junction","filled-circle"],handler:z5e},{semanticName:"Loop Limit",name:"Trapezoidal Pentagon",shortName:"notch-pent",description:"Loop limit step",aliases:["loop-limit","notched-pentagon"],handler:DEe},{semanticName:"Manual File",name:"Flipped Triangle",shortName:"flip-tri",description:"Manual file operation",aliases:["manual-file","flipped-triangle"],handler:q5e},{semanticName:"Manual Input",name:"Sloped Rectangle",shortName:"sl-rect",description:"Manual input step",aliases:["manual-input","sloped-rectangle"],handler:bEe},{semanticName:"Multi-Document",name:"Stacked Document",shortName:"docs",description:"Multiple documents",aliases:["documents","st-doc","stacked-document"],handler:uEe},{semanticName:"Multi-Process",name:"Stacked Rectangle",shortName:"st-rect",description:"Multiple processes",aliases:["procs","processes","stacked-rectangle"],handler:cEe},{semanticName:"Stored Data",name:"Bow Tie Rectangle",shortName:"bow-rect",description:"Stored data",aliases:["stored-data","bow-tie-rectangle"],handler:h5e},{semanticName:"Summary",name:"Crossed Circle",shortName:"cross-circ",description:"Summary",aliases:["summary","crossed-circle"],handler:_5e},{semanticName:"Tagged Document",name:"Tagged Document",shortName:"tag-doc",description:"Tagged document",aliases:["tag-doc","tagged-document"],handler:_Ee},{semanticName:"Tagged Process",name:"Tagged Rectangle",shortName:"tag-rect",description:"Tagged process",aliases:["tagged-rectangle","tag-proc","tagged-process"],handler:SEe},{semanticName:"Paper Tape",name:"Flag",shortName:"flag",description:"Paper tape",aliases:["paper-tape"],handler:BEe},{semanticName:"Odd",name:"Odd",shortName:"odd",description:"Odd shape",internalAliases:["rect_left_inv_arrow"],handler:fEe},{semanticName:"Lined Document",name:"Lined Document",shortName:"lin-doc",description:"Lined document",aliases:["lined-document"],handler:lEe}],QEe=s(()=>{let e=[...Object.entries({state:kEe,choice:k5e,note:hEe,composite:w5e,rectWithTitle:mEe,labelRect:eEe,block_arrow:g5e,collapsedGroup:p5e,iconSquare:Q5e,iconCircle:X5e,icon:G5e,iconRounded:K5e,imageSquare:Z5e,anchor:c5e,kanbanItem:jEe,mindmapCircle:GEe,defaultMindmapNode:HEe,classBox:qEe,erBox:Gz,requirementBox:WEe}),...KEe.flatMap(t=>[t.shortName,..."aliases"in t?t.aliases:[],..."internalAliases"in t?t.internalAliases:[]].map(r=>[r,t.handler]))];return Object.fromEntries(e)},"generateShapeMap"),j_=QEe(),s(XEe,"isValidShape")}),ZEe,JEe,eSe,V_,tSe,rSe,iSe,nSe,aSe,Qz,sSe,Zz,oSe,Jz,lSe=G(()=>{"use strict";ZEe=new Set(["system_queue","external_system_queue","container_queue","external_container_queue","component_queue","external_component_queue"]),JEe=new Set(["system_db","external_system_db","container_db","external_container_db","component_db","external_component_db"]),eSe={person:"person",box:"rounded",rounded:"rounded",cylinder:"cylinder",database:"cylinder",db:"cylinder",queue:"h-cyl",pipe:"h-cyl",component:"fr-rect"},V_=s(e=>e?eSe[e.toLowerCase()]:void 0,"keywordShape"),tSe=s(e=>{let t=V_(e.shape)??V_(e.sprite);if(t)return t;if(e.tags)for(let i of e.tags.split(",")){let n=V_(i.trim());if(n)return n}let r=e.typeC4Shape.text;return r==="person"||r==="external_person"?"person":JEe.has(r)?"cylinder":ZEe.has(r)?"h-cyl":"rounded"},"resolveNodeShape"),rSe={person:"Person",system:"Software System",container:"Container",component:"Component"},iSe=s(e=>{let t=e.replace(/^external_/,"").replace(/_(db|queue)$/,"");return rSe[t]??t.replace(/_/g," ")},"stereotypeLabel"),nSe=s(e=>e.startsWith("external_"),"isExternal"),aSe=s(e=>{let t=iSe(e.typeC4Shape.text);return e.techn?.text?`[${t}: ${e.techn.text}]`:`[${t}]`},"stereotypeText"),Qz=["person","system","system_db","system_queue","container","container_db","container_queue","component","component_db","component_queue"].flatMap(e=>[e,`external_${e}`]),sSe=new Set(Qz),Zz=s(e=>sSe.has(e),"isC4ElementType"),oSe=s((e,t)=>{let r=e.typeC4Shape.text,i=e.bgColor??(Zz(r)&&t[`${r}_bg_color`]),n=e.borderColor??(Zz(r)&&t[`${r}_border_color`]),a=[];return i&&a.push(`fill:${i}`),n&&a.push(`stroke:${n}`),a.push(`color:${e.fontColor??"#FFFFFF"}`),a},"elementCssStyles"),Jz=s((e,t,r,i,n)=>{let a=e.typeC4Shape.text,o=["c4-shape",`c4-${a}`];nSe(a)&&o.push("c4-external");let l=tSe(e),u=oSe(e,t);return(l==="rounded"||l==="fr-rect")&&u.push("rx:12px","ry:12px"),{id:e.alias,label:e.label.text,stereotype:aSe(e),description:e.descr?.text?[e.descr.text]:void 0,labelType:"string",isGroup:!1,shape:l,cssClasses:o.join(" "),cssStyles:u,padding:r,look:i,useHtmlLabels:!1,width:n}},"buildC4Node")});function Up(e,t,r,i,n){let a=t[e];if(!a.width)if(r)a.text=Uy(a.text,n,i),a.textLines=a.text.split(Vt.lineBreakRegex).length,a.width=n,a.height=u_(a.text,i);else{let o=a.text.split(Vt.lineBreakRegex);a.textLines=o.length;let l=0;a.height=0,a.width=0;for(let u of o)a.width=Math.max(al(u,i),a.width),l=u_(u,i),a.height=a.height+l}return a}async function eq(e,t,r,i,n){let a=n.db,o=new rq(n);o.data.widthLimit=r.data.widthLimit/Math.min(U_,i.length);for(let[l,u]of i.entries()){let h=0;u.image={width:0,height:0,Y:0},u.sprite&&(u.image.width=48,u.image.height=48,u.image.Y=h,h=u.image.Y+u.image.height);let d=u.wrap&&Wr.wrap,p=D4(Wr);p.fontSize=p.fontSize+2,p.fontWeight="bold";let f=Up("label",u,d,p,o.data.widthLimit);if(f.Y=h+8,h=f.Y+f.height,u.type&&u.type.text!==""){u.type.text="["+u.type.text+"]";let k=D4(Wr),C=Up("type",u,d,k,o.data.widthLimit);C.Y=h+5,h=C.Y+C.height}if(u.descr&&u.descr.text!==""){let k=D4(Wr);k.fontSize=k.fontSize-2;let C=Up("descr",u,d,k,o.data.widthLimit);C.Y=h+20,h=C.Y+C.height}if(l==0||l%U_===0){let k=r.data.startx+Wr.diagramMarginX,C=r.data.stopy+Wr.diagramMarginY+h;o.setData(k,k,C,C)}else{let k=o.data.stopx!==o.data.startx?o.data.stopx+Wr.diagramMarginX:o.data.startx,C=o.data.starty;o.setData(k,k,C,C)}o.name=u.alias;let g=a.getC4ShapeArray(u.alias),m=a.getC4ShapeKeys(u.alias);m.length>0&&await nq(o,e,g,m),t=u.alias;let y=a.getBoundaries(t);y.length>0&&await eq(e,t,o,y,n),u.alias!=="global"&&iq(e,u,o),r.data.stopy=Math.max(o.data.stopy+Wr.c4ShapeMargin,r.data.stopy),r.data.stopx=Math.max(o.data.stopx+Wr.c4ShapeMargin,r.data.stopx),n2=Math.max(n2,r.data.stopx),a2=Math.max(a2,r.data.stopy)}}var n2,a2,tq,U_,Wr,rq,Y_,D4,cSe,iq,nq,uSe,aq,hSe,dSe,pSe,sq,PSt=G(()=>{"use strict";var e,t;B5t(),Nt(),Cye(),mi(),c2e(),Er(),Mye(),$x(),Ar(),eEt(),vn(),Kz(),lSe(),n2=0,a2=0,tq=4,U_=2,yT.yy=FS,Wr={},rq=(e=class{constructor(i){this.name="",this.data={},this.data.startx=void 0,this.data.stopx=void 0,this.data.starty=void 0,this.data.stopy=void 0,this.data.widthLimit=void 0,this.nextData={},this.nextData.startx=void 0,this.nextData.stopx=void 0,this.nextData.starty=void 0,this.nextData.stopy=void 0,this.nextData.cnt=0,Y_(i.db.getConfig())}setData(i,n,a,o){this.nextData.startx=this.data.startx=i,this.nextData.stopx=this.data.stopx=n,this.nextData.starty=this.data.starty=a,this.nextData.stopy=this.data.stopy=o}updateVal(i,n,a,o){i[n]===void 0?i[n]=a:i[n]=o(a,i[n])}insert(i){this.nextData.cnt=this.nextData.cnt+1;let n=this.nextData.stopx,a=this.data.widthLimit,o=this.nextData.startx===this.nextData.stopx?n+i.margin:n+i.margin*2,l=o+i.width,u=this.nextData.starty+i.margin*2,h=u+i.height;(o>=a||l>=a||this.nextData.cnt>tq)&&(o=this.nextData.startx+i.margin+Wr.nextLinePaddingX,u=this.nextData.stopy+i.margin*2,this.nextData.stopx=l=o+i.width,this.nextData.starty=this.nextData.stopy,this.nextData.stopy=h=u+i.height,this.nextData.cnt=1),i.x=o,i.y=u,this.updateVal(this.data,"startx",o,Math.min),this.updateVal(this.data,"starty",u,Math.min),this.updateVal(this.data,"stopx",l,Math.max),this.updateVal(this.data,"stopy",h,Math.max),this.updateVal(this.nextData,"startx",o,Math.min),this.updateVal(this.nextData,"starty",u,Math.min),this.updateVal(this.nextData,"stopx",l,Math.max),this.updateVal(this.nextData,"stopy",h,Math.max)}init(i){this.name="",this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0,widthLimit:void 0},this.nextData={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0,cnt:0},Y_(i.db.getConfig())}bumpLastMargin(i){this.data.stopx+=i,this.data.stopy+=i}},s(e,"Bounds"),e),Y_=s(function(r){Sa(Wr,r),r?.fontFamily&&(Wr.personFontFamily=Wr.systemFontFamily=Wr.messageFontFamily=r.fontFamily),r?.fontSize&&(Wr.personFontSize=Wr.systemFontSize=Wr.messageFontSize=r.fontSize),r?.fontWeight&&(Wr.personFontWeight=Wr.systemFontWeight=Wr.messageFontWeight=r.fontWeight)},"setConf"),D4=s(r=>({fontFamily:r.boundaryFontFamily,fontSize:r.boundaryFontSize,fontWeight:r.boundaryFontWeight}),"boundaryFont"),cSe=s(r=>({fontFamily:r.messageFontFamily,fontSize:r.messageFontSize,fontWeight:r.messageFontWeight}),"messageFont"),s(Up,"calcC4ShapeTextWH"),iq=s(function(r,i,n){let a=n.data.startx,o=n.data.starty;i.x=a,i.y=o,i.width=n.data.stopx-a,i.height=n.data.stopy-o,i.label.y=Wr.c4ShapeMargin-35;let l=i.wrap&&Wr.wrap,u=D4(Wr);u.fontSize=u.fontSize+2,u.fontWeight="bold";let h=al(i.label.text,u);Up("label",i,l,u,h),Fu.drawBoundary(r,i,Wr)},"drawBoundary"),nq=s(async function(r,i,n,a){let o=it(),l=o.look??"classic",u={config:o},h=i.attr("id")??"",d=a.map(f=>n[Number(f)]),p=s(f=>{let g=f.shape?j_[f.shape]:void 0;if(!g)throw new Error(`C4: no shape handler for "${f.shape}"`);return g},"shapeHandlerFor");await Promise.all(d.map(async f=>{let g=Jz(f,Wr,Wr.c4ShapePadding,l,Wr.width);g.domId=`${h}-${g.id}`;let m=await p(g)(i,g,u);f.width=g.width??Wr.width,f.height=g.height??Wr.height,f.margin=Wr.c4ShapeMargin,m.remove()}));for(let f of d)r.insert(f);await Promise.all(d.map(async f=>{let g=Jz(f,Wr,Wr.c4ShapePadding,l,Wr.width);g.domId=`${h}-${g.id}`,g.x=f.x+f.width/2,g.y=f.y+f.height/2;let m=i.append("g").attr("transform",`translate(${f.x+f.width/2}, ${f.y+f.height/2})`);await p(g)(m,g,u),f.intersect=g.intersect})),r.bumpLastMargin(Wr.c4ShapeMargin)},"drawC4ShapeArray"),uSe=(t=class{constructor(i,n){this.x=i,this.y=n}},s(t,"Point"),t),aq=s(function(r,i){if(!r.intersect)throw new Error(`C4 shape "${r.alias}" has no intersect function. Please report this to https://github.com/mermaid-js/mermaid/issues`);let{x:n,y:a}=r.intersect(i);return new uSe(n,a)},"getIntersectPoint"),hSe=s(function(r,i){let n={x:0,y:0};n.x=i.x+i.width/2,n.y=i.y+i.height/2;let a=aq(r,n);n.x=r.x+r.width/2,n.y=r.y+r.height/2;let o=aq(i,n);return{startPoint:a,endPoint:o}},"getIntersectPoints"),dSe=s(function(r,i,n,a,o){let l=a.db.getC4Type(),u=0;for(let h of i){u=u+1;let d=h.wrap&&Wr.wrap,p=cSe(Wr);l==="C4Dynamic"&&(h.label.text=u+": "+h.label.text);let f=al(h.label.text,p);Up("label",h,d,p,f),h.techn&&h.techn.text!==""&&(f=al(h.techn.text,p),Up("techn",h,d,p,f)),h.descr&&h.descr.text!==""&&(f=al(h.descr.text,p),Up("descr",h,d,p,f));let g=n(h.from),m=n(h.to);if(!g||!m)throw new Error(`C4 rel "${h.from}" -> "${h.to}" references an unknown shape`);let y=hSe(g,m);if(!y.startPoint||!y.endPoint)throw new Error(`Could not calculate intersection points for rel "${h.from}" -> "${h.to}"`);h.startPoint=y.startPoint,h.endPoint=y.endPoint}Fu.drawRels(r,i,Wr,o)},"drawRels"),s(eq,"drawInsideBoundary"),pSe=s(async function(r,i,n,a){Wr=WO("c4");let o=it().securityLevel,{root:l}=ACe(i,o),u=a.db;u.setWrap(Wr.wrap),tq=u.getC4ShapeInRow(),U_=u.getC4BoundaryInRow(),Ee.debug(`C:${JSON.stringify(Wr,null,2)}`);let h=l.select(`[id="${i}"]`);Fu.insertComputerIcon(h,i),Fu.insertDatabaseIcon(h,i),Fu.insertClockIcon(h,i);let d=new rq(a);d.setData(Wr.diagramMarginX,Wr.diagramMarginX,Wr.diagramMarginY,Wr.diagramMarginY),d.data.widthLimit=screen.availWidth,n2=Wr.diagramMarginX,a2=Wr.diagramMarginY;let p=u.getTitle(),f=u.getBoundaries("");await eq(h,"",d,f,a),Fu.insertArrowHead(h,i),Fu.insertArrowEnd(h,i),Fu.insertArrowCrossHead(h,i),Fu.insertArrowFilledHead(h,i),dSe(h,u.getRels(),u.getC4Shape,a,i),d.data.stopx=n2,d.data.stopy=a2;let g=d.data,m=g.startx,y=g.starty,k=a2-y+2*Wr.diagramMarginY,C=n2-m,S=C+2*Wr.diagramMarginX;p&&h.append("text").text(p).attr("x",C/2-4*Wr.diagramMarginX).attr("y",y+Wr.diagramMarginY),aa(h,k,S,Wr.useMaxWidth);let E=p?60:0;h.attr("viewBox",m-Wr.diagramMarginX+" -"+(Wr.diagramMarginY+E)+" "+S+" "+(k+E)),Ee.debug("models:",g)},"draw"),sq={drawPersonOrSystemArray:nq,drawBoundary:iq,setConf:Y_,draw:pSe}}),fSe,gSe,mSe,FSt=G(()=>{"use strict";Er(),lSe(),fSe=s(()=>{let e=it().c4??{},t=new CSSStyleSheet;for(let r of Qz){let i=t.cssRules[t.insertRule(`.c4-shape.c4-${r} .label {}`,t.cssRules.length)],n=e[`${r}FontFamily`],a=e[`${r}FontSize`],o=e[`${r}FontWeight`];n&&i.style.setProperty("font-family",n),a&&i.style.setProperty("font-size",typeof a=="number"?`${a}px`:a),o&&i.style.setProperty("font-weight",String(o))}return[...t.cssRules].filter(r=>r.style.length>0).map(r=>` ${r.cssText}`).join(` +`)},"elementFontStyles"),gSe=s(e=>`.person { + stroke: ${e.personBorder}; + fill: ${e.personBkg}; + } +${fSe()} + + /* The element font colour is set inline per element (default white); the + label text takes it via currentColor. */ + .c4-shape .label, + .c4-shape .label text { + color: inherit; + fill: currentColor; + } + /* Structurizr typography: bold name, smaller stereotype/type and description lines. */ + .c4-shape .label .c4-name { + font-weight: bold; + } + .c4-shape .label .c4-type { + font-size: 0.75em; + } + .c4-shape .label .c4-descr { + font-size: 0.82em; + } + .c4-shape .basic, + .c4-shape rect, + .c4-shape path, + .c4-shape circle, + .c4-shape ellipse, + .c4-shape line { + stroke-width: 2px; + } +`,"getStyles"),mSe=gSe}),ySe={};Mr(ySe,{diagram:()=>vSe});var vSe,zSt=G(()=>{"use strict";Cye(),c2e(),PSt(),FSt(),vSe={parser:kye,db:FS,renderer:sq,styles:mSe,init:s(({c4:e,wrap:t})=>{sq.setConf(e),FS.setWrap(t)},"init")}});function bSe(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function s2(){if(wSe)return hd;wSe=1;function e(o){return typeof o>"u"||o===null}s(e,"isNothing");function t(o){return typeof o=="object"&&o!==null}s(t,"isObject");function r(o){return Array.isArray(o)?o:e(o)?[]:[o]}s(r,"toArray");function i(o,l){if(l){let u=Object.keys(l);for(let h=0,d=u.length;hp&&(h=" ... ",a=l-p+h.length),o-l>p&&(d=" ...",o=l+p-d.length),{str:h+n.slice(a,o).replace(/\t/g,"→")+d,pos:l-a+h.length}}s(t,"getLine");function r(n,a){return e.repeat(" ",a-n.length)+n}s(r,"padStart");function i(n,a){if(a=Object.create(a||null),!n.buffer)return null;a.maxLength||(a.maxLength=79),typeof a.indent!="number"&&(a.indent=1),typeof a.linesBefore!="number"&&(a.linesBefore=3),typeof a.linesAfter!="number"&&(a.linesAfter=2);let o=/\r?\n|\r|\0/g,l=[0],u=[],h,d=-1;for(;h=o.exec(n.buffer);)u.push(h.index),l.push(h.index+h[0].length),n.position<=h.index&&d<0&&(d=l.length-2);d<0&&(d=l.length-1);let p="",f=Math.min(n.line+a.linesAfter,u.length).toString().length,g=a.maxLength-(a.indent+f+3);for(let y=1;y<=a.linesBefore&&!(d-y<0);y++){let k=t(n.buffer,l[d-y],u[d-y],n.position-(l[d]-l[d-y]),g);p=e.repeat(" ",a.indent)+r((n.line-y+1).toString(),f)+" | "+k.str+` +`+p}let m=t(n.buffer,l[d],u[d],n.position,g);p+=e.repeat(" ",a.indent)+r((n.line+1).toString(),f)+" | "+m.str+` +`,p+=e.repeat("-",a.indent+f+3+m.pos)+`^ +`;for(let y=1;y<=a.linesAfter&&!(d+y>=u.length);y++){let k=t(n.buffer,l[d+y],u[d+y],n.position-(l[d]-l[d+y]),g);p+=e.repeat(" ",a.indent)+r((n.line+y+1).toString(),f)+" | "+k.str+` +`}return p.replace(/\n$/,"")}return s(i,"makeSnippet"),Eq=i,Eq}function oo(){if(_Se)return Sq;_Se=1;let e=o2(),t=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],r=["scalar","sequence","mapping"];function i(a){let o={};return a!==null&&Object.keys(a).forEach(function(l){a[l].forEach(function(u){o[String(u)]=l})}),o}s(i,"compileStyleAliases");function n(a,o){if(o=o||{},Object.keys(o).forEach(function(l){if(t.indexOf(l)===-1)throw new e('Unknown option "'+l+'" is met in definition of "'+a+'" YAML type.')}),this.options=o,this.tag=a,this.kind=o.kind||null,this.resolve=o.resolve||function(){return!0},this.construct=o.construct||function(l){return l},this.instanceOf=o.instanceOf||null,this.predicate=o.predicate||null,this.represent=o.represent||null,this.representName=o.representName||null,this.defaultStyle=o.defaultStyle||null,this.multi=o.multi||!1,this.styleAliases=i(o.styleAliases||null),r.indexOf(this.kind)===-1)throw new e('Unknown kind "'+this.kind+'" is specified for "'+a+'" YAML type.')}return s(n,"Type2"),Sq=n,Sq}function oq(){if(ASe)return _q;ASe=1;let e=o2(),t=oo();function r(a,o){let l=[];return a[o].forEach(function(u){let h=l.length;l.forEach(function(d,p){d.tag===u.tag&&d.kind===u.kind&&d.multi===u.multi&&(h=p)}),l[h]=u}),l}s(r,"compileList");function i(){let a={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}};function o(l){l.multi?(a.multi[l.kind].push(l),a.multi.fallback.push(l)):a[l.kind][l.tag]=a.fallback[l.tag]=l}s(o,"collectType");for(let l=0,u=arguments.length;l=48&&h<=57||h>=65&&h<=70||h>=97&&h<=102}s(r,"isHexCode");function i(h){return h>=48&&h<=55}s(i,"isOctCode");function n(h){return h>=48&&h<=57}s(n,"isDecCode");function a(h){if(h===null)return!1;let d=h.length,p=0,f=!1;if(!d)return!1;let g=h[p];if((g==="-"||g==="+")&&(g=h[++p]),g==="0"){if(p+1===d)return!0;if(g=h[++p],g==="b"){for(p++;p=0?"0b"+h.toString(2):"-0b"+h.toString(2).slice(1)},"binary"),octal:s(function(h){return h>=0?"0o"+h.toString(8):"-0o"+h.toString(8).slice(1)},"octal"),decimal:s(function(h){return h.toString(10)},"decimal"),hexadecimal:s(function(h){return h>=0?"0x"+h.toString(16).toUpperCase():"-0x"+h.toString(16).toUpperCase().slice(1)},"hexadecimal")},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}}),Dq}function gq(){if(OSe)return Nq;OSe=1;let e=s2(),t=oo(),r=new RegExp("^(?:[-+]?(?:[0-9]+)(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),i=new RegExp("^(?:[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");function n(h){return h===null||!r.test(h)?!1:isFinite(parseFloat(h,10))?!0:i.test(h)}s(n,"resolveYamlFloat");function a(h){let d=h.toLowerCase(),p=d[0]==="-"?-1:1;return"+-".indexOf(d[0])>=0&&(d=d.slice(1)),d===".inf"?p===1?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:d===".nan"?NaN:p*parseFloat(d,10)}s(a,"constructYamlFloat");let o=/^[-+]?[0-9]+e/;function l(h,d){if(isNaN(h))switch(d){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===h)switch(d){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===h)switch(d){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(e.isNegativeZero(h))return"-0.0";let p=h.toString(10);return o.test(p)?p.replace("e",".e"):p}s(l,"representYamlFloat");function u(h){return Object.prototype.toString.call(h)==="[object Number]"&&(h%1!==0||e.isNegativeZero(h))}return s(u,"isFloat"),Nq=new t("tag:yaml.org,2002:float",{kind:"scalar",resolve:n,construct:a,predicate:u,represent:l,defaultStyle:"lowercase"}),Nq}function mq(){return PSe||(PSe=1,BSe=hq().extend({implicit:[dq(),pq(),fq(),gq()]})),BSe}function yq(){return zSe||(zSe=1,FSe=mq()),FSe}function vq(){if(qSe)return Oq;qSe=1;let e=oo(),t=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),r=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");function i(o){return o===null?!1:t.exec(o)!==null||r.exec(o)!==null}s(i,"resolveYamlTimestamp");function n(o){let l=0,u=null,h=t.exec(o);if(h===null&&(h=r.exec(o)),h===null)throw new Error("Date resolve error");let d=+h[1],p=+h[2]-1,f=+h[3];if(!h[4])return new Date(Date.UTC(d,p,f));let g=+h[4],m=+h[5],y=+h[6];if(h[7]){for(l=h[7].slice(0,3);l.length<3;)l+="0";l=+l}if(h[9]){let C=+h[10],S=+(h[11]||0);u=(C*60+S)*6e4,h[9]==="-"&&(u=-u)}let k=new Date(Date.UTC(d,p,f,g,m,y,l));return u&&k.setTime(k.getTime()-u),k}s(n,"constructYamlTimestamp");function a(o){return o.toISOString()}return s(a,"representYamlTimestamp"),Oq=new e("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:i,construct:n,instanceOf:Date,represent:a}),Oq}function bq(){if(WSe)return Bq;WSe=1;let e=oo();function t(r){return r==="<<"||r===null}return s(t,"resolveYamlMerge"),Bq=new e("tag:yaml.org,2002:merge",{kind:"scalar",resolve:t}),Bq}function xq(){if(jSe)return Pq;jSe=1;let e=oo(),t=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= +\r`;function r(o){if(o===null)return!1;let l=0,u=o.length,h=t;for(let d=0;d64)){if(p<0)return!1;l+=6}}return l%8===0}s(r,"resolveYamlBinary");function i(o){let l=o.replace(/[\r\n=]/g,""),u=l.length,h=t,d=0,p=[];for(let g=0;g>16&255),p.push(d>>8&255),p.push(d&255)),d=d<<6|h.indexOf(l.charAt(g));let f=u%4*6;return f===0?(p.push(d>>16&255),p.push(d>>8&255),p.push(d&255)):f===18?(p.push(d>>10&255),p.push(d>>2&255)):f===12&&p.push(d>>4&255),new Uint8Array(p)}s(i,"constructYamlBinary");function n(o){let l="",u=0,h=o.length,d=t;for(let f=0;f>18&63],l+=d[u>>12&63],l+=d[u>>6&63],l+=d[u&63]),u=(u<<8)+o[f];let p=h%3;return p===0?(l+=d[u>>18&63],l+=d[u>>12&63],l+=d[u>>6&63],l+=d[u&63]):p===2?(l+=d[u>>10&63],l+=d[u>>4&63],l+=d[u<<2&63],l+=d[64]):p===1&&(l+=d[u>>2&63],l+=d[u<<4&63],l+=d[64],l+=d[64]),l}s(n,"representYamlBinary");function a(o){return Object.prototype.toString.call(o)==="[object Uint8Array]"}return s(a,"isBinary"),Pq=new e("tag:yaml.org,2002:binary",{kind:"scalar",resolve:r,construct:i,predicate:a,represent:n}),Pq}function Tq(){if(VSe)return Fq;VSe=1;let e=oo(),t=Object.prototype.hasOwnProperty,r=Object.prototype.toString;function i(a){if(a===null)return!0;let o=[],l=a;for(let u=0,h=l.length;u=48&&W<=57)return W-48;let Ae=W|32;return Ae>=97&&Ae<=102?Ae-97+10:-1}s(v,"fromHexCode");function x(W){return W===120?2:W===117?4:W===85?8:0}s(x,"escapedHexLen");function w(W){return W>=48&&W<=57?W-48:-1}s(w,"fromDecimalCode");function R(W){switch(W){case 48:return"\0";case 97:return"\x07";case 98:return"\b";case 116:return" ";case 9:return" ";case 110:return` +`;case 118:return"\v";case 102:return"\f";case 114:return"\r";case 101:return"\x1B";case 32:return" ";case 34:return'"';case 47:return"/";case 92:return"\\";case 78:return"…";case 95:return" ";case 76:return"\u2028";case 80:return"\u2029";default:return""}}s(R,"simpleEscapeSequence");function $(W){return W<=65535?String.fromCharCode(W):String.fromCharCode((W-65536>>10)+55296,(W-65536&1023)+56320)}s($,"charFromCodepoint");function M(W,Ae,K){Ae==="__proto__"?Object.defineProperty(W,Ae,{configurable:!0,enumerable:!0,writable:!0,value:K}):W[Ae]=K}s(M,"setProperty");let N=new Array(256),j=new Array(256);for(let W=0;W<256;W++)N[W]=R(W)?1:0,j[W]=R(W);function I(W,Ae){this.input=W,this.filename=Ae.filename||null,this.schema=Ae.schema||i,this.onWarning=Ae.onWarning||null,this.legacy=Ae.legacy||!1,this.json=Ae.json||!1,this.listener=Ae.listener||null,this.maxDepth=typeof Ae.maxDepth=="number"?Ae.maxDepth:100,this.maxTotalMergeKeys=typeof Ae.maxTotalMergeKeys=="number"?Ae.maxTotalMergeKeys:1e4,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=W.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.depth=0,this.totalMergeKeys=0,this.firstTabInLine=-1,this.documents=[],this.anchorMapTransactions=[]}s(I,"State");function z(W,Ae){let K={name:W.filename,buffer:W.input.slice(0,-1),position:W.position,line:W.line,column:W.position-W.lineStart};return K.snippet=r(K),new t(Ae,K)}s(z,"generateError");function F(W,Ae){throw z(W,Ae)}s(F,"throwError");function O(W,Ae){W.onWarning&&W.onWarning.call(null,z(W,Ae))}s(O,"throwWarning");function B(W,Ae,K){let de=W.anchorMapTransactions;if(de.length!==0){let ee=de[de.length-1];n.call(ee,Ae)||(ee[Ae]={existed:n.call(W.anchorMap,Ae),value:W.anchorMap[Ae]})}W.anchorMap[Ae]=K}s(B,"storeAnchor");function Y(W){W.anchorMapTransactions.push(Object.create(null))}s(Y,"beginAnchorTransaction");function U(W){let Ae=W.anchorMapTransactions.pop(),K=W.anchorMapTransactions;if(K.length===0)return;let de=K[K.length-1],ee=Object.keys(Ae);for(let ve=0,X=ee.length;ve=0;de-=1){let ee=Ae[K[de]];ee.existed?W.anchorMap[K[de]]=ee.value:delete W.anchorMap[K[de]]}}s(H,"rollbackAnchorTransaction");function J(W){return{position:W.position,line:W.line,lineStart:W.lineStart,lineIndent:W.lineIndent,firstTabInLine:W.firstTabInLine,tag:W.tag,anchor:W.anchor,kind:W.kind,result:W.result}}s(J,"snapshotState");function te(W,Ae){W.position=Ae.position,W.line=Ae.line,W.lineStart=Ae.lineStart,W.lineIndent=Ae.lineIndent,W.firstTabInLine=Ae.firstTabInLine,W.tag=Ae.tag,W.anchor=Ae.anchor,W.kind=Ae.kind,W.result=Ae.result}s(te,"restoreState");let ie={YAML:s(function(W,Ae,K){W.version!==null&&F(W,"duplication of %YAML directive"),K.length!==1&&F(W,"YAML directive accepts exactly one argument");let de=/^([0-9]+)\.([0-9]+)$/.exec(K[0]);de===null&&F(W,"ill-formed argument of the YAML directive");let ee=parseInt(de[1],10),ve=parseInt(de[2],10);ee!==1&&F(W,"unacceptable YAML version of the document"),W.version=K[0],W.checkLineBreaks=ve<2,ve!==1&&ve!==2&&O(W,"unsupported YAML version of the document")},"handleYamlDirective"),TAG:s(function(W,Ae,K){let de;K.length!==2&&F(W,"TAG directive accepts exactly two arguments");let ee=K[0];de=K[1],y.test(ee)||F(W,"ill-formed tag handle (first argument) of the TAG directive"),n.call(W.tagMap,ee)&&F(W,'there is a previously declared suffix for "'+ee+'" tag handle'),k.test(de)||F(W,"ill-formed tag prefix (second argument) of the TAG directive");try{de=decodeURIComponent(de)}catch{F(W,"tag prefix is malformed: "+de)}W.tagMap[ee]=de},"handleTagDirective")};function ue(W,Ae,K,de){if(Ae=32&&me<=1114111||F(W,"expected valid JSON character")}else f.test(ee)&&F(W,"the stream contains non-printable characters");W.result+=ee}}s(ue,"captureSegment");function se(W,Ae,K,de){e.isObject(K)||F(W,"cannot merge mappings; the provided source object is unacceptable");let ee=Object.keys(K);for(let ve=0,X=ee.length;veW.maxTotalMergeKeys&&F(W,"merge keys exceeded maxTotalMergeKeys ("+W.maxTotalMergeKeys+")"),n.call(Ae,me)||(M(Ae,me,K[me]),de[me]=!0)}}s(se,"mergeMappings");function le(W,Ae,K,de,ee,ve,X,me,Ce){if(Array.isArray(ee)){ee=Array.prototype.slice.call(ee);for(let Be=0,Re=ee.length;Be1&&(W.result+=e.repeat(` +`,Ae-1))}s(ge,"writeFoldedLines");function ke(W,Ae,K){let de,ee,ve,X,me,Ce,Be=W.kind,Re=W.result,Ke=W.input.charCodeAt(W.position);if(_(Ke)||b(Ke)||Ke===35||Ke===38||Ke===42||Ke===33||Ke===124||Ke===62||Ke===39||Ke===34||Ke===37||Ke===64||Ke===96)return!1;if(Ke===63||Ke===45){let De=W.input.charCodeAt(W.position+1);if(_(De)||K&&b(De))return!1}for(W.kind="scalar",W.result="",de=ee=W.position,ve=!1;Ke!==0;){if(Ke===58){let De=W.input.charCodeAt(W.position+1);if(_(De)||K&&b(De))break}else if(Ke===35){let De=W.input.charCodeAt(W.position-1);if(_(De))break}else{if(W.position===W.lineStart&&fe(W)||K&&b(Ke))break;if(S(Ke))if(X=W.line,me=W.lineStart,Ce=W.lineIndent,oe(W,!1,-1),W.lineIndent>=Ae){ve=!0,Ke=W.input.charCodeAt(W.position);continue}else{W.position=ee,W.line=X,W.lineStart=me,W.lineIndent=Ce;break}}ve&&(ue(W,de,ee,!1),ge(W,W.line-X),de=ee=W.position,ve=!1),E(Ke)||(ee=W.position+1),Ke=W.input.charCodeAt(++W.position)}return ue(W,de,ee,!1),W.result?!0:(W.kind=Be,W.result=Re,!1)}s(ke,"readPlainScalar");function Ne(W,Ae){let K,de,ee=W.input.charCodeAt(W.position);if(ee!==39)return!1;for(W.kind="scalar",W.result="",W.position++,K=de=W.position;(ee=W.input.charCodeAt(W.position))!==0;)if(ee===39)if(ue(W,K,W.position,!0),ee=W.input.charCodeAt(++W.position),ee===39)K=W.position,W.position++,de=W.position;else return!0;else S(ee)?(ue(W,K,de,!0),ge(W,oe(W,!1,Ae)),K=de=W.position):W.position===W.lineStart&&fe(W)?F(W,"unexpected end of the document within a single quoted scalar"):(W.position++,E(ee)||(de=W.position));F(W,"unexpected end of the stream within a single quoted scalar")}s(Ne,"readSingleQuotedScalar");function xe(W,Ae){let K,de,ee,ve=W.input.charCodeAt(W.position);if(ve!==34)return!1;for(W.kind="scalar",W.result="",W.position++,K=de=W.position;(ve=W.input.charCodeAt(W.position))!==0;){if(ve===34)return ue(W,K,W.position,!0),W.position++,!0;if(ve===92){if(ue(W,K,W.position,!0),ve=W.input.charCodeAt(++W.position),S(ve))oe(W,!1,Ae);else if(ve<256&&N[ve])W.result+=j[ve],W.position++;else if((ee=x(ve))>0){let X=ee,me=0;for(;X>0;X--)ve=W.input.charCodeAt(++W.position),(ee=v(ve))>=0?me=(me<<4)+ee:F(W,"expected hexadecimal character");W.result+=$(me),W.position++}else F(W,"unknown escape sequence");K=de=W.position}else S(ve)?(ue(W,K,de,!0),ge(W,oe(W,!1,Ae)),K=de=W.position):W.position===W.lineStart&&fe(W)?F(W,"unexpected end of the document within a double quoted scalar"):(W.position++,E(ve)||(de=W.position))}F(W,"unexpected end of the stream within a double quoted scalar")}s(xe,"readDoubleQuotedScalar");function $e(W,Ae){let K=!0,de,ee,ve,X=W.tag,me,Ce=W.anchor,Be,Re,Ke,De,Ze=Object.create(null),Fe,Je,Le,ct=W.input.charCodeAt(W.position);if(ct===91)Be=93,De=!1,me=[];else if(ct===123)Be=125,De=!0,me={};else return!1;for(W.anchor!==null&&B(W,W.anchor,me),ct=W.input.charCodeAt(++W.position);ct!==0;){if(oe(W,!0,Ae),ct=W.input.charCodeAt(W.position),ct===Be)return W.position++,W.tag=X,W.anchor=Ce,W.kind=De?"mapping":"sequence",W.result=me,!0;if(K?ct===44&&F(W,"expected the node content, but found ','"):F(W,"missed comma between flow collection entries"),Je=Fe=Le=null,Re=Ke=!1,ct===63){let qe=W.input.charCodeAt(W.position+1);_(qe)&&(Re=Ke=!0,W.position++,oe(W,!0,Ae))}de=W.line,ee=W.lineStart,ve=W.position,ye(W,Ae,a,!1,!0),Je=W.tag,Fe=W.result,oe(W,!0,Ae),ct=W.input.charCodeAt(W.position),(Ke||W.line===de)&&ct===58&&(Re=!0,ct=W.input.charCodeAt(++W.position),oe(W,!0,Ae),ye(W,Ae,a,!1,!0),Le=W.result),De?le(W,me,Ze,Je,Fe,Le,de,ee,ve):Re?me.push(le(W,null,Ze,Je,Fe,Le,de,ee,ve)):me.push(Fe),oe(W,!0,Ae),ct=W.input.charCodeAt(W.position),ct===44?(K=!0,ct=W.input.charCodeAt(++W.position)):K=!1}F(W,"unexpected end of the stream within a flow collection")}s($e,"readFlowCollection");function Me(W,Ae){let K,de=h,ee=!1,ve=!1,X=Ae,me=0,Ce=!1,Be,Re=W.input.charCodeAt(W.position);if(Re===124)K=!1;else if(Re===62)K=!0;else return!1;for(W.kind="scalar",W.result="";Re!==0;)if(Re=W.input.charCodeAt(++W.position),Re===43||Re===45)h===de?de=Re===43?p:d:F(W,"repeat of a chomping mode identifier");else if((Be=w(Re))>=0)Be===0?F(W,"bad explicit indentation width of a block scalar; it cannot be less than one"):ve?F(W,"repeat of an indentation width identifier"):(X=Ae+Be-1,ve=!0);else break;if(E(Re)){do Re=W.input.charCodeAt(++W.position);while(E(Re));if(Re===35)do Re=W.input.charCodeAt(++W.position);while(!S(Re)&&Re!==0)}for(;Re!==0;){for(Te(W),W.lineIndent=0,Re=W.input.charCodeAt(W.position);(!ve||W.lineIndentX&&(X=W.lineIndent),S(Re)){me++;continue}if(!ve&&X===0&&F(W,"missing indentation for block scalar"),W.lineIndentAe)&&X!==0)F(W,"bad indentation of a sequence entry");else if(W.lineIndentAe)&&(Fe&&(ee=W.line,ve=W.lineStart,X=W.position),ye(W,Ae,u,!0,de)&&(Fe?De=W.result:Ze=W.result),Fe||(le(W,Be,Re,Ke,De,Ze,ee,ve,X),Ke=De=Ze=null),oe(W,!0,-1),Le=W.input.charCodeAt(W.position)),(W.line===qe||W.lineIndent>Ae)&&Le!==0)F(W,"bad indentation of a mapping entry");else if(W.lineIndent=W.maxDepth&&F(W,"nesting exceeded maxDepth ("+W.maxDepth+")"),W.depth+=1,W.listener!==null&&W.listener("open",W),W.tag=null,W.anchor=null,W.kind=null,W.result=null;let Fe=ve=X=u===K||l===K;if(de&&oe(W,!0,-1)&&(Ce=!0,W.lineIndent>Ae?me=1:W.lineIndent===Ae?me=0:W.lineIndentAe?me=1:W.lineIndent===Ae?me=0:W.lineIndent tag; it should be "scalar", not "'+W.kind+'"');for(let Je=0,Le=W.implicitTypes.length;Je"),W.result!==null&&Ke.kind!==W.kind&&F(W,"unacceptable node kind for !<"+W.tag+'> tag; it should be "'+Ke.kind+'", not "'+W.kind+'"'),Ke.resolve(W.result,W.tag)?(W.result=Ke.construct(W.result,W.tag),W.anchor!==null&&B(W,W.anchor,W.result)):F(W,"cannot resolve a node with !<"+W.tag+"> explicit tag")}return W.listener!==null&&W.listener("close",W),W.depth-=1,W.tag!==null||W.anchor!==null||Be}s(ye,"composeNode");function be(W){let Ae=W.position,K=!1,de;for(W.version=null,W.checkLineBreaks=W.legacy,W.tagMap=Object.create(null),W.anchorMap=Object.create(null);(de=W.input.charCodeAt(W.position))!==0&&(oe(W,!0,-1),de=W.input.charCodeAt(W.position),!(W.lineIndent>0||de!==37));){K=!0,de=W.input.charCodeAt(++W.position);let ee=W.position;for(;de!==0&&!_(de);)de=W.input.charCodeAt(++W.position);let ve=W.input.slice(ee,W.position),X=[];for(ve.length<1&&F(W,"directive name must not be less than one character in length");de!==0;){for(;E(de);)de=W.input.charCodeAt(++W.position);if(de===35){do de=W.input.charCodeAt(++W.position);while(de!==0&&!S(de));break}if(S(de))break;for(ee=W.position;de!==0&&!_(de);)de=W.input.charCodeAt(++W.position);X.push(W.input.slice(ee,W.position))}de!==0&&Te(W),n.call(ie,ve)?ie[ve](W,ve,X):O(W,'unknown document directive "'+ve+'"')}if(oe(W,!0,-1),W.lineIndent===0&&W.input.charCodeAt(W.position)===45&&W.input.charCodeAt(W.position+1)===45&&W.input.charCodeAt(W.position+2)===45?(W.position+=3,oe(W,!0,-1)):K&&F(W,"directives end mark is expected"),ye(W,W.lineIndent-1,u,!1,!0),oe(W,!0,-1),W.checkLineBreaks&&g.test(W.input.slice(Ae,W.position))&&O(W,"non-ASCII line breaks are interpreted as content"),W.documents.push(W.result),W.position===W.lineStart&&fe(W)){W.input.charCodeAt(W.position)===46&&(W.position+=3,oe(W,!0,-1));return}W.position"u"&&(K=Ae,Ae=null);let de=Ie(W,K);if(typeof Ae!="function")return de;for(let ee=0,ve=de.length;ee=32&&X<=126||X>=161&&X<=55295&&X!==8232&&X!==8233||X>=57344&&X<=65533&&X!==a||X>=65536&&X<=1114111}s(se,"isPrintable");function le(X){return se(X)&&X!==a&&X!==u&&X!==l}s(le,"isNsCharOrWhitespace");function Te(X,me,Ce){let Be=le(X),Re=Be&&!ue(X);return(Ce?Be:Be&&X!==C&&X!==w&&X!==R&&X!==M&&X!==j)&&X!==f&&!(me===E&&!Re)||le(me)&&!ue(me)&&X===f||me===E&&Re}s(Te,"isPlainSafe");function oe(X){return se(X)&&X!==a&&!ue(X)&&X!==S&&X!==v&&X!==E&&X!==C&&X!==w&&X!==R&&X!==M&&X!==j&&X!==f&&X!==m&&X!==k&&X!==d&&X!==N&&X!==_&&X!==b&&X!==y&&X!==p&&X!==g&&X!==x&&X!==$}s(oe,"isPlainSafeFirst");function fe(X){return!ue(X)&&X!==E}s(fe,"isPlainSafeLast");function ge(X,me){let Ce=X.charCodeAt(me),Be;return Ce>=55296&&Ce<=56319&&me+1=56320&&Be<=57343)?(Ce-55296)*1024+Be-56320+65536:Ce}s(ge,"codePointAt");function ke(X){return/^\n* /.test(X)}s(ke,"needIndentIndicator");let Ne=1,xe=2,$e=3,Me=4,He=5;function Ge(X,me,Ce,Be,Re,Ke,De,Ze){let Fe,Je=0,Le=null,ct=!1,qe=!1,mt=Be!==-1,St=-1,Lt=oe(ge(X,0))&&fe(ge(X,X.length-1));if(me||De)for(Fe=0;Fe=65536?Fe+=2:Fe++){if(Je=ge(X,Fe),!se(Je))return He;Lt=Lt&&Te(Je,Le,Ze),Le=Je}else{for(Fe=0;Fe=65536?Fe+=2:Fe++){if(Je=ge(X,Fe),Je===l)ct=!0,mt&&(qe=qe||Fe-St-1>Be&&X[St+1]!==" ",St=Fe);else if(!se(Je))return He;Lt=Lt&&Te(Je,Le,Ze),Le=Je}qe=qe||mt&&Fe-St-1>Be&&X[St+1]!==" "}return!ct&&!qe?Lt&&!De&&!Re(X)?Ne:Ke===U?He:xe:Ce>9&&ke(X)?He:De?Ke===U?He:xe:qe?Me:$e}s(Ge,"chooseScalarStyle");function ze(X,me,Ce,Be,Re){X.dump=(function(){if(me.length===0)return X.quotingType===U?'""':"''";if(!X.noCompatMode&&(z.indexOf(me)!==-1||F.test(me)))return X.quotingType===U?'"'+me+'"':"'"+me+"'";let Ke=X.indent*Math.max(1,Ce),De=X.lineWidth===-1?-1:Math.max(Math.min(X.lineWidth,40),X.lineWidth-Ke),Ze=Be||X.flowLevel>-1&&Ce>=X.flowLevel;function Fe(Je){return ie(X,Je)}switch(s(Fe,"testAmbiguity"),Ge(me,Ze,X.indent,De,Fe,X.quotingType,X.forceQuotes&&!Be,Re)){case Ne:return me;case xe:return"'"+me.replace(/'/g,"''")+"'";case $e:return"|"+Ve(me,X.indent)+et(J(me,Ke));case Me:return">"+Ve(me,X.indent)+et(J(he(me,De),Ke));case He:return'"'+be(me)+'"';default:throw new t("impossible error: invalid scalar style")}})()}s(ze,"writeScalar");function Ve(X,me){let Ce=ke(X)?String(me):"",Be=X[X.length-1]===` +`,Re=Be&&(X[X.length-2]===` +`||X===` +`)?"+":Be?"":"-";return Ce+Re+` +`}s(Ve,"blockHeader");function et(X){return X[X.length-1]===` +`?X.slice(0,-1):X}s(et,"dropEndingNewline");function he(X,me){let Ce=/(\n+)([^\n]*)/g,Be=(function(){let Ze=X.indexOf(` +`);return Ze=Ze!==-1?Ze:X.length,Ce.lastIndex=Ze,ye(X.slice(0,Ze),me)})(),Re=X[0]===` +`||X[0]===" ",Ke,De;for(;De=Ce.exec(X);){let Ze=De[1],Fe=De[2];Ke=Fe[0]===" ",Be+=Ze+(!Re&&!Ke&&Fe!==""?` +`:"")+ye(Fe,me),Re=Ke}return Be}s(he,"foldString");function ye(X,me){if(X===""||X[0]===" ")return X;let Ce=/ [^ ]/g,Be,Re=0,Ke,De=0,Ze=0,Fe="";for(;Be=Ce.exec(X);)Ze=Be.index,Ze-Re>me&&(Ke=De>Re?De:Ze,Fe+=` +`+X.slice(Re,Ke),Re=Ke+1),De=Ze;return Fe+=` +`,X.length-Re>me&&De>Re?Fe+=X.slice(Re,De)+` +`+X.slice(De+1):Fe+=X.slice(Re),Fe.slice(1)}s(ye,"foldLine");function be(X){let me="",Ce=0;for(let Be=0;Be=65536?Be+=2:Be++){Ce=ge(X,Be);let Re=I[Ce];!Re&&se(Ce)?(me+=X[Be],Ce>=65536&&(me+=X[Be+1])):me+=Re||B(Ce)}return me}s(be,"escapeString");function Ie(X,me,Ce){let Be="",Re=X.tag;for(let Ke=0,De=Ce.length;Ke"u"&&K(X,me,null,!1,!1))&&(Be!==""&&(Be+=","+(X.condenseFlow?"":" ")),Be+=X.dump)}X.tag=Re,X.dump="["+Be+"]"}s(Ie,"writeFlowSequence");function pe(X,me,Ce,Be){let Re="",Ke=X.tag;for(let De=0,Ze=Ce.length;De"u"&&K(X,me+1,null,!0,!0,!1,!0))&&((!Be||Re!=="")&&(Re+=te(X,me)),X.dump&&l===X.dump.charCodeAt(0)?Re+="-":Re+="- ",Re+=X.dump)}X.tag=Ke,X.dump=Re||"[]"}s(pe,"writeBlockSequence");function Qe(X,me,Ce){let Be="",Re=X.tag,Ke=Object.keys(Ce);for(let De=0,Ze=Ke.length;De1024&&(Fe+="? "),Fe+=X.dump+(X.condenseFlow?'"':"")+":"+(X.condenseFlow?"":" "),K(X,me,Le,!1,!1)&&(Fe+=X.dump,Be+=Fe))}X.tag=Re,X.dump="{"+Be+"}"}s(Qe,"writeFlowMapping");function W(X,me,Ce,Be){let Re="",Ke=X.tag,De=Object.keys(Ce);if(X.sortKeys===!0)De.sort();else if(typeof X.sortKeys=="function")De.sort(X.sortKeys);else if(X.sortKeys)throw new t("sortKeys must be a boolean or a function");for(let Ze=0,Fe=De.length;Ze1024;qe&&(X.dump&&l===X.dump.charCodeAt(0)?Je+="?":Je+="? "),Je+=X.dump,qe&&(Je+=te(X,me)),K(X,me+1,ct,!0,qe)&&(X.dump&&l===X.dump.charCodeAt(0)?Je+=":":Je+=": ",Je+=X.dump,Re+=Je)}X.tag=Ke,X.dump=Re||"{}"}s(W,"writeBlockMapping");function Ae(X,me,Ce){let Be=Ce?X.explicitTypes:X.implicitTypes;for(let Re=0,Ke=Be.length;Re tag resolver accepts not "'+Ze+'" style');X.dump=Fe}return!0}}return!1}s(Ae,"detectType");function K(X,me,Ce,Be,Re,Ke,De){X.tag=null,X.dump=Ce,Ae(X,Ce,!1)||Ae(X,Ce,!0);let Ze=i.call(X.dump),Fe=Be;Be&&(Be=X.flowLevel<0||X.flowLevel>me);let Je=Ze==="[object Object]"||Ze==="[object Array]",Le,ct;if(Je&&(Le=X.duplicates.indexOf(Ce),ct=Le!==-1),(X.tag!==null&&X.tag!=="?"||ct||X.indent!==2&&me>0)&&(Re=!1),ct&&X.usedDuplicates[Le])X.dump="*ref_"+Le;else{if(Je&&ct&&!X.usedDuplicates[Le]&&(X.usedDuplicates[Le]=!0),Ze==="[object Object]")Be&&Object.keys(X.dump).length!==0?(W(X,me,X.dump,Re),ct&&(X.dump="&ref_"+Le+X.dump)):(Qe(X,me,X.dump),ct&&(X.dump="&ref_"+Le+" "+X.dump));else if(Ze==="[object Array]")Be&&X.dump.length!==0?(X.noArrayIndent&&!De&&me>0?pe(X,me-1,X.dump,Re):pe(X,me,X.dump,Re),ct&&(X.dump="&ref_"+Le+X.dump)):(Ie(X,me,X.dump),ct&&(X.dump="&ref_"+Le+" "+X.dump));else if(Ze==="[object String]")X.tag!=="?"&&ze(X,X.dump,me,Ke,Fe);else{if(Ze==="[object Undefined]"||X.skipInvalid)return!1;throw new t("unacceptable kind of an object to dump "+Ze)}if(X.tag!==null&&X.tag!=="?"){let qe=encodeURI(X.tag[0]==="!"?X.tag.slice(1):X.tag).replace(/!/g,"%21");X.tag[0]==="!"?qe="!"+qe:qe.slice(0,18)==="tag:yaml.org,2002:"?qe="!!"+qe.slice(18):qe="!<"+qe+">",X.dump=qe+" "+X.dump}}return!0}s(K,"writeNode");function de(X,me){let Ce=[],Be=[];ee(X,Ce,Be);let Re=Be.length;for(let Ke=0;Ke{"use strict";s(bSe,"getDefaultExportFromCjs"),Vs={},N4={},hd={},s(s2,"requireCommon"),s(o2,"requireException"),s(xSe,"requireSnippet"),s(oo,"requireType"),s(oq,"requireSchema"),s(lq,"requireStr"),s(cq,"requireSeq"),s(uq,"requireMap"),s(hq,"requireFailsafe"),s(dq,"require_null"),s(pq,"requireBool"),s(fq,"requireInt"),s(gq,"requireFloat"),s(mq,"requireJson"),s(yq,"requireCore"),s(vq,"requireTimestamp"),s(bq,"requireMerge"),s(xq,"requireBinary"),s(Tq,"requireOmap"),s(kq,"requirePairs"),s(Cq,"requireSet"),s(H_,"require_default"),s(TSe,"requireLoader"),G_={},s(kSe,"requireDumper"),s(CSe,"requireJsYaml"),ZSe=CSe(),JSe=bSe(ZSe),{Type:qSt,Schema:WSt,FAILSAFE_SCHEMA:jSt,JSON_SCHEMA:O4,CORE_SCHEMA:VSt,DEFAULT_SCHEMA:USt,load:B4,loadAll:YSt,dump:HSt,YAMLException:GSt,types:XSt,safeLoad:KSt,safeLoadAll:QSt,safeDump:ZSt}=JSe}),e6e,t6e,JSt=G(()=>{"use strict";var e;pi(),X_(),Er(),Nt(),Kz(),Ar(),mi(),fn(),pg(),zx(),e6e="flowchart-",t6e=(e=class{constructor(){this.vertexCounter=0,this.config=it(),this.diagramId="",this.vertices=new Map,this.edges=[],this.classes=new Map,this.subGraphs=[],this.subGraphLookup=new Map,this.tooltips=new Map,this.subCount=0,this.firstGraphFlag=!0,this.secCount=-1,this.posCrossRef=[],this.funs=[],this.setAccTitle=bn,this.setAccDescription=In,this.setDiagramTitle=Dn,this.getAccTitle=Ln,this.getAccDescription=Mn,this.getDiagramTitle=xn,this.funs.push(this.setupToolTips.bind(this)),this.addVertex=this.addVertex.bind(this),this.firstGraph=this.firstGraph.bind(this),this.setDirection=this.setDirection.bind(this),this.addSubGraph=this.addSubGraph.bind(this),this.addLink=this.addLink.bind(this),this.setLink=this.setLink.bind(this),this.updateLink=this.updateLink.bind(this),this.addClass=this.addClass.bind(this),this.setClass=this.setClass.bind(this),this.destructLink=this.destructLink.bind(this),this.setClickEvent=this.setClickEvent.bind(this),this.setTooltip=this.setTooltip.bind(this),this.updateLinkInterpolate=this.updateLinkInterpolate.bind(this),this.setClickFun=this.setClickFun.bind(this),this.bindFunctions=this.bindFunctions.bind(this),this.lex={firstGraph:this.firstGraph.bind(this)},this.clear(),this.setGen("gen-2")}sanitizeText(r){return Vt.sanitizeText(r,this.config)}sanitizeNodeLabelType(r){switch(r){case"markdown":case"string":case"text":return r;default:return"markdown"}}setDiagramId(r){this.diagramId=r}lookUpDomId(r){for(let i of this.vertices.values())if(i.id===r)return this.diagramId?`${this.diagramId}-${i.domId}`:i.domId;return this.diagramId?`${this.diagramId}-${r}`:r}addVertex(r,i,n,a,o,l,u={},h){if(!r||r.trim().length===0)return;let d;if(h!==void 0){let y;h.includes(` +`)?y=h+` +`:y=`{ +`+h+` +}`,d=B4(y,{schema:O4})}let p=this.subGraphLookup.get(r);if(p&&d){p.metadata={...p.metadata,...d};return}let f=this.edges.find(y=>y.id===r);if(f){let y=d;y?.animate!==void 0&&(f.animate=y.animate),y?.animation!==void 0&&(f.animation=y.animation),y?.curve!==void 0&&(f.interpolate=y.curve);return}let g,m=this.vertices.get(r);if(m===void 0&&(i===void 0&&n===void 0&&a!==void 0&&a!==null&&Ee.warn(`Style applied to unknown node "${r}". This may indicate a typo. The node will be created automatically.`),m={id:r,labelType:"text",domId:e6e+r+"-"+this.vertexCounter,styles:[],classes:[]},this.vertices.set(r,m)),this.vertexCounter++,i!==void 0?(this.config=it(),g=this.sanitizeText(i.text.trim()),m.labelType=i.type,g.startsWith('"')&&g.endsWith('"')&&(g=g.substring(1,g.length-1)),m.text=g):m.text===void 0&&(m.text=r),n!==void 0&&(m.type=n),a?.forEach(y=>{m.styles.push(y)}),o?.forEach(y=>{m.classes.push(y)}),l!==void 0&&(m.dir=l),m.props===void 0?m.props=u:u!==void 0&&Object.assign(m.props,u),d!==void 0){if(d.shape){if(d.shape!==d.shape.toLowerCase()||d.shape.includes("_"))throw new Error(`No such shape: ${d.shape}. Shape names should be lowercase.`);if(!XEe(d.shape))throw new Error(`No such shape: ${d.shape}.`);m.type=d?.shape}d?.label&&(m.text=d?.label,m.labelType=this.sanitizeNodeLabelType(d?.labelType)),d?.icon&&(m.icon=d?.icon,!d.label?.trim()&&m.text===r&&(m.text="")),d?.form&&(m.form=d?.form),d?.pos&&(m.pos=d?.pos),d?.img&&(m.img=d?.img,!d.label?.trim()&&m.text===r&&(m.text="")),d?.constraint&&(m.constraint=d.constraint),d.w&&(m.assetWidth=Number(d.w)),d.h&&(m.assetHeight=Number(d.h))}}addSingleLink(r,i,n,a){let o={start:r,end:i,type:void 0,text:"",labelType:"text",classes:[],isUserDefinedId:!1,interpolate:this.edges.defaultInterpolate};Ee.info("abc78 Got edge...",o);let l=n.text;if(l!==void 0&&(o.text=this.sanitizeText(l.text.trim()),o.text.startsWith('"')&&o.text.endsWith('"')&&(o.text=o.text.substring(1,o.text.length-1)),o.labelType=this.sanitizeNodeLabelType(l.type)),n!==void 0&&(o.type=n.type,o.stroke=n.stroke,o.length=n.length>10?10:n.length),a&&!this.edges.some(u=>u.id===a))o.id=a,o.isUserDefinedId=!0;else{let u=this.edges.filter(h=>h.start===o.start&&h.end===o.end);u.length===0?o.id=gg(o.start,o.end,{counter:0,prefix:"L"}):o.id=gg(o.start,o.end,{counter:u.length+1,prefix:"L"})}if(this.edges.length<(this.config.maxEdges??500))Ee.info("Pushing edge..."),this.edges.push(o);else throw new Error(`Edge limit exceeded. ${this.edges.length} edges found, but the limit is ${this.config.maxEdges}. + +Initialize mermaid with maxEdges set to a higher number to allow more edges. +You cannot set this config via configuration inside the diagram as it is a secure config. +You have to call mermaid.initialize.`)}isLinkData(r){return r!==null&&typeof r=="object"&&"id"in r&&typeof r.id=="string"}addLink(r,i,n){let a=this.isLinkData(n)?n.id.replace("@",""):void 0;Ee.info("addLink",r,i,a);for(let o of r)for(let l of i){let u=o===r[r.length-1],h=l===i[0];u&&h?this.addSingleLink(o,l,n,a):this.addSingleLink(o,l,n,void 0)}}updateLinkInterpolate(r,i){r.forEach(n=>{n==="default"?this.edges.defaultInterpolate=i:this.edges[n].interpolate=i})}updateLink(r,i){r.forEach(n=>{if(typeof n=="number"&&n>=this.edges.length)throw new Error(`The index ${n} for linkStyle is out of bounds. Valid indices for linkStyle are between 0 and ${this.edges.length-1}. (Help: Ensure that the index is within the range of existing edges.)`);n==="default"?this.edges.defaultStyle=i:(this.edges[n].style=i,(this.edges[n]?.style?.length??0)>0&&!this.edges[n]?.style?.some(a=>a?.startsWith("fill"))&&this.edges[n]?.style?.push("fill:none"))})}addClass(r,i){let n=i.join().replace(/\\,/g,"§§§").replace(/,/g,";").replace(/§§§/g,",").split(";");r.split(",").forEach(a=>{let o=this.classes.get(a);o===void 0&&(o={id:a,styles:[],textStyles:[]},this.classes.set(a,o)),n?.forEach(l=>{if(/color/.exec(l)){let u=l.replace("fill","bgFill");o.textStyles.push(u)}o.styles.push(l)})})}setDirection(r){this.direction=r.trim(),/.*/.exec(this.direction)&&(this.direction="LR"),/.*v/.exec(this.direction)&&(this.direction="TB"),this.direction==="TD"&&(this.direction="TB")}setClass(r,i){for(let n of r.split(",")){let a=this.vertices.get(n);a&&a.classes.push(i);let o=this.edges.find(u=>u.id===n);o&&o.classes.push(i);let l=this.subGraphLookup.get(n);l&&l.classes.push(i)}}setTooltip(r,i){if(i!==void 0){i=this.sanitizeText(i);for(let n of r.split(","))this.tooltips.set(this.version==="gen-1"?this.lookUpDomId(n):n,i)}}setClickFun(r,i,n){if(it().securityLevel!=="loose"||i===void 0)return;let a=[];if(typeof n=="string"){a=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(let l=0;l{let l=this.lookUpDomId(r),u=document.querySelector(`[id="${l}"]`);u!==null&&u.addEventListener("click",()=>{ai.runFunc(i,...a)},!1)}))}setLink(r,i,n){r.split(",").forEach(a=>{let o=this.vertices.get(a);o!==void 0&&(o.link=ai.formatUrl(i,this.config),o.linkTarget=n)}),this.setClass(r,"clickable")}getTooltip(r){return this.tooltips.get(r)}setClickEvent(r,i,n){r.split(",").forEach(a=>{this.setClickFun(a,i,n)}),this.setClass(r,"clickable")}bindFunctions(r){this.funs.forEach(i=>{i(r)})}getDirection(){return this.direction?.trim()}getVertices(){return this.vertices}getEdges(){return this.edges}getClasses(){return this.classes}setupToolTips(r){let i=H6();zt(r).select("svg").selectAll("g.node").on("mouseover",n=>{let a=zt(n.currentTarget),o=a.attr("title");if(o===null)return;let l=n.currentTarget?.getBoundingClientRect();i.transition().duration(200).style("opacity",".9"),i.text(a.attr("title")).style("left",window.scrollX+l.left+(l.right-l.left)/2+"px").style("top",window.scrollY+l.bottom+"px"),i.html(wu.sanitize(o)),a.classed("hover",!0)}).on("mouseout",n=>{i.transition().duration(500).style("opacity",0),zt(n.currentTarget).classed("hover",!1)})}clear(r="gen-2"){this.vertices=new Map,this.classes=new Map,this.edges=[],this.funs=[this.setupToolTips.bind(this)],this.diagramId="",this.subGraphs=[],this.subGraphLookup=new Map,this.subCount=0,this.tooltips=new Map,this.firstGraphFlag=!0,this.version=r,this.config=it(),pn()}setGen(r){this.version=r||"gen-2"}defaultStyle(){return"fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;"}addSubGraph(r,i,n){let a=r.text.trim(),o=n.text;r===n&&/\s/.exec(n.text)&&(a=void 0);let l=s(f=>{let g={boolean:{},number:{},string:{}},m=[],y;return{nodeList:f.filter(function(k){let C=typeof k;return k.stmt&&k.stmt==="dir"?(y=k.value,!1):k.trim()===""?!1:C in g?g[C].hasOwnProperty(k)?!1:g[C][k]=!0:m.includes(k)?!1:m.push(k)}),dir:y}},"uniq")(i.flat()),u=l.nodeList,h=l.dir,d=it().flowchart??{};if(h=h??(d.inheritDir?this.getDirection()??it().direction??void 0:void 0),this.version==="gen-1")for(let f=0;f2e3)return{result:!1,count:0};if(this.posCrossRef[this.secCount]=i,this.subGraphs[i].id===r)return{result:!0,count:0};let a=0,o=1;for(;a=0){let u=this.indexNodes2(r,l);if(u.result)return{result:!0,count:o+u.count};o=o+u.count}a=a+1}return{result:!1,count:o}}getDepthFirstPos(r){return this.posCrossRef[r]}indexNodes(){this.secCount=-1,this.subGraphs.length>0&&this.indexNodes2("none",this.subGraphs.length-1)}getSubGraphs(){return this.subGraphs}firstGraph(){return this.firstGraphFlag?(this.firstGraphFlag=!1,!0):!1}destructStartLink(r){let i=r.trim(),n="arrow_open";switch(i[0]){case"<":n="arrow_point",i=i.slice(1);break;case"x":n="arrow_cross",i=i.slice(1);break;case"o":n="arrow_circle",i=i.slice(1);break}let a="normal";return i.includes("=")&&(a="thick"),i.includes(".")&&(a="dotted"),{type:n,stroke:a}}countChar(r,i){let n=i.length,a=0;for(let o=0;o":a="arrow_point",i.startsWith("<")&&(a="double_"+a,n=n.slice(1));break;case"o":a="arrow_circle",i.startsWith("o")&&(a="double_"+a,n=n.slice(1));break}let o="normal",l=n.length-1;n.startsWith("=")&&(o="thick"),n.startsWith("~")&&(o="invisible");let u=this.countChar(".",n);return u&&(o="dotted",l=u),{type:a,stroke:o,length:l}}destructLink(r,i){let n=this.destructEndLink(r),a;if(i){if(a=this.destructStartLink(i),a.stroke!==n.stroke)return{type:"INVALID",stroke:"INVALID"};if(a.type==="arrow_open")a.type=n.type;else{if(a.type!==n.type)return{type:"INVALID",stroke:"INVALID"};a.type="double_"+a.type}return a.type==="double_arrow"&&(a.type="double_arrow_point"),a.length=n.length,a}return n}exists(r,i){for(let n of r)if(n.nodes.includes(i))return!0;return!1}makeUniq(r,i){let n=[];return r.nodes.forEach((a,o)=>{this.exists(i,a)||n.push(r.nodes[o])}),{nodes:n}}getTypeFromVertex(r){if(r.img)return"imageSquare";if(r.icon)return r.form==="circle"?"iconCircle":r.form==="square"?"iconSquare":r.form==="rounded"?"iconRounded":"icon";switch(r.type){case"square":case void 0:return"squareRect";case"round":return"roundedRect";case"ellipse":return"ellipse";default:return r.type}}findNode(r,i){return r.find(n=>n.id===i)}destructEdgeType(r){let i="none",n="arrow_point";switch(r){case"arrow_point":case"arrow_circle":case"arrow_cross":n=r;break;case"double_arrow_point":case"double_arrow_circle":case"double_arrow_cross":i=r.replace("double_",""),n=i;break}return{arrowTypeStart:i,arrowTypeEnd:n}}addNodeFromVertex(r,i,n,a,o,l){let u=n.get(r.id),h=a.get(r.id)??!1,d=this.findNode(i,r.id);if(d)d.cssStyles=r.styles,d.cssCompiledStyles=this.getCompiledStyles(r.classes),d.cssClasses=r.classes.join(" ");else{let p={id:r.id,label:r.text,labelType:r.labelType,labelStyle:"",parentId:u,padding:o.flowchart?.padding||8,cssStyles:r.styles,cssCompiledStyles:this.getCompiledStyles(["default","node",...r.classes]),cssClasses:"default "+r.classes.join(" "),dir:r.dir,domId:r.domId,look:l,link:r.link,linkTarget:r.linkTarget,tooltip:this.getTooltip(r.id),icon:r.icon,pos:r.pos,img:r.img,assetWidth:r.assetWidth,assetHeight:r.assetHeight,constraint:r.constraint};h?i.push({...p,isGroup:!0,shape:"rect"}):i.push({...p,isGroup:!1,shape:this.getTypeFromVertex(r)})}}getCompiledStyles(r){let i=[];for(let n of r){let a=this.classes.get(n);a?.styles&&(i=[...i,...a.styles??[]].map(o=>o.trim())),a?.textStyles&&(i=[...i,...a.textStyles??[]].map(o=>o.trim()))}return i}getData(){let r=it(),i=[],n=[],a=this.getSubGraphs(),o=new Map,l=new Map,u=new Map;for(let m of a)for(let y of m.nodes)this.subGraphLookup.has(y)&&u.set(y,m.id);let h=s(m=>this.subGraphLookup.get(m)?.metadata?.view==="collapsed","isCollapsed"),d=s(m=>{let y,k=new Set,C=m;for(;C!==void 0&&!k.has(C);)k.add(C),h(C)&&(y=C),C=u.get(C);return y},"outermostCollapsed"),p=new Set,f=new Map;for(let m of a){let y=d(m.id);if(y!==void 0){m.id!==y&&(p.add(m.id),f.set(m.id,y));for(let k of m.nodes)k!==y&&(p.add(k),f.set(k,y))}}for(let m=a.length-1;m>=0;m--){let y=a[m];if(!p.has(y.id)){y.nodes.length>0&&l.set(y.id,!0);for(let k of y.nodes)o.set(k,y.id)}}for(let m=a.length-1;m>=0;m--){let y=a[m];p.has(y.id)||(y.metadata?.view==="collapsed"?i.push({id:y.id,label:y.title,labelStyle:"",labelType:y.labelType,parentId:o.get(y.id),padding:8,cssCompiledStyles:this.getCompiledStyles(y.classes),cssClasses:y.classes.join(" "),shape:"collapsedGroup",dir:y.dir,isGroup:!1,look:r.look}):i.push({id:y.id,label:y.title,labelStyle:"",labelType:y.labelType,parentId:o.get(y.id),padding:8,cssCompiledStyles:this.getCompiledStyles(y.classes),cssClasses:y.classes.join(" "),shape:"rect",dir:y.dir,isGroup:!0,look:r.look}))}this.getVertices().forEach(m=>{p.has(m.id)||this.addNodeFromVertex(m,i,o,l,r,r.look||"classic")});let g=this.getEdges();return g.forEach((m,y)=>{let{arrowTypeStart:k,arrowTypeEnd:C}=this.destructEdgeType(m.type),S=[...g.defaultStyle??[]],E=f.get(m.start)??m.start,_=f.get(m.end)??m.end;if(E===_&&(f.has(m.start)||f.has(m.end)))return;m.style&&S.push(...m.style);let b={id:gg(E,_,{counter:y,prefix:"L"},m.id),isUserDefinedId:m.isUserDefinedId,start:E,end:_,type:m.type??"normal",label:m.text,labelType:m.labelType,labelpos:"c",thickness:m.stroke,minlen:m.length,classes:m?.stroke==="invisible"?"":"edge-thickness-normal edge-pattern-solid flowchart-link",arrowTypeStart:m?.stroke==="invisible"||m?.type==="arrow_open"?"none":k,arrowTypeEnd:m?.stroke==="invisible"||m?.type==="arrow_open"?"none":C,arrowheadStyle:"fill: #333",cssCompiledStyles:this.getCompiledStyles(m.classes),labelStyle:S,style:S,pattern:m.stroke,look:r.look,animate:m.animate,animation:m.animation,curve:m.interpolate||this.edges.defaultInterpolate||r.flowchart?.curve};n.push(b)}),{nodes:i,edges:n,other:{},config:r}}defaultConfig(){return qO.flowchart}},s(e,"FlowDB"),e)}),bg,l2=G(()=>{"use strict";pi(),bg=s((e,t)=>{let r;return t==="sandbox"&&(r=zt("#i"+e)),zt(t==="sandbox"?r.nodes()[0].contentDocument.body:"body").select(`[id="${e}"]`)},"getDiagramElement")}),c2,K_=G(()=>{"use strict";c2=s(({flowchart:e})=>{let t=e?.subGraphTitleMargin?.top??0,r=e?.subGraphTitleMargin?.bottom??0,i=t+r;return{subGraphTitleTopMargin:t,subGraphTitleBottomMargin:r,subGraphTitleTotalMargin:i}},"getSubGraphTitleMargins")}),r6e,e6t=G(()=>{"use strict";Er(),Gr(),Nt(),pi(),vr(),ec(),bz(),gr(),r6e=s(async(e,t)=>{let r=it(),{themeVariables:i,handDrawnSeed:n}=r,{clusterBkg:a,clusterBorder:o}=i,l=o,{labelStyles:u,nodeStyles:h,borderStyles:d,backgroundStyles:p}=cr(t),f=e.insert("g").attr("class","cluster swimlane "+(t.cssClasses||"")).attr("id",t.id).attr("data-id",t.id).attr("data-et","cluster").attr("data-look",t.look),g=_c(r.flowchart.htmlLabels),m=t.direction==="LR",y=f.insert("g").attr("class","cluster-label swimlane-label"),k=await Io(y,t.label,{style:t.labelStyle,useHtmlLabels:g,isNode:!0,width:t.width}),C=k.getBBox();if(g){let j=k.children[0],I=zt(k);C=j.getBoundingClientRect(),I.attr("width",C.width),I.attr("height",C.height)}let S=t.padding??0,E=t.width<=C.width+S?C.width+S:t.width;t.width<=C.width+S?t.diff=(E-t.width)/2-S:t.diff=-S;let _=t.height,b=t.y-_/2,v=t.y+_/2,x=t.x-E/2,w=t.swimlaneContentTop!==void 0?t.swimlaneContentTop:b+_/3,R=m?4:0,$=C.height+2*R,M,N;if(m){let j=Math.max($,C.height+2*R),I=x+j,z=Math.max(0,E-j);if(t.look==="handDrawn"){let B=ur.svg(f),Y=pr(t,{roughness:.7,fill:a,stroke:l,fillWeight:3,seed:n}),U=pr(t,{roughness:.7,fill:"none",stroke:l,seed:n}),H=B.rectangle(x,b,j,_,Y);M=f.insert(()=>H,":first-child");let J=B.rectangle(I,b,z,_,U);N=f.insert(()=>J,":first-child"),M.select("path:nth-child(2)").attr("style",d.join(";")),M.select("path").attr("style",p.join(";").replace("fill","stroke"))}else M=f.insert("rect",":first-child"),N=f.insert("rect",":first-child"),M.attr("class","swimlane-title").attr("style",h).attr("x",x).attr("y",b).attr("width",j).attr("height",_).attr("fill",a).attr("stroke",l),N.attr("class","swimlane-body").attr("style",h).attr("x",I).attr("y",b).attr("width",z).attr("height",_).attr("fill","none").attr("stroke",l);let F=x+j/2,O=t.y;y.attr("transform",`translate(${F}, ${O}) rotate(-90) translate(${-C.width/2}, ${-C.height/2})`)}else{let j=Math.max(0,w-b),I=Math.min($,j),z=b+I,F=Math.max(0,v-z),O=t.x-E/2;if(t.look==="handDrawn"){let U=ur.svg(f),H=pr(t,{roughness:.7,fill:a,stroke:l,fillWeight:3,seed:n}),J=pr(t,{roughness:.7,fill:"none",stroke:l,seed:n}),te=U.rectangle(O,b,E,I,H);M=f.insert(()=>te,":first-child");let ie=U.rectangle(O,z,E,F,J);N=f.insert(()=>ie,":first-child"),M.select("path:nth-child(2)").attr("style",d.join(";")),M.select("path").attr("style",p.join(";").replace("fill","stroke"))}else M=f.insert("rect",":first-child"),N=f.insert("rect",":first-child"),M.attr("class","swimlane-title").attr("style",h).attr("x",O).attr("y",b).attr("width",E).attr("height",I).attr("fill",a).attr("stroke",l),N.attr("class","swimlane-body").attr("style",h).attr("x",O).attr("y",z).attr("width",E).attr("height",F).attr("fill","none").attr("stroke",l);let B=t.x-C.width/2,Y=b+(I-C.height)/2;y.attr("transform",`translate(${B}, ${Y})`)}if(Ee.trace("Swimlane data ",t,JSON.stringify(t)),u){let j=y.select("span");j&&j.attr("style",u)}return t.offsetX=0,t.width=E,t.height=_,t.offsetY=C.height-S/2,t.intersect=function(j){return Wp(t,j)},{cluster:f,labelBBox:C}},"swimlane")}),Wq,i6e,n6e,a6e,s6e,o6e,l6e,jq,P4,c6e,Q_=G(()=>{"use strict";Er(),Gr(),Nt(),K_(),pi(),vr(),ec(),bz(),jz(),Vu(),gr(),e6t(),Wq=s(async(e,t)=>{Ee.info("Creating subgraph rect for ",t.id,t);let r=it(),{themeVariables:i,handDrawnSeed:n}=r,{clusterBkg:a,clusterBorder:o}=i,{labelStyles:l,nodeStyles:u,borderStyles:h,backgroundStyles:d}=cr(t),p=e.insert("g").attr("class","cluster "+t.cssClasses).attr("id",t.domId).attr("data-look",t.look),f=Ps(r),g=p.insert("g").attr("class","cluster-label "),m;t.labelType==="markdown"?m=await Io(g,t.label,{style:t.labelStyle,useHtmlLabels:f,isNode:!0,width:t.width}):m=await ud(g,t.label,t.labelStyle||"",!1,!0);let y=m.getBBox();if(Ps(r)){let x=m.children[0],w=zt(m);y=x.getBoundingClientRect(),w.attr("width",y.width),w.attr("height",y.height)}let k=t.width<=y.width+t.padding?y.width+t.padding:t.width;t.width<=y.width+t.padding?t.diff=(k-t.width)/2-t.padding:t.diff=-t.padding;let C=t.height,S=t.x-k/2,E=t.y-C/2;Ee.trace("Data ",t,JSON.stringify(t));let _;if(t.look==="handDrawn"){let x=ur.svg(p),w=pr(t,{roughness:.7,fill:a,stroke:o,fillWeight:3,seed:n}),R=x.path(Sl(S,E,k,C,0),w);_=p.insert(()=>(Ee.debug("Rough node insert CXC",R),R),":first-child"),_.select("path:nth-child(2)").attr("style",h.join(";")),_.select("path").attr("style",d.join(";").replace("fill","stroke"))}else _=p.insert("rect",":first-child"),_.attr("style",u).attr("rx",t.rx).attr("ry",t.ry).attr("x",S).attr("y",E).attr("width",k).attr("height",C);let{subGraphTitleTopMargin:b}=c2(r);if(g.attr("transform",`translate(${t.x-y.width/2}, ${t.y-t.height/2+b})`),l){let x=g.select("span");x&&x.attr("style",l)}let v=_.node().getBBox();return t.offsetX=0,t.width=v.width,t.height=v.height,t.offsetY=y.height-t.padding/2,t.intersect=function(x){return Wp(t,x)},{cluster:p,labelBBox:y}},"rect"),i6e=s((e,t)=>{let r=e.insert("g").attr("class","note-cluster").attr("id",t.domId),i=r.insert("rect",":first-child"),n=0*t.padding,a=n/2;i.attr("rx",t.rx).attr("ry",t.ry).attr("x",t.x-t.width/2-a).attr("y",t.y-t.height/2-a).attr("width",t.width+n).attr("height",t.height+n).attr("fill","none");let o=i.node().getBBox();return t.width=o.width,t.height=o.height,t.intersect=function(l){return Wp(t,l)},{cluster:r,labelBBox:{width:0,height:0}}},"noteGroup"),n6e=s(async(e,t)=>{let r=it(),{themeVariables:i,handDrawnSeed:n}=r,{altBackground:a,compositeBackground:o,compositeTitleBackground:l,nodeBorder:u}=i,h=e.insert("g").attr("class",t.cssClasses).attr("id",t.domId).attr("data-id",t.id).attr("data-look",t.look),d=h.insert("g",":first-child"),p=h.insert("g").attr("class","cluster-label"),f=h.append("rect"),g=await ud(p,t.label,t.labelStyle,void 0,!0),m=g.getBBox();if(Ps(r)){let R=g.children[0],$=zt(g);m=R.getBoundingClientRect(),$.attr("width",m.width),$.attr("height",m.height)}let y=0*t.padding,k=y/2,C=(t.width<=m.width+t.padding?m.width+t.padding:t.width)+y;t.width<=m.width+t.padding?t.diff=(C-t.width)/2-t.padding:t.diff=-t.padding;let S=t.height+y,E=t.height+y-m.height-6,_=t.x-C/2,b=t.y-S/2;t.width=C;let v=t.y-t.height/2-k+m.height+2,x;if(t.look==="handDrawn"){let R=t.cssClasses.includes("statediagram-cluster-alt"),$=ur.svg(h),M=t.rx||t.ry?$.path(Sl(_,b,C,S,10),{roughness:.7,fill:l,fillStyle:"solid",stroke:u,seed:n}):$.rectangle(_,b,C,S,{seed:n});x=h.insert(()=>M,":first-child");let N=$.rectangle(_,v,C,E,{fill:R?a:o,fillStyle:R?"hachure":"solid",stroke:u,seed:n});x=h.insert(()=>M,":first-child"),f=h.insert(()=>N)}else x=d.insert("rect",":first-child"),x.attr("class","outer").attr("x",_).attr("y",b).attr("width",C).attr("height",S).attr("data-look",t.look),f.attr("class","inner").attr("x",_).attr("y",v).attr("width",C).attr("height",E);p.attr("transform",`translate(${t.x-m.width/2}, ${b+1-(Ps(r)?0:3)})`);let w=x.node().getBBox();return t.height=w.height,t.offsetX=0,t.offsetY=m.height-t.padding/2,t.labelBBox=m,t.intersect=function(R){return Wp(t,R)},{cluster:h,labelBBox:m}},"roundedWithTitle"),a6e=s(async(e,t)=>{Ee.info("Creating subgraph rect for ",t.id,t);let r=it(),{themeVariables:i,handDrawnSeed:n}=r,{clusterBkg:a,clusterBorder:o}=i,{labelStyles:l,nodeStyles:u,borderStyles:h,backgroundStyles:d}=cr(t),p=e.insert("g").attr("class","cluster "+t.cssClasses).attr("id",t.domId).attr("data-look",t.look),f=Ps(r),g=p.insert("g").attr("class","cluster-label "),m=await Io(g,t.label,{style:t.labelStyle,useHtmlLabels:f,isNode:!0,width:t.width}),y=m.getBBox();if(Ps(r)){let x=m.children[0],w=zt(m);y=x.getBoundingClientRect(),w.attr("width",y.width),w.attr("height",y.height)}let k=t.width<=y.width+t.padding?y.width+t.padding:t.width;t.width<=y.width+t.padding?t.diff=(k-t.width)/2-t.padding:t.diff=-t.padding;let C=t.height,S=t.x-k/2,E=t.y-C/2;Ee.trace("Data ",t,JSON.stringify(t));let _;if(t.look==="handDrawn"){let x=ur.svg(p),w=pr(t,{roughness:.7,fill:a,stroke:o,fillWeight:4,seed:n}),R=x.path(Sl(S,E,k,C,t.rx),w);_=p.insert(()=>(Ee.debug("Rough node insert CXC",R),R),":first-child"),_.select("path:nth-child(2)").attr("style",h.join(";")),_.select("path").attr("style",d.join(";").replace("fill","stroke"))}else _=p.insert("rect",":first-child"),_.attr("style",u).attr("rx",t.rx).attr("ry",t.ry).attr("x",S).attr("y",E).attr("width",k).attr("height",C);let{subGraphTitleTopMargin:b}=c2(r);if(g.attr("transform",`translate(${t.x-y.width/2}, ${t.y-t.height/2+b})`),l){let x=g.select("span");x&&x.attr("style",l)}let v=_.node().getBBox();return t.offsetX=0,t.width=v.width,t.height=v.height,t.offsetY=y.height-t.padding/2,t.intersect=function(x){return Wp(t,x)},{cluster:p,labelBBox:y}},"kanbanSection"),s6e=s((e,t)=>{let r=it(),{themeVariables:i,handDrawnSeed:n}=r,{nodeBorder:a}=i,o=e.insert("g").attr("class",t.cssClasses).attr("id",t.domId).attr("data-look",t.look),l=o.insert("g",":first-child"),u=0*t.padding,h=t.width+u;t.diff=-t.padding;let d=t.height+u,p=t.x-h/2,f=t.y-d/2;t.width=h;let g;if(t.look==="handDrawn"){let y=ur.svg(o).rectangle(p,f,h,d,{fill:"lightgrey",roughness:.5,strokeLineDash:[5],stroke:a,seed:n});g=o.insert(()=>y,":first-child")}else{g=l.insert("rect",":first-child");let y="outer";t.look,y="divider",g.attr("class",y).attr("x",p).attr("y",f).attr("width",h).attr("height",d).attr("data-look",t.look)}let m=g.node().getBBox();return t.height=m.height,t.offsetX=0,t.offsetY=0,t.intersect=function(y){return Wp(t,y)},{cluster:o,labelBBox:{}}},"divider"),o6e=Wq,l6e={rect:Wq,squareRect:o6e,roundedWithTitle:n6e,noteGroup:i6e,divider:s6e,kanbanSection:a6e,swimlane:r6e},jq=new Map,P4=s(async(e,t)=>{let r=t.shape||"rect",i=await l6e[r](e,t);return jq.set(t.id,i),i},"insertCluster"),c6e=s(()=>{jq=new Map},"clear")}),u2,t6t=G(()=>{"use strict";u2=s((e,t)=>{if(t)return"translate("+-e.width/2+", "+-e.height/2+")";let r=e.x??0,i=e.y??0;return"translate("+-(r+e.width/2)+", "+-(i+e.height/2)+")"},"computeLabelTransform")});function F4(e,t){if(e===void 0||t===void 0)return{angle:0,deltaX:0,deltaY:0};e=Xn(e),t=Xn(t);let[r,i]=[e.x,e.y],[n,a]=[t.x,t.y],o=n-r,l=a-i;return{angle:Math.atan(l/o),deltaX:o,deltaY:l}}var rs,Vq,Xn,u6e,h6e=G(()=>{"use strict";rs={aggregation:17.25,extension:17.25,composition:17.25,dependency:6,lollipop:13.5,arrow_point:4,arrow_barb:0,arrow_barb_neo:5.5},Vq={arrow_point:4,arrow_cross:12.5,arrow_circle:12.5},s(F4,"calculateDeltaAndAngle"),Xn=s(e=>Array.isArray(e)?{x:e[0],y:e[1]}:e,"pointTransformer"),u6e=s(e=>({x:s(function(t,r,i){let n=0,a=Xn(i[0]).x=0?1:-1)}else if(r===i.length-1&&Object.hasOwn(rs,e.arrowTypeEnd)){let{angle:g,deltaX:m}=F4(i[i.length-1],i[i.length-2]);n=rs[e.arrowTypeEnd]*Math.cos(g)*(m>=0?1:-1)}let o=Math.abs(Xn(t).x-Xn(i[i.length-1]).x),l=Math.abs(Xn(t).y-Xn(i[i.length-1]).y),u=Math.abs(Xn(t).x-Xn(i[0]).x),h=Math.abs(Xn(t).y-Xn(i[0]).y),d=rs[e.arrowTypeStart],p=rs[e.arrowTypeEnd],f=1;if(o0&&l0&&h=0?1:-1)}else if(r===i.length-1&&Object.hasOwn(rs,e.arrowTypeEnd)){let{angle:g,deltaY:m}=F4(i[i.length-1],i[i.length-2]);n=rs[e.arrowTypeEnd]*Math.abs(Math.sin(g))*(m>=0?1:-1)}let o=Math.abs(Xn(t).y-Xn(i[i.length-1]).y),l=Math.abs(Xn(t).x-Xn(i[i.length-1]).x),u=Math.abs(Xn(t).y-Xn(i[0]).y),h=Math.abs(Xn(t).x-Xn(i[0]).x),d=rs[e.arrowTypeStart],p=rs[e.arrowTypeEnd],f=1;if(o0&&l0&&h{"use strict";Nt(),d6e=s((e,t,r,i,n,a=!1,o)=>{t.arrowTypeStart&&Uq(e,"start",t.arrowTypeStart,r,i,n,a,o),t.arrowTypeEnd&&Uq(e,"end",t.arrowTypeEnd,r,i,n,a,o)},"addEdgeMarkers"),p6e={arrow_cross:{type:"cross",fill:!1},arrow_point:{type:"point",fill:!0},arrow_barb:{type:"barb",fill:!0},arrow_barb_neo:{type:"barb",fill:!0},arrow_circle:{type:"circle",fill:!1},aggregation:{type:"aggregation",fill:!1},extension:{type:"extension",fill:!1},composition:{type:"composition",fill:!0},dependency:{type:"dependency",fill:!0},lollipop:{type:"lollipop",fill:!1},only_one:{type:"onlyOne",fill:!1},zero_or_one:{type:"zeroOrOne",fill:!1},one_or_more:{type:"oneOrMore",fill:!1},zero_or_more:{type:"zeroOrMore",fill:!1},requirement_arrow:{type:"requirement_arrow",fill:!1},requirement_contains:{type:"requirement_contains",fill:!1}},f6e=["cross","point","circle","lollipop","aggregation","extension","composition","dependency","barb"],Uq=s((e,t,r,i,n,a,o=!1,l)=>{if(!r||r==="none")return;let u=p6e[r],h=u&&f6e.includes(u.type);if(!u){Ee.warn(`Unknown arrow type: ${r}`);return}let d=u.type,p=`${n}_${a}-${d}${t==="start"?"Start":"End"}${o&&h?"-margin":""}`;if(l&&l.trim()!==""){let f=l.replace(/[^\dA-Za-z]/g,"_"),g=`${p}_${f}`;if(!document.getElementById(g)){let m=document.getElementById(p);if(m){let y=m.cloneNode(!0);y.id=g,y.querySelectorAll("path, circle, line").forEach(k=>{k.setAttribute("stroke",l),u.fill&&k.setAttribute("fill",l)}),m.parentNode?.appendChild(y)}}e.attr(`marker-${t}`,`url(${i}#${g})`)}else e.attr(`marker-${t}`,`url(${i}#${p})`)},"addEdgeMarker")});function z4(e,t){Ps(it())&&e&&(e.style.width=t.length*9+"px",e.style.height="12px")}function g6e(e){let t=[],r=[];for(let i=1;i5&&Math.abs(a.y-n.y)>5||n.y===a.y&&a.x===o.x&&Math.abs(a.x-n.x)>5&&Math.abs(a.y-o.y)>5)&&(t.push(a),r.push(i))}return{cornerPoints:t,cornerPointPositions:r}}function m6e(e,t){if(e.length<2)return"";let r="",i=e.length,n=1e-5;for(let a=0;a({...n}));if(e.length>=2&&rs[t.arrowTypeStart]){let n=rs[t.arrowTypeStart],a=e[0],o=e[1],{angle:l}=Yq(a,o),u=n*Math.cos(l),h=n*Math.sin(l);r[0].x=a.x+u,r[0].y=a.y+h}let i=e.length;if(i>=2&&rs[t.arrowTypeEnd]){let n=rs[t.arrowTypeEnd],a=e[i-1],o=e[i-2],{angle:l}=Yq(o,a),u=n*Math.cos(l),h=n*Math.sin(l);r[i-1].x=a.x-u,r[i-1].y=a.y-h}return r}var v6e,xg,ga,b6e,Hq,h2,Tg,Z_,x6e,T6e,k6e,Gq,Xq,C6e,w6e,q4,W4=G(()=>{"use strict";Er(),Gr(),Nt(),ec(),lz(),t6t(),Ar(),h6e(),K_(),pi(),vr(),jz(),r6t(),gr(),v6e=s(e=>typeof e=="string"?e:it()?.flowchart?.curve,"resolveEdgeCurveType"),xg=new Map,ga=new Map,b6e=s(()=>{xg.clear(),ga.clear()},"clear"),Hq=s(e=>!!(e.label||e.startLabelLeft||e.startLabelRight||e.endLabelLeft||e.endLabelRight),"hasEdgeLabel"),h2=s(e=>e?typeof e=="string"?e:e.reduce((t,r)=>t+";"+r,""):"","getLabelStyles"),Tg=s(async(e,t)=>{let r=it(),i=Ps(r),{labelStyles:n}=cr(t);t.labelStyle=n;let a=e.insert("g").attr("class","edgeLabel"),o=a.insert("g").attr("class","label").attr("data-id",t.id),l=t.labelType==="markdown",u=await Io(e,t.label,{style:h2(t.labelStyle),useHtmlLabels:i,addSvgBackground:!0,isNode:!1,markdown:l,width:void 0},r);o.node().appendChild(u),Ee.info("abc82",t,t.labelType);let h,d;if(i){let f=u.children[0],g=zt(u);h=await zp.measure(()=>f.getBoundingClientRect()),d=h,g.attr("width",h.width),g.attr("height",h.height)}else{let f=zt(u).select("text").node();await zp.measure(()=>{h=u.getBBox(),f&&typeof f.getBBox=="function"?d=f.getBBox():d=h})}o.attr("transform",u2(d,i)),xg.set(t.id,a),t.width=h.width,t.height=h.height;let p;if(t.startLabelLeft){let f=e.insert("g").attr("class","edgeTerminals"),g=f.insert("g").attr("class","inner"),m=await ud(g,t.startLabelLeft,h2(t.labelStyle)||"",!1,!1);p=m;let y=m.getBBox();if(i){let k=m.children[0],C=zt(m);y=k.getBoundingClientRect(),C.attr("width",y.width),C.attr("height",y.height)}g.attr("transform",u2(y,i)),ga.get(t.id)||ga.set(t.id,{}),ga.get(t.id).startLeft=f,z4(p,t.startLabelLeft)}if(t.startLabelRight){let f=e.insert("g").attr("class","edgeTerminals"),g=f.insert("g").attr("class","inner"),m=await ud(g,t.startLabelRight,h2(t.labelStyle)||"",!1,!1);p=m;let y=m.getBBox();if(i){let k=m.children[0],C=zt(m);y=k.getBoundingClientRect(),C.attr("width",y.width),C.attr("height",y.height)}g.attr("transform",u2(y,i)),ga.get(t.id)||ga.set(t.id,{}),ga.get(t.id).startRight=f,z4(p,t.startLabelRight)}if(t.endLabelLeft){let f=e.insert("g").attr("class","edgeTerminals"),g=f.insert("g").attr("class","inner"),m=await ud(f,t.endLabelLeft,h2(t.labelStyle)||"",!1,!1);p=m;let y=m.getBBox();if(i){let k=m.children[0],C=zt(m);y=k.getBoundingClientRect(),C.attr("width",y.width),C.attr("height",y.height)}g.attr("transform",u2(y,i)),ga.get(t.id)||ga.set(t.id,{}),ga.get(t.id).endLeft=f,z4(p,t.endLabelLeft)}if(t.endLabelRight){let f=e.insert("g").attr("class","edgeTerminals"),g=f.insert("g").attr("class","inner"),m=await ud(f,t.endLabelRight,h2(t.labelStyle)||"",!1,!1);p=m;let y=m.getBBox();if(i){let k=m.children[0],C=zt(m);y=k.getBoundingClientRect(),C.attr("width",y.width),C.attr("height",y.height)}g.attr("transform",u2(y,i)),ga.get(t.id)||ga.set(t.id,{}),ga.get(t.id).endRight=f,z4(p,t.endLabelRight)}return u},"insertEdgeLabel"),s(z4,"setTerminalWidth"),Z_=s((e,t)=>{Ee.debug("Moving label abc88 ",e.id,e.label,xg.get(e.id),t);let r=t.updatedPath?t.updatedPath:t.originalPath,i=it(),{subGraphTitleTotalMargin:n}=c2(i);if(e.label){let a=xg.get(e.id),o=e.x,l=e.y;if(r){let u=ai.calcLabelPosition(r);Ee.debug("Moving label "+e.label+" from (",o,",",l,") to (",u.x,",",u.y,") abc88"),t.updatedPath&&(o=u.x,l=u.y)}a.attr("transform",`translate(${o}, ${l+n/2})`)}if(e.startLabelLeft){let a=ga.get(e.id).startLeft,o=e.x,l=e.y;if(r){let u=ai.calcTerminalLabelPosition(e.arrowTypeStart?10:0,"start_left",r);o=u.x,l=u.y}a.attr("transform",`translate(${o}, ${l})`)}if(e.startLabelRight){let a=ga.get(e.id).startRight,o=e.x,l=e.y;if(r){let u=ai.calcTerminalLabelPosition(e.arrowTypeStart?10:0,"start_right",r);o=u.x,l=u.y}a.attr("transform",`translate(${o}, ${l})`)}if(e.endLabelLeft){let a=ga.get(e.id).endLeft,o=e.x,l=e.y;if(r){let u=ai.calcTerminalLabelPosition(e.arrowTypeEnd?10:0,"end_left",r);o=u.x,l=u.y}a.attr("transform",`translate(${o}, ${l})`)}if(e.endLabelRight){let a=ga.get(e.id).endRight,o=e.x,l=e.y;if(r){let u=ai.calcTerminalLabelPosition(e.arrowTypeEnd?10:0,"end_right",r);o=u.x,l=u.y}a.attr("transform",`translate(${o}, ${l})`)}},"positionEdgeLabel"),x6e=s((e,t)=>{if(!e?.isLabelEdge||!e?.id?.endsWith("-to-label")||!Array.isArray(t)||t.length!==2)return t;let[r,i]=t,n=Math.abs(i.x-r.x),a=Math.abs(i.y-r.y);return n<.001||a<.001?t:a>=n?[r,{x:r.x,y:i.y},i]:[r,{x:i.x,y:r.y},i]},"orthogonalizeToLabelClippedPoints"),T6e=s((e,t)=>{let r=e.x,i=e.y,n=Math.abs(t.x-r),a=Math.abs(t.y-i),o=e.width/2,l=e.height/2;return n>=o||a>=l},"outsideNode"),k6e=s((e,t,r)=>{Ee.debug(`intersection calc abc89: + outsidePoint: ${JSON.stringify(t)} + insidePoint : ${JSON.stringify(r)} + node : x:${e.x} y:${e.y} w:${e.width} h:${e.height}`);let i=e.x,n=e.y,a=Math.abs(i-r.x),o=e.width/2,l=r.xMath.abs(i-t.x)*u){let p=r.y{Ee.warn("abc88 cutPathAtIntersect",e,t);let r=[],i=e[0],n=!1;return e.forEach(a=>{if(Ee.info("abc88 checking point",a,t),!T6e(t,a)&&!n){let o=k6e(t,i,a);Ee.debug("abc88 inside",a,i,o),Ee.debug("abc88 intersection",o,t);let l=!1;r.forEach(u=>{l=l||u.x===o.x&&u.y===o.y}),r.some(u=>u.x===o.x&&u.y===o.y)?Ee.warn("abc88 no intersect",o,r):r.push(o),n=!0}else Ee.warn("abc88 outside",a,i),i=a,n||r.push(a)}),Ee.debug("returning points",r),r},"cutPathAtIntersect"),s(g6e,"extractCornerPoints"),Xq=s(function(e,t,r){let i=t.x-e.x,n=t.y-e.y,a=Math.sqrt(i*i+n*n),o=r/a;return{x:t.x-o*i,y:t.y-o*n}},"findAdjacentPoint"),C6e=s(function(e){let{cornerPointPositions:t}=g6e(e),r=[];for(let i=0;i10&&Math.abs(a.y-n.y)>=10){Ee.debug("Corner point fixing",Math.abs(a.x-n.x),Math.abs(a.y-n.y));let g=5;o.x===l.x?f={x:h<0?l.x-g+p:l.x+g-p,y:d<0?l.y-p:l.y+p}:f={x:h<0?l.x-p:l.x+p,y:d<0?l.y-g+p:l.y+g-p}}else Ee.debug("Corner point skipping fixing",Math.abs(a.x-n.x),Math.abs(a.y-n.y));r.push(f,u)}else r.push(e[i]);return r},"fixCorners"),w6e=s((e,t,r)=>{let i=e-t-r,n=2,a=2,o=n+a,l=Math.floor(i/o),u=Number.isFinite(l)?Math.max(0,l):0,h=Array(u).fill(`${n} ${a}`).join(" ");return`0 ${t} ${h} ${r}`},"generateDashArray"),q4=s(function(e,t,r,i,n,a,o,l=!1){if(!o)throw new Error(`insertEdge: missing diagramId for edge "${t.id}" — edge IDs require a diagram prefix for uniqueness`);let{handDrawnSeed:u,layout:h}=it(),d=t.points,p=!1,f=n;var g=a;let m=[];for(let B in t.cssCompiledStyles)__(B)||m.push(t.cssCompiledStyles[B]);if(h==="swimlane"){if(g.intersect&&f.intersect&&Array.isArray(d)&&d.length>=2)if(d.length===2)d=[f.intersect(d[0]),g.intersect(d[1])];else{let B=d.slice(1,-1),Y=B[0],U=B[B.length-1],H=.5,J=Math.abs(d[d.length-1].x-U.x)!Number.isNaN(B.y)),C=v6e(t.curve);C!=="rounded"&&(k=C6e(k));let S=Fy;switch(C){case"linear":S=Fy;break;case"basis":S=a4;break;case"cardinal":S=XP;break;case"bumpX":S=jP;break;case"bumpY":S=VP;break;case"catmullRom":S=QP;break;case"monotoneX":S=oF;break;case"monotoneY":S=lF;break;case"natural":S=hF;break;case"step":S=dF;break;case"stepAfter":S=fF;break;case"stepBefore":S=pF;break;case"rounded":S=Fy;break;default:S=a4}let{x:E,y:_}=u6e(t),b=r4().x(E).y(_).curve(S),v;switch(t.thickness){case"normal":v="edge-thickness-normal";break;case"thick":v="edge-thickness-thick";break;case"invisible":v="edge-thickness-invisible";break;default:v="edge-thickness-normal"}switch(t.pattern){case"solid":v+=" edge-pattern-solid";break;case"dotted":v+=" edge-pattern-dotted";break;case"dashed":v+=" edge-pattern-dashed";break;default:v+=" edge-pattern-solid"}let x,w=C==="rounded"?m6e(y6e(k,t),5):b(k),R=Array.isArray(t.style)?t.style:[t.style],$=R.find(B=>B?.startsWith("stroke:")),M="";t.animate&&(M="edge-animation-fast"),t.animation&&(M="edge-animation-"+t.animation);let N=!1;if(t.look==="handDrawn"){let B=ur.svg(e);Object.assign([],k);let Y=B.path(w,{roughness:.3,seed:u});v+=" transition",x=zt(Y).select("path").attr("id",`${o}-${t.id}`).attr("class"," "+v+(t.classes?" "+t.classes:"")+(M?" "+M:"")).attr("style",R?R.reduce((H,J)=>H+";"+J,""):"");let U=x.attr("d");x.attr("d",U),e.node().appendChild(x.node())}else{let B=m.join(";"),Y=R?R.reduce((ue,se)=>ue+se+";",""):"",U=(B?B+";"+Y+";":Y)+";"+(R?R.reduce((ue,se)=>ue+";"+se,""):"");x=e.append("path").attr("d",w).attr("id",`${o}-${t.id}`).attr("class"," "+v+(t.classes?" "+t.classes:"")+(M?" "+M:"")).attr("style",U),$=U.match(/stroke:([^;]+)/)?.[1],N=t.animate===!0||!!t.animation||B.includes("animation");let H=x.node(),J=typeof H.getTotalLength=="function"?H.getTotalLength():0,te=Vq[t.arrowTypeStart]||0,ie=Vq[t.arrowTypeEnd]||0;if(t.look==="neo"&&!N){let ue=`stroke-dasharray: ${t.pattern==="dotted"||t.pattern==="dashed"?w6e(J,te,ie):`0 ${te} ${J-te-ie} ${ie}`}; stroke-dashoffset: 0;`;x.attr("style",ue+x.attr("style"))}}x.attr("data-edge",!0),x.attr("data-et","edge"),x.attr("data-id",t.id),x.attr("data-points",y),x.attr("data-look",qa(t.look)),t.showPoints&&k.forEach(B=>{e.append("circle").style("stroke","red").style("fill","red").attr("r",1).attr("cx",B.x).attr("cy",B.y)});let j="";(it().flowchart.arrowMarkerAbsolute||it().state.arrowMarkerAbsolute)&&(j=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,j=j.replace(/\(/g,"\\(").replace(/\)/g,"\\)")),Ee.info("arrowTypeStart",t.arrowTypeStart),Ee.info("arrowTypeEnd",t.arrowTypeEnd);let I=!N&&t?.look==="neo";d6e(x,t,j,o,i,I,$);let z=Math.floor(d.length/2),F=d[z];ai.isLabelCoordinateInPath(F,x.attr("d"))||(p=!0);let O={};return p&&(O.updatedPath=d),O.originalPath=t.points,O},"insertEdge"),s(m6e,"generateRoundedPath"),s(Yq,"calculateDeltaAndAngle"),s(y6e,"applyMarkerOffsetsToPoints")}),E6e,S6e,_6e,A6e,R6e,$6e,L6e,I6e,M6e,D6e,N6e,O6e,B6e,P6e,F6e,z6e,q6e,W6e,j6e,V6e,U6e,Y6e,H6e,G6e,J_,Kq=G(()=>{"use strict";Nt(),Gr(),E6e=s((e,t,r,i)=>{t.forEach(n=>{G6e[n](e,r,i)})},"insertMarkers"),S6e=s((e,t,r)=>{Ee.trace("Making markers for ",r),e.append("defs").append("marker").attr("id",r+"_"+t+"-extensionStart").attr("class","marker extension "+t).attr("refX",18).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M 1,7 L18,13 V 1 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-extensionEnd").attr("class","marker extension "+t).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M 1,1 V 13 L18,7 Z"),e.append("marker").attr("id",r+"_"+t+"-extensionStart-margin").attr("class","marker extension "+t).attr("refX",18).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").attr("markerUnits","userSpaceOnUse").attr("viewBox","0 0 20 14").append("polygon").attr("points","10,7 18,13 18,1").style("stroke-width",2).style("stroke-dasharray","0"),e.append("defs").append("marker").attr("id",r+"_"+t+"-extensionEnd-margin").attr("class","marker extension "+t).attr("refX",9).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").attr("markerUnits","userSpaceOnUse").attr("viewBox","0 0 20 14").append("polygon").attr("points","10,1 10,13 18,7").style("stroke-width",2).style("stroke-dasharray","0")},"extension"),_6e=s((e,t,r)=>{e.append("defs").append("marker").attr("id",r+"_"+t+"-compositionStart").attr("class","marker composition "+t).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-compositionEnd").attr("class","marker composition "+t).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-compositionStart-margin").attr("class","marker composition "+t).attr("refX",15).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").style("stroke-width",0).attr("viewBox","0 0 15 15").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-compositionEnd-margin").attr("class","marker composition "+t).attr("refX",3.5).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").style("stroke-width",0).attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},"composition"),A6e=s((e,t,r)=>{e.append("defs").append("marker").attr("id",r+"_"+t+"-aggregationStart").attr("class","marker aggregation "+t).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-aggregationEnd").attr("class","marker aggregation "+t).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-aggregationStart-margin").attr("class","marker aggregation "+t).attr("refX",15).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").style("stroke-width",2).attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-aggregationEnd-margin").attr("class","marker aggregation "+t).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").style("stroke-width",2).attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},"aggregation"),R6e=s((e,t,r)=>{e.append("defs").append("marker").attr("id",r+"_"+t+"-dependencyStart").attr("class","marker dependency "+t).attr("refX",6).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-dependencyEnd").attr("class","marker dependency "+t).attr("refX",13).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-dependencyStart-margin").attr("class","marker dependency "+t).attr("refX",4).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").style("stroke-width",0).attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-dependencyEnd-margin").attr("class","marker dependency "+t).attr("refX",16).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").style("stroke-width",0).attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},"dependency"),$6e=s((e,t,r)=>{e.append("defs").append("marker").attr("id",r+"_"+t+"-lollipopStart").attr("class","marker lollipop "+t).attr("refX",13).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("circle").attr("fill","transparent").attr("cx",7).attr("cy",7).attr("r",6),e.append("defs").append("marker").attr("id",r+"_"+t+"-lollipopEnd").attr("class","marker lollipop "+t).attr("refX",1).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("circle").attr("fill","transparent").attr("cx",7).attr("cy",7).attr("r",6),e.append("defs").append("marker").attr("id",r+"_"+t+"-lollipopStart-margin").attr("class","marker lollipop "+t).attr("refX",13).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("circle").attr("fill","transparent").attr("cx",7).attr("cy",7).attr("r",6).attr("stroke-width",2),e.append("defs").append("marker").attr("id",r+"_"+t+"-lollipopEnd-margin").attr("class","marker lollipop "+t).attr("refX",1).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("circle").attr("fill","transparent").attr("cx",7).attr("cy",7).attr("r",6).attr("stroke-width",2)},"lollipop"),L6e=s((e,t,r)=>{e.append("marker").attr("id",r+"_"+t+"-pointEnd").attr("class","marker "+t).attr("viewBox","0 0 10 10").attr("refX",5).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",8).attr("markerHeight",8).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),e.append("marker").attr("id",r+"_"+t+"-pointStart").attr("class","marker "+t).attr("viewBox","0 0 10 10").attr("refX",4.5).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",8).attr("markerHeight",8).attr("orient","auto").append("path").attr("d","M 0 5 L 10 10 L 10 0 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),e.append("marker").attr("id",r+"_"+t+"-pointEnd-margin").attr("class","marker "+t).attr("viewBox","0 0 11.5 14").attr("refX",11.5).attr("refY",7).attr("markerUnits","userSpaceOnUse").attr("markerWidth",10.5).attr("markerHeight",14).attr("orient","auto").append("path").attr("d","M 0 0 L 11.5 7 L 0 14 z").attr("class","arrowMarkerPath").style("stroke-width",0).style("stroke-dasharray","1,0"),e.append("marker").attr("id",r+"_"+t+"-pointStart-margin").attr("class","marker "+t).attr("viewBox","0 0 11.5 14").attr("refX",1).attr("refY",7).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11.5).attr("markerHeight",14).attr("orient","auto").append("polygon").attr("points","0,7 11.5,14 11.5,0").attr("class","arrowMarkerPath").style("stroke-width",0).style("stroke-dasharray","1,0")},"point"),I6e=s((e,t,r)=>{e.append("marker").attr("id",r+"_"+t+"-circleEnd").attr("class","marker "+t).attr("viewBox","0 0 10 10").attr("refX",11).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),e.append("marker").attr("id",r+"_"+t+"-circleStart").attr("class","marker "+t).attr("viewBox","0 0 10 10").attr("refX",-1).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),e.append("marker").attr("id",r+"_"+t+"-circleEnd-margin").attr("class","marker "+t).attr("viewBox","0 0 10 10").attr("refY",5).attr("refX",12.25).attr("markerUnits","userSpaceOnUse").attr("markerWidth",14).attr("markerHeight",14).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",0).style("stroke-dasharray","1,0"),e.append("marker").attr("id",r+"_"+t+"-circleStart-margin").attr("class","marker "+t).attr("viewBox","0 0 10 10").attr("refX",-2).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",14).attr("markerHeight",14).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",0).style("stroke-dasharray","1,0")},"circle"),M6e=s((e,t,r)=>{e.append("marker").attr("id",r+"_"+t+"-crossEnd").attr("class","marker cross "+t).attr("viewBox","0 0 11 11").attr("refX",12).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0"),e.append("marker").attr("id",r+"_"+t+"-crossStart").attr("class","marker cross "+t).attr("viewBox","0 0 11 11").attr("refX",-1).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0"),e.append("marker").attr("id",r+"_"+t+"-crossEnd-margin").attr("class","marker cross "+t).attr("viewBox","0 0 15 15").attr("refX",17.7).attr("refY",7.5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 1,1 L 14,14 M 1,14 L 14,1").attr("class","arrowMarkerPath").style("stroke-width",2.5),e.append("marker").attr("id",r+"_"+t+"-crossStart-margin").attr("class","marker cross "+t).attr("viewBox","0 0 15 15").attr("refX",-3.5).attr("refY",7.5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 1,1 L 14,14 M 1,14 L 14,1").attr("class","arrowMarkerPath").style("stroke-width",2.5).style("stroke-dasharray","1,0")},"cross"),D6e=s((e,t,r)=>{e.append("defs").append("marker").attr("id",r+"_"+t+"-barbEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",14).attr("markerUnits","userSpaceOnUse").attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z")},"barb"),N6e=s((e,t,r)=>{let i=Rr(),{themeVariables:n}=i,{transitionColor:a}=n;e.append("defs").append("marker").attr("id",r+"_"+t+"-barbEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",14).attr("markerUnits","strokeWidth").attr("orient","auto").append("path").attr("d","M 19,7 L11,14 L13,7 L11,0 Z"),e.append("defs").append("marker").attr("id",r+"_"+t+"-barbEnd-margin").attr("refX",17).attr("refY",7).attr("markerWidth",20).attr("markerHeight",14).attr("markerUnits","userSpaceOnUse").attr("orient","auto").append("path").attr("d","M 19,7 L11,14 L13,7 L11,0 Z").attr("fill",`${a}`)},"barbNeo"),O6e=s((e,t,r)=>{e.append("defs").append("marker").attr("id",r+"_"+t+"-onlyOneStart").attr("class","marker onlyOne "+t).attr("refX",0).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("d","M9,0 L9,18 M15,0 L15,18"),e.append("defs").append("marker").attr("id",r+"_"+t+"-onlyOneEnd").attr("class","marker onlyOne "+t).attr("refX",18).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("d","M3,0 L3,18 M9,0 L9,18")},"only_one"),B6e=s((e,t,r)=>{let i=e.append("defs").append("marker").attr("id",r+"_"+t+"-zeroOrOneStart").attr("class","marker zeroOrOne "+t).attr("refX",0).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto");i.append("circle").attr("fill","white").attr("cx",21).attr("cy",9).attr("r",6),i.append("path").attr("d","M9,0 L9,18");let n=e.append("defs").append("marker").attr("id",r+"_"+t+"-zeroOrOneEnd").attr("class","marker zeroOrOne "+t).attr("refX",30).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto");n.append("circle").attr("fill","white").attr("cx",9).attr("cy",9).attr("r",6),n.append("path").attr("d","M21,0 L21,18")},"zero_or_one"),P6e=s((e,t,r)=>{e.append("defs").append("marker").attr("id",r+"_"+t+"-oneOrMoreStart").attr("class","marker oneOrMore "+t).attr("refX",18).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("d","M0,18 Q 18,0 36,18 Q 18,36 0,18 M42,9 L42,27"),e.append("defs").append("marker").attr("id",r+"_"+t+"-oneOrMoreEnd").attr("class","marker oneOrMore "+t).attr("refX",27).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("d","M3,9 L3,27 M9,18 Q27,0 45,18 Q27,36 9,18")},"one_or_more"),F6e=s((e,t,r)=>{let i=e.append("defs").append("marker").attr("id",r+"_"+t+"-zeroOrMoreStart").attr("class","marker zeroOrMore "+t).attr("refX",18).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto");i.append("circle").attr("fill","white").attr("cx",48).attr("cy",18).attr("r",6),i.append("path").attr("d","M0,18 Q18,0 36,18 Q18,36 0,18");let n=e.append("defs").append("marker").attr("id",r+"_"+t+"-zeroOrMoreEnd").attr("class","marker zeroOrMore "+t).attr("refX",39).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto");n.append("circle").attr("fill","white").attr("cx",9).attr("cy",18).attr("r",6),n.append("path").attr("d","M21,18 Q39,0 57,18 Q39,36 21,18")},"zero_or_more"),z6e=s((e,t,r)=>{let i=Rr(),{themeVariables:n}=i,{strokeWidth:a}=n;e.append("defs").append("marker").attr("id",r+"_"+t+"-onlyOneStart").attr("class","marker onlyOne "+t).attr("refX",0).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M9,0 L9,18 M15,0 L15,18").attr("stroke-width",`${a}`),e.append("defs").append("marker").attr("id",r+"_"+t+"-onlyOneEnd").attr("class","marker onlyOne "+t).attr("refX",18).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M3,0 L3,18 M9,0 L9,18").attr("stroke-width",`${a}`)},"only_one_neo"),q6e=s((e,t,r)=>{let i=Rr(),{themeVariables:n}=i,{strokeWidth:a,mainBkg:o}=n,l=e.append("defs").append("marker").attr("id",r+"_"+t+"-zeroOrOneStart").attr("class","marker zeroOrOne "+t).attr("refX",0).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto").attr("markerUnits","userSpaceOnUse");l.append("circle").attr("fill",o??"white").attr("cx",21).attr("cy",9).attr("stroke-width",`${a}`).attr("r",6),l.append("path").attr("d","M9,0 L9,18").attr("stroke-width",`${a}`);let u=e.append("defs").append("marker").attr("id",r+"_"+t+"-zeroOrOneEnd").attr("class","marker zeroOrOne "+t).attr("refX",30).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("markerUnits","userSpaceOnUse").attr("orient","auto");u.append("circle").attr("fill",o??"white").attr("cx",9).attr("cy",9).attr("stroke-width",`${a}`).attr("r",6),u.append("path").attr("d","M21,0 L21,18").attr("stroke-width",`${a}`)},"zero_or_one_neo"),W6e=s((e,t,r)=>{let i=Rr(),{themeVariables:n}=i,{strokeWidth:a}=n;e.append("defs").append("marker").attr("id",r+"_"+t+"-oneOrMoreStart").attr("class","marker oneOrMore "+t).attr("refX",18).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("path").attr("d","M0,18 Q 18,0 36,18 Q 18,36 0,18 M42,9 L42,27").attr("stroke-width",`${a}`),e.append("defs").append("marker").attr("id",r+"_"+t+"-oneOrMoreEnd").attr("class","marker oneOrMore "+t).attr("refX",27).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("markerUnits","userSpaceOnUse").attr("orient","auto").append("path").attr("d","M3,9 L3,27 M9,18 Q27,0 45,18 Q27,36 9,18").attr("stroke-width",`${a}`)},"one_or_more_neo"),j6e=s((e,t,r)=>{let i=Rr(),{themeVariables:n}=i,{strokeWidth:a,mainBkg:o}=n,l=e.append("defs").append("marker").attr("id",r+"_"+t+"-zeroOrMoreStart").attr("class","marker zeroOrMore "+t).attr("refX",18).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("markerUnits","userSpaceOnUse").attr("orient","auto");l.append("circle").attr("fill",o??"white").attr("cx",45.5).attr("cy",18).attr("r",6).attr("stroke-width",`${a}`),l.append("path").attr("d","M0,18 Q18,0 36,18 Q18,36 0,18").attr("stroke-width",`${a}`);let u=e.append("defs").append("marker").attr("id",r+"_"+t+"-zeroOrMoreEnd").attr("class","marker zeroOrMore "+t).attr("refX",39).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto").attr("markerUnits","userSpaceOnUse");u.append("circle").attr("fill",o??"white").attr("cx",11).attr("cy",18).attr("r",6).attr("stroke-width",`${a}`),u.append("path").attr("d","M21,18 Q39,0 57,18 Q39,36 21,18").attr("stroke-width",`${a}`)},"zero_or_more_neo"),V6e=s((e,t,r)=>{e.append("defs").append("marker").attr("id",r+"_"+t+"-requirement_arrowEnd").attr("refX",20).attr("refY",10).attr("markerWidth",20).attr("markerHeight",20).attr("orient","auto").append("path").attr("d",`M0,0 + L20,10 + M20,10 + L0,20`)},"requirement_arrow"),U6e=s((e,t,r)=>{let i=Rr(),{themeVariables:n}=i,{strokeWidth:a}=n;e.append("defs").append("marker").attr("id",r+"_"+t+"-requirement_arrowEnd").attr("refX",20).attr("refY",10).attr("markerWidth",20).attr("markerHeight",20).attr("orient","auto").attr("markerUnits","userSpaceOnUse").attr("stroke-width",`${a}`).attr("viewBox","0 0 25 20").append("path").attr("d",`M0,0 + L20,10 + M20,10 + L0,20`).attr("stroke-linejoin","miter")},"requirement_arrow_neo"),Y6e=s((e,t,r)=>{let i=e.append("defs").append("marker").attr("id",r+"_"+t+"-requirement_containsStart").attr("refX",0).attr("refY",10).attr("markerWidth",20).attr("markerHeight",20).attr("orient","auto").append("g");i.append("circle").attr("cx",10).attr("cy",10).attr("r",9).attr("fill","none"),i.append("line").attr("x1",1).attr("x2",19).attr("y1",10).attr("y2",10),i.append("line").attr("y1",1).attr("y2",19).attr("x1",10).attr("x2",10)},"requirement_contains"),H6e=s((e,t,r)=>{let i=Rr(),{themeVariables:n}=i,{strokeWidth:a}=n,o=e.append("defs").append("marker").attr("id",r+"_"+t+"-requirement_containsStart").attr("refX",0).attr("refY",10).attr("markerWidth",20).attr("markerHeight",20).attr("orient","auto").attr("markerUnits","userSpaceOnUse").append("g");o.append("circle").attr("cx",10).attr("cy",10).attr("r",9).attr("fill","none"),o.append("line").attr("x1",1).attr("x2",19).attr("y1",10).attr("y2",10),o.append("line").attr("y1",1).attr("y2",19).attr("x1",10).attr("x2",10),o.selectAll("*").attr("stroke-width",`${a}`)},"requirement_contains_neo"),G6e={extension:S6e,composition:_6e,aggregation:A6e,dependency:R6e,lollipop:$6e,point:L6e,circle:I6e,cross:M6e,barb:D6e,barbNeo:N6e,only_one:O6e,zero_or_one:B6e,one_or_more:P6e,zero_or_more:F6e,only_one_neo:z6e,zero_or_one_neo:q6e,one_or_more_neo:W6e,zero_or_more_neo:j6e,requirement_arrow:V6e,requirement_contains:Y6e,requirement_arrow_neo:U6e,requirement_contains_neo:H6e},J_=E6e});async function d2(e,t,r){let i,n;t.shape==="rect"&&(t.rx&&t.ry?t.shape="roundedRect":t.shape="squareRect");let a=t.shape?j_[t.shape]:void 0;if(!a)throw new Error(`No such shape: ${t.shape}. Please check your syntax.`);if(t.link){let o;r.config.securityLevel==="sandbox"?o="_top":t.linkTarget&&(o=t.linkTarget||"_blank"),i=e.insert("svg:a").attr("xlink:href",t.link).attr("target",o??null),n=await a(i,t,r)}else n=await a(e,t,r),i=n;return i.attr("data-look",qa(t.look)),t.tooltip&&n.attr("title",t.tooltip),j4.set(t.id,i),t.haveCallback&&i.attr("class",i.attr("class")+" clickable"),i}var j4,X6e,K6e,kg,p2=G(()=>{"use strict";Nt(),Kz(),Ar(),j4=new Map,s(d2,"insertNode"),X6e=s((e,t)=>{j4.set(t.id,e)},"setNodeElem"),K6e=s(()=>{j4.clear()},"clear"),kg=s(e=>{let t=j4.get(e.id);Ee.trace("Transforming node",e.diff,e,"translate("+(e.x-e.width/2-5)+", "+e.width/2+")");let r=8,i=e.diff||0;return e.clusterNode?t.attr("transform","translate("+(e.x+i-e.width/2)+", "+(e.y-e.height/2-r)+")"):t.attr("transform","translate("+e.x+", "+e.y+")"),i},"positionNode")}),Q6e,i6t=G(()=>{"use strict";Gr(),mi(),Nt(),Q_(),W4(),Kq(),p2(),lr(),Ar(),Q6e={common:Vt,getConfig:Rr,insertCluster:P4,insertEdge:q4,insertEdgeLabel:Tg,insertMarkers:J_,insertNode:d2,interpolateToCurve:OF,labelHelper:Nr,log:Ee,positionEdgeLabel:Z_}}),Z6e,Qq,J6e=G(()=>{"use strict";Z6e=typeof global=="object"&&global&&global.Object===Object&&global,Qq=Z6e}),e_e,t_e,tc,zc=G(()=>{"use strict";J6e(),e_e=typeof self=="object"&&self&&self.Object===Object&&self,t_e=Qq||e_e||Function("return this")(),tc=t_e}),r_e,_l,f2=G(()=>{"use strict";zc(),r_e=tc.Symbol,_l=r_e});function i_e(e){var t=n_e.call(e,g2),r=e[g2];try{e[g2]=void 0;var i=!0}catch{}var n=a_e.call(e);return i&&(t?e[g2]=r:delete e[g2]),n}var Zq,n_e,a_e,g2,s_e,n6t=G(()=>{"use strict";f2(),Zq=Object.prototype,n_e=Zq.hasOwnProperty,a_e=Zq.toString,g2=_l?_l.toStringTag:void 0,s(i_e,"getRawTag"),s_e=i_e});function o_e(e){return c_e.call(e)}var l_e,c_e,u_e,a6t=G(()=>{"use strict";l_e=Object.prototype,c_e=l_e.toString,s(o_e,"objectToString"),u_e=o_e});function h_e(e){return e==null?e===void 0?p_e:d_e:Jq&&Jq in Object(e)?s_e(e):u_e(e)}var d_e,p_e,Jq,dd,Cg=G(()=>{"use strict";f2(),n6t(),a6t(),d_e="[object Null]",p_e="[object Undefined]",Jq=_l?_l.toStringTag:void 0,s(h_e,"baseGetTag"),dd=h_e});function f_e(e){return e!=null&&typeof e=="object"}var rc,Hu=G(()=>{"use strict";s(f_e,"isObjectLike"),rc=f_e});function g_e(e){return typeof e=="symbol"||rc(e)&&dd(e)==m_e}var m_e,Yp,m2=G(()=>{"use strict";Cg(),Hu(),m_e="[object Symbol]",s(g_e,"isSymbol"),Yp=g_e});function y_e(e,t){for(var r=-1,i=e==null?0:e.length,n=Array(i);++r{"use strict";s(y_e,"arrayMap"),wg=y_e}),v_e,ma,is=G(()=>{"use strict";v_e=Array.isArray,ma=v_e});function eW(e){if(typeof e=="string")return e;if(ma(e))return wg(e,eW)+"";if(Yp(e))return rW?rW.call(e):"";var t=e+"";return t=="0"&&1/e==-b_e?"-0":t}var b_e,tW,rW,x_e,s6t=G(()=>{"use strict";f2(),e7(),is(),m2(),b_e=1/0,tW=_l?_l.prototype:void 0,rW=tW?tW.toString:void 0,s(eW,"baseToString"),x_e=eW});function T_e(e){for(var t=e.length;t--&&k_e.test(e.charAt(t)););return t}var k_e,C_e,o6t=G(()=>{"use strict";k_e=/\s/,s(T_e,"trimmedEndIndex"),C_e=T_e});function w_e(e){return e&&e.slice(0,C_e(e)+1).replace(E_e,"")}var E_e,S_e,l6t=G(()=>{"use strict";o6t(),E_e=/^\s+/,s(w_e,"baseTrim"),S_e=w_e});function __e(e){var t=typeof e;return e!=null&&(t=="object"||t=="function")}var ol,qc=G(()=>{"use strict";s(__e,"isObject"),ol=__e});function A_e(e){if(typeof e=="number")return e;if(Yp(e))return iW;if(ol(e)){var t=typeof e.valueOf=="function"?e.valueOf():e;e=ol(t)?t+"":t}if(typeof e!="string")return e===0?e:+e;e=S_e(e);var r=$_e.test(e);return r||L_e.test(e)?I_e(e.slice(2),r?2:8):R_e.test(e)?iW:+e}var iW,R_e,$_e,L_e,I_e,M_e,c6t=G(()=>{"use strict";l6t(),qc(),m2(),iW=NaN,R_e=/^[-+]0x[0-9a-f]+$/i,$_e=/^0b[01]+$/i,L_e=/^0o[0-7]+$/i,I_e=parseInt,s(A_e,"toNumber"),M_e=A_e});function D_e(e){if(!e)return e===0?e:0;if(e=M_e(e),e===nW||e===-nW){var t=e<0?-1:1;return t*N_e}return e===e?e:0}var nW,N_e,V4,O_e=G(()=>{"use strict";c6t(),nW=1/0,N_e=17976931348623157e292,s(D_e,"toFinite"),V4=D_e});function B_e(e){var t=V4(e),r=t%1;return t===t?r?t-r:t:0}var P_e,u6t=G(()=>{"use strict";O_e(),s(B_e,"toInteger"),P_e=B_e});function F_e(e){return e}var Hp,Eg=G(()=>{"use strict";s(F_e,"identity"),Hp=F_e});function z_e(e){if(!ol(e))return!1;var t=dd(e);return t==W_e||t==j_e||t==q_e||t==V_e}var q_e,W_e,j_e,V_e,y2,t7=G(()=>{"use strict";Cg(),qc(),q_e="[object AsyncFunction]",W_e="[object Function]",j_e="[object GeneratorFunction]",V_e="[object Proxy]",s(z_e,"isFunction"),y2=z_e}),U_e,r7,h6t=G(()=>{"use strict";zc(),U_e=tc["__core-js_shared__"],r7=U_e});function Y_e(e){return!!aW&&aW in e}var aW,H_e,d6t=G(()=>{"use strict";h6t(),aW=(function(){var e=/[^.]+$/.exec(r7&&r7.keys&&r7.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""})(),s(Y_e,"isMasked"),H_e=Y_e});function G_e(e){if(e!=null){try{return K_e.call(e)}catch{}try{return e+""}catch{}}return""}var X_e,K_e,Gp,Q_e=G(()=>{"use strict";X_e=Function.prototype,K_e=X_e.toString,s(G_e,"toSource"),Gp=G_e});function Z_e(e){if(!ol(e)||H_e(e))return!1;var t=y2(e)?a7e:e7e;return t.test(Gp(e))}var J_e,e7e,t7e,r7e,i7e,n7e,a7e,s7e,p6t=G(()=>{"use strict";t7(),d6t(),qc(),Q_e(),J_e=/[\\^$.*+?()[\]{}|]/g,e7e=/^\[object .+?Constructor\]$/,t7e=Function.prototype,r7e=Object.prototype,i7e=t7e.toString,n7e=r7e.hasOwnProperty,a7e=RegExp("^"+i7e.call(n7e).replace(J_e,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),s(Z_e,"baseIsNative"),s7e=Z_e});function o7e(e,t){return e?.[t]}var l7e,f6t=G(()=>{"use strict";s(o7e,"getValue"),l7e=o7e});function c7e(e,t){var r=l7e(e,t);return s7e(r)?r:void 0}var Xp,Sg=G(()=>{"use strict";p6t(),f6t(),s(c7e,"getNative"),Xp=c7e}),u7e,i7,g6t=G(()=>{"use strict";Sg(),zc(),u7e=Xp(tc,"WeakMap"),i7=u7e}),sW,h7e,d7e,m6t=G(()=>{"use strict";qc(),sW=Object.create,h7e=(function(){function e(){}return s(e,"object"),function(t){if(!ol(t))return{};if(sW)return sW(t);e.prototype=t;var r=new e;return e.prototype=void 0,r}})(),d7e=h7e});function p7e(e,t,r){switch(r.length){case 0:return e.call(t);case 1:return e.call(t,r[0]);case 2:return e.call(t,r[0],r[1]);case 3:return e.call(t,r[0],r[1],r[2])}return e.apply(t,r)}var f7e,y6t=G(()=>{"use strict";s(p7e,"apply"),f7e=p7e});function g7e(){}var m7e,v6t=G(()=>{"use strict";s(g7e,"noop"),m7e=g7e});function y7e(e,t){var r=-1,i=e.length;for(t||(t=Array(i));++r{"use strict";s(y7e,"copyArray"),oW=y7e});function b7e(e){var t=0,r=0;return function(){var i=k7e(),n=T7e-(i-r);if(r=i,n>0){if(++t>=x7e)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}var x7e,T7e,k7e,C7e,b6t=G(()=>{"use strict";x7e=800,T7e=16,k7e=Date.now,s(b7e,"shortOut"),C7e=b7e});function w7e(e){return function(){return e}}var pd,E7e=G(()=>{"use strict";s(w7e,"constant"),pd=w7e}),S7e,U4,_7e=G(()=>{"use strict";Sg(),S7e=(function(){try{var e=Xp(Object,"defineProperty");return e({},"",{}),e}catch{}})(),U4=S7e}),A7e,R7e,x6t=G(()=>{"use strict";E7e(),_7e(),Eg(),A7e=U4?function(e,t){return U4(e,"toString",{configurable:!0,enumerable:!1,value:pd(t),writable:!0})}:Hp,R7e=A7e}),$7e,lW,L7e=G(()=>{"use strict";x6t(),b6t(),$7e=C7e(R7e),lW=$7e});function I7e(e,t){for(var r=-1,i=e==null?0:e.length;++r{"use strict";s(I7e,"arrayEach"),cW=I7e});function D7e(e,t,r,i){for(var n=e.length,a=r+(i?1:-1);i?a--:++a{"use strict";s(D7e,"baseFindIndex"),uW=D7e});function O7e(e){return e!==e}var B7e,T6t=G(()=>{"use strict";s(O7e,"baseIsNaN"),B7e=O7e});function P7e(e,t,r){for(var i=r-1,n=e.length;++i{"use strict";s(P7e,"strictIndexOf"),F7e=P7e});function z7e(e,t,r){return t===t?F7e(e,t,r):uW(e,B7e,r)}var q7e,C6t=G(()=>{"use strict";N7e(),T6t(),k6t(),s(z7e,"baseIndexOf"),q7e=z7e});function W7e(e,t){var r=e==null?0:e.length;return!!r&&q7e(e,t,0)>-1}var j7e,w6t=G(()=>{"use strict";C6t(),s(W7e,"arrayIncludes"),j7e=W7e});function V7e(e,t){var r=typeof e;return t=t??U7e,!!t&&(r=="number"||r!="symbol"&&Y7e.test(e))&&e>-1&&e%1==0&&e{"use strict";U7e=9007199254740991,Y7e=/^(?:0|[1-9]\d*)$/,s(V7e,"isIndex"),Y4=V7e});function H7e(e,t,r){t=="__proto__"&&U4?U4(e,t,{configurable:!0,enumerable:!0,value:r,writable:!0}):e[t]=r}var H4,a7=G(()=>{"use strict";_7e(),s(H7e,"baseAssignValue"),H4=H7e});function G7e(e,t){return e===t||e!==e&&t!==t}var _g,v2=G(()=>{"use strict";s(G7e,"eq"),_g=G7e});function X7e(e,t,r){var i=e[t];(!(Q7e.call(e,t)&&_g(i,r))||r===void 0&&!(t in e))&&H4(e,t,r)}var K7e,Q7e,G4,s7=G(()=>{"use strict";a7(),v2(),K7e=Object.prototype,Q7e=K7e.hasOwnProperty,s(X7e,"assignValue"),G4=X7e});function Z7e(e,t,r,i){var n=!r;r||(r={});for(var a=-1,o=t.length;++a{"use strict";s7(),a7(),s(Z7e,"copyObject"),b2=Z7e});function J7e(e,t,r){return t=hW(t===void 0?e.length-1:t,0),function(){for(var i=arguments,n=-1,a=hW(i.length-t,0),o=Array(a);++n{"use strict";y6t(),hW=Math.max,s(J7e,"overRest"),dW=J7e});function tAe(e,t){return lW(dW(e,t,Hp),e+"")}var K4,o7=G(()=>{"use strict";Eg(),eAe(),L7e(),s(tAe,"baseRest"),K4=tAe});function rAe(e){return typeof e=="number"&&e>-1&&e%1==0&&e<=iAe}var iAe,l7,pW=G(()=>{"use strict";iAe=9007199254740991,s(rAe,"isLength"),l7=rAe});function nAe(e){return e!=null&&l7(e.length)&&!y2(e)}var Gu,fd=G(()=>{"use strict";t7(),pW(),s(nAe,"isArrayLike"),Gu=nAe});function aAe(e,t,r){if(!ol(r))return!1;var i=typeof t;return(i=="number"?Gu(r)&&Y4(t,r.length):i=="string"&&t in r)?_g(r[t],e):!1}var x2,c7=G(()=>{"use strict";v2(),fd(),n7(),qc(),s(aAe,"isIterateeCall"),x2=aAe});function sAe(e){return K4(function(t,r){var i=-1,n=r.length,a=n>1?r[n-1]:void 0,o=n>2?r[2]:void 0;for(a=e.length>3&&typeof a=="function"?(n--,a):void 0,o&&x2(r[0],r[1],o)&&(a=n<3?void 0:a,n=1),t=Object(t);++i{"use strict";o7(),c7(),s(sAe,"createAssigner"),oAe=sAe});function lAe(e){var t=e&&e.constructor,r=typeof t=="function"&&t.prototype||cAe;return e===r}var cAe,Q4,u7=G(()=>{"use strict";cAe=Object.prototype,s(lAe,"isPrototype"),Q4=lAe});function uAe(e,t){for(var r=-1,i=Array(e);++r{"use strict";s(uAe,"baseTimes"),hAe=uAe});function dAe(e){return rc(e)&&dd(e)==pAe}var pAe,fW,_6t=G(()=>{"use strict";Cg(),Hu(),pAe="[object Arguments]",s(dAe,"baseIsArguments"),fW=dAe}),gW,fAe,gAe,mAe,Ag,Z4=G(()=>{"use strict";_6t(),Hu(),gW=Object.prototype,fAe=gW.hasOwnProperty,gAe=gW.propertyIsEnumerable,mAe=fW((function(){return arguments})())?fW:function(e){return rc(e)&&fAe.call(e,"callee")&&!gAe.call(e,"callee")},Ag=mAe});function yAe(){return!1}var vAe,A6t=G(()=>{"use strict";s(yAe,"stubFalse"),vAe=yAe}),mW,yW,bAe,vW,xAe,TAe,Rg,J4=G(()=>{"use strict";zc(),A6t(),mW=typeof exports=="object"&&exports&&!exports.nodeType&&exports,yW=mW&&typeof module=="object"&&module&&!module.nodeType&&module,bAe=yW&&yW.exports===mW,vW=bAe?tc.Buffer:void 0,xAe=vW?vW.isBuffer:void 0,TAe=xAe||vAe,Rg=TAe});function kAe(e){return rc(e)&&l7(e.length)&&!!Nn[dd(e)]}var CAe,wAe,EAe,SAe,_Ae,AAe,RAe,$Ae,LAe,IAe,MAe,DAe,NAe,OAe,BAe,PAe,FAe,zAe,qAe,WAe,jAe,VAe,UAe,YAe,Nn,HAe,R6t=G(()=>{"use strict";Cg(),pW(),Hu(),CAe="[object Arguments]",wAe="[object Array]",EAe="[object Boolean]",SAe="[object Date]",_Ae="[object Error]",AAe="[object Function]",RAe="[object Map]",$Ae="[object Number]",LAe="[object Object]",IAe="[object RegExp]",MAe="[object Set]",DAe="[object String]",NAe="[object WeakMap]",OAe="[object ArrayBuffer]",BAe="[object DataView]",PAe="[object Float32Array]",FAe="[object Float64Array]",zAe="[object Int8Array]",qAe="[object Int16Array]",WAe="[object Int32Array]",jAe="[object Uint8Array]",VAe="[object Uint8ClampedArray]",UAe="[object Uint16Array]",YAe="[object Uint32Array]",Nn={},Nn[PAe]=Nn[FAe]=Nn[zAe]=Nn[qAe]=Nn[WAe]=Nn[jAe]=Nn[VAe]=Nn[UAe]=Nn[YAe]=!0,Nn[CAe]=Nn[wAe]=Nn[OAe]=Nn[EAe]=Nn[BAe]=Nn[SAe]=Nn[_Ae]=Nn[AAe]=Nn[RAe]=Nn[$Ae]=Nn[LAe]=Nn[IAe]=Nn[MAe]=Nn[DAe]=Nn[NAe]=!1,s(kAe,"baseIsTypedArray"),HAe=kAe});function GAe(e){return function(t){return e(t)}}var e3,h7=G(()=>{"use strict";s(GAe,"baseUnary"),e3=GAe}),bW,T2,XAe,d7,KAe,$g,xW=G(()=>{"use strict";J6e(),bW=typeof exports=="object"&&exports&&!exports.nodeType&&exports,T2=bW&&typeof module=="object"&&module&&!module.nodeType&&module,XAe=T2&&T2.exports===bW,d7=XAe&&Qq.process,KAe=(function(){try{var e=T2&&T2.require&&T2.require("util").types;return e||d7&&d7.binding&&d7.binding("util")}catch{}})(),$g=KAe}),TW,QAe,t3,p7=G(()=>{"use strict";R6t(),h7(),xW(),TW=$g&&$g.isTypedArray,QAe=TW?e3(TW):HAe,t3=QAe});function ZAe(e,t){var r=ma(e),i=!r&&Ag(e),n=!r&&!i&&Rg(e),a=!r&&!i&&!n&&t3(e),o=r||i||n||a,l=o?hAe(e.length,String):[],u=l.length;for(var h in e)(t||e8e.call(e,h))&&!(o&&(h=="length"||n&&(h=="offset"||h=="parent")||a&&(h=="buffer"||h=="byteLength"||h=="byteOffset")||Y4(h,u)))&&l.push(h);return l}var JAe,e8e,kW,t8e=G(()=>{"use strict";S6t(),Z4(),is(),J4(),n7(),p7(),JAe=Object.prototype,e8e=JAe.hasOwnProperty,s(ZAe,"arrayLikeKeys"),kW=ZAe});function r8e(e,t){return function(r){return e(t(r))}}var CW,i8e=G(()=>{"use strict";s(r8e,"overArg"),CW=r8e}),n8e,a8e,$6t=G(()=>{"use strict";i8e(),n8e=CW(Object.keys,Object),a8e=n8e});function s8e(e){if(!Q4(e))return a8e(e);var t=[];for(var r in Object(e))l8e.call(e,r)&&r!="constructor"&&t.push(r);return t}var o8e,l8e,f7,wW=G(()=>{"use strict";u7(),$6t(),o8e=Object.prototype,l8e=o8e.hasOwnProperty,s(s8e,"baseKeys"),f7=s8e});function c8e(e){return Gu(e)?kW(e):f7(e)}var ll,Kp=G(()=>{"use strict";t8e(),wW(),fd(),s(c8e,"keys"),ll=c8e});function u8e(e){var t=[];if(e!=null)for(var r in Object(e))t.push(r);return t}var h8e,L6t=G(()=>{"use strict";s(u8e,"nativeKeysIn"),h8e=u8e});function d8e(e){if(!ol(e))return h8e(e);var t=Q4(e),r=[];for(var i in e)i=="constructor"&&(t||!f8e.call(e,i))||r.push(i);return r}var p8e,f8e,g8e,I6t=G(()=>{"use strict";qc(),u7(),L6t(),p8e=Object.prototype,f8e=p8e.hasOwnProperty,s(d8e,"baseKeysIn"),g8e=d8e});function m8e(e){return Gu(e)?kW(e,!0):g8e(e)}var Qp,Lg=G(()=>{"use strict";t8e(),I6t(),fd(),s(m8e,"keysIn"),Qp=m8e});function y8e(e,t){if(ma(e))return!1;var r=typeof e;return r=="number"||r=="symbol"||r=="boolean"||e==null||Yp(e)?!0:b8e.test(e)||!v8e.test(e)||t!=null&&e in Object(t)}var v8e,b8e,g7,EW=G(()=>{"use strict";is(),m2(),v8e=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,b8e=/^\w*$/,s(y8e,"isKey"),g7=y8e}),x8e,k2,m7=G(()=>{"use strict";Sg(),x8e=Xp(Object,"create"),k2=x8e});function T8e(){this.__data__=k2?k2(null):{},this.size=0}var k8e,M6t=G(()=>{"use strict";m7(),s(T8e,"hashClear"),k8e=T8e});function C8e(e){var t=this.has(e)&&delete this.__data__[e];return this.size-=t?1:0,t}var w8e,D6t=G(()=>{"use strict";s(C8e,"hashDelete"),w8e=C8e});function E8e(e){var t=this.__data__;if(k2){var r=t[e];return r===S8e?void 0:r}return A8e.call(t,e)?t[e]:void 0}var S8e,_8e,A8e,R8e,N6t=G(()=>{"use strict";m7(),S8e="__lodash_hash_undefined__",_8e=Object.prototype,A8e=_8e.hasOwnProperty,s(E8e,"hashGet"),R8e=E8e});function $8e(e){var t=this.__data__;return k2?t[e]!==void 0:I8e.call(t,e)}var L8e,I8e,M8e,O6t=G(()=>{"use strict";m7(),L8e=Object.prototype,I8e=L8e.hasOwnProperty,s($8e,"hashHas"),M8e=$8e});function D8e(e,t){var r=this.__data__;return this.size+=this.has(e)?0:1,r[e]=k2&&t===void 0?N8e:t,this}var N8e,O8e,B6t=G(()=>{"use strict";m7(),N8e="__lodash_hash_undefined__",s(D8e,"hashSet"),O8e=D8e});function Ig(e){var t=-1,r=e==null?0:e.length;for(this.clear();++t{"use strict";M6t(),D6t(),N6t(),O6t(),B6t(),s(Ig,"Hash"),Ig.prototype.clear=k8e,Ig.prototype.delete=w8e,Ig.prototype.get=R8e,Ig.prototype.has=M8e,Ig.prototype.set=O8e,SW=Ig});function B8e(){this.__data__=[],this.size=0}var P8e,F6t=G(()=>{"use strict";s(B8e,"listCacheClear"),P8e=B8e});function F8e(e,t){for(var r=e.length;r--;)if(_g(e[r][0],t))return r;return-1}var r3,y7=G(()=>{"use strict";v2(),s(F8e,"assocIndexOf"),r3=F8e});function z8e(e){var t=this.__data__,r=r3(t,e);if(r<0)return!1;var i=t.length-1;return r==i?t.pop():W8e.call(t,r,1),--this.size,!0}var q8e,W8e,j8e,z6t=G(()=>{"use strict";y7(),q8e=Array.prototype,W8e=q8e.splice,s(z8e,"listCacheDelete"),j8e=z8e});function V8e(e){var t=this.__data__,r=r3(t,e);return r<0?void 0:t[r][1]}var U8e,q6t=G(()=>{"use strict";y7(),s(V8e,"listCacheGet"),U8e=V8e});function Y8e(e){return r3(this.__data__,e)>-1}var H8e,W6t=G(()=>{"use strict";y7(),s(Y8e,"listCacheHas"),H8e=Y8e});function G8e(e,t){var r=this.__data__,i=r3(r,e);return i<0?(++this.size,r.push([e,t])):r[i][1]=t,this}var X8e,j6t=G(()=>{"use strict";y7(),s(G8e,"listCacheSet"),X8e=G8e});function Mg(e){var t=-1,r=e==null?0:e.length;for(this.clear();++t{"use strict";F6t(),z6t(),q6t(),W6t(),j6t(),s(Mg,"ListCache"),Mg.prototype.clear=P8e,Mg.prototype.delete=j8e,Mg.prototype.get=U8e,Mg.prototype.has=H8e,Mg.prototype.set=X8e,i3=Mg}),K8e,C2,_W=G(()=>{"use strict";Sg(),zc(),K8e=Xp(tc,"Map"),C2=K8e});function Q8e(){this.size=0,this.__data__={hash:new SW,map:new(C2||i3),string:new SW}}var Z8e,V6t=G(()=>{"use strict";P6t(),v7(),_W(),s(Q8e,"mapCacheClear"),Z8e=Q8e});function J8e(e){var t=typeof e;return t=="string"||t=="number"||t=="symbol"||t=="boolean"?e!=="__proto__":e===null}var eRe,U6t=G(()=>{"use strict";s(J8e,"isKeyable"),eRe=J8e});function tRe(e,t){var r=e.__data__;return eRe(t)?r[typeof t=="string"?"string":"hash"]:r.map}var n3,b7=G(()=>{"use strict";U6t(),s(tRe,"getMapData"),n3=tRe});function rRe(e){var t=n3(this,e).delete(e);return this.size-=t?1:0,t}var iRe,Y6t=G(()=>{"use strict";b7(),s(rRe,"mapCacheDelete"),iRe=rRe});function nRe(e){return n3(this,e).get(e)}var aRe,H6t=G(()=>{"use strict";b7(),s(nRe,"mapCacheGet"),aRe=nRe});function sRe(e){return n3(this,e).has(e)}var oRe,G6t=G(()=>{"use strict";b7(),s(sRe,"mapCacheHas"),oRe=sRe});function lRe(e,t){var r=n3(this,e),i=r.size;return r.set(e,t),this.size+=r.size==i?0:1,this}var cRe,X6t=G(()=>{"use strict";b7(),s(lRe,"mapCacheSet"),cRe=lRe});function Dg(e){var t=-1,r=e==null?0:e.length;for(this.clear();++t{"use strict";V6t(),Y6t(),H6t(),G6t(),X6t(),s(Dg,"MapCache"),Dg.prototype.clear=Z8e,Dg.prototype.delete=iRe,Dg.prototype.get=aRe,Dg.prototype.has=oRe,Dg.prototype.set=cRe,a3=Dg});function x7(e,t){if(typeof e!="function"||t!=null&&typeof t!="function")throw new TypeError(uRe);var r=s(function(){var i=arguments,n=t?t.apply(this,i):i[0],a=r.cache;if(a.has(n))return a.get(n);var o=e.apply(this,i);return r.cache=a.set(n,o)||a,o},"memoized");return r.cache=new(x7.Cache||a3),r}var uRe,hRe,K6t=G(()=>{"use strict";AW(),uRe="Expected a function",s(x7,"memoize"),x7.Cache=a3,hRe=x7});function dRe(e){var t=hRe(e,function(i){return r.size===pRe&&r.clear(),i}),r=t.cache;return t}var pRe,fRe,Q6t=G(()=>{"use strict";K6t(),pRe=500,s(dRe,"memoizeCapped"),fRe=dRe}),gRe,mRe,yRe,vRe,Z6t=G(()=>{"use strict";Q6t(),gRe=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,mRe=/\\(\\)?/g,yRe=fRe(function(e){var t=[];return e.charCodeAt(0)===46&&t.push(""),e.replace(gRe,function(r,i,n,a){t.push(n?a.replace(mRe,"$1"):i||r)}),t}),vRe=yRe});function bRe(e){return e==null?"":x_e(e)}var RW,xRe=G(()=>{"use strict";s6t(),s(bRe,"toString"),RW=bRe});function TRe(e,t){return ma(e)?e:g7(e,t)?[e]:vRe(RW(e))}var s3,T7=G(()=>{"use strict";is(),EW(),Z6t(),xRe(),s(TRe,"castPath"),s3=TRe});function kRe(e){if(typeof e=="string"||Yp(e))return e;var t=e+"";return t=="0"&&1/e==-CRe?"-0":t}var CRe,w2,o3=G(()=>{"use strict";m2(),CRe=1/0,s(kRe,"toKey"),w2=kRe});function wRe(e,t){t=s3(t,e);for(var r=0,i=t.length;e!=null&&r{"use strict";T7(),o3(),s(wRe,"baseGet"),l3=wRe});function ERe(e,t,r){var i=e==null?void 0:l3(e,t);return i===void 0?r:i}var SRe,J6t=G(()=>{"use strict";k7(),s(ERe,"get"),SRe=ERe});function _Re(e,t){for(var r=-1,i=t.length,n=e.length;++r{"use strict";s(_Re,"arrayPush"),C7=_Re});function ARe(e){return ma(e)||Ag(e)||!!(LW&&e&&e[LW])}var LW,RRe,e_t=G(()=>{"use strict";f2(),Z4(),is(),LW=_l?_l.isConcatSpreadable:void 0,s(ARe,"isFlattenable"),RRe=ARe});function IW(e,t,r,i,n){var a=-1,o=e.length;for(r||(r=RRe),n||(n=[]);++a0&&r(l)?t>1?IW(l,t-1,r,i,n):C7(n,l):i||(n[n.length]=l)}return n}var w7,MW=G(()=>{"use strict";$W(),e_t(),s(IW,"baseFlatten"),w7=IW});function $Re(e){var t=e==null?0:e.length;return t?w7(e,1):[]}var Ng,LRe=G(()=>{"use strict";MW(),s($Re,"flatten"),Ng=$Re});function IRe(e){return lW(dW(e,void 0,Ng),e+"")}var MRe,t_t=G(()=>{"use strict";LRe(),eAe(),L7e(),s(IRe,"flatRest"),MRe=IRe}),DRe,E7,DW=G(()=>{"use strict";i8e(),DRe=CW(Object.getPrototypeOf,Object),E7=DRe});function NRe(e){if(!rc(e)||dd(e)!=ORe)return!1;var t=E7(e);if(t===null)return!0;var r=FRe.call(t,"constructor")&&t.constructor;return typeof r=="function"&&r instanceof r&&NW.call(r)==zRe}var ORe,BRe,PRe,NW,FRe,zRe,qRe,r_t=G(()=>{"use strict";Cg(),DW(),Hu(),ORe="[object Object]",BRe=Function.prototype,PRe=Object.prototype,NW=BRe.toString,FRe=PRe.hasOwnProperty,zRe=NW.call(Object),s(NRe,"isPlainObject"),qRe=NRe});function WRe(e){return KRe.test(e)}var jRe,VRe,URe,YRe,HRe,GRe,XRe,KRe,QRe,i_t=G(()=>{"use strict";jRe="\\ud800-\\udfff",VRe="\\u0300-\\u036f",URe="\\ufe20-\\ufe2f",YRe="\\u20d0-\\u20ff",HRe=VRe+URe+YRe,GRe="\\ufe0e\\ufe0f",XRe="\\u200d",KRe=RegExp("["+XRe+jRe+HRe+GRe+"]"),s(WRe,"hasUnicode"),QRe=WRe});function ZRe(e,t,r,i){var n=-1,a=e==null?0:e.length;for(i&&a&&(r=e[++n]);++n{"use strict";s(ZRe,"arrayReduce"),JRe=ZRe});function e$e(){this.__data__=new i3,this.size=0}var t$e,a_t=G(()=>{"use strict";v7(),s(e$e,"stackClear"),t$e=e$e});function r$e(e){var t=this.__data__,r=t.delete(e);return this.size=t.size,r}var i$e,s_t=G(()=>{"use strict";s(r$e,"stackDelete"),i$e=r$e});function n$e(e){return this.__data__.get(e)}var a$e,o_t=G(()=>{"use strict";s(n$e,"stackGet"),a$e=n$e});function s$e(e){return this.__data__.has(e)}var o$e,l_t=G(()=>{"use strict";s(s$e,"stackHas"),o$e=s$e});function l$e(e,t){var r=this.__data__;if(r instanceof i3){var i=r.__data__;if(!C2||i.length{"use strict";v7(),_W(),AW(),c$e=200,s(l$e,"stackSet"),u$e=l$e});function Og(e){var t=this.__data__=new i3(e);this.size=t.size}var Bg,S7=G(()=>{"use strict";v7(),a_t(),s_t(),o_t(),l_t(),c_t(),s(Og,"Stack"),Og.prototype.clear=t$e,Og.prototype.delete=i$e,Og.prototype.get=a$e,Og.prototype.has=o$e,Og.prototype.set=u$e,Bg=Og});function h$e(e,t){return e&&b2(t,ll(t),e)}var d$e,u_t=G(()=>{"use strict";X4(),Kp(),s(h$e,"baseAssign"),d$e=h$e});function p$e(e,t){return e&&b2(t,Qp(t),e)}var f$e,h_t=G(()=>{"use strict";X4(),Lg(),s(p$e,"baseAssignIn"),f$e=p$e});function g$e(e,t){if(t)return e.slice();var r=e.length,i=FW?FW(r):new e.constructor(r);return e.copy(i),i}var OW,BW,m$e,PW,FW,zW,y$e=G(()=>{"use strict";zc(),OW=typeof exports=="object"&&exports&&!exports.nodeType&&exports,BW=OW&&typeof module=="object"&&module&&!module.nodeType&&module,m$e=BW&&BW.exports===OW,PW=m$e?tc.Buffer:void 0,FW=PW?PW.allocUnsafe:void 0,s(g$e,"cloneBuffer"),zW=g$e});function v$e(e,t){for(var r=-1,i=e==null?0:e.length,n=0,a=[];++r{"use strict";s(v$e,"arrayFilter"),qW=v$e});function x$e(){return[]}var WW,T$e=G(()=>{"use strict";s(x$e,"stubArray"),WW=x$e}),k$e,C$e,jW,w$e,_7,VW=G(()=>{"use strict";b$e(),T$e(),k$e=Object.prototype,C$e=k$e.propertyIsEnumerable,jW=Object.getOwnPropertySymbols,w$e=jW?function(e){return e==null?[]:(e=Object(e),qW(jW(e),function(t){return C$e.call(e,t)}))}:WW,_7=w$e});function E$e(e,t){return b2(e,_7(e),t)}var S$e,d_t=G(()=>{"use strict";X4(),VW(),s(E$e,"copySymbols"),S$e=E$e}),_$e,A$e,UW,R$e=G(()=>{"use strict";$W(),DW(),VW(),T$e(),_$e=Object.getOwnPropertySymbols,A$e=_$e?function(e){for(var t=[];e;)C7(t,_7(e)),e=E7(e);return t}:WW,UW=A$e});function $$e(e,t){return b2(e,UW(e),t)}var L$e,p_t=G(()=>{"use strict";X4(),R$e(),s($$e,"copySymbolsIn"),L$e=$$e});function I$e(e,t,r){var i=t(e);return ma(e)?i:C7(i,r(e))}var YW,M$e=G(()=>{"use strict";$W(),is(),s(I$e,"baseGetAllKeys"),YW=I$e});function D$e(e){return YW(e,ll,_7)}var A7,N$e=G(()=>{"use strict";M$e(),VW(),Kp(),s(D$e,"getAllKeys"),A7=D$e});function O$e(e){return YW(e,Qp,UW)}var B$e,f_t=G(()=>{"use strict";M$e(),R$e(),Lg(),s(O$e,"getAllKeysIn"),B$e=O$e}),P$e,R7,g_t=G(()=>{"use strict";Sg(),zc(),P$e=Xp(tc,"DataView"),R7=P$e}),F$e,$7,m_t=G(()=>{"use strict";Sg(),zc(),F$e=Xp(tc,"Promise"),$7=F$e}),z$e,Pg,q$e=G(()=>{"use strict";Sg(),zc(),z$e=Xp(tc,"Set"),Pg=z$e}),HW,W$e,GW,XW,KW,QW,j$e,V$e,U$e,Y$e,H$e,Zp,Jp,E2=G(()=>{"use strict";g_t(),_W(),m_t(),q$e(),g6t(),Cg(),Q_e(),HW="[object Map]",W$e="[object Object]",GW="[object Promise]",XW="[object Set]",KW="[object WeakMap]",QW="[object DataView]",j$e=Gp(R7),V$e=Gp(C2),U$e=Gp($7),Y$e=Gp(Pg),H$e=Gp(i7),Zp=dd,(R7&&Zp(new R7(new ArrayBuffer(1)))!=QW||C2&&Zp(new C2)!=HW||$7&&Zp($7.resolve())!=GW||Pg&&Zp(new Pg)!=XW||i7&&Zp(new i7)!=KW)&&(Zp=s(function(e){var t=dd(e),r=t==W$e?e.constructor:void 0,i=r?Gp(r):"";if(i)switch(i){case j$e:return QW;case V$e:return HW;case U$e:return GW;case Y$e:return XW;case H$e:return KW}return t},"getTag")),Jp=Zp});function G$e(e){var t=e.length,r=new e.constructor(t);return t&&typeof e[0]=="string"&&K$e.call(e,"index")&&(r.index=e.index,r.input=e.input),r}var X$e,K$e,Q$e,y_t=G(()=>{"use strict";X$e=Object.prototype,K$e=X$e.hasOwnProperty,s(G$e,"initCloneArray"),Q$e=G$e}),Z$e,c3,J$e=G(()=>{"use strict";zc(),Z$e=tc.Uint8Array,c3=Z$e});function eLe(e){var t=new e.constructor(e.byteLength);return new c3(t).set(new c3(e)),t}var L7,ZW=G(()=>{"use strict";J$e(),s(eLe,"cloneArrayBuffer"),L7=eLe});function tLe(e,t){var r=t?L7(e.buffer):e.buffer;return new e.constructor(r,e.byteOffset,e.byteLength)}var rLe,v_t=G(()=>{"use strict";ZW(),s(tLe,"cloneDataView"),rLe=tLe});function iLe(e){var t=new e.constructor(e.source,nLe.exec(e));return t.lastIndex=e.lastIndex,t}var nLe,aLe,b_t=G(()=>{"use strict";nLe=/\w*$/,s(iLe,"cloneRegExp"),aLe=iLe});function sLe(e){return ej?Object(ej.call(e)):{}}var JW,ej,oLe,x_t=G(()=>{"use strict";f2(),JW=_l?_l.prototype:void 0,ej=JW?JW.valueOf:void 0,s(sLe,"cloneSymbol"),oLe=sLe});function lLe(e,t){var r=t?L7(e.buffer):e.buffer;return new e.constructor(r,e.byteOffset,e.length)}var tj,cLe=G(()=>{"use strict";ZW(),s(lLe,"cloneTypedArray"),tj=lLe});function uLe(e,t,r){var i=e.constructor;switch(t){case bLe:return L7(e);case hLe:case dLe:return new i(+e);case xLe:return rLe(e,r);case TLe:case kLe:case CLe:case wLe:case ELe:case SLe:case _Le:case ALe:case RLe:return tj(e,r);case pLe:return new i;case fLe:case yLe:return new i(e);case gLe:return aLe(e);case mLe:return new i;case vLe:return oLe(e)}}var hLe,dLe,pLe,fLe,gLe,mLe,yLe,vLe,bLe,xLe,TLe,kLe,CLe,wLe,ELe,SLe,_Le,ALe,RLe,$Le,T_t=G(()=>{"use strict";ZW(),v_t(),b_t(),x_t(),cLe(),hLe="[object Boolean]",dLe="[object Date]",pLe="[object Map]",fLe="[object Number]",gLe="[object RegExp]",mLe="[object Set]",yLe="[object String]",vLe="[object Symbol]",bLe="[object ArrayBuffer]",xLe="[object DataView]",TLe="[object Float32Array]",kLe="[object Float64Array]",CLe="[object Int8Array]",wLe="[object Int16Array]",ELe="[object Int32Array]",SLe="[object Uint8Array]",_Le="[object Uint8ClampedArray]",ALe="[object Uint16Array]",RLe="[object Uint32Array]",s(uLe,"initCloneByTag"),$Le=uLe});function LLe(e){return typeof e.constructor=="function"&&!Q4(e)?d7e(E7(e)):{}}var rj,ILe=G(()=>{"use strict";m6t(),DW(),u7(),s(LLe,"initCloneObject"),rj=LLe});function MLe(e){return rc(e)&&Jp(e)==DLe}var DLe,NLe,k_t=G(()=>{"use strict";E2(),Hu(),DLe="[object Map]",s(MLe,"baseIsMap"),NLe=MLe}),ij,OLe,BLe,C_t=G(()=>{"use strict";k_t(),h7(),xW(),ij=$g&&$g.isMap,OLe=ij?e3(ij):NLe,BLe=OLe});function PLe(e){return rc(e)&&Jp(e)==FLe}var FLe,zLe,w_t=G(()=>{"use strict";E2(),Hu(),FLe="[object Set]",s(PLe,"baseIsSet"),zLe=PLe}),nj,qLe,WLe,E_t=G(()=>{"use strict";w_t(),h7(),xW(),nj=$g&&$g.isSet,qLe=nj?e3(nj):zLe,WLe=qLe});function u3(e,t,r,i,n,a){var o,l=t&jLe,u=t&VLe,h=t&ULe;if(r&&(o=n?r(e,i,n,a):r(e)),o!==void 0)return o;if(!ol(e))return e;var d=ma(e);if(d){if(o=Q$e(e),!l)return oW(e,o)}else{var p=Jp(e),f=p==sj||p==KLe;if(Rg(e))return zW(e,l);if(p==oj||p==aj||f&&!n){if(o=u||f?{}:rj(e),!l)return u?L$e(e,f$e(o,e)):S$e(e,d$e(o,e))}else{if(!Tn[p])return n?e:{};o=$Le(e,p,l)}}a||(a=new Bg);var g=a.get(e);if(g)return g;a.set(e,o),WLe(e)?e.forEach(function(k){o.add(u3(k,t,r,k,e,a))}):BLe(e)&&e.forEach(function(k,C){o.set(C,u3(k,t,r,C,e,a))});var m=h?u?B$e:A7:u?Qp:ll,y=d?void 0:m(e);return cW(y||e,function(k,C){y&&(C=k,k=e[C]),G4(o,C,u3(k,t,r,C,e,a))}),o}var jLe,VLe,ULe,aj,YLe,HLe,GLe,XLe,sj,KLe,QLe,ZLe,oj,JLe,e9e,t9e,r9e,i9e,n9e,a9e,s9e,o9e,l9e,c9e,u9e,h9e,d9e,p9e,f9e,Tn,g9e,S_t=G(()=>{"use strict";S7(),M7e(),s7(),u_t(),h_t(),y$e(),v7e(),d_t(),p_t(),N$e(),f_t(),E2(),y_t(),T_t(),ILe(),is(),J4(),C_t(),qc(),E_t(),Kp(),Lg(),jLe=1,VLe=2,ULe=4,aj="[object Arguments]",YLe="[object Array]",HLe="[object Boolean]",GLe="[object Date]",XLe="[object Error]",sj="[object Function]",KLe="[object GeneratorFunction]",QLe="[object Map]",ZLe="[object Number]",oj="[object Object]",JLe="[object RegExp]",e9e="[object Set]",t9e="[object String]",r9e="[object Symbol]",i9e="[object WeakMap]",n9e="[object ArrayBuffer]",a9e="[object DataView]",s9e="[object Float32Array]",o9e="[object Float64Array]",l9e="[object Int8Array]",c9e="[object Int16Array]",u9e="[object Int32Array]",h9e="[object Uint8Array]",d9e="[object Uint8ClampedArray]",p9e="[object Uint16Array]",f9e="[object Uint32Array]",Tn={},Tn[aj]=Tn[YLe]=Tn[n9e]=Tn[a9e]=Tn[HLe]=Tn[GLe]=Tn[s9e]=Tn[o9e]=Tn[l9e]=Tn[c9e]=Tn[u9e]=Tn[QLe]=Tn[ZLe]=Tn[oj]=Tn[JLe]=Tn[e9e]=Tn[t9e]=Tn[r9e]=Tn[h9e]=Tn[d9e]=Tn[p9e]=Tn[f9e]=!0,Tn[XLe]=Tn[sj]=Tn[i9e]=!1,s(u3,"baseClone"),g9e=u3});function m9e(e){return g9e(e,y9e|v9e)}var y9e,v9e,b9e,__t=G(()=>{"use strict";S_t(),y9e=1,v9e=4,s(m9e,"cloneDeep"),b9e=m9e});function x9e(e){return this.__data__.set(e,T9e),this}var T9e,k9e,A_t=G(()=>{"use strict";T9e="__lodash_hash_undefined__",s(x9e,"setCacheAdd"),k9e=x9e});function C9e(e){return this.__data__.has(e)}var w9e,R_t=G(()=>{"use strict";s(C9e,"setCacheHas"),w9e=C9e});function h3(e){var t=-1,r=e==null?0:e.length;for(this.__data__=new a3;++t{"use strict";AW(),A_t(),R_t(),s(h3,"SetCache"),h3.prototype.add=h3.prototype.push=k9e,h3.prototype.has=w9e,lj=h3});function S9e(e,t){for(var r=-1,i=e==null?0:e.length;++r{"use strict";s(S9e,"arraySome"),_9e=S9e});function A9e(e,t){return e.has(t)}var cj,R9e=G(()=>{"use strict";s(A9e,"cacheHas"),cj=A9e});function $9e(e,t,r,i,n,a){var o=r&L9e,l=e.length,u=t.length;if(l!=u&&!(o&&u>l))return!1;var h=a.get(e),d=a.get(t);if(h&&d)return h==t&&d==e;var p=-1,f=!0,g=r&I9e?new lj:void 0;for(a.set(e,t),a.set(t,e);++p{"use strict";E9e(),$_t(),R9e(),L9e=1,I9e=2,s($9e,"equalArrays"),uj=$9e});function D9e(e){var t=-1,r=Array(e.size);return e.forEach(function(i,n){r[++t]=[n,i]}),r}var N9e,L_t=G(()=>{"use strict";s(D9e,"mapToArray"),N9e=D9e});function O9e(e){var t=-1,r=Array(e.size);return e.forEach(function(i){r[++t]=i}),r}var I7,hj=G(()=>{"use strict";s(O9e,"setToArray"),I7=O9e});function B9e(e,t,r,i,n,a,o){switch(r){case K9e:if(e.byteLength!=t.byteLength||e.byteOffset!=t.byteOffset)return!1;e=e.buffer,t=t.buffer;case X9e:return!(e.byteLength!=t.byteLength||!a(new c3(e),new c3(t)));case z9e:case q9e:case V9e:return _g(+e,+t);case W9e:return e.name==t.name&&e.message==t.message;case U9e:case H9e:return e==t+"";case j9e:var l=N9e;case Y9e:var u=i&P9e;if(l||(l=I7),e.size!=t.size&&!u)return!1;var h=o.get(e);if(h)return h==t;i|=F9e,o.set(e,t);var d=uj(l(e),l(t),i,n,a,o);return o.delete(e),d;case G9e:if(M7)return M7.call(e)==M7.call(t)}return!1}var P9e,F9e,z9e,q9e,W9e,j9e,V9e,U9e,Y9e,H9e,G9e,X9e,K9e,dj,M7,Q9e,I_t=G(()=>{"use strict";f2(),J$e(),v2(),M9e(),L_t(),hj(),P9e=1,F9e=2,z9e="[object Boolean]",q9e="[object Date]",W9e="[object Error]",j9e="[object Map]",V9e="[object Number]",U9e="[object RegExp]",Y9e="[object Set]",H9e="[object String]",G9e="[object Symbol]",X9e="[object ArrayBuffer]",K9e="[object DataView]",dj=_l?_l.prototype:void 0,M7=dj?dj.valueOf:void 0,s(B9e,"equalByTag"),Q9e=B9e});function Z9e(e,t,r,i,n,a){var o=r&J9e,l=A7(e),u=l.length,h=A7(t),d=h.length;if(u!=d&&!o)return!1;for(var p=u;p--;){var f=l[p];if(!(o?f in t:tIe.call(t,f)))return!1}var g=a.get(e),m=a.get(t);if(g&&m)return g==t&&m==e;var y=!0;a.set(e,t),a.set(t,e);for(var k=o;++p{"use strict";N$e(),J9e=1,eIe=Object.prototype,tIe=eIe.hasOwnProperty,s(Z9e,"equalObjects"),rIe=Z9e});function iIe(e,t,r,i,n,a){var o=ma(e),l=ma(t),u=o?fj:Jp(e),h=l?fj:Jp(t);u=u==pj?d3:u,h=h==pj?d3:h;var d=u==d3,p=h==d3,f=u==h;if(f&&Rg(e)){if(!Rg(t))return!1;o=!0,d=!1}if(f&&!d)return a||(a=new Bg),o||t3(e)?uj(e,t,r,i,n,a):Q9e(e,t,u,r,i,n,a);if(!(r&nIe)){var g=d&&gj.call(e,"__wrapped__"),m=p&&gj.call(t,"__wrapped__");if(g||m){var y=g?e.value():e,k=m?t.value():t;return a||(a=new Bg),n(y,k,r,i,a)}}return f?(a||(a=new Bg),rIe(e,t,r,i,n,a)):!1}var nIe,pj,fj,d3,aIe,gj,sIe,D_t=G(()=>{"use strict";S7(),M9e(),I_t(),M_t(),E2(),is(),J4(),p7(),nIe=1,pj="[object Arguments]",fj="[object Array]",d3="[object Object]",aIe=Object.prototype,gj=aIe.hasOwnProperty,s(iIe,"baseIsEqualDeep"),sIe=iIe});function mj(e,t,r,i,n){return e===t?!0:e==null||t==null||!rc(e)&&!rc(t)?e!==e&&t!==t:sIe(e,t,r,i,mj,n)}var yj,oIe=G(()=>{"use strict";D_t(),Hu(),s(mj,"baseIsEqual"),yj=mj});function lIe(e,t,r,i){var n=r.length,a=n,o=!i;if(e==null)return!a;for(e=Object(e);n--;){var l=r[n];if(o&&l[2]?l[1]!==e[l[0]]:!(l[0]in e))return!1}for(;++n{"use strict";S7(),oIe(),cIe=1,uIe=2,s(lIe,"baseIsMatch"),hIe=lIe});function dIe(e){return e===e&&!ol(e)}var vj,pIe=G(()=>{"use strict";qc(),s(dIe,"isStrictComparable"),vj=dIe});function fIe(e){for(var t=ll(e),r=t.length;r--;){var i=t[r],n=e[i];t[r]=[i,n,vj(n)]}return t}var gIe,O_t=G(()=>{"use strict";pIe(),Kp(),s(fIe,"getMatchData"),gIe=fIe});function mIe(e,t){return function(r){return r==null?!1:r[e]===t&&(t!==void 0||e in Object(r))}}var bj,yIe=G(()=>{"use strict";s(mIe,"matchesStrictComparable"),bj=mIe});function vIe(e){var t=gIe(e);return t.length==1&&t[0][2]?bj(t[0][0],t[0][1]):function(r){return r===e||hIe(r,e,t)}}var bIe,B_t=G(()=>{"use strict";N_t(),O_t(),yIe(),s(vIe,"baseMatches"),bIe=vIe});function xIe(e,t){return e!=null&&t in Object(e)}var TIe,P_t=G(()=>{"use strict";s(xIe,"baseHasIn"),TIe=xIe});function kIe(e,t,r){t=s3(t,e);for(var i=-1,n=t.length,a=!1;++i{"use strict";T7(),Z4(),is(),n7(),pW(),o3(),s(kIe,"hasPath"),xj=kIe});function wIe(e,t){return e!=null&&xj(e,t,TIe)}var Tj,EIe=G(()=>{"use strict";P_t(),CIe(),s(wIe,"hasIn"),Tj=wIe});function SIe(e,t){return g7(e)&&vj(t)?bj(w2(e),t):function(r){var i=SRe(r,e);return i===void 0&&i===t?Tj(r,e):yj(t,i,_Ie|AIe)}}var _Ie,AIe,RIe,F_t=G(()=>{"use strict";oIe(),J6t(),EIe(),EW(),pIe(),yIe(),o3(),_Ie=1,AIe=2,s(SIe,"baseMatchesProperty"),RIe=SIe});function $Ie(e){return function(t){return t?.[e]}}var kj,LIe=G(()=>{"use strict";s($Ie,"baseProperty"),kj=$Ie});function IIe(e){return function(t){return l3(t,e)}}var MIe,z_t=G(()=>{"use strict";k7(),s(IIe,"basePropertyDeep"),MIe=IIe});function DIe(e){return g7(e)?kj(w2(e)):MIe(e)}var NIe,q_t=G(()=>{"use strict";LIe(),z_t(),EW(),o3(),s(DIe,"property"),NIe=DIe});function OIe(e){return typeof e=="function"?e:e==null?Hp:typeof e=="object"?ma(e)?RIe(e[0],e[1]):bIe(e):NIe(e)}var gd,ef=G(()=>{"use strict";B_t(),F_t(),Eg(),is(),q_t(),s(OIe,"baseIteratee"),gd=OIe});function BIe(e){return function(t,r,i){for(var n=-1,a=Object(t),o=i(t),l=o.length;l--;){var u=o[e?l:++n];if(r(a[u],u,a)===!1)break}return t}}var PIe,W_t=G(()=>{"use strict";s(BIe,"createBaseFor"),PIe=BIe}),FIe,D7,Cj=G(()=>{"use strict";W_t(),FIe=PIe(),D7=FIe});function zIe(e,t){return e&&D7(e,t,ll)}var N7,wj=G(()=>{"use strict";Cj(),Kp(),s(zIe,"baseForOwn"),N7=zIe});function qIe(e,t){return function(r,i){if(r==null)return r;if(!Gu(r))return e(r,i);for(var n=r.length,a=t?n:-1,o=Object(r);(t?a--:++a{"use strict";fd(),s(qIe,"createBaseEach"),WIe=qIe}),jIe,p3,O7=G(()=>{"use strict";wj(),j_t(),jIe=WIe(N7),p3=jIe}),VIe,Ej,V_t=G(()=>{"use strict";zc(),VIe=s(function(){return tc.Date.now()},"now"),Ej=VIe}),Sj,UIe,YIe,HIe,U_t=G(()=>{"use strict";o7(),v2(),c7(),Lg(),Sj=Object.prototype,UIe=Sj.hasOwnProperty,YIe=K4(function(e,t){e=Object(e);var r=-1,i=t.length,n=i>2?t[2]:void 0;for(n&&x2(t[0],t[1],n)&&(i=1);++r{"use strict";a7(),v2(),s(GIe,"assignMergeValue"),B7=GIe});function KIe(e){return rc(e)&&Gu(e)}var _j,QIe=G(()=>{"use strict";fd(),Hu(),s(KIe,"isArrayLikeObject"),_j=KIe});function ZIe(e,t){if(!(t==="constructor"&&typeof e[t]=="function")&&t!="__proto__")return e[t]}var P7,JIe=G(()=>{"use strict";s(ZIe,"safeGet"),P7=ZIe});function eMe(e){return b2(e,Qp(e))}var tMe,Y_t=G(()=>{"use strict";X4(),Lg(),s(eMe,"toPlainObject"),tMe=eMe});function rMe(e,t,r,i,n,a,o){var l=P7(e,r),u=P7(t,r),h=o.get(u);if(h){B7(e,r,h);return}var d=a?a(l,u,r+"",e,t,o):void 0,p=d===void 0;if(p){var f=ma(u),g=!f&&Rg(u),m=!f&&!g&&t3(u);d=u,f||g||m?ma(l)?d=l:_j(l)?d=oW(l):g?(p=!1,d=zW(u,!0)):m?(p=!1,d=tj(u,!0)):d=[]:qRe(u)||Ag(u)?(d=l,Ag(l)?d=tMe(l):(!ol(l)||y2(l))&&(d=rj(u))):p=!1}p&&(o.set(u,d),n(d,u,i,a,o),o.delete(u)),B7(e,r,d)}var iMe,H_t=G(()=>{"use strict";XIe(),y$e(),cLe(),v7e(),ILe(),Z4(),is(),QIe(),J4(),t7(),qc(),r_t(),p7(),JIe(),Y_t(),s(rMe,"baseMergeDeep"),iMe=rMe});function Aj(e,t,r,i,n){e!==t&&D7(t,function(a,o){if(n||(n=new Bg),ol(a))iMe(e,t,o,r,Aj,i,n);else{var l=i?i(P7(e,o),a,o+"",e,t,n):void 0;l===void 0&&(l=a),B7(e,o,l)}},Qp)}var nMe,G_t=G(()=>{"use strict";S7(),XIe(),Cj(),H_t(),qc(),Lg(),JIe(),s(Aj,"baseMerge"),nMe=Aj});function aMe(e,t,r){for(var i=-1,n=e==null?0:e.length;++i{"use strict";s(aMe,"arrayIncludesWith"),sMe=aMe});function oMe(e){var t=e==null?0:e.length;return t?e[t-1]:void 0}var f3,K_t=G(()=>{"use strict";s(oMe,"last"),f3=oMe});function lMe(e){return typeof e=="function"?e:Hp}var F7,Rj=G(()=>{"use strict";Eg(),s(lMe,"castFunction"),F7=lMe});function cMe(e,t){var r=ma(e)?cW:p3;return r(e,F7(t))}var It,uMe=G(()=>{"use strict";M7e(),O7(),Rj(),is(),s(cMe,"forEach"),It=cMe}),Q_t=G(()=>{"use strict";uMe()});function hMe(e,t){var r=[];return p3(e,function(i,n,a){t(i,n,a)&&r.push(i)}),r}var dMe,Z_t=G(()=>{"use strict";O7(),s(hMe,"baseFilter"),dMe=hMe});function pMe(e,t){var r=ma(e)?qW:dMe;return r(e,gd(t,3))}var Xu,J_t=G(()=>{"use strict";b$e(),Z_t(),ef(),is(),s(pMe,"filter"),Xu=pMe});function fMe(e){return function(t,r,i){var n=Object(t);if(!Gu(t)){var a=gd(r,3);t=ll(t),r=s(function(l){return a(n[l],l,n)},"predicate")}var o=e(t,r,i);return o>-1?n[a?t[o]:o]:void 0}}var gMe,e7t=G(()=>{"use strict";ef(),fd(),Kp(),s(fMe,"createFind"),gMe=fMe});function mMe(e,t,r){var i=e==null?0:e.length;if(!i)return-1;var n=r==null?0:P_e(r);return n<0&&(n=yMe(i+n,0)),uW(e,gd(t,3),n)}var yMe,vMe,t7t=G(()=>{"use strict";N7e(),ef(),u6t(),yMe=Math.max,s(mMe,"findIndex"),vMe=mMe}),bMe,z7,r7t=G(()=>{"use strict";e7t(),t7t(),bMe=gMe(vMe),z7=bMe});function xMe(e,t){var r=-1,i=Gu(e)?Array(e.length):[];return p3(e,function(n,a,o){i[++r]=t(n,a,o)}),i}var $j,TMe=G(()=>{"use strict";O7(),fd(),s(xMe,"baseMap"),$j=xMe});function kMe(e,t){var r=ma(e)?wg:$j;return r(e,gd(t,3))}var ns,i7t=G(()=>{"use strict";e7(),ef(),TMe(),is(),s(kMe,"map"),ns=kMe});function CMe(e,t){return e==null?e:D7(e,F7(t),Qp)}var wMe,n7t=G(()=>{"use strict";Cj(),Rj(),Lg(),s(CMe,"forIn"),wMe=CMe});function EMe(e,t){return e&&N7(e,F7(t))}var SMe,a7t=G(()=>{"use strict";wj(),Rj(),s(EMe,"forOwn"),SMe=EMe});function _Me(e,t){return e>t}var AMe,s7t=G(()=>{"use strict";s(_Me,"baseGt"),AMe=_Me});function RMe(e,t){return e!=null&&LMe.call(e,t)}var $Me,LMe,IMe,o7t=G(()=>{"use strict";$Me=Object.prototype,LMe=$Me.hasOwnProperty,s(RMe,"baseHas"),IMe=RMe});function MMe(e,t){return e!=null&&xj(e,t,IMe)}var Lj,l7t=G(()=>{"use strict";o7t(),CIe(),s(MMe,"has"),Lj=MMe});function DMe(e){return typeof e=="string"||!ma(e)&&rc(e)&&dd(e)==NMe}var NMe,OMe,c7t=G(()=>{"use strict";Cg(),is(),Hu(),NMe="[object String]",s(DMe,"isString"),OMe=DMe});function BMe(e,t){return wg(t,function(r){return e[r]})}var PMe,u7t=G(()=>{"use strict";e7(),s(BMe,"baseValues"),PMe=BMe});function FMe(e){return e==null?[]:PMe(e,ll(e))}var Ku,h7t=G(()=>{"use strict";u7t(),Kp(),s(FMe,"values"),Ku=FMe});function zMe(e){if(e==null)return!0;if(Gu(e)&&(ma(e)||typeof e=="string"||typeof e.splice=="function"||Rg(e)||t3(e)||Ag(e)))return!e.length;var t=Jp(e);if(t==qMe||t==WMe)return!e.size;if(Q4(e))return!f7(e).length;for(var r in e)if(VMe.call(e,r))return!1;return!0}var qMe,WMe,jMe,VMe,Ij,d7t=G(()=>{"use strict";wW(),E2(),Z4(),is(),fd(),J4(),u7(),p7(),qMe="[object Map]",WMe="[object Set]",jMe=Object.prototype,VMe=jMe.hasOwnProperty,s(zMe,"isEmpty"),Ij=zMe});function UMe(e){return e===void 0}var Us,p7t=G(()=>{"use strict";s(UMe,"isUndefined"),Us=UMe});function YMe(e,t){return e{"use strict";s(YMe,"baseLt"),Mj=YMe});function GMe(e,t){var r={};return t=gd(t,3),N7(e,function(i,n,a){H4(r,n,t(i,n,a))}),r}var g3,f7t=G(()=>{"use strict";a7(),wj(),ef(),s(GMe,"mapValues"),g3=GMe});function XMe(e,t,r){for(var i=-1,n=e.length;++i{"use strict";m2(),s(XMe,"baseExtremum"),q7=XMe});function KMe(e){return e&&e.length?q7(e,Hp,AMe):void 0}var tf,g7t=G(()=>{"use strict";Dj(),s7t(),Eg(),s(KMe,"max"),tf=KMe}),QMe,W7,m7t=G(()=>{"use strict";G_t(),E6t(),QMe=oAe(function(e,t,r){nMe(e,t,r)}),W7=QMe});function ZMe(e){return e&&e.length?q7(e,Hp,Mj):void 0}var S2,y7t=G(()=>{"use strict";Dj(),HMe(),Eg(),s(ZMe,"min"),S2=ZMe});function JMe(e,t){return e&&e.length?q7(e,gd(t,2),Mj):void 0}var j7,v7t=G(()=>{"use strict";Dj(),ef(),HMe(),s(JMe,"minBy"),j7=JMe});function eDe(e,t,r,i){if(!ol(e))return e;t=s3(t,e);for(var n=-1,a=t.length,o=a-1,l=e;l!=null&&++n{"use strict";s7(),T7(),n7(),qc(),o3(),s(eDe,"baseSet"),tDe=eDe});function rDe(e,t,r){for(var i=-1,n=t.length,a={};++i{"use strict";k7(),b7t(),T7(),s(rDe,"basePickBy"),iDe=rDe});function nDe(e,t){var r=e.length;for(e.sort(t);r--;)e[r]=e[r].value;return e}var aDe,T7t=G(()=>{"use strict";s(nDe,"baseSortBy"),aDe=nDe});function sDe(e,t){if(e!==t){var r=e!==void 0,i=e===null,n=e===e,a=Yp(e),o=t!==void 0,l=t===null,u=t===t,h=Yp(t);if(!l&&!h&&!a&&e>t||a&&o&&u&&!l&&!h||i&&o&&u||!r&&u||!n)return 1;if(!i&&!a&&!h&&e{"use strict";m2(),s(sDe,"compareAscending"),oDe=sDe});function lDe(e,t,r){for(var i=-1,n=e.criteria,a=t.criteria,o=n.length,l=r.length;++i=l)return u;var h=r[i];return u*(h=="desc"?-1:1)}}return e.index-t.index}var cDe,C7t=G(()=>{"use strict";k7t(),s(lDe,"compareMultiple"),cDe=lDe});function uDe(e,t,r){t.length?t=wg(t,function(a){return ma(a)?function(o){return l3(o,a.length===1?a[0]:a)}:a}):t=[Hp];var i=-1;t=wg(t,e3(gd));var n=$j(e,function(a,o,l){var u=wg(t,function(h){return h(a)});return{criteria:u,index:++i,value:a}});return aDe(n,function(a,o){return cDe(a,o,r)})}var hDe,w7t=G(()=>{"use strict";e7(),k7(),ef(),TMe(),T7t(),h7(),C7t(),Eg(),is(),s(uDe,"baseOrderBy"),hDe=uDe}),dDe,pDe,E7t=G(()=>{"use strict";LIe(),dDe=kj("length"),pDe=dDe});function fDe(e){for(var t=qj.lastIndex=0;qj.test(e);)++t;return t}var Nj,gDe,mDe,yDe,vDe,bDe,xDe,V7,U7,TDe,Oj,Bj,Pj,kDe,Fj,zj,CDe,wDe,EDe,qj,SDe,S7t=G(()=>{"use strict";Nj="\\ud800-\\udfff",gDe="\\u0300-\\u036f",mDe="\\ufe20-\\ufe2f",yDe="\\u20d0-\\u20ff",vDe=gDe+mDe+yDe,bDe="\\ufe0e\\ufe0f",xDe="["+Nj+"]",V7="["+vDe+"]",U7="\\ud83c[\\udffb-\\udfff]",TDe="(?:"+V7+"|"+U7+")",Oj="[^"+Nj+"]",Bj="(?:\\ud83c[\\udde6-\\uddff]){2}",Pj="[\\ud800-\\udbff][\\udc00-\\udfff]",kDe="\\u200d",Fj=TDe+"?",zj="["+bDe+"]?",CDe="(?:"+kDe+"(?:"+[Oj,Bj,Pj].join("|")+")"+zj+Fj+")*",wDe=zj+Fj+CDe,EDe="(?:"+[Oj+V7+"?",V7,Bj,Pj,xDe].join("|")+")",qj=RegExp(U7+"(?="+U7+")|"+EDe+wDe,"g"),s(fDe,"unicodeSize"),SDe=fDe});function _De(e){return QRe(e)?SDe(e):pDe(e)}var ADe,_7t=G(()=>{"use strict";E7t(),i_t(),S7t(),s(_De,"stringSize"),ADe=_De});function RDe(e,t){return iDe(e,t,function(r,i){return Tj(e,i)})}var $De,A7t=G(()=>{"use strict";x7t(),EIe(),s(RDe,"basePick"),$De=RDe}),LDe,m3,R7t=G(()=>{"use strict";A7t(),t_t(),LDe=MRe(function(e,t){return e==null?{}:$De(e,t)}),m3=LDe});function IDe(e,t,r,i){for(var n=-1,a=DDe(MDe((t-e)/(r||1)),0),o=Array(a);a--;)o[i?a:++n]=e,e+=r;return o}var MDe,DDe,NDe,$7t=G(()=>{"use strict";MDe=Math.ceil,DDe=Math.max,s(IDe,"baseRange"),NDe=IDe});function ODe(e){return function(t,r,i){return i&&typeof i!="number"&&x2(t,r,i)&&(r=i=void 0),t=V4(t),r===void 0?(r=t,t=0):r=V4(r),i=i===void 0?t{"use strict";$7t(),c7(),O_e(),s(ODe,"createRange"),BDe=ODe}),PDe,Fg,I7t=G(()=>{"use strict";L7t(),PDe=BDe(),Fg=PDe});function FDe(e,t,r,i,n){return n(e,function(a,o,l){r=i?(i=!1,a):t(r,a,o,l)}),r}var zDe,M7t=G(()=>{"use strict";s(FDe,"baseReduce"),zDe=FDe});function qDe(e,t,r){var i=ma(e)?JRe:zDe,n=arguments.length<3;return i(e,gd(t,4),r,n,p3)}var _2,D7t=G(()=>{"use strict";n_t(),O7(),ef(),M7t(),is(),s(qDe,"reduce"),_2=qDe});function WDe(e){if(e==null)return 0;if(Gu(e))return OMe(e)?ADe(e):e.length;var t=Jp(e);return t==jDe||t==VDe?e.size:f7(e).length}var jDe,VDe,UDe,N7t=G(()=>{"use strict";wW(),E2(),fd(),c7t(),_7t(),jDe="[object Map]",VDe="[object Set]",s(WDe,"size"),UDe=WDe}),YDe,A2,O7t=G(()=>{"use strict";MW(),w7t(),o7(),c7(),YDe=K4(function(e,t){if(e==null)return[];var r=t.length;return r>1&&x2(e,t[0],t[1])?t=[]:r>2&&x2(t[0],t[1],t[2])&&(t=[t[0]]),hDe(e,w7(t,1),[])}),A2=YDe}),HDe,GDe,XDe,B7t=G(()=>{"use strict";q$e(),v6t(),hj(),HDe=1/0,GDe=Pg&&1/I7(new Pg([,-0]))[1]==HDe?function(e){return new Pg(e)}:m7e,XDe=GDe});function KDe(e,t,r){var i=-1,n=j7e,a=e.length,o=!0,l=[],u=l;if(r)o=!1,n=sMe;else if(a>=QDe){var h=t?null:XDe(e);if(h)return I7(h);o=!1,n=cj,u=new lj}else u=t?[]:l;e:for(;++i{"use strict";E9e(),w6t(),X_t(),R9e(),B7t(),hj(),QDe=200,s(KDe,"baseUniq"),ZDe=KDe}),JDe,eNe,F7t=G(()=>{"use strict";MW(),o7(),P7t(),QIe(),JDe=K4(function(e){return ZDe(w7(e,1,_j,!0))}),eNe=JDe});function tNe(e){var t=++rNe;return RW(e)+t}var rNe,Y7,z7t=G(()=>{"use strict";xRe(),rNe=0,s(tNe,"uniqueId"),Y7=tNe});function iNe(e,t,r){for(var i=-1,n=e.length,a=t.length,o={};++i{"use strict";s(iNe,"baseZipObject"),nNe=iNe});function aNe(e,t){return nNe(e||[],t||[],G4)}var sNe,W7t=G(()=>{"use strict";s7(),q7t(),s(aNe,"zipObject"),sNe=aNe}),gn=G(()=>{"use strict";__t(),E7e(),U_t(),Q_t(),J_t(),r7t(),LRe(),uMe(),n7t(),a7t(),l7t(),is(),d7t(),t7(),p7t(),Kp(),K_t(),i7t(),f7t(),g7t(),m7t(),y7t(),v7t(),V_t(),R7t(),I7t(),D7t(),N7t(),O7t(),F7t(),z7t(),h7t(),W7t()});function Wj(e,t){e[t]?e[t]++:e[t]=1}function jj(e,t){--e[t]||delete e[t]}function R2(e,t,r,i){var n=""+t,a=""+r;if(!e&&n>a){var o=n;n=a,a=o}return n+Vj+a+Vj+(Us(i)?lNe:i)}function oNe(e,t,r,i){var n=""+t,a=""+r;if(!e&&n>a){var o=n;n=a,a=o}var l={v:n,w:a};return i&&(l.name=i),l}function H7(e,t){return R2(e,t.v,t.w,t.name)}var lNe,rf,Vj,lo,cNe=G(()=>{"use strict";var e;gn(),lNe="\0",rf="\0",Vj="",lo=(e=class{constructor(r={}){this._isDirected=Object.prototype.hasOwnProperty.call(r,"directed")?r.directed:!0,this._isMultigraph=Object.prototype.hasOwnProperty.call(r,"multigraph")?r.multigraph:!1,this._isCompound=Object.prototype.hasOwnProperty.call(r,"compound")?r.compound:!1,this._label=void 0,this._defaultNodeLabelFn=pd(void 0),this._defaultEdgeLabelFn=pd(void 0),this._nodes={},this._isCompound&&(this._parent={},this._children={},this._children[rf]={}),this._in={},this._preds={},this._out={},this._sucs={},this._edgeObjs={},this._edgeLabels={}}isDirected(){return this._isDirected}isMultigraph(){return this._isMultigraph}isCompound(){return this._isCompound}setGraph(r){return this._label=r,this}graph(){return this._label}setDefaultNodeLabel(r){return y2(r)||(r=pd(r)),this._defaultNodeLabelFn=r,this}nodeCount(){return this._nodeCount}nodes(){return ll(this._nodes)}sources(){var r=this;return Xu(this.nodes(),function(i){return Ij(r._in[i])})}sinks(){var r=this;return Xu(this.nodes(),function(i){return Ij(r._out[i])})}setNodes(r,i){var n=arguments,a=this;return It(r,function(o){n.length>1?a.setNode(o,i):a.setNode(o)}),this}setNode(r,i){return Object.prototype.hasOwnProperty.call(this._nodes,r)?(arguments.length>1&&(this._nodes[r]=i),this):(this._nodes[r]=arguments.length>1?i:this._defaultNodeLabelFn(r),this._isCompound&&(this._parent[r]=rf,this._children[r]={},this._children[rf][r]=!0),this._in[r]={},this._preds[r]={},this._out[r]={},this._sucs[r]={},++this._nodeCount,this)}node(r){return this._nodes[r]}hasNode(r){return Object.prototype.hasOwnProperty.call(this._nodes,r)}removeNode(r){if(Object.prototype.hasOwnProperty.call(this._nodes,r)){var i=s(n=>this.removeEdge(this._edgeObjs[n]),"removeEdge");delete this._nodes[r],this._isCompound&&(this._removeFromParentsChildList(r),delete this._parent[r],It(this.children(r),n=>{this.setParent(n)}),delete this._children[r]),It(ll(this._in[r]),i),delete this._in[r],delete this._preds[r],It(ll(this._out[r]),i),delete this._out[r],delete this._sucs[r],--this._nodeCount}return this}setParent(r,i){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(Us(i))i=rf;else{i+="";for(var n=i;!Us(n);n=this.parent(n))if(n===r)throw new Error("Setting "+i+" as parent of "+r+" would create a cycle");this.setNode(i)}return this.setNode(r),this._removeFromParentsChildList(r),this._parent[r]=i,this._children[i][r]=!0,this}_removeFromParentsChildList(r){delete this._children[this._parent[r]][r]}parent(r){if(this._isCompound){var i=this._parent[r];if(i!==rf)return i}}children(r){if(Us(r)&&(r=rf),this._isCompound){var i=this._children[r];if(i)return ll(i)}else{if(r===rf)return this.nodes();if(this.hasNode(r))return[]}}predecessors(r){var i=this._preds[r];if(i)return ll(i)}successors(r){var i=this._sucs[r];if(i)return ll(i)}neighbors(r){var i=this.predecessors(r);if(i)return eNe(i,this.successors(r))}isLeaf(r){var i;return this.isDirected()?i=this.successors(r):i=this.neighbors(r),i.length===0}filterNodes(r){var i=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});i.setGraph(this.graph());var n=this;It(this._nodes,function(l,u){r(u)&&i.setNode(u,l)}),It(this._edgeObjs,function(l){i.hasNode(l.v)&&i.hasNode(l.w)&&i.setEdge(l,n.edge(l))});var a={};function o(l){var u=n.parent(l);return u===void 0||i.hasNode(u)?(a[l]=u,u):u in a?a[u]:o(u)}return s(o,"findParent"),this._isCompound&&It(i.nodes(),function(l){i.setParent(l,o(l))}),i}setDefaultEdgeLabel(r){return y2(r)||(r=pd(r)),this._defaultEdgeLabelFn=r,this}edgeCount(){return this._edgeCount}edges(){return Ku(this._edgeObjs)}setPath(r,i){var n=this,a=arguments;return _2(r,function(o,l){return a.length>1?n.setEdge(o,l,i):n.setEdge(o,l),l}),this}setEdge(){var r,i,n,a,o=!1,l=arguments[0];typeof l=="object"&&l!==null&&"v"in l?(r=l.v,i=l.w,n=l.name,arguments.length===2&&(a=arguments[1],o=!0)):(r=l,i=arguments[1],n=arguments[3],arguments.length>2&&(a=arguments[2],o=!0)),r=""+r,i=""+i,Us(n)||(n=""+n);var u=R2(this._isDirected,r,i,n);if(Object.prototype.hasOwnProperty.call(this._edgeLabels,u))return o&&(this._edgeLabels[u]=a),this;if(!Us(n)&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(r),this.setNode(i),this._edgeLabels[u]=o?a:this._defaultEdgeLabelFn(r,i,n);var h=oNe(this._isDirected,r,i,n);return r=h.v,i=h.w,Object.freeze(h),this._edgeObjs[u]=h,Wj(this._preds[i],r),Wj(this._sucs[r],i),this._in[i][u]=h,this._out[r][u]=h,this._edgeCount++,this}edge(r,i,n){var a=arguments.length===1?H7(this._isDirected,arguments[0]):R2(this._isDirected,r,i,n);return this._edgeLabels[a]}hasEdge(r,i,n){var a=arguments.length===1?H7(this._isDirected,arguments[0]):R2(this._isDirected,r,i,n);return Object.prototype.hasOwnProperty.call(this._edgeLabels,a)}removeEdge(r,i,n){var a=arguments.length===1?H7(this._isDirected,arguments[0]):R2(this._isDirected,r,i,n),o=this._edgeObjs[a];return o&&(r=o.v,i=o.w,delete this._edgeLabels[a],delete this._edgeObjs[a],jj(this._preds[i],r),jj(this._sucs[r],i),delete this._in[i][a],delete this._out[r][a],this._edgeCount--),this}inEdges(r,i){var n=this._in[r];if(n){var a=Ku(n);return i?Xu(a,function(o){return o.v===i}):a}}outEdges(r,i){var n=this._out[r];if(n){var a=Ku(n);return i?Xu(a,function(o){return o.w===i}):a}}nodeEdges(r,i){var n=this.inEdges(r,i);if(n)return n.concat(this.outEdges(r,i))}},s(e,"Graph"),e),lo.prototype._nodeCount=0,lo.prototype._edgeCount=0,s(Wj,"incrementOrInitEntry"),s(jj,"decrementOrRemoveEntry"),s(R2,"edgeArgsToId"),s(oNe,"edgeArgsToObj"),s(H7,"edgeObjToId")}),ic=G(()=>{"use strict";cNe()});function Uj(e){e._prev._next=e._next,e._next._prev=e._prev,delete e._next,delete e._prev}function uNe(e,t){if(e!=="_next"&&e!=="_prev")return t}var hNe,j7t=G(()=>{"use strict";var e;hNe=(e=class{constructor(){var r={};r._next=r._prev=r,this._sentinel=r}dequeue(){var r=this._sentinel,i=r._prev;if(i!==r)return Uj(i),i}enqueue(r){var i=this._sentinel;r._prev&&r._next&&Uj(r),r._next=i._next,i._next._prev=r,i._next=r,r._prev=i}toString(){for(var r=[],i=this._sentinel,n=i._prev;n!==i;)r.push(JSON.stringify(n,uNe)),n=n._prev;return"["+r.join(", ")+"]"}},s(e,"List"),e),s(Uj,"unlink"),s(uNe,"filterOutLinks")});function dNe(e,t){if(e.nodeCount()<=1)return[];var r=fNe(e,t||gNe),i=pNe(r.graph,r.buckets,r.zeroIdx);return Ng(ns(i,function(n){return e.outEdges(n.v,n.w)}))}function pNe(e,t,r){for(var i=[],n=t[t.length-1],a=t[0],o;e.nodeCount();){for(;o=a.dequeue();)G7(e,t,r,o);for(;o=n.dequeue();)G7(e,t,r,o);if(e.nodeCount()){for(var l=t.length-2;l>0;--l)if(o=t[l].dequeue(),o){i=i.concat(G7(e,t,r,o,!0));break}}}return i}function G7(e,t,r,i,n){var a=n?[]:void 0;return It(e.inEdges(i.v),function(o){var l=e.edge(o),u=e.node(o.v);n&&a.push({v:o.v,w:o.w}),u.out-=l,X7(t,r,u)}),It(e.outEdges(i.v),function(o){var l=e.edge(o),u=o.w,h=e.node(u);h.in-=l,X7(t,r,h)}),e.removeNode(i.v),a}function fNe(e,t){var r=new lo,i=0,n=0;It(e.nodes(),function(l){r.setNode(l,{v:l,in:0,out:0})}),It(e.edges(),function(l){var u=r.edge(l.v,l.w)||0,h=t(l),d=u+h;r.setEdge(l.v,l.w,d),n=Math.max(n,r.node(l.v).out+=h),i=Math.max(i,r.node(l.w).in+=h)});var a=Fg(n+i+3).map(function(){return new hNe}),o=i+1;return It(r.nodes(),function(l){X7(a,o,r.node(l))}),{graph:r,buckets:a,zeroIdx:o}}function X7(e,t,r){r.out?r.in?e[r.out-r.in+t].enqueue(r):e[e.length-1].enqueue(r):e[0].enqueue(r)}var gNe,V7t=G(()=>{"use strict";gn(),ic(),j7t(),gNe=pd(1),s(dNe,"greedyFAS"),s(pNe,"doGreedyFAS"),s(G7,"removeNode"),s(fNe,"buildState"),s(X7,"assignBucket")});function mNe(e){var t=e.graph().acyclicer==="greedy"?dNe(e,r(e)):yNe(e);It(t,function(i){var n=e.edge(i);e.removeEdge(i),n.forwardName=i.name,n.reversed=!0,e.setEdge(i.w,i.v,n,Y7("rev"))});function r(i){return function(n){return i.edge(n).weight}}s(r,"weightFn")}function yNe(e){var t=[],r={},i={};function n(a){Object.prototype.hasOwnProperty.call(i,a)||(i[a]=!0,r[a]=!0,It(e.outEdges(a),function(o){Object.prototype.hasOwnProperty.call(r,o.w)?t.push(o):n(o.w)}),delete r[a])}return s(n,"dfs"),It(e.nodes(),n),t}function vNe(e){It(e.edges(),function(t){var r=e.edge(t);if(r.reversed){e.removeEdge(t);var i=r.forwardName;delete r.reversed,delete r.forwardName,e.setEdge(t.w,t.v,r,i)}})}var bNe=G(()=>{"use strict";gn(),V7t(),s(mNe,"run"),s(yNe,"dfsFAS"),s(vNe,"undo")});function zg(e,t,r,i){var n;do n=Y7(i);while(e.hasNode(n));return r.dummy=t,e.setNode(n,r),n}function xNe(e){var t=new lo().setGraph(e.graph());return It(e.nodes(),function(r){t.setNode(r,e.node(r))}),It(e.edges(),function(r){var i=t.edge(r.v,r.w)||{weight:0,minlen:1},n=e.edge(r);t.setEdge(r.v,r.w,{weight:i.weight+n.weight,minlen:Math.max(i.minlen,n.minlen)})}),t}function Yj(e){var t=new lo({multigraph:e.isMultigraph()}).setGraph(e.graph());return It(e.nodes(),function(r){e.children(r).length||t.setNode(r,e.node(r))}),It(e.edges(),function(r){t.setEdge(r,e.edge(r))}),t}function Hj(e,t){var r=e.x,i=e.y,n=t.x-r,a=t.y-i,o=e.width/2,l=e.height/2;if(!n&&!a)throw new Error("Not possible to find intersection inside of the rectangle");var u,h;return Math.abs(a)*o>Math.abs(n)*l?(a<0&&(l=-l),u=l*n/a,h=l):(n<0&&(o=-o),u=o,h=o*a/n),{x:r+u,y:i+h}}function y3(e){var t=ns(Fg(Xj(e)+1),function(){return[]});return It(e.nodes(),function(r){var i=e.node(r),n=i.rank;Us(n)||(t[n][i.order]=r)}),t}function TNe(e){var t=S2(ns(e.nodes(),function(r){return e.node(r).rank}));It(e.nodes(),function(r){var i=e.node(r);Lj(i,"rank")&&(i.rank-=t)})}function kNe(e){var t=S2(ns(e.nodes(),function(a){return e.node(a).rank})),r=[];It(e.nodes(),function(a){var o=e.node(a).rank-t;r[o]||(r[o]=[]),r[o].push(a)});var i=0,n=e.graph().nodeRankFactor;It(r,function(a,o){Us(a)&&o%n!==0?--i:i&&It(a,function(l){e.node(l).rank+=i})})}function Gj(e,t,r,i){var n={width:0,height:0};return arguments.length>=4&&(n.rank=r,n.order=i),zg(e,"border",n,t)}function Xj(e){return tf(ns(e.nodes(),function(t){var r=e.node(t).rank;if(!Us(r))return r}))}function CNe(e,t){var r={lhs:[],rhs:[]};return It(e,function(i){t(i)?r.lhs.push(i):r.rhs.push(i)}),r}function wNe(e,t){var r=Ej();try{return t()}finally{console.log(e+" time: "+(Ej()-r)+"ms")}}function ENe(e,t){return t()}var md=G(()=>{"use strict";gn(),ic(),s(zg,"addDummyNode"),s(xNe,"simplify"),s(Yj,"asNonCompoundGraph"),s(Hj,"intersectRect"),s(y3,"buildLayerMatrix"),s(TNe,"normalizeRanks"),s(kNe,"removeEmptyRanks"),s(Gj,"addBorderNode"),s(Xj,"maxRank"),s(CNe,"partition"),s(wNe,"time"),s(ENe,"notime")});function SNe(e){function t(r){var i=e.children(r),n=e.node(r);if(i.length&&It(i,t),Object.prototype.hasOwnProperty.call(n,"minRank")){n.borderLeft=[],n.borderRight=[];for(var a=n.minRank,o=n.maxRank+1;a{"use strict";gn(),md(),s(SNe,"addBorderSegments"),s(Kj,"addBorderNode")});function _Ne(e){var t=e.graph().rankdir.toLowerCase();(t==="lr"||t==="rl")&&Qj(e)}function ANe(e){var t=e.graph().rankdir.toLowerCase();(t==="bt"||t==="rl")&&RNe(e),(t==="lr"||t==="rl")&&($Ne(e),Qj(e))}function Qj(e){It(e.nodes(),function(t){Zj(e.node(t))}),It(e.edges(),function(t){Zj(e.edge(t))})}function Zj(e){var t=e.width;e.width=e.height,e.height=t}function RNe(e){It(e.nodes(),function(t){K7(e.node(t))}),It(e.edges(),function(t){var r=e.edge(t);It(r.points,K7),Object.prototype.hasOwnProperty.call(r,"y")&&K7(r)})}function K7(e){e.y=-e.y}function $Ne(e){It(e.nodes(),function(t){Q7(e.node(t))}),It(e.edges(),function(t){var r=e.edge(t);It(r.points,Q7),Object.prototype.hasOwnProperty.call(r,"x")&&Q7(r)})}function Q7(e){var t=e.x;e.x=e.y,e.y=t}var Y7t=G(()=>{"use strict";gn(),s(_Ne,"adjust"),s(ANe,"undo"),s(Qj,"swapWidthHeight"),s(Zj,"swapWidthHeightOne"),s(RNe,"reverseY"),s(K7,"reverseYOne"),s($Ne,"swapXY"),s(Q7,"swapXYOne")});function LNe(e){e.graph().dummyChains=[],It(e.edges(),function(t){INe(e,t)})}function INe(e,t){var r=t.v,i=e.node(r).rank,n=t.w,a=e.node(n).rank,o=t.name,l=e.edge(t),u=l.labelRank;if(a!==i+1){e.removeEdge(t);var h=void 0,d,p;for(p=0,++i;i{"use strict";gn(),md(),s(LNe,"run"),s(INe,"normalizeEdge"),s(MNe,"undo")});function Z7(e){var t={};function r(i){var n=e.node(i);if(Object.prototype.hasOwnProperty.call(t,i))return n.rank;t[i]=!0;var a=S2(ns(e.outEdges(i),function(o){return r(o.w)-e.edge(o).minlen}));return(a===Number.POSITIVE_INFINITY||a===void 0||a===null)&&(a=0),n.rank=a}s(r,"dfs"),It(e.sources(),r)}function $2(e,t){return e.node(t.w).rank-e.node(t.v).rank-e.edge(t).minlen}var Jj=G(()=>{"use strict";gn(),s(Z7,"longestPath"),s($2,"slack")});function eV(e){var t=new lo({directed:!1}),r=e.nodes()[0],i=e.nodeCount();t.setNode(r,{});for(var n,a;NNe(t,e){"use strict";gn(),ic(),Jj(),s(eV,"feasibleTree"),s(NNe,"tightTree"),s(ONe,"findMinSlackEdge"),s(BNe,"shiftRanks")}),H7t=G(()=>{"use strict"}),FNe=G(()=>{"use strict"}),G7t,zNe=G(()=>{"use strict";gn(),FNe(),G7t=pd(1)}),X7t=G(()=>{"use strict";zNe()}),qNe=G(()=>{"use strict"}),K7t=G(()=>{"use strict";qNe()}),Q7t,Z7t=G(()=>{"use strict";gn(),Q7t=pd(1)});function WNe(e){var t={},r={},i=[];function n(a){if(Object.prototype.hasOwnProperty.call(r,a))throw new v3;Object.prototype.hasOwnProperty.call(t,a)||(r[a]=!0,t[a]=!0,It(e.predecessors(a),n),delete r[a],i.push(a))}if(s(n,"visit"),It(e.sinks(),n),UDe(t)!==e.nodeCount())throw new v3;return i}function v3(){}var jNe=G(()=>{"use strict";gn(),WNe.CycleException=v3,s(WNe,"topsort"),s(v3,"CycleException"),v3.prototype=new Error}),J7t=G(()=>{"use strict";jNe()});function tV(e,t,r){ma(t)||(t=[t]);var i=(e.isDirected()?e.successors:e.neighbors).bind(e),n=[],a={};return It(t,function(o){if(!e.hasNode(o))throw new Error("Graph does not have node: "+o);rV(e,o,r==="post",a,i,n)}),n}function rV(e,t,r,i,n,a){Object.prototype.hasOwnProperty.call(i,t)||(i[t]=!0,r||a.push(t),It(n(t),function(o){rV(e,o,r,i,n,a)}),r&&a.push(t))}var VNe=G(()=>{"use strict";gn(),s(tV,"dfs"),s(rV,"doDfs")});function UNe(e,t){return tV(e,t,"post")}var eAt=G(()=>{"use strict";VNe(),s(UNe,"postorder")});function YNe(e,t){return tV(e,t,"pre")}var tAt=G(()=>{"use strict";VNe(),s(YNe,"preorder")}),rAt=G(()=>{"use strict";FNe(),cNe()}),iAt=G(()=>{"use strict";H7t(),zNe(),X7t(),K7t(),Z7t(),J7t(),eAt(),tAt(),rAt(),qNe(),jNe()});function nf(e){e=xNe(e),Z7(e);var t=eV(e);eA(t),J7(t,e);for(var r,i;r=aV(t);)i=sV(t,e,r),oV(t,e,r,i)}function J7(e,t){var r=UNe(e,e.nodes());r=r.slice(0,r.length-1),It(r,function(i){HNe(e,t,i)})}function HNe(e,t,r){var i=e.node(r),n=i.parent;e.edge(r,n).cutvalue=iV(e,t,r)}function iV(e,t,r){var i=e.node(r),n=i.parent,a=!0,o=t.edge(r,n),l=0;return o||(a=!1,o=t.edge(n,r)),l=o.weight,It(t.nodeEdges(r),function(u){var h=u.v===r,d=h?u.w:u.v;if(d!==n){var p=h===a,f=t.edge(u).weight;if(l+=p?f:-f,XNe(e,r,d)){var g=e.edge(r,d).cutvalue;l+=p?-g:g}}}),l}function eA(e,t){arguments.length<2&&(t=e.nodes()[0]),nV(e,{},1,t)}function nV(e,t,r,i,n){var a=r,o=e.node(i);return t[i]=!0,It(e.neighbors(i),function(l){Object.prototype.hasOwnProperty.call(t,l)||(r=nV(e,t,r,l,i))}),o.low=a,o.lim=r++,n?o.parent=n:delete o.parent,r}function aV(e){return z7(e.edges(),function(t){return e.edge(t).cutvalue<0})}function sV(e,t,r){var i=r.v,n=r.w;t.hasEdge(i,n)||(i=r.w,n=r.v);var a=e.node(i),o=e.node(n),l=a,u=!1;a.lim>o.lim&&(l=o,u=!0);var h=Xu(t.edges(),function(d){return u===lV(e,e.node(d.v),l)&&u!==lV(e,e.node(d.w),l)});return j7(h,function(d){return $2(t,d)})}function oV(e,t,r,i){var n=r.v,a=r.w;e.removeEdge(n,a),e.setEdge(i.v,i.w,{}),eA(e),J7(e,t),GNe(e,t)}function GNe(e,t){var r=z7(e.nodes(),function(n){return!t.node(n).parent}),i=YNe(e,r);i=i.slice(1),It(i,function(n){var a=e.node(n).parent,o=t.edge(n,a),l=!1;o||(o=t.edge(a,n),l=!0),t.node(n).rank=t.node(a).rank+(l?o.minlen:-o.minlen)})}function XNe(e,t,r){return e.hasEdge(t,r)}function lV(e,t,r){return r.low<=t.lim&&t.lim<=r.lim}var nAt=G(()=>{"use strict";gn(),iAt(),md(),PNe(),Jj(),nf.initLowLimValues=eA,nf.initCutValues=J7,nf.calcCutValue=iV,nf.leaveEdge=aV,nf.enterEdge=sV,nf.exchangeEdges=oV,s(nf,"networkSimplex"),s(J7,"initCutValues"),s(HNe,"assignCutValue"),s(iV,"calcCutValue"),s(eA,"initLowLimValues"),s(nV,"dfsAssignLowLim"),s(aV,"leaveEdge"),s(sV,"enterEdge"),s(oV,"exchangeEdges"),s(GNe,"updateRanks"),s(XNe,"isTreeEdge"),s(lV,"isDescendant")});function KNe(e){switch(e.graph().ranker){case"network-simplex":cV(e);break;case"tight-tree":QNe(e);break;case"longest-path":ZNe(e);break;default:cV(e)}}function QNe(e){Z7(e),eV(e)}function cV(e){nf(e)}var ZNe,JNe=G(()=>{"use strict";PNe(),nAt(),Jj(),s(KNe,"rank"),ZNe=Z7,s(QNe,"tightTreeRanker"),s(cV,"networkSimplexRanker")});function eOe(e){var t=zg(e,"root",{},"_root"),r=tOe(e),i=tf(Ku(r))-1,n=2*i+1;e.graph().nestingRoot=t,It(e.edges(),function(o){e.edge(o).minlen*=n});var a=rOe(e)+1;It(e.children(),function(o){uV(e,t,n,a,i,r,o)}),e.graph().nodeRankFactor=n}function uV(e,t,r,i,n,a,o){var l=e.children(o);if(!l.length){o!==t&&e.setEdge(t,o,{weight:0,minlen:r});return}var u=Gj(e,"_bt"),h=Gj(e,"_bb"),d=e.node(o);e.setParent(u,o),d.borderTop=u,e.setParent(h,o),d.borderBottom=h,It(l,function(p){uV(e,t,r,i,n,a,p);var f=e.node(p),g=f.borderTop?f.borderTop:p,m=f.borderBottom?f.borderBottom:p,y=f.borderTop?i:2*i,k=g!==m?1:n-a[o]+1;e.setEdge(u,g,{weight:y,minlen:k,nestingEdge:!0}),e.setEdge(m,h,{weight:y,minlen:k,nestingEdge:!0})}),e.parent(o)||e.setEdge(t,u,{weight:0,minlen:n+a[o]})}function tOe(e){var t={};function r(i,n){var a=e.children(i);a&&a.length&&It(a,function(o){r(o,n+1)}),t[i]=n}return s(r,"dfs"),It(e.children(),function(i){r(i,1)}),t}function rOe(e){return _2(e.edges(),function(t,r){return t+e.edge(r).weight},0)}function iOe(e){var t=e.graph();e.removeNode(t.nestingRoot),delete t.nestingRoot,It(e.edges(),function(r){var i=e.edge(r);i.nestingEdge&&e.removeEdge(r)})}var aAt=G(()=>{"use strict";gn(),md(),s(eOe,"run"),s(uV,"dfs"),s(tOe,"treeDepths"),s(rOe,"sumWeights"),s(iOe,"cleanup")});function nOe(e,t,r){var i={},n;It(r,function(a){for(var o=e.parent(a),l,u;o;){if(l=e.parent(o),l?(u=i[l],i[l]=o):(u=n,n=o),u&&u!==o){t.setEdge(u,o);return}o=l}})}var sAt=G(()=>{"use strict";gn(),s(nOe,"addSubgraphConstraints")});function aOe(e,t,r){var i=sOe(e),n=new lo({compound:!0}).setGraph({root:i}).setDefaultNodeLabel(function(a){return e.node(a)});return It(e.nodes(),function(a){var o=e.node(a),l=e.parent(a);(o.rank===t||o.minRank<=t&&t<=o.maxRank)&&(n.setNode(a),n.setParent(a,l||i),It(e[r](a),function(u){var h=u.v===a?u.w:u.v,d=n.edge(h,a),p=Us(d)?0:d.weight;n.setEdge(h,a,{weight:e.edge(u).weight+p})}),Object.prototype.hasOwnProperty.call(o,"minRank")&&n.setNode(a,{borderLeft:o.borderLeft[t],borderRight:o.borderRight[t]}))}),n}function sOe(e){for(var t;e.hasNode(t=Y7("_root")););return t}var oAt=G(()=>{"use strict";gn(),ic(),s(aOe,"buildLayerGraph"),s(sOe,"createRootNode")});function oOe(e,t){for(var r=0,i=1;i0;)d%2&&(p+=l[d+1]),d=d-1>>1,l[d]+=h.weight;u+=h.weight*p})),u}var lAt=G(()=>{"use strict";gn(),s(oOe,"crossCount"),s(lOe,"twoLayerCrossCount")});function cOe(e){var t={},r=Xu(e.nodes(),function(l){return!e.children(l).length}),i=tf(ns(r,function(l){return e.node(l).rank})),n=ns(Fg(i+1),function(){return[]});function a(l){if(!Lj(t,l)){t[l]=!0;var u=e.node(l);n[u.rank].push(l),It(e.successors(l),a)}}s(a,"dfs");var o=A2(r,function(l){return e.node(l).rank});return It(o,a),n}var cAt=G(()=>{"use strict";gn(),s(cOe,"initOrder")});function uOe(e,t){return ns(t,function(r){var i=e.inEdges(r);if(i.length){var n=_2(i,function(a,o){var l=e.edge(o),u=e.node(o.v);return{sum:a.sum+l.weight*u.order,weight:a.weight+l.weight}},{sum:0,weight:0});return{v:r,barycenter:n.sum/n.weight,weight:n.weight}}else return{v:r}})}var uAt=G(()=>{"use strict";gn(),s(uOe,"barycenter")});function hOe(e,t){var r={};It(e,function(n,a){var o=r[n.v]={indegree:0,in:[],out:[],vs:[n.v],i:a};Us(n.barycenter)||(o.barycenter=n.barycenter,o.weight=n.weight)}),It(t.edges(),function(n){var a=r[n.v],o=r[n.w];!Us(a)&&!Us(o)&&(o.indegree++,a.out.push(r[n.w]))});var i=Xu(r,function(n){return!n.indegree});return dOe(i)}function dOe(e){var t=[];function r(a){return function(o){o.merged||(Us(o.barycenter)||Us(a.barycenter)||o.barycenter>=a.barycenter)&&pOe(a,o)}}s(r,"handleIn");function i(a){return function(o){o.in.push(a),--o.indegree===0&&e.push(o)}}for(s(i,"handleOut");e.length;){var n=e.pop();t.push(n),It(n.in.reverse(),r(n)),It(n.out,i(n))}return ns(Xu(t,function(a){return!a.merged}),function(a){return m3(a,["vs","i","barycenter","weight"])})}function pOe(e,t){var r=0,i=0;e.weight&&(r+=e.barycenter*e.weight,i+=e.weight),t.weight&&(r+=t.barycenter*t.weight,i+=t.weight),e.vs=t.vs.concat(e.vs),e.barycenter=r/i,e.weight=i,e.i=Math.min(t.i,e.i),t.merged=!0}var hAt=G(()=>{"use strict";gn(),s(hOe,"resolveConflicts"),s(dOe,"doResolveConflicts"),s(pOe,"mergeEntries")});function fOe(e,t){var r=CNe(e,function(d){return Object.prototype.hasOwnProperty.call(d,"barycenter")}),i=r.lhs,n=A2(r.rhs,function(d){return-d.i}),a=[],o=0,l=0,u=0;i.sort(gOe(!!t)),u=hV(a,n,u),It(i,function(d){u+=d.vs.length,a.push(d.vs),o+=d.barycenter*d.weight,l+=d.weight,u=hV(a,n,u)});var h={vs:Ng(a)};return l&&(h.barycenter=o/l,h.weight=l),h}function hV(e,t,r){for(var i;t.length&&(i=f3(t)).i<=r;)t.pop(),e.push(i.vs),r++;return r}function gOe(e){return function(t,r){return t.barycenterr.barycenter?1:e?r.i-t.i:t.i-r.i}}var dAt=G(()=>{"use strict";gn(),md(),s(fOe,"sort"),s(hV,"consumeUnsortable"),s(gOe,"compareWithBias")});function dV(e,t,r,i){var n=e.children(t),a=e.node(t),o=a?a.borderLeft:void 0,l=a?a.borderRight:void 0,u={};o&&(n=Xu(n,function(m){return m!==o&&m!==l}));var h=uOe(e,n);It(h,function(m){if(e.children(m.v).length){var y=dV(e,m.v,r,i);u[m.v]=y,Object.prototype.hasOwnProperty.call(y,"barycenter")&&yOe(m,y)}});var d=hOe(h,r);mOe(d,u);var p=fOe(d,i);if(o&&(p.vs=Ng([o,p.vs,l]),e.predecessors(o).length)){var f=e.node(e.predecessors(o)[0]),g=e.node(e.predecessors(l)[0]);Object.prototype.hasOwnProperty.call(p,"barycenter")||(p.barycenter=0,p.weight=0),p.barycenter=(p.barycenter*p.weight+f.order+g.order)/(p.weight+2),p.weight+=2}return p}function mOe(e,t){It(e,function(r){r.vs=Ng(r.vs.map(function(i){return t[i]?t[i].vs:i}))})}function yOe(e,t){Us(e.barycenter)?(e.barycenter=t.barycenter,e.weight=t.weight):(e.barycenter=(e.barycenter*e.weight+t.barycenter*t.weight)/(e.weight+t.weight),e.weight+=t.weight)}var pAt=G(()=>{"use strict";gn(),uAt(),hAt(),dAt(),s(dV,"sortSubgraph"),s(mOe,"expandSubgraphs"),s(yOe,"mergeBarycenters")});function vOe(e){var t=Xj(e),r=pV(e,Fg(1,t+1),"inEdges"),i=pV(e,Fg(t-1,-1,-1),"outEdges"),n=cOe(e);fV(e,n);for(var a=Number.POSITIVE_INFINITY,o,l=0,u=0;u<4;++l,++u){bOe(l%2?r:i,l%4>=2),n=y3(e);var h=oOe(e,n);h{"use strict";gn(),ic(),md(),sAt(),oAt(),lAt(),cAt(),pAt(),s(vOe,"order"),s(pV,"buildLayerGraphs"),s(bOe,"sweepLayerGraphs"),s(fV,"assignOrder")});function xOe(e){var t=kOe(e);It(e.graph().dummyChains,function(r){for(var i=e.node(r),n=i.edgeObj,a=TOe(e,t,n.v,n.w),o=a.path,l=a.lca,u=0,h=o[u],d=!0;r!==n.w;){if(i=e.node(r),d){for(;(h=o[u])!==l&&e.node(h).maxRanko||l>t[u].lim));for(h=u,u=i;(u=e.parent(u))!==h;)a.push(u);return{path:n.concat(a.reverse()),lca:h}}function kOe(e){var t={},r=0;function i(n){var a=r;It(e.children(n),i),t[n]={low:a,lim:r++}}return s(i,"dfs"),It(e.children(),i),t}var gAt=G(()=>{"use strict";gn(),s(xOe,"parentDummyChains"),s(TOe,"findPath"),s(kOe,"postorder")});function COe(e,t){var r={};function i(n,a){var o=0,l=0,u=n.length,h=f3(a);return It(a,function(d,p){var f=EOe(e,d),g=f?e.node(f).order:u;(f||d===h)&&(It(a.slice(l,p+1),function(m){It(e.predecessors(m),function(y){var k=e.node(y),C=k.order;(Ch)&&gV(r,f,d)})})}s(i,"scan");function n(a,o){var l=-1,u,h=0;return It(o,function(d,p){if(e.node(d).dummy==="border"){var f=e.predecessors(d);f.length&&(u=e.node(f[0]).order,i(o,h,p,l,u),h=p,l=u)}i(o,h,o.length,u,a.length)}),o}return s(n,"visitLayer"),_2(t,n),r}function EOe(e,t){if(e.node(t).dummy)return z7(e.predecessors(t),function(r){return e.node(r).dummy})}function gV(e,t,r){if(t>r){var i=t;t=r,r=i}Object.prototype.hasOwnProperty.call(e,t)||Object.defineProperty(e,t,{enumerable:!0,configurable:!0,value:{},writable:!0});var n=e[t];Object.defineProperty(n,r,{enumerable:!0,configurable:!0,value:!0,writable:!0})}function SOe(e,t,r){if(t>r){var i=t;t=r,r=i}return!!e[t]&&Object.prototype.hasOwnProperty.call(e[t],r)}function _Oe(e,t,r,i){var n={},a={},o={};return It(t,function(l){It(l,function(u,h){n[u]=u,a[u]=u,o[u]=h})}),It(t,function(l){var u=-1;It(l,function(h){var d=i(h);if(d.length){d=A2(d,function(y){return o[y]});for(var p=(d.length-1)/2,f=Math.floor(p),g=Math.ceil(p);f<=g;++f){var m=d[f];a[h]===h&&u{"use strict";gn(),ic(),md(),s(COe,"findType1Conflicts"),s(wOe,"findType2Conflicts"),s(EOe,"findOtherInnerSegmentNode"),s(gV,"addConflict"),s(SOe,"hasConflict"),s(_Oe,"verticalAlignment"),s(AOe,"horizontalCompaction"),s(ROe,"buildBlockGraph"),s($Oe,"findSmallestWidthAlignment"),s(LOe,"alignCoordinates"),s(IOe,"balance"),s(MOe,"positionX"),s(DOe,"sep"),s(NOe,"width")});function OOe(e){e=Yj(e),BOe(e),SMe(MOe(e),function(t,r){e.node(r).x=t})}function BOe(e){var t=y3(e),r=e.graph().ranksep,i=0;It(t,function(n){var a=tf(ns(n,function(o){return e.node(o).height}));It(n,function(o){e.node(o).y=i+a/2}),i+=a+r})}var yAt=G(()=>{"use strict";gn(),md(),mAt(),s(OOe,"position"),s(BOe,"positionY")});function mV(e,t){var r=t&&t.debugTiming?wNe:ENe;r("layout",()=>{var i=r(" buildLayoutGraph",()=>zOe(e));r(" runLayout",()=>POe(i,r)),r(" updateInputGraph",()=>FOe(e,i))})}function POe(e,t){t(" makeSpaceForEdgeLabels",()=>qOe(e)),t(" removeSelfEdges",()=>KOe(e)),t(" acyclic",()=>mNe(e)),t(" nestingGraph.run",()=>eOe(e)),t(" rank",()=>KNe(Yj(e))),t(" injectEdgeLabelProxies",()=>WOe(e)),t(" removeEmptyRanks",()=>kNe(e)),t(" nestingGraph.cleanup",()=>iOe(e)),t(" normalizeRanks",()=>TNe(e)),t(" assignRankMinMax",()=>jOe(e)),t(" removeEdgeLabelProxies",()=>VOe(e)),t(" normalize.run",()=>LNe(e)),t(" parentDummyChains",()=>xOe(e)),t(" addBorderSegments",()=>SNe(e)),t(" order",()=>vOe(e)),t(" insertSelfEdges",()=>QOe(e)),t(" adjustCoordinateSystem",()=>_Ne(e)),t(" position",()=>OOe(e)),t(" positionSelfEdges",()=>ZOe(e)),t(" removeBorderNodes",()=>XOe(e)),t(" normalize.undo",()=>MNe(e)),t(" fixupEdgeLabelCoords",()=>HOe(e)),t(" undoCoordinateSystem",()=>ANe(e)),t(" translateGraph",()=>UOe(e)),t(" assignNodeIntersects",()=>YOe(e)),t(" reversePoints",()=>GOe(e)),t(" acyclic.undo",()=>vNe(e))}function FOe(e,t){It(e.nodes(),function(r){var i=e.node(r),n=t.node(r);i&&(i.x=n.x,i.y=n.y,t.children(r).length&&(i.width=n.width,i.height=n.height))}),It(e.edges(),function(r){var i=e.edge(r),n=t.edge(r);i.points=n.points,Object.prototype.hasOwnProperty.call(n,"x")&&(i.x=n.x,i.y=n.y)}),e.graph().width=t.graph().width,e.graph().height=t.graph().height}function zOe(e){var t=new lo({multigraph:!0,compound:!0}),r=rA(e.graph());return t.setGraph(W7({},eBe,tA(r,JOe),m3(r,tBe))),It(e.nodes(),function(i){var n=rA(e.node(i));t.setNode(i,HIe(tA(n,rBe),iBe)),t.setParent(i,e.parent(i))}),It(e.edges(),function(i){var n=rA(e.edge(i));t.setEdge(i,W7({},aBe,tA(n,nBe),m3(n,sBe)))}),t}function qOe(e){var t=e.graph();t.ranksep/=2,It(e.edges(),function(r){var i=e.edge(r);i.minlen*=2,i.labelpos.toLowerCase()!=="c"&&(t.rankdir==="TB"||t.rankdir==="BT"?i.width+=i.labeloffset:i.height+=i.labeloffset)})}function WOe(e){It(e.edges(),function(t){var r=e.edge(t);if(r.width&&r.height){var i=e.node(t.v),n=e.node(t.w),a={rank:(n.rank-i.rank)/2+i.rank,e:t};zg(e,"edge-proxy",a,"_ep")}})}function jOe(e){var t=0;It(e.nodes(),function(r){var i=e.node(r);i.borderTop&&(i.minRank=e.node(i.borderTop).rank,i.maxRank=e.node(i.borderBottom).rank,t=tf(t,i.maxRank))}),e.graph().maxRank=t}function VOe(e){It(e.nodes(),function(t){var r=e.node(t);r.dummy==="edge-proxy"&&(e.edge(r.e).labelRank=r.rank,e.removeNode(t))})}function UOe(e){var t=Number.POSITIVE_INFINITY,r=0,i=Number.POSITIVE_INFINITY,n=0,a=e.graph(),o=a.marginx||0,l=a.marginy||0;function u(h){var d=h.x,p=h.y,f=h.width,g=h.height;t=Math.min(t,d-f/2),r=Math.max(r,d+f/2),i=Math.min(i,p-g/2),n=Math.max(n,p+g/2)}s(u,"getExtremes"),It(e.nodes(),function(h){u(e.node(h))}),It(e.edges(),function(h){var d=e.edge(h);Object.prototype.hasOwnProperty.call(d,"x")&&u(d)}),t-=o,i-=l,It(e.nodes(),function(h){var d=e.node(h);d.x-=t,d.y-=i}),It(e.edges(),function(h){var d=e.edge(h);It(d.points,function(p){p.x-=t,p.y-=i}),Object.prototype.hasOwnProperty.call(d,"x")&&(d.x-=t),Object.prototype.hasOwnProperty.call(d,"y")&&(d.y-=i)}),a.width=r-t+o,a.height=n-i+l}function YOe(e){It(e.edges(),function(t){var r=e.edge(t),i=e.node(t.v),n=e.node(t.w),a,o;r.points?(a=r.points[0],o=r.points[r.points.length-1]):(r.points=[],a=n,o=i),r.points.unshift(Hj(i,a)),r.points.push(Hj(n,o))})}function HOe(e){It(e.edges(),function(t){var r=e.edge(t);if(Object.prototype.hasOwnProperty.call(r,"x"))switch((r.labelpos==="l"||r.labelpos==="r")&&(r.width-=r.labeloffset),r.labelpos){case"l":r.x-=r.width/2+r.labeloffset;break;case"r":r.x+=r.width/2+r.labeloffset;break}})}function GOe(e){It(e.edges(),function(t){var r=e.edge(t);r.reversed&&r.points.reverse()})}function XOe(e){It(e.nodes(),function(t){if(e.children(t).length){var r=e.node(t),i=e.node(r.borderTop),n=e.node(r.borderBottom),a=e.node(f3(r.borderLeft)),o=e.node(f3(r.borderRight));r.width=Math.abs(o.x-a.x),r.height=Math.abs(n.y-i.y),r.x=a.x+r.width/2,r.y=i.y+r.height/2}}),It(e.nodes(),function(t){e.node(t).dummy==="border"&&e.removeNode(t)})}function KOe(e){It(e.edges(),function(t){if(t.v===t.w){var r=e.node(t.v);r.selfEdges||(r.selfEdges=[]),r.selfEdges.push({e:t,label:e.edge(t)}),e.removeEdge(t)}})}function QOe(e){var t=y3(e);It(t,function(r){var i=0;It(r,function(n,a){var o=e.node(n);o.order=a+i,It(o.selfEdges,function(l){zg(e,"selfedge",{width:l.label.width,height:l.label.height,rank:o.rank,order:a+ ++i,e:l.e,label:l.label},"_se")}),delete o.selfEdges})})}function ZOe(e){It(e.nodes(),function(t){var r=e.node(t);if(r.dummy==="selfedge"){var i=e.node(r.e.v),n=i.x+i.width/2,a=i.y,o=r.x-n,l=i.height/2;e.setEdge(r.e,r.label),e.removeNode(t),r.label.points=[{x:n+2*o/3,y:a-l},{x:n+5*o/6,y:a-l},{x:n+o,y:a},{x:n+5*o/6,y:a+l},{x:n+2*o/3,y:a+l}],r.label.x=r.x,r.label.y=r.y}})}function tA(e,t){return g3(m3(e,t),Number)}function rA(e){var t={};return It(e,function(r,i){t[i.toLowerCase()]=r}),t}var JOe,eBe,tBe,rBe,iBe,nBe,aBe,sBe,vAt=G(()=>{"use strict";gn(),ic(),U7t(),Y7t(),bNe(),DNe(),JNe(),aAt(),fAt(),gAt(),yAt(),md(),s(mV,"layout"),s(POe,"runLayout"),s(FOe,"updateInputGraph"),JOe=["nodesep","edgesep","ranksep","marginx","marginy"],eBe={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},tBe=["acyclicer","ranker","rankdir","align"],rBe=["width","height"],iBe={width:0,height:0},nBe=["minlen","weight","width","height","labeloffset"],aBe={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},sBe=["labelpos"],s(zOe,"buildLayoutGraph"),s(qOe,"makeSpaceForEdgeLabels"),s(WOe,"injectEdgeLabelProxies"),s(jOe,"assignRankMinMax"),s(VOe,"removeEdgeLabelProxies"),s(UOe,"translateGraph"),s(YOe,"assignNodeIntersects"),s(HOe,"fixupEdgeLabelCoords"),s(GOe,"reversePointsForReversedEdges"),s(XOe,"removeBorderNodes"),s(KOe,"removeSelfEdges"),s(QOe,"insertSelfEdges"),s(ZOe,"positionSelfEdges"),s(tA,"selectNumberAttrs"),s(rA,"canonicalize")}),oBe=G(()=>{"use strict";bNe(),vAt(),DNe(),JNe()}),bAt=G(()=>{"use strict"}),lBe={};Mr(lBe,{captureNodeSizes:()=>dBe,shouldCaptureSizes:()=>cBe});function yV(){if(!(typeof globalThis>"u"))return globalThis}function cBe(){return!!yV()?.mermaidCaptureSizes}function uBe(){return typeof location>"u"?"browser-dev":`${location.pathname}${location.search}`}function hBe(e,t){let r=yV();if(!r)return;let i=t.node(),n=((i&&"ownerSVGElement"in i?i.ownerSVGElement:null)??i)?.id??"(unknown)";r.mermaidCapturedSizes??=[];let a={svgId:n,sizes:e};r.mermaidCapturedSizes.push(a),r.mermaidLastCapturedSizes=a}function dBe(e,t){let r=[];for(let i of t.nodes)i.isGroup||r.push({id:i.id,width:i.width??0,height:i.height??0});r.length!==0&&hBe({metadata:{captureVersion:1,capturedAt:new Date().toISOString(),capturedFrom:uBe()},nodes:r},e)}var xAt=G(()=>{"use strict";bAt(),s(yV,"getCaptureGlobal"),s(cBe,"shouldCaptureSizes"),s(uBe,"capturedFromLocation"),s(hBe,"emitCapturedSizes"),s(dBe,"captureNodeSizes")});function vV(e,{edgePathsClass:t="edges edgePaths"}={}){let r=e.insert("g").attr("class","root"),i=r.insert("g").attr("class","clusters"),n=r.insert("g").attr("class",t),a=r.insert("g").attr("class","edgeLabels"),o=r.insert("g").attr("class","nodes");return{clusters:i,edgePaths:n,edgeLabels:a,nodes:o,rootGroups:r}}async function pBe(e,t){if(t.label){let{shapeSvg:r,bbox:i}=await Nr(e,t);t.labelBBox={width:i.width,height:i.height},r.remove()}else t.labelBBox={width:0,height:0}}async function bV(e,t,r){let i=await d2(e,t,r),n=i.node()?.getBBox()??{width:0,height:0};return t.width=n.width,t.height=n.height,i}async function fBe(e,t){let r=new lo({multigraph:!0,compound:!0}),i=[...t.edges],n=it(),a=vV(e),{edgeLabels:o,nodes:l}=a,u=new Map,h=e.node()!=null;await Promise.all(t.nodes.map(async d=>{if(d.isGroup)h&&await pBe(l,d),r.setNode(d.id,{...d});else{if(h){let p=await bV(l,d,{config:n,dir:d.dir});u.set(d.id,p)}r.setNode(d.id,{...d})}}));for(let d of i)h&&Hq(d)&&await Tg(o,d),r.setEdge(d.start,d.end,{...d},d.id),t.edges.some(p=>p.id===d.id)||t.edges.push(d);if(globalThis.mermaidCaptureSizes){let{captureNodeSizes:d}=await Promise.resolve().then(()=>(xAt(),lBe));d(e,t)}return{graph:r,groups:a,nodeElements:u}}var gBe=G(()=>{"use strict";ic(),Er(),W4(),p2(),lr(),s(vV,"createLayoutElementGroups"),s(pBe,"measureGroupLabel"),s(bV,"insertMeasuredNode"),s(fBe,"createGraphWithElements")}),hi,af,xV,mBe,L2,yBe,TV,kV,vBe,qg,CV,bBe,wV,EV,iA,SV,xBe,TBe=G(()=>{"use strict";Nt(),ic(),hi=new Map,af=new Map,xV=new Map,mBe=s(()=>{af.clear(),xV.clear(),hi.clear()},"clear"),L2=s((e,t)=>{let r=af.get(t)||[];return Ee.trace("In isDescendant",t," ",e," = ",r.includes(e)),r.includes(e)},"isDescendant"),yBe=s((e,t)=>{let r=af.get(t)||[];return Ee.info("Descendants of ",t," is ",r),Ee.info("Edge is ",e),e.v===t||e.w===t?!1:r?r.includes(e.v)||L2(e.v,t)||L2(e.w,t)||r.includes(e.w):(Ee.debug("Tilt, ",t,",not in descendants"),!1)},"edgeInCluster"),TV=s((e,t,r,i)=>{Ee.debug("Copying children of ",e,"root",i,"data",t.node(e),i);let n=t.children(e)||[];e!==i&&n.push(e),Ee.debug("Copying (nodes) clusterId",e,"nodes",n),n.forEach(a=>{if(t.children(a).length>0)TV(a,t,r,i);else{let o=t.node(a);Ee.info("cp ",a," to ",i," with parent ",e),r.setNode(a,o),i!==t.parent(a)&&(Ee.debug("Setting parent",a,t.parent(a)),r.setParent(a,t.parent(a))),e!==i&&a!==e?(Ee.debug("Setting parent",a,e),r.setParent(a,e)):(Ee.info("In copy ",e,"root",i,"data",t.node(e),i),Ee.debug("Not Setting parent for node=",a,"cluster!==rootId",e!==i,"node!==clusterId",a!==e));let l=t.edges(a);Ee.debug("Copying Edges",l),l.forEach(u=>{Ee.info("Edge",u);let h=t.edge(u.v,u.w,u.name);Ee.info("Edge data",h,i);try{yBe(u,i)?(Ee.info("Copying as ",u.v,u.w,h,u.name),r.setEdge(u.v,u.w,h,u.name),Ee.info("newGraph edges ",r.edges(),r.edge(r.edges()[0]))):Ee.info("Skipping copy of edge ",u.v,"-->",u.w," rootId: ",i," clusterId:",e)}catch(d){Ee.error(d)}})}Ee.debug("Removing node",a),t.removeNode(a)})},"copy"),kV=s((e,t)=>{let r=t.children(e),i=[...r];for(let n of r)xV.set(n,e),i=[...i,...kV(n,t)];return i},"extractDescendants"),vBe=s((e,t,r)=>{let i=e.edges().filter(l=>l.v===t||l.w===t),n=e.edges().filter(l=>l.v===r||l.w===r),a=i.map(l=>({v:l.v===t?r:l.v,w:l.w===t?t:l.w})),o=n.map(l=>({v:l.v,w:l.w}));return a.filter(l=>o.some(u=>l.v===u.v&&l.w===u.w))},"findCommonEdges"),qg=s((e,t,r)=>{let i=t.children(e);if(Ee.trace("Searching children of id ",e,i),i.length<1)return e;let n;for(let a of i){let o=qg(a,t,r),l=vBe(t,r,o);if(o)if(l.length>0)n=o;else return o}return n},"findNonClusterChild"),CV=s(e=>!hi.has(e)||!hi.get(e).externalConnections?e:hi.has(e)?hi.get(e).id:e,"getAnchorId"),bBe=s((e,t)=>{if(!e||t>10){Ee.debug("Opting out, no graph ");return}else Ee.debug("Opting in, graph ");e.nodes().forEach(function(r){e.children(r).length>0&&(Ee.debug("Cluster identified",r," Replacement id in edges: ",qg(r,e,r)),af.set(r,kV(r,e)),hi.set(r,{id:qg(r,e,r),clusterData:e.node(r)}))}),e.nodes().forEach(function(r){let i=e.children(r),n=e.edges();i.length>0?(Ee.debug("Cluster identified",r,af),n.forEach(a=>{let o=L2(a.v,r),l=L2(a.w,r);o^l&&(Ee.debug("Edge: ",a," leaves cluster ",r),Ee.debug("Descendants of XXX ",r,": ",af.get(r)),hi.get(r).externalConnections=!0)})):Ee.debug("Not a cluster ",r,af)});for(let r of hi.keys()){let i=hi.get(r).id,n=e.parent(i);n!==r&&hi.has(n)&&!hi.get(n).externalConnections&&(hi.get(r).id=n);let a=e.edges().some(o=>o.v===r);if(i&&hi.get(r)?.externalConnections&&a&&SV(e,i,r)){let o=xBe(e,r,e.parent(i));o&&(hi.get(r).id=o)}}e.edges().forEach(function(r){let i=e.edge(r);Ee.debug("Edge "+r.v+" -> "+r.w+": "+JSON.stringify(r)),Ee.debug("Edge "+r.v+" -> "+r.w+": "+JSON.stringify(e.edge(r)));let n=r.v,a=r.w;if(Ee.debug("Fix XXX",hi,"ids:",r.v,r.w,"Translating: ",hi.get(r.v)," --- ",hi.get(r.w)),hi.get(r.v)||hi.get(r.w)){if(Ee.debug("Fixing and trying - removing XXX",r.v,r.w,r.name),n=CV(r.v),a=CV(r.w),e.removeEdge(r.v,r.w,r.name),n!==r.v){let o=e.parent(n);hi.get(o).externalConnections=!0,i.fromCluster=r.v}if(a!==r.w){let o=e.parent(a);hi.get(o).externalConnections=!0,i.toCluster=r.w}Ee.debug("Fix Replacing with XXX",n,a,r.name),e.setEdge(n,a,i,r.name)}}),wV(e,0),Ee.trace(hi)},"adjustClustersAndEdges"),wV=s((e,t)=>{if(t>10){Ee.error("Bailing out");return}let r=e.nodes(),i=!1;for(let n of r){let a=e.children(n);i=i||a.length>0}if(!i){Ee.debug("Done, no node has children",e.nodes());return}Ee.debug("Nodes = ",r,t);for(let n of r)if(Ee.debug("Extracting node",n,hi,hi.has(n)&&!hi.get(n).externalConnections,!e.parent(n),e.node(n),e.children("D")," Depth ",t),!hi.has(n))Ee.debug("Not a cluster",n,t);else if(!hi.get(n).externalConnections&&e.children(n)&&e.children(n).length>0){Ee.debug("Cluster without external connections, without a parent and with children",n,t);let a=e.graph().rankdir==="TB"?"LR":"TB";hi.get(n)?.clusterData?.dir&&(a=hi.get(n).clusterData.dir,Ee.debug("Fixing dir",hi.get(n).clusterData.dir,a));let o=new lo({multigraph:!0,compound:!0}).setGraph({rankdir:a,nodesep:50,ranksep:50,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}});TV(n,e,o,n),e.setNode(n,{clusterNode:!0,id:n,clusterData:hi.get(n).clusterData,label:hi.get(n).label,graph:o})}else Ee.debug("Cluster ** ",n," **not meeting the criteria !externalConnections:",!hi.get(n).externalConnections," no parent: ",!e.parent(n)," children ",e.children(n)&&e.children(n).length>0,e.children("D"),t),Ee.debug(hi);r=e.nodes(),Ee.debug("New list of nodes",r);for(let n of r){let a=e.node(n);Ee.debug(" Now next level",n,a),a?.clusterNode&&wV(a.graph,t+1)}},"extractor"),EV=s((e,t)=>{if(t.length===0)return[];let r=Object.assign([],t);return t.forEach(i=>{let n=e.children(i),a=EV(e,n);r=[...r,...a]}),r},"sorter"),iA=s(e=>EV(e,e.children()),"sortNodesByHierarchy"),SV=s((e,t,r)=>{let i=e.parent(t);for(;i&&i!==r;){let n=hi.get(i);if(n&&!n.externalConnections)return!0;i=e.parent(i)}return!1},"isNodeInExtractableCluster"),xBe=s((e,t,r)=>{let i=e.children(t)??[];for(let n of i){if(n===r||L2(n,r))continue;let a=qg(n,e,t);if(a&&!SV(e,a,t))return a}return null},"findSafeAnchorNode")});function nA({prepareLayout:e,measureLayout:t,runLayoutCore:r,paintLayout:i,afterPaint:n,paintOptions:a}){let o=t??AV;return s(async function(l,u,h,d){let p=u.select("g");(h?.insertMarkers??J_)(p,l.markers,l.type,l.diagramId),_V();let f={element:p,helpers:h,options:d};f.preparedLayout=await e?.(l,f);let g=await o(l,f),m=await r(l,f),y={...f,measure:g};i?await i(l,y,m):await RV(l,y,a),await n?.(l,y,m)},"render")}function _V(){K6e(),b6e(),c6e(),mBe()}async function AV(e,{element:t}){return await fBe(t,e)}async function RV(e,t,r={}){let{measure:i}=t,{groups:n}=i;for(let o of r.getNodes?.(e,t)??e.nodes)r.skipNode?.(o,t)||await kBe(n,o,t,r);let a=wBe(e.nodes);for(let o of e.edges)EBe(o,r)||await SBe(n,o,a,e,r,t)}async function kBe(e,t,r,i){t.clusterNode?kg(t):CBe(t,r,i)?await P4(e.clusters,t):kg(t)}function CBe(e,t,r){return e.isGroup===!0&&(r.isCluster?.(e,t)??!0)}function wBe(e){let t=new Map;for(let r of e)r?.id&&t.set(r.id,r);return t}function EBe(e,t){return e.isLayoutOnly||!!t.skipEdge?.(e)}async function SBe(e,t,r,i,n,a){let o=q4(e.edgePaths,{...t},n.clusterDb??new Map,i.type,$V(t.start,t,r,a,n),$V(t.end,t,r,a,n),i.diagramId,_Be(t,n));Hq(t)&&(xg.has(t.id)||await Tg(e.edgeLabels,t),ABe(t,o))}function $V(e,t,r,i,n){return n.getEdgeNode?.(e,t,i)??(e?r.get(e)??{}:{})}function _Be(e,t){return typeof t.skipIntersect=="function"?t.skipIntersect(e):t.skipIntersect??!1}function ABe(e,t){let r=t?.updatedPath??t?.originalPath,i=Rr(),{subGraphTitleTotalMargin:n}=c2({flowchart:i.flowchart??{}});if(e.label){let a=xg.get(e.id),o=e.x,l=e.y;if(r){let u=ai.calcLabelPosition(r);Ee.debug("Moving label "+e.label+" from (",o,",",l,") to (",u.x,",",u.y,") abc88"),t?.updatedPath&&(o=u.x,l=u.y)}a.attr("transform",`translate(${o}, ${l+n/2})`)}if(e?.startLabelLeft){let a=ga.get(e.id).startLeft,o=e?.x,l=e?.y;if(r){let u=ai.calcTerminalLabelPosition(e.arrowTypeStart?10:0,"start_left",r);o=u.x,l=u.y}a.attr("transform",`translate(${o}, ${l})`)}if(e.startLabelRight){let a=ga.get(e.id).startRight,o=e.x,l=e.y;if(r){let u=ai.calcTerminalLabelPosition(e.arrowTypeStart?10:0,"start_right",r);o=u.x,l=u.y}a.attr("transform",`translate(${o}, ${l})`)}if(e.endLabelLeft){let a=ga.get(e.id).endLeft,o=e.x,l=e.y;if(r){let u=ai.calcTerminalLabelPosition(e.arrowTypeEnd?10:0,"end_left",r);o=u.x,l=u.y}a.attr("transform",`translate(${o}, ${l})`)}if(e.endLabelRight){let a=ga.get(e.id).endRight,o=e.x,l=e.y;if(r){let u=ai.calcTerminalLabelPosition(e.arrowTypeEnd?10:0,"end_right",r);o=u.x,l=u.y}a.attr("transform",`translate(${o}, ${l})`)}}var LV=G(()=>{"use strict";Nt(),g4(),Gr(),Ar(),K_(),gBe(),Q_(),W4(),Kq(),p2(),TBe(),s(nA,"createCommonLayoutRenderer"),s(_V,"clearLayoutRenderState"),s(AV,"defaultMeasureLayout"),s(RV,"paintLayoutData"),s(kBe,"paintLayoutNode"),s(CBe,"shouldPaintAsCluster"),s(wBe,"buildNodeLookup"),s(EBe,"shouldSkipPaintEdge"),s(SBe,"paintLayoutEdge"),s($V,"getRenderedNode"),s(_Be,"shouldSkipIntersect"),s(ABe,"positionRenderedEdgeLabel")}),RBe={};Mr(RBe,{applyDagreLayoutResult:()=>BV,getEdgesToRender:()=>aA,measureDagreLayout:()=>PV,prepareLayoutForDagre:()=>sA,render:()=>qBe,runDagreLayoutCore:()=>FV});var IV,MV,$Be,LBe,IBe,MBe,DBe,aA,DV,NV,NBe,OV,OBe,BV,BBe,PBe,sA,PV,FV,FBe,zBe,qBe,TAt=G(()=>{"use strict";oBe(),ic(),gBe(),LV(),g4(),lr(),TBe(),p2(),Q_(),W4(),Nt(),K_(),Er(),IV=s((e,t,r)=>Math.max(t,Math.min(r,e)),"clamp"),MV=s((e="TB")=>{switch(e){case"BT":return"bottom";case"LR":return"right";case"RL":return"left";default:return"top"}},"getDefaultSelfLoopSide"),$Be=s(e=>e==="flowchart"||e==="flowchart-v2"||e==="stateDiagram"||e==="er"||e==="classDiagram","shouldMergeSelfLoopSegments"),LBe=["x","y","width","height","labelBBox","intersect","calcIntersect","diff","clusterNode"],IBe=s((e,t,r,i,n)=>{let a=[],o=new Set;if(r.forEach(({start:d,end:p})=>{d!==i&&o.add(d),p!==i&&o.add(p)}),o.forEach(d=>{let p=e.node(d);typeof p?.x=="number"&&typeof p?.y=="number"&&a.push(p)}),a.length===0&&r.forEach(({edge:d})=>{(d.points??[]).forEach(p=>{typeof p?.x=="number"&&typeof p?.y=="number"&&a.push(p)})}),a.length===0)return MV(n);let l=a.reduce((d,p)=>({x:d.x+p.x/a.length,y:d.y+p.y/a.length}),{x:0,y:0}),u=l.x-t.x,h=l.y-t.y;return Math.abs(u)>Math.abs(h)?u>0?"right":"left":Math.abs(h)>0?h>0?"bottom":"top":MV(n)},"getSelfLoopSide"),MBe=s((e,t="top",r=0,i=0)=>{let n=e.x,a=e.y-r,o=e.width/2,l=e.height/2,u=Math.max(36,Math.min(100,e.width*.8)),h=IV(Math.max(i,e.width*.35),36,u),d=IV(Math.min(e.width,e.height)*.45,24,48);switch(t){case"bottom":{let p=a+l;return[{x:n-h/2,y:p},{x:n-h/2,y:p+d},{x:n+h/2,y:p+d},{x:n+h/2,y:p}]}case"right":{let p=n+o;return[{x:p,y:a-h/2},{x:p+d,y:a-h/2},{x:p+d,y:a+h/2},{x:p,y:a+h/2}]}case"left":{let p=n-o;return[{x:p,y:a-h/2},{x:p-d,y:a-h/2},{x:p-d,y:a+h/2},{x:p,y:a+h/2}]}default:{let p=a-l;return[{x:n-h/2,y:p},{x:n-h/2,y:p-d},{x:n+h/2,y:p-d},{x:n+h/2,y:p}]}}},"getSelfLoopPoints"),DBe=s((e,t,r="top",i=0,n={})=>{let a=e.x,o=e.y-i,l=n.width??0,u=n.height??0;switch(r){case"bottom":return{x:a,y:Math.max(...t.map(h=>h.y))+u/2+4};case"right":return{x:Math.max(...t.map(h=>h.x))+l/2+4,y:o};case"left":return{x:Math.min(...t.map(h=>h.x))-l/2-4,y:o};default:return{x:a,y:Math.min(...t.map(h=>h.y))-u/2-4}}},"getSelfLoopLabelPosition"),aA=s((e,t=0,{mergeSelfLoops:r=!0}={})=>{let i=new Map,n=[],a=e.graph()?.rankdir;return e.edges().forEach(o=>{let l=e.edge(o);if(r&&l.selfLoop){let u=l.selfLoop.id;i.has(u)||i.set(u,[]),i.get(u).push({edge:l,start:o.v,end:o.w})}else n.push({edge:l,start:o.v,end:o.w})}),i.forEach(o=>{if(o.length!==3){o.forEach(C=>n.push(C));return}o.sort((C,S)=>C.edge.selfLoop.order-S.edge.selfLoop.order);let[l,u,h]=o,d=l.edge.originalEdge??u.edge.originalEdge??h.edge.originalEdge??u.edge,p=e.node(d.start);if(!p){o.forEach(C=>n.push(C));return}let f={width:u.edge.width,height:u.edge.height},g=IBe(e,p,o,d.start,a),m=MBe(p,g,t,f.width??0),y=DBe(p,m,g,t,f),k={...u.edge,...d,id:d.id,points:m,start:d.start,end:d.end,x:y.x,y:y.y,width:f.width,height:f.height,labelStyle:u.edge.labelStyle,fromCluster:l.edge.fromCluster??u.edge.fromCluster??h.edge.fromCluster,toCluster:l.edge.toCluster??u.edge.toCluster??h.edge.toCluster};delete k.selfLoop,delete k.originalEdge,n.push({edge:k,start:k.start,end:k.end})}),n},"getEdgesToRender"),DV=s(async({element:e,graph:t,diagramType:r,id:i,parentCluster:n,siteConfig:a})=>{let o=t.graph().rankdir;Ee.trace("Dir in recursive render - dir:",o);let{clusters:l,edgePaths:u,edgeLabels:h,nodes:d,rootGroups:p}=vV(e,{edgePathsClass:"edgePaths"});t.nodes()?Ee.info("Recursive render XXX",t.nodes()):Ee.info("No nodes found for",t),t.edges().length>0&&Ee.info("Recursive edges",t.edge(t.edges()[0]));let f=$Be(r);await Promise.all(t.nodes().map(async function(m){let y=t.node(m);if(n!==void 0){let k=JSON.parse(JSON.stringify(n.clusterData));Ee.trace(`Setting data for parent cluster XXX + Node.id = `,m,` + data=`,k.height,` +Parent cluster`,n.height),t.setNode(n.id,k),t.parent(m)||(Ee.trace("Setting parent",m,n.id),t.setParent(m,n.id,k))}if(Ee.info("(Insert) Node XXX"+m+": "+JSON.stringify(t.node(m))),y?.clusterNode){Ee.info("Cluster identified XBX",m,y.width,t.node(m));let{ranksep:k,nodesep:C}=t.graph();y.graph.setGraph({...y.graph.graph(),ranksep:k+25,nodesep:C});let S=await PBe({element:d,graph:y.graph,diagramType:r,id:i,parentCluster:t.node(m),siteConfig:a}),E=S.elem;fr(y,E),y.diff=S.diff||0,Ee.info("New compound node after recursive render XAX",m,"width",y.width,"height",y.height),X6e(E,y)}else t.children(m).length>0?(Ee.trace("Cluster - the non recursive path XBX",m,y.id,y,y.width,"Graph:",t),Ee.trace(qg(y.id,t)),hi.set(y.id,{id:qg(y.id,t),node:y})):(Ee.trace("Node - the non recursive path XAX",m,d,t.node(m),o),await bV(d,t.node(m),{config:a,dir:o}))})),await s(async()=>{let m=t.edges().map(async function(y){let k=t.edge(y.v,y.w,y.name);if(Ee.info("Edge "+y.v+" -> "+y.w+": "+JSON.stringify(y)),Ee.info("Edge "+y.v+" -> "+y.w+": ",y," ",JSON.stringify(t.edge(y))),Ee.info("Fix",hi,"ids:",y.v,y.w,"Translating: ",hi.get(y.v),hi.get(y.w)),f&&k.selfLoop){if(k.selfLoop.order!==1)return;let C={...k.originalEdge,...k,id:k.selfLoop.id,startLabelLeft:k.originalEdge?.startLabelLeft??k.startLabelLeft,startLabelRight:k.originalEdge?.startLabelRight??k.startLabelRight,endLabelLeft:k.originalEdge?.endLabelLeft??k.endLabelLeft,endLabelRight:k.originalEdge?.endLabelRight??k.endLabelRight};await Tg(h,C),k.width=C.width,k.height=C.height,k.labelStyle=C.labelStyle;return}await Tg(h,k)});await Promise.all(m)},"processEdges")();let{subGraphTitleTotalMargin:g}=c2(a);return{elem:p,graph:t,groups:{clusters:l,edgePaths:u,edgeLabels:h,nodes:d,rootGroups:p},diagramType:r,id:i,mergeSelfLoops:f,subGraphTitleTotalMargin:g}},"measureDagreGraph"),NV=s(e=>{Ee.info("############################################# XXX"),Ee.info("### Layout ### XXX"),Ee.info("############################################# XXX"),mV(e)},"runDagreGraphLayout"),NBe=s((e,t,r)=>{let i=e.node(t);if(!i)return;let n={...i};return i?.clusterNode?n.y=(i.y??0)+r:e.children(t).length>0?n.height=(i.height??0)+r:n.y=(i.y??0)+r/2,n},"normalizeDagreNode"),OV=s((e,t)=>{LBe.forEach(r=>{t[r]!==void 0&&(e[r]=t[r])})},"applyDagreNodeLayout"),OBe=s((e,t,r,i)=>({...e,start:e.start??t,end:e.end??r,points:(e.points??[]).map(n=>({...n,y:typeof n.y=="number"?n.y+i:n.y}))}),"normalizeDagreEdge"),BV=s((e,t)=>{let{graph:r,mergeSelfLoops:i,subGraphTitleTotalMargin:n=0}=t,a=new Map(e.nodes.map(l=>[l.id,l]));iA(r).forEach(l=>{let u=NBe(r,l,n);if(!u)return;OV(r.node(l),u);let h=a.get(l);h&&OV(h,u)});let o=n/2;return e.edges=aA(r,o,{mergeSelfLoops:i}).map(({edge:l,start:u,end:h})=>OBe(l,u,h,o)),e},"applyDagreLayoutResult"),BBe=s(async({elem:e,graph:t,groups:{clusters:r,edgePaths:i},diagramType:n,id:a,mergeSelfLoops:o,subGraphTitleTotalMargin:l})=>{let u=0;await Promise.all(iA(t).map(async function(d){let p=t.node(d);if(Ee.info("Position XBX => "+d+": ("+p.x,","+p.y,") width: ",p.width," height: ",p.height),p?.clusterNode)p.y+=l,Ee.info("A tainted cluster node XBX1",d,p.id,p.width,p.height,p.x,p.y,t.parent(d)),hi.get(p.id).node=p,kg(p);else if(t.children(d).length>0){Ee.info("A pure cluster node XBX1",d,p.id,p.x,p.y,p.width,p.height,t.parent(d)),p.height+=l,t.node(p.parentId);let f=p?.padding/2||0,g=p?.labelBBox?.height||0,m=g-f||0;Ee.debug("OffsetY",m,"labelHeight",g,"halfPadding",f),await P4(r,p),hi.get(p.id).node=p}else{let f=t.node(p.parentId);p.y+=l/2,Ee.info("A regular node XBX1 - using the padding",p.id,"parent",p.parentId,p.width,p.height,p.x,p.y,"offsetY",p.offsetY,"parent",f,f?.offsetY,p),kg(p)}}));let h=l/2;return aA(t,h,{mergeSelfLoops:o}).forEach(function({edge:d,start:p,end:f}){Ee.info("Edge "+p+" -> "+f+": "+JSON.stringify(d),d),d.points.forEach(k=>k.y+=h);let g=t.node(p),m=t.node(f),y=q4(i,d,hi,n,g,m,a);Z_(d,y)}),t.nodes().forEach(function(d){let p=t.node(d);Ee.info(d,p.type,p.diff),p.isGroup&&(u=p.diff)}),Ee.warn("Returning from recursive render XAX",e,u),{elem:e,diff:u}},"paintDagreLayoutCore"),PBe=s(async e=>{let t=await DV(e);return NV(t.graph),await BBe(t)},"renderDagreSubgraph"),sA=s(e=>{let t=new lo({multigraph:!0,compound:!0}).setGraph({rankdir:e.direction,nodesep:e.config?.nodeSpacing||e.nodeSpacing||e.config?.flowchart?.nodeSpacing,ranksep:e.config?.rankSpacing||e.rankSpacing||e.config?.flowchart?.rankSpacing,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}});return e.nodes.forEach(r=>{t.setNode(r.id,{...r}),r.parentId&&t.setParent(r.id,r.parentId)}),Ee.debug("Edges:",e.edges),e.edges.forEach(r=>{if(r.start===r.end){let i=r.start,n=i+"---"+i+"---1",a=i+"---"+i+"---2",o=t.node(i);t.setNode(n,{domId:n,id:n,parentId:o.parentId,labelStyle:"",label:"",padding:0,shape:"labelRect",style:"",width:10,height:10}),t.setParent(n,o.parentId),t.setNode(a,{domId:a,id:a,parentId:o.parentId,labelStyle:"",padding:0,shape:"labelRect",label:"",style:"",width:10,height:10}),t.setParent(a,o.parentId);let l=structuredClone(r),u=structuredClone(r),h=structuredClone(r),d=structuredClone(r);u.originalEdge=l,u.selfLoop={id:l.id,order:0},h.originalEdge=l,h.selfLoop={id:l.id,order:1},d.originalEdge=l,d.selfLoop={id:l.id,order:2},u.label="",u.arrowTypeEnd="none",u.endLabelLeft="",u.endLabelRight="",u.startLabelLeft="",u.id=i+"-cyclic-special-1",h.startLabelRight="",h.startLabelLeft="",h.endLabelLeft="",h.endLabelRight="",h.arrowTypeStart="none",h.arrowTypeEnd="none",h.id=i+"-cyclic-special-mid",d.label="",d.startLabelRight="",d.startLabelLeft="",d.arrowTypeStart="none",o.isGroup&&(u.fromCluster=i,d.toCluster=i),d.id=i+"-cyclic-special-2",d.arrowTypeStart="none",t.setEdge(i,n,u,i+"-cyclic-special-0"),t.setEdge(n,a,h,i+"-cyclic-special-1"),t.setEdge(a,i,d,i+"-cyclic-special-2")}else t.setEdge(r.start,r.end,{...r},r.id)}),bBe(t),{graph:t}},"prepareLayoutForDagre"),PV=s(async(e,{element:t,preparedLayout:r})=>{let i=r??sA(e),n=it(),a=await DV({element:t,graph:i.graph,diagramType:e.type,id:e.diagramId,parentCluster:void 0,siteConfig:n});return i.measuredLayout=a,a},"measureDagreLayout"),FV=s((e,t)=>{let r=t.preparedLayout?.measuredLayout;if(!r)throw new Error("runDagreLayoutCore requires measureDagreLayout to run first");return NV(r.graph),BV(e,r),r},"runDagreLayoutCore"),FBe=s((e,{measure:t})=>iA(t.graph).map(r=>t.graph.node(r)).filter(Boolean),"getDagrePaintNodes"),zBe=s((e,t,{measure:r})=>e?r.graph.node(e):void 0,"getDagreEdgeNode"),qBe=nA({prepareLayout:sA,measureLayout:PV,runLayoutCore:FV,paintOptions:{clusterDb:hi,getNodes:FBe,getEdgeNode:zBe,skipNode:s((e,{measure:t})=>!t.graph.hasNode(e.id),"skipNode"),isCluster:s((e,{measure:t})=>t.graph.hasNode(e.id)&&(t.graph.children(e.id)??[]).length>0,"isCluster")}})});function oA(e){let t=[];for(let r=0;r=1-x3||f<=x3||f>=1-x3?null:{point:{x:e.x+p*n,y:e.y+p*a},tA:p,tB:f}}function zV(e){return Math.abs(e.b.x-e.a.x)>=Math.abs(e.b.y-e.a.y)}function jBe(e){let t=[];for(let r=0;r=Math.abs(r)?t>=0?1:0:r>=0?1:0}function UBe(e,t){if(e.length<2)return e.map(a=>({...a}));let r=e.map(a=>({...a})),i=t.arrowTypeStart&&rs[t.arrowTypeStart];if(i){let a=e[0],o=e[1],l=Math.atan2(o.y-a.y,o.x-a.x);r[0].x=a.x+i*Math.cos(l),r[0].y=a.y+i*Math.sin(l)}let n=t.arrowTypeEnd&&rs[t.arrowTypeEnd];if(n){let a=e.length,o=e[a-2],l=e[a-1],u=Math.atan2(l.y-o.y,l.x-o.x);r[a-1].x=l.x-n*Math.cos(u),r[a-1].y=l.y-n*Math.sin(u)}return r}function YBe(e,t,r,i,n){let a=e.point.x,o=e.point.y,l={x:a-t*e.r,y:o-r*e.r},u={x:a+t*e.r,y:o+r*e.r},h=[`L${I2(l)}`];return n==="arc"?h.push(`A${Wc(e.r)},${Wc(e.r)} 0 0 ${i} ${I2(u)}`):h.push(`M${I2(u)}`),h}function qV(e,t,r,i){let n=t.x-e.x,a=t.y-e.y,o=r.x-t.x,l=r.y-t.y,u=Math.hypot(n,a),h=Math.hypot(o,l);if(u0){let E=qV(n[h-1],n[h],n[h+1]??n[h],WV);E&&(y=E.cutLen)}let k=p,C=null;a&&hE.t-_.t);for(let E of S)E.r=Math.min(E.r,E.d-y,k-E.d);for(let E=0;E_){let b=_/2;S[E].r=Math.min(S[E].r,b),S[E+1].r=Math.min(S[E+1].r,b)}}for(let E of S)E.r=2?i:null}catch{return null}}function QBe(e,t,r){if(!r.enabled)return;let i=e.node();if(!i)return;let n=new Map;for(let h of t)n.set(h.id,h);let a=[],o=new Map;for(let h of t){let d=typeof CSS<"u"&&CSS.escape?CSS.escape(h.id):h.id,p=i.querySelector(`path[data-id="${d}"]`);if(!p)continue;o.set(h.id,p);let f=KBe(p.getAttribute("data-points"))??h.points;a.push({...h,points:f})}let l=jBe(a);if(l.length===0)return;let u=new Map;for(let h of l){let d=u.get(h.jumpEdgeId)??[];d.push(h),u.set(h.jumpEdgeId,d)}for(let h of a){let d=u.get(h.id);if(!d||d.length===0)continue;let p=n.get(h.id)?.curve;if(p!==void 0&&!XBe(p))continue;let f=o.get(h.id);if(!f)continue;if(p===void 0){let S=f.getAttribute("d")??"";if(!GBe(S))continue}let g=f.getAttribute("style")??"",m=/stroke-dasharray\s*:\s*0\s+([\d.]+)\s+[\d.]+\s+([\d.]+)/.exec(g),y=m?Number.parseFloat(m[1]):null,k=m?Number.parseFloat(m[2]):null,C=HBe(h,d,r);if(f.setAttribute("d",C),y!==null&&k!==null&&typeof f.getTotalLength=="function"){let S=f.getTotalLength(),E=Math.max(0,S-y-k),_=`0 ${y} ${E} ${k}`,b=g.replace(/stroke-dasharray\s*:[^;]*;?/g,`stroke-dasharray: ${_};`).replace(/;\s*;+/g,";");f.setAttribute("style",b)}}}var WV,b3,x3,ZBe,kAt=G(()=>{"use strict";h6e(),WV=5,b3=1e-5,x3=1e-6,s(oA,"buildSegmentList"),s(WBe,"segmentIntersection"),s(zV,"isHorizontalSeg"),s(jBe,"findEdgeIntersections"),s(Wc,"fmt"),s(I2,"pointToString"),s(VBe,"getArcSweepFlag"),ZBe=.001,s(UBe,"applyMarkerOffsets"),s(YBe,"emitJump"),s(qV,"computeRoundedCorner"),s(HBe,"rewriteEdgePath"),s(GBe,"isStraightPath"),s(XBe,"curveSupportsLineHops"),s(KBe,"decodeDataPoints"),s(QBe,"applyLineJumpsToSvg")});function JBe(e,{measure:t}){let r=e.config?.swimlane?.lineHops;if(r===!1)return;let i=r==="gap"?"gap":"arc",n=e.edges.filter(a=>Array.isArray(a.points)&&a.points.length>=2).map(a=>({id:a.id,points:a.points,curve:a.curve,arrowTypeStart:a.arrowTypeStart,arrowTypeEnd:a.arrowTypeEnd}));QBe(t.groups.edgePaths,n,{enabled:!0,jumpRadius:6,jumpStyle:i})}var CAt=G(()=>{"use strict";kAt(),s(JBe,"applySwimlaneLineJumps")});function jV(e){return Math.max(e.padding??20,20)}function ePe(e){let{x:t,y:r,width:i,height:n}=e,a=e.swimlaneContentTop;if(typeof t!="number"||typeof r!="number"||typeof i!="number"||typeof n!="number"||typeof a!="number"||!Number.isFinite(t)||!Number.isFinite(r)||!Number.isFinite(i)||!Number.isFinite(n)||!Number.isFinite(a)||i<=0||n<=0){delete e.groupTitleRect;return}let o=r-n/2,l=Math.min(a,r+n/2),u=Math.min(21,Math.max(0,l-o)),h=o+u;if(h<=o){delete e.groupTitleRect;return}e.groupTitleRect={left:t-i/2,right:t+i/2,top:o,bottom:h}}function tPe(e){let t=e.direction,r=e.nodes??=[];for(let a of e.nodes??[])a.isGroup&&!a.parentId&&(a.shape="swimlane",t&&(a.direction=t));let i=r.filter(a=>!a.isGroup&&!a.parentId);if(i.length===0)return;let n=r.find(a=>a.id===lA);n?n.isGroup&&(n.shape="swimlane",t&&(n.direction=t)):(n={id:lA,label:"",isGroup:!0,shape:"swimlane",padding:20,...t?{direction:t}:{}},r.push(n));for(let a of i)a.parentId=lA}function rPe(e){let t=new Map;for(let o of e.nodes??[])t.set(o.id,o);let r=[];for(let o of e.edges??[]){let l=typeof o.start=="string"?o.start:void 0,u=typeof o.end=="string"?o.end:void 0;!l||!u||o.labelNodeId||r.push({id:o.id,src:l,dst:u,ref:o})}let i=e.nodes??[],n=i.filter(o=>o.isGroup),a=i.filter(o=>!o.isGroup);return{nodes:[...[...n].reverse(),...a].map(o=>o.id),edges:r,layout:e,nodeById:t}}function iPe(e,t,r,i){let{layout:n}=e,a=e.nodeById,o=i?.layerGap??100,l=i?.nodeGap??40,u=0;for(let f of t.layers){let g=0;for(let m of f){let y=a.get(m);if(!y){g++;continue}y.layer=u,y.order=g;let k=r.x[m]??g*l,C=r.y[m]??u*o;y.x=k,y.y=C,g++}u++}let h=n.nodes??[],d=new Map,p=[];for(let f of h){if(!f?.isGroup)continue;f.parentId||p.push(f);let g=h.filter(S=>S.parentId===f.id),m=1/0,y=-1/0,k=1/0,C=-1/0;for(let S of g){let E=S.x??r.x[S.id],_=S.y??r.y[S.id],b=S.width??0,v=S.height??0;E!=null&&_!=null&&(m=Math.min(m,E-b/2),y=Math.max(y,E+b/2),k=Math.min(k,_-v/2),C=Math.max(C,_+v/2))}if(m===1/0||k===1/0)f.x=f.x??0,f.y=f.y??0,f.width=f.width??0,f.height=f.height??0;else{let S=f.padding??20,E=f.parentId?S:2*jV(f),_=S,b=Math.max(0,y-m)+E,v=Math.max(0,C-k)+_,x=(m+y)/2,w=(k+C)/2;f.x=x,f.y=w,f.width=b,f.height=v,d.set(f.id,{minX:m,maxX:y,minY:k,maxY:C})}}if(p.length>0&&d.size>0){let f=1/0,g=-1/0,m=0;for(let y of p){let k=y.padding??20;k>m&&(m=k);let C=d.get(y.id);C&&(f=Math.min(f,C.minY),g=Math.max(g,C.maxY))}if(f!==1/0&&g!==-1/0){let y=Math.max(0,g-f),k=Math.max(m,36),C=y+2*k,S=(f+g)/2;for(let w of p)w.y=S,w.height=C,w.swimlaneContentTop=f;let E=[...p].sort((w,R)=>{let $=w.x??0,M=R.x??0;return $-M}),_=[],b=[],v=[];for(let w of E){let R=d.get(w.id);if(!R)continue;let $=Math.max(0,R.maxX-R.minX)+2*jV(w),M=(R.minX+R.maxX)/2;_.push(w.id),b.push(M),v.push($)}let x=_.length;if(x>0){let w=new Map;if(x===1)w.set(_[0],v[0]);else{let R=[];for(let I=0;I{"use strict";lA="__swimlane_default__",s(jV,"topLaneHorizontalPadding"),s(ePe,"assignTopLaneTitleRect"),s(tPe,"prepareLayoutForSwimlanes"),s(rPe,"toGraphView"),s(iPe,"writeBackToLayoutData")});function aPe(e){let t=[],r=[],i=new Map;for(let o of e.nodes)i.set(o.id,o);for(let o of e.edges){if(!o.label||o.label.length===0||o.isLayoutOnly||o.labelNodeId)continue;let l=o.start?i.get(o.start):void 0,u=o.end?i.get(o.end):void 0;if(!l||!u){Ee.warn(sPe,`Edge ${o.id} has missing source or target node`);continue}let h=`edge-label-${o.start}-${o.end}-${o.id}`,d=l.parentId!==u.parentId?u.parentId:l.parentId,p={id:h,label:o.label,edgeStart:o.start??"",edgeEnd:o.end??"",shape:"labelRect",width:0,height:0,isEdgeLabel:!0,isDummy:!0,parentId:d,isGroup:!1,labelStyle:Array.isArray(o.labelStyle)?o.labelStyle[0]:o.labelStyle??"",...l.dir?{dir:l.dir}:{}};t.push(p),o.labelNodeId=h,o.label=void 0,o.text=void 0;let f={id:`${o.id}-to-label`,start:o.start,end:h,type:"normal",isLayoutOnly:!0},g={id:`${o.id}-from-label`,start:h,end:o.end,type:"normal",isLayoutOnly:!0};r.push(f,g)}let n=[...e.nodes,...t],a=[...e.edges,...r];return{...e,nodes:n,edges:a}}var sPe,wAt=G(()=>{"use strict";Nt(),sPe="[EdgeLabelNodes]",s(aPe,"createEdgeLabelNodes")});function VV(e){let t=e.x??0,r=e.y??0,i=e.width??0,n=e.height??0;return i>0&&n>0?{cx:t,cy:r,rect:D2(t,r,i,n)}:void 0}function UV(e){if(e.isGroup)return;let t=VV(e);return t?{id:String(e.id??""),cx:t.cx,cy:t.cy,rect:t.rect}:void 0}function nc(e,t,r=.001){return Math.abs(e.x-t.x)r}function va(e,t,r=.001){return Ii(e,t,r)&&Math.abs(e.y-t.y)>r}function Do(e,t,r,i){return Math.max(0,Math.min(Math.max(e,t),Math.max(r,i))-Math.max(Math.min(e,t),Math.min(r,i)))}function Qu(e,t,r=.001){return e.horizontal&&t.horizontal&&Oi(e.a,t.a,r)?Do(e.a.x,e.b.x,t.a.x,t.b.x):e.vertical&&t.vertical&&Ii(e.a,t.a,r)?Do(e.a.y,e.b.y,t.a.y,t.b.y):0}function M2(e,t=.001){let r=[];for(let i=0;i0?r[r.length-1]:void 0;(!n||!nc(n,i,t))&&r.push({x:i.x,y:i.y})}return r}function YV(e,t=.001){if(!e||e.length!==4)return;let[r,i,n,a]=e;return ya(r,i,t)&&va(i,n,t)&&ya(n,a,t)?{kind:"HVH",p0:r,p1:i,p2:n,p3:a}:va(r,i,t)&&ya(i,n,t)&&va(n,a,t)?{kind:"VHV",p0:r,p1:i,p2:n,p3:a}:void 0}function cA(e,t,r,i=0){let n=Math.min(e.x,t.x),a=Math.max(e.x,t.x),o=Math.min(e.y,t.y),l=Math.max(e.y,t.y);return a>r.left-i&&nr.top-i&&ot.left+r&&e.xt.top+r&&e.y=t.right&&e.top<=t.top&&e.bottom>=t.bottom}function uA(e,t){return e.leftt.left&&e.topt.top}function GV(e,t){return{left:e.left-t,right:e.right+t,top:e.top-t,bottom:e.bottom+t}}function D2(e,t,r,i){return{left:e-r/2,right:e+r/2,top:t-i/2,bottom:t+i/2}}function cl(e){return VV(e)?.rect}function Wg(e,t){switch(t){case"top":return{x:e.cx,y:e.rect.top};case"bottom":return{x:e.cx,y:e.rect.bottom};case"left":return{x:e.rect.left,y:e.cy};case"right":return{x:e.rect.right,y:e.cy}}}function XV(e,t,r,i,n,a=.001){let o=t==="left"||t==="right",l=i==="left"||i==="right";if(o&&l){if(t==="right"&&i==="left"&&e.xr.x){if(Oi(e,r,a))return[e,r];let d=(e.x+r.x)/2;return[e,{x:d,y:e.y},{x:d,y:r.y},r]}if(t===i){if(Oi(e,r,a))return;let d=t==="left"?Math.min(e.x,r.x)-n:Math.max(e.x,r.x)+n;return[e,{x:d,y:e.y},{x:d,y:r.y},r]}return}if(!o&&!l){if(t===i){if(Ii(e,r,a))return;let p=t==="top"?Math.min(e.y,r.y)-n:Math.max(e.y,r.y)+n;return[e,{x:e.x,y:p},{x:r.x,y:p},r]}if(!(t==="bottom"&&i==="top"&&e.yr.y))return;if(Ii(e,r,a))return[e,r];let d=(e.y+r.y)/2;return[e,{x:e.x,y:d},{x:r.x,y:d},r]}if(o&&!l){let d=t==="right"&&r.x>e.x||t==="left"&&r.xr.y;return d&&p?[e,{x:r.x,y:e.y},r]:void 0}let u=t==="bottom"&&r.y>e.y||t==="top"&&r.yr.x;return u&&h?[e,{x:e.x,y:r.y},r]:void 0}function KV(e,t,r,i){return t==="left"||t==="right"?[e,{x:i,y:e.y},{x:i,y:r.y},r]:[e,{x:e.x,y:i},{x:r.x,y:i},r]}function hA(e){let t=new Map,r=[];for(let i of e){if(i.isEdgeLabel)continue;let n=UV(i);n&&(t.set(n.id,n),r.push({id:n.id,rect:n.rect}))}return{nodeInfoById:t,realNodeRects:r}}function sf(e){let t=[],r=[];for(let i of e){let n=UV(i);if(!n)continue;let a={id:n.id,rect:n.rect};i.isEdgeLabel?r.push(a):t.push(a)}return{realNodeRects:t,labelNodeRects:r}}function lPe(e,{includeEdgeLabels:t=!0}={}){let r=[];for(let i of e){if(i.isGroup||!t&&i.isEdgeLabel)continue;let n=i.x??0,a=i.y??0,o=i.width??0,l=i.height??0;r.push({nodeId:i.id,...D2(n,a,o,l)})}return r}function QV(e,t,r=.001){let i=e.start,n=e.end;if(!i||!n)return;let a=t.get(i),o=t.get(n);if(!(!a||!o))return{srcId:i,dstId:n,srcInfo:a,dstInfo:o,collinearX:Math.abs(a.cx-o.cx)m||fC)return!1;let S=Math.abs(y-d.a.x)n:a&&l&&Oi(e,r,n)?Do(e.x,t.x,r.x,i.x)>n:!1}function dA(e,t,r,i,{epsilon:n=.001,skipDegenerateOther:a=!1}={}){for(let o of r){if(o===i||o.isLayoutOnly)continue;let l=o.points;if(!(!l||l.length<2))for(let u=0;uf+n&&my+n&&pi+.001&&e=2?t[t.length-2]:void 0,l=o&&Ii(o,n)?{x:n.x,y:a.y}:{x:a.x,y:n.y};t.push(l)}t.push(a)}let r=[];for(let i of t){let n=r[r.length-1];(!n||!nc(n,i))&&r.push(i)}return r}function Zu(e){if(e.length<3)return e;let t=[...e];for(let r=0;r<32;r++){let i=hPe(t);if(t=i.points,!i.changed)break}return t}var Ju=G(()=>{"use strict";s(VV,"measuredNodeRect"),s(UV,"nodeBoundsInfoFor"),s(nc,"samePoint"),s(Ii,"sameX"),s(Oi,"sameY"),s(ya,"isHorizontalSegment"),s(va,"isVerticalSegment"),s(Do,"overlapLength"),s(Qu,"sameAxisSegmentOverlapLength"),s(M2,"orthogonalSegmentsForPoints"),s(Al,"countOrthogonalBends"),s(Ui,"dedupeConsecutivePoints"),s(YV,"classifyThreeSegmentRoute"),s(cA,"segmentBoundsOverlapRect"),s(HV,"pointInsideRect"),s(oPe,"rectContainsRect"),s(uA,"rectsOverlap"),s(GV,"inflateRect"),s(D2,"rectFromCenterSize"),s(cl,"rectOfNodeBounds"),s(Wg,"portForRectSide"),s(XV,"buildOrthogonalPortPath"),s(KV,"buildSameSideTrackPath"),s(hA,"collectRealNodeBounds"),s(sf,"collectNodeRectEntries"),s(lPe,"collectLayoutNodeRects"),s(QV,"getNodePairGeometry"),s(La,"segmentHitsAnyRect"),s(ZV,"orthogonalSegmentsCross"),s(cPe,"sameAxisSegmentsOverlap"),s(dA,"segmentConflictsWithAnyEdge"),s(yd,"orthogonalSegmentsStrictlyCross"),s(JV,"strictlyBetween"),s(uPe,"isCollinearIntermediate"),s(hPe,"simplifyPolylineOnce"),s(pA,"orthogonalizePolyline"),s(Zu,"simplifyPolyline")});function eU(e,t,r){let i=e;if(i.isLayoutOnly||!i.points||i.points.length=0&&n=e.length)return e;let a=n-i;if(a<0||a>=e.length)return e;let o=dPe(e[n],e[a],t);return r?[o,...e.slice(n)]:[...e.slice(0,n+1),o]}function pPe(e,t){for(let r of e){let i=eU(r,t,2);if(!i)continue;let n=[...i.points];i.srcRect&&(n=tU(n,i.srcRect,!0)),i.dstRect&&(n=tU(n,i.dstRect,!1)),n=Zu(pA(n)),n=sU(n,i.srcRect,i.dstRect),i.edge.points=Zu(pA(n))}}function rU(e,t,r,i=!1){if(Oi(e,t,Qr)){if(t.yr.bottom+Qr)return t;if(i){if(e.xr.right+Qr)return{x:r.right,y:e.y}}return{x:Math.abs(t.x-r.left)<=Math.abs(t.x-r.right)?r.left:r.right,y:e.y}}if(Ii(e,t,Qr)){if(t.xr.right+Qr)return t;if(i){if(e.yr.bottom+Qr)return{x:e.x,y:r.bottom}}let n=Math.abs(t.y-r.top)<=Math.abs(t.y-r.bottom);return{x:e.x,y:n?r.top:r.bottom}}return t}function fA(e,t,r){let i=e[t];for(let n=t+r;n>=0&&ni.lo)),r=Math.min(...e.map(i=>i.hi));if(!(t>r))return{lo:t,hi:r}}function nU(e,t){return t==="left"||t==="right"?gA(e.top,e.bottom):gA(e.left,e.right)}function mA(e,t,r){let i=e.y>=r.top-Qr&&e.y<=r.bottom+Qr,n=e.x>=r.left-Qr&&e.x<=r.right+Qr;if(Oi(e,t,Qr)&&i){if(Math.abs(e.x-r.left)0?fPe(a):void 0}function aU(e,t,r,i,n){let a=gPe(e,t,r,i,n);if(!a)return;let o=n?e.y:e.x,l=Math.min(a.hi,Math.max(a.lo,o));if(!(Math.abs(l-o)({...l}));for(let l=t;l>=0&&l=r.left-Qr&&Math.max(e.x,t.x)<=r.right+Qr,n=Math.min(e.y,t.y)>=r.top-Qr&&Math.max(e.y,t.y)<=r.bottom+Qr;if(Math.abs(e.y-r.top)i.bottom+Qr;case"left":return Oi(t,r,Qr)&&r.xi.right+Qr}}function uU(e,t,r){if(e.length<3)return e;if(r){let a=lU(e[0],e[1],t);return a&&cU(a,e[1],e[2],t)?e.slice(1):e}let i=e.length-1,n=lU(e[i-1],e[i],t);return n&&cU(n,e[i-1],e[i-2],t)?e.slice(0,i):e}function vPe(e,t,r){let i=e;if(t){let a=fA(i,0,1);if(a){let o=rU(a,i[0],t);o!==i[0]&&(i=[o,...i.slice(1)])}i=uU(i,t,!0)}if(r){let a=i.length-1,o=fA(i,a,-1);if(o){let l=rU(o,i[a],r,!0);l!==i[a]&&(i=[...i.slice(0,a),l])}i=uU(i,r,!1)}let n=sU(i,t,r);return n!==i||i.length===2?n:(t&&(i=oU(i,t,!0)),r&&(i=oU(i,r,!1)),i)}function hU(e,t){for(let r of e){let i=eU(r,t,2);if(!i)continue;let n=Ui(i.points,Qr),a=vPe(n,i.srcRect,i.dstRect);if(a.length<3){i.edge.points=a;continue}let o=[a[0],{...a[0]},...a.slice(1,-1),a[a.length-1],{...a[a.length-1]}];i.edge.points=o}}var Qr,bPe,dU,EAt=G(()=>{"use strict";Ju(),Qr=.001,bPe=.5,dU=4,s(eU,"endpointContextFor"),s(dPe,"segmentEnterPoint"),s(tU,"clipEndpoint"),s(pPe,"clipEdgeEndpointsToNodeBoundaries"),s(rU,"snapEndpointToBoundary"),s(fA,"firstDistinctAdjacent"),s(gA,"cornerClearanceRange"),s(iU,"clampToCornerClearance"),s(fPe,"intersectRanges"),s(nU,"clearanceRangeForSide"),s(mA,"terminalSideForSegment"),s(T3,"isHorizontalSide"),s(gPe,"straightClearanceRange"),s(aU,"clearStraightEndpointCornerAxis"),s(sU,"clearStraightEndpointCornerConnections"),s(mPe,"cornerClearedEndpoint"),s(yPe,"moveCollinearEndpointRun"),s(oU,"clearEndpointCornerConnection"),s(lU,"borderSideForSegment"),s(cU,"leavesOutward"),s(uU,"collapseOwnBorderStub"),s(vPe,"snapAndCollapseEndpoints"),s(hU,"prepareEdgeEndpointsForRenderer")});function pU(e){return new Map(e.map(t=>[t.id,t]))}function xPe(e,t){let r=e.parentId,i=null;for(;r;){let n=t.get(r);if(!n?.isGroup)break;i=n.id,r=n.parentId}return i}function fU(e,t){let r=0,i=e.parentId;for(;i;){let n=t.get(i);if(!n?.isGroup)break;r++,i=n.parentId}return r}function gU(e){let t=1/0,r=-1/0,i=1/0,n=-1/0;for(let a of e){let o=a.x,l=a.y;if(typeof o!="number"||typeof l!="number")continue;let u=a.width??0,h=a.height??0;t=Math.min(t,o-u/2),r=Math.max(r,o+u/2),i=Math.min(i,l-h/2),n=Math.max(n,l+h/2)}return t===1/0||i===1/0?null:{minX:t,maxX:r,minY:i,maxY:n}}function TPe(e,t){let r=e.padding??20;e.x=(t.minX+t.maxX)/2,e.y=(t.minY+t.maxY)/2,e.width=Math.max(0,t.maxX-t.minX)+r,e.height=Math.max(0,t.maxY-t.minY)+r}function kPe(e){let t=pU(e),r=e.filter(i=>i.isGroup&&i.parentId).sort((i,n)=>fU(n,t)-fU(i,t));for(let i of r){let n=e.filter(o=>o.parentId===i.id),a=gU(n);a&&TPe(i,a)}}function yA(e,t){let r=e.nodes??[],i=e.edges??[],n=r.filter(u=>!u.isGroup),a=1/0,o=-1/0;for(let u of n){let h=u[t];typeof h=="number"&&(a=Math.min(a,h),o=Math.max(o,h))}if(!Number.isFinite(a)||!Number.isFinite(o))return!1;let l=s(u=>a+o-u,"mirror");for(let u of r){let h=u[t];typeof h=="number"&&(u[t]=l(h));let d=u.groupTitleRect;d&&(u.groupTitleRect=t==="x"?{...d,left:l(d.right),right:l(d.left)}:{...d,top:l(d.bottom),bottom:l(d.top)})}for(let u of i)for(let h of u.points??[])h[t]=l(h[t]);return!0}function CPe(e){return(e.nodes??[]).some(t=>!t.isGroup)?yA(e,"y"):!0}function wPe(e,t="LR"){let r=e.nodes??[],i=e.edges??[],n=r.filter(M=>!M.isGroup),a=1/0,o=1/0;for(let M of n){let N=M.x??0,j=M.y??0;N0?Math.max(1,d/p):1;for(let M of n){let N=M.x??0,j=((M.y??0)-o)*f+l,I=N-a;M.x=j,M.y=I}for(let M of i)if(M.points)for(let N of M.points){let j=N.x,I=(N.y-o)*f+l,z=j-a;N.x=I,N.y=z}kPe(r);let g=r.filter(M=>M.isGroup&&!M.parentId);if(g.length===0)return t==="RL"&&yA(e,"x"),!0;let m=pU(r),y=new Map;for(let M of r){if(M.isGroup)continue;let N=xPe(M,m);if(!N)continue;let j=y.get(N)??[];j.push(M),y.set(N,j)}let k=0;for(let M of g){let N=M.padding??0;N>k&&(k=N)}let C=[],S=1/0,E=-1/0;for(let M of g){let N=y.get(M.id)??[],j=gU(N);j&&(S=Math.min(S,j.minX),E=Math.max(E,j.maxX),C.push({lane:M,contentTop:j.minY,contentBottom:j.maxY,centerY:(j.minY+j.maxY)/2}))}if(S===1/0||E===-1/0)return!0;let _=Math.max(0,E-S),b=Math.max(k,10),v=_+2*b,x=l+v,w=(S+E)/2-v/2-l,R=w+x/2,$=Math.max(k,l);C.sort((M,N)=>M.centerY-N.centerY);for(let M=0;M{"use strict";s(pU,"buildNodeMap"),s(xPe,"resolveTopLevelGroupId"),s(fU,"groupDepth"),s(gU,"boundsForChildren"),s(TPe,"applyGroupBounds"),s(kPe,"recomputeNestedGroupBounds"),s(yA,"mirrorAxis"),s(CPe,"applyBtDirectionTransform"),s(wPe,"applyLrDirectionTransform")});function EPe(e,t){let{nodeInfoById:r,realNodeRects:i}=hA(t);for(let n of e){if(n.isLayoutOnly)continue;let a=n.points;if(!a||a.length<4)continue;let o=YV(Ui(a,ac),ac);if(!o)continue;let{p3:l}=o,u=o.kind==="HVH",h=QV(n,r,ac);if(!h)continue;let{srcId:d,dstId:p,srcInfo:f,dstInfo:g,collinearX:m,collinearY:y}=h;if(m||y)continue;let k,C=f.rect;for(let S of _Pe){let E,_,b;if(u){let $=g.cy>f.cy?C.bottom:C.top,M=f.cx+S;if(M<=C.left+ac||M>=C.right-ac)continue;E={x:M,y:$},_={x:M,y:l.y},b={x:l.x,y:l.y}}else{let $=g.cx>f.cx?C.right:C.left,M=f.cy+S;if(M<=C.top+ac||M>=C.bottom-ac)continue;E={x:$,y:M},_={x:l.x,y:M},b={x:l.x,y:l.y}}let v=nc(E,_,ac),x=nc(_,b,ac);if(v&&x||!v&&La(E,_,i,[d],1)||!x&&La(_,b,i,[p],1))continue;let w=!v&&dA(E,_,e,n,{epsilon:ac,skipDegenerateOther:!0}),R=!x&&dA(_,b,e,n,{epsilon:ac,skipDegenerateOther:!0});if(!(w||R)){v?k=[_,b]:x?k=[E,_]:k=[E,_,b];break}}k&&(n.points=k)}}var ac,SPe,k3,_Pe,_At=G(()=>{"use strict";Ju(),ac=1e-6,SPe=8,k3=SPe,_Pe=[0,k3,-k3,2*k3,-2*k3],s(EPe,"portSwapToLShape")});function APe(e,t){let{realNodeRects:r,labelNodeRects:i}=sf(t.values());for(let n of e){if(n.isLayoutOnly)continue;let a=n.points;if(!a||a.length<4)continue;let o=Ui(a,.001);if(o.length<4)continue;let l=o.length-1,u=o[l],h=o[l-1],d=o[l-2],p=u.x-h.x,f=u.y-h.y,g=Math.hypot(p,f);if(g>=10||g<.001)continue;let m=h.x-d.x,y=h.y-d.y;if(Math.hypot(m,y)<.001)continue;let k=ya(h,u,.001),C=va(h,u,.001),S=ya(d,h,.001),E=va(d,h,.001);if(!(k&&E||C&&S))continue;let _=n.end,b=n.start,v=_?t.get(_):void 0;if(!v)continue;let x=v.x??0,w=v.y??0,R=cl(v);if(!R)continue;let $,M;if(E){let O=y<0;$={x,y:d.y},M={x,y:O?R.bottom:R.top}}else{let O=m>0;$={x:d.x,y:w},M={x:O?R.right:R.left,y:w}}if(La($,M,r,_?[_]:[],-2)||La($,M,i,[],-2))continue;if(b){let O=t.get(b),B=O?cl(O):void 0;if(B&&HV($,B,2))continue}let N=s((O,B)=>`${O.x.toFixed(3)},${O.y.toFixed(3)}|${B.x.toFixed(3)},${B.y.toFixed(3)}`,"ownSegmentKey"),j=new Set;for(let O=0;O{for(let Y of e){if(Y===n||Y.isLayoutOnly)continue;let U=Y.points;if(!(!U||U.length<2))for(let H=0;H=0){let O=o[l-3],B=[b,_].filter(Y=>!!Y);if(La(O,$,r,B,-2)||I(O,$))continue}let z=[...o.slice(0,l-2),$,M];n.points=z;let F=n.labelNodeId;if(F){let O=t.get(F);if(O){let B=O.width??0,Y=O.height??0;if(B>0&&Y>0){let U,H,J=-1;for(let te=0;te=B+2||Te&&se>=Y+2)&&se>J&&(J=se,U=(ie.x+ue.x)/2,H=(ie.y+ue.y)/2)}U!==void 0&&H!==void 0&&(O.x=U,O.y=H)}}}}}var AAt=G(()=>{"use strict";Ju(),s(APe,"collapseShortTerminalStub")});function RPe(e,t){let r=s((p,f)=>{let g=p.x??0,m=p.y??0,y=f.x-g,k=f.y-m,C=(p.width??0)/2,S=(p.height??0)/2;return Math.abs(k)*C>Math.abs(y)*S?(k<0&&(S=-S),{x:g+(k===0?0:S*y/k),y:m+S}):(y<0&&(C=-C),{x:g+C,y:m+(y===0?0:C*k/y)})},"rectIntersect"),i=s((p,f)=>{let g=Ui(p.points??[]);if(g.length<2)return;let m=f?p.start:p.end,y=m?t.get(m):void 0,k=y?cl(y):void 0;if(!y||!m||!k)return;let C=f?g[0]:g[g.length-1],S=f?g[1]:g[g.length-2],E=r(y,C),_=C;if(vA(S,E)&&(_=S),Ii(E,_,$r))return{edge:p,edgeId:String(p.id??""),nodeId:m,atStart:f,orientation:"V",coord:E.x,min:Math.min(E.y,_.y),max:Math.max(E.y,_.y),boundary:E,railEnd:_,rect:k};if(Oi(E,_,$r))return{edge:p,edgeId:String(p.id??""),nodeId:m,atStart:f,orientation:"H",coord:E.y,min:Math.min(E.x,_.x),max:Math.max(E.x,_.x),boundary:E,railEnd:_,rect:k}},"terminalLaneFor"),n=s((p,f)=>Math.max(0,Math.min(p.max,f.max)-Math.max(p.min,f.min)),"projectedOverlapLength"),a=s((p,f)=>p.nodeId!==f.nodeId||p.orientation!==f.orientation?!1:p.orientation==="H"?(Math.abs(p.boundary.x-p.rect.left)<1||Math.abs(p.boundary.x-p.rect.right)<1)&&Ii(p.boundary,f.boundary,1):(Math.abs(p.boundary.y-p.rect.top)<1||Math.abs(p.boundary.y-p.rect.bottom)<1)&&Oi(p.boundary,f.boundary,1),"sameTerminalFace"),o=s((p,f)=>p.nodeId!==f.nodeId||p.orientation!==f.orientation?!1:n(p,f)>=as&&Math.abs(p.coord-f.coord)<.5,"exactTerminalLaneConflict"),l=s((p,f)=>{if(p.nodeId!==f.nodeId||p.orientation!==f.orientation||p.orientation!=="H"||p.atStart===f.atStart)return!1;let g=n(p,f);if(g2*m?!1:a(p,f)&&Math.abs(p.coord-f.coord)<16},"nearTerminalLaneConflict"),u=s((p,f)=>{let g=Ui(p.edge.points??[]);if(g.length<2)return;let m=p.orientation==="V"?{x:p.boundary.x+f,y:p.boundary.y}:{x:p.boundary.x,y:p.boundary.y+f},y=p.orientation==="V"?{x:p.railEnd.x+f,y:p.railEnd.y}:{x:p.railEnd.x,y:p.railEnd.y+f};if(!s(()=>Math.abs(p.boundary.y-p.rect.top)<1||Math.abs(p.boundary.y-p.rect.bottom)<1?Oi(m,p.boundary,$r)&&m.x>=p.rect.left+1&&m.x<=p.rect.right-1:Math.abs(p.boundary.x-p.rect.left)<1||Math.abs(p.boundary.x-p.rect.right)<1?Ii(m,p.boundary,$r)&&m.y>=p.rect.top+1&&m.y<=p.rect.bottom-1:!1,"boundaryStaysOnSameFace")())return;if(p.atStart){let E=g.length>1&&nc(g[1],p.railEnd,$r),_=g.slice(E?2:1),b=_[0];return b&&!vA(b,y)?void 0:[m,y,..._]}let k=g.length>1&&nc(g[g.length-2],p.railEnd,$r),C=g.slice(0,k?-2:-1),S=C[C.length-1];if(!(S&&!vA(S,y)))return[...C,y,m]},"shiftedCandidate"),h=s(p=>{let f=p.edge,g=Ui(f.points??[]);if(g.length!==2)return!1;let m=f.start,y=f.end,k=m?t.get(m):void 0,C=y?t.get(y):void 0;if(!k||!C)return!1;let S=k.x??0,E=k.y??0,_=C.x??0,b=C.y??0,[v,x]=g;return Oi(v,x,$r)&&Math.abs(E-b)<1&&Math.abs(S-_)>1||Ii(v,x,$r)&&Math.abs(S-_)<1&&Math.abs(E-b)>1},"laneIsStraightCollinearConnector"),d=[-7,7,-14,14,-21,21];for(let p=0;p<8;p++){let f=e.filter(m=>!m.isLayoutOnly).flatMap(m=>[i(m,!0),i(m,!1)]).filter(m=>!!m),g=!1;for(let m=0;m{let v=h(_),x=h(b);return v!==x?Number(v)-Number(x):+!b.atStart-+!_.atStart});for(let _ of E){for(let b of d){let v=u(_,b);if(!v)continue;let x=i({..._.edge,points:v},_.atStart);if(!(!x||f.some(w=>w.edge!==_.edge&&(o(x,w)||S&&l(x,w))))){_.edge.points=v,g=!0;break}}if(g)break}}if(!g)return}}function $Pe(e,t){let{realNodeRects:r,labelNodeRects:i}=sf(t.values()),n=s((o,l)=>{let u=o.start,h=o.end,d=yi(l);if(d.length!==l.length-1)return!1;let p=[u,h].filter(f=>!!f);for(let f of d)if(La(f.a,f.b,r,p,-2)||La(f.a,f.b,i,[],-2))return!1;for(let f of e){if(f===o||f.isLayoutOnly)continue;let g=f.points;if(!(!g||g.length<2)){for(let m of d)for(let y of yi(Ui(g)))if(Qu(m,y,.5)>=as||yd(m.a,m.b,y.a,y.b,$r))return!1}}return!0},"candidateIsSafe"),a=s((o,l)=>{if(l+4>=o.length)return;let u=o[l],h=o[l+1],d=o[l+2],p=o[l+3],f=o[l+4],g=ya(u,h)&&va(h,d)&&ya(d,p)&&va(p,f)&&Ii(u,p,$r)&&Ii(u,f,$r)&&Ii(h,d,$r)&&(h.x-u.x)*(p.x-d.x)<0,m=va(u,h)&&ya(h,d)&&va(d,p)&&ya(p,f)&&Oi(u,p,$r)&&Oi(u,f,$r)&&Oi(h,d,$r)&&(h.y-u.y)*(p.y-d.y)<0;if(g||m)return Ui([...o.slice(0,l+1),f,...o.slice(l+5)]);if(l+5>=o.length)return;let y=o[l+5],k=va(u,h)&&ya(h,d)&&va(d,p)&&ya(p,f)&&va(f,y)&&Ii(u,f,$r)&&Ii(u,y,$r)&&Ii(d,p,$r)&&(d.x-h.x)*(f.x-p.x)<0,C=ya(u,h)&&va(h,d)&&ya(d,p)&&va(p,f)&&ya(f,y)&&Oi(u,f,$r)&&Oi(u,y,$r)&&Oi(d,p,$r)&&(d.y-h.y)*(f.y-p.y)<0;if(!(!k&&!C))return Ui([...o.slice(0,l+1),y,...o.slice(l+6)])},"withoutDogleg");for(let o=0;o<8;o++){let l=!1;for(let u of e){if(u.isLayoutOnly)continue;let h=Ui(u.points??[]);for(let d=0;d<=h.length-5;d++){let p=a(h,d);if(!(!p||!n(u,p))){u.points=p,l=!0;break}}if(l)break}if(!l)return}}function mU(e,t){let{realNodeRects:r,labelNodeRects:i}=sf(t.values()),n=e.filter(p=>!p.isLayoutOnly),a=s((p,f,g)=>Ui(p===f?g??[]:p.points??[]),"pointsFor"),o=s((p,f)=>{let g=0;for(let m=0;m{let f=yi(p);if(f.length!==3)return;let g=f[1];if(!(f[0].horizontal===g.horizontal||f[2].horizontal===g.horizontal))return{index:g.index,horizontal:g.horizontal,vertical:g.vertical,segment:g}},"middleRail"),u=s((p,f)=>{let g=[p.start,p.end].filter(m=>!!m);return r.filter(m=>{if(g.includes(m.id))return!1;let y=m.rect;return f.horizontal?Do(f.a.x,f.b.x,y.left,y.right)>=as&&f.a.y>=y.top-2&&f.a.y<=y.bottom+2:Do(f.a.y,f.b.y,y.top,y.bottom)>=as&&f.a.x>=y.left-2&&f.a.x<=y.right+2})},"blockingRectsFor"),h=s((p,f,g)=>{let m=p.map(k=>({...k}));if(f.horizontal)m[f.index].y=g,m[f.index+1].y=g;else if(f.vertical)m[f.index].x=g,m[f.index+1].x=g;else return;let y=Zu(Ui(m));return yi(y).length===y.length-1?y:void 0},"candidateByMovingRail"),d=s((p,f,g)=>{let m=[p.start,p.end].filter(k=>!!k),y=yi(f);if(y.length!==f.length-1)return!1;for(let k of y)if(La(k.a,k.b,r,m,-2)||La(k.a,k.b,i,[],-2))return!1;for(let k of n)if(k!==p){for(let C of y)for(let S of yi(a(k)))if(Qu(C,S,.5)>=as)return!1}return o(p,f)<=g},"candidateIsSafe");for(let p=0;p<8;p++){let f=o(),g=!1;for(let m of n){let y=a(m),k=l(y);if(!k)continue;let C=u(m,k.segment);if(C.length===0)continue;let S=k.horizontal?[Math.min(...C.map(E=>E.rect.top))-20,Math.max(...C.map(E=>E.rect.bottom))+20]:[Math.min(...C.map(E=>E.rect.left))-20,Math.max(...C.map(E=>E.rect.right))+20];for(let E of S){let _=h(y,k.segment,E);if(!(!_||!d(m,_,f))){m.points=_,g=!0;break}}if(g)break}if(!g)return}}function yU(e,t){let r=s(l=>{let u=l.groupTitleRect;if(!(!u||typeof u.left!="number"||typeof u.right!="number"||typeof u.top!="number"||typeof u.bottom!="number"||!Number.isFinite(u.left)||!Number.isFinite(u.right)||!Number.isFinite(u.top)||!Number.isFinite(u.bottom)||u.right<=u.left||u.bottom<=u.top))return{left:u.left,right:u.right,top:u.top,bottom:u.bottom}},"validTitleRect"),i=s(l=>{if(!l.isGroup||l.parentId)return;let u=l.direction,h=typeof u=="string"?u.toUpperCase():"";if(h==="LR"||h==="RL"||h==="BT")return;let d=r(l),p=l.y,f=l.height;if(!d||typeof p!="number"||typeof f!="number"||!Number.isFinite(p)||!Number.isFinite(f)||f<=0)return;let g=d.right-d.left,m=d.bottom-d.top;if(!(m<=0||g{if(!l.horizontal)return!1;let h=l.a.y;return h<=u.top+$r||h>=u.bottom-$r?!1:Do(l.a.x,l.b.x,u.left,u.right)>=as},"horizontalSegmentIntersectsTitle"),a=[...t.values()].map(i).filter(l=>!!l);if(a.length===0)return;let o=0;for(let l of e){if(l.isLayoutOnly)continue;let u=Ui(l.points??[]);for(let h of yi(u))for(let d of a)n(h,d.rect)&&(o=Math.max(o,d.rect.bottom-h.a.y+4))}if(!(o<=$r))for(let l of a){let u=l.node.y,h=l.node.height;typeof u!="number"||typeof h!="number"||!Number.isFinite(u)||!Number.isFinite(h)||h<=0||(l.node.y=u-o/2,l.node.height=h+o,l.node.groupTitleRect={...l.rect,top:l.rect.top-o,bottom:l.rect.bottom-o})}}function vU(e,t){let r=s(u=>{let h=u.groupTitleRect;if(!(!h||typeof h.left!="number"||typeof h.right!="number"||typeof h.top!="number"||typeof h.bottom!="number"||!Number.isFinite(h.left)||!Number.isFinite(h.right)||!Number.isFinite(h.top)||!Number.isFinite(h.bottom)||h.right<=h.left||h.bottom<=h.top))return{left:h.left,right:h.right,top:h.top,bottom:h.bottom}},"validTitleRect"),i=s(u=>{if(!u.isGroup||u.parentId||u.direction!=="LR")return;let h=r(u),d=u.x,p=u.width;if(!h||typeof d!="number"||typeof p!="number"||!Number.isFinite(d)||!Number.isFinite(p)||p<=0)return;let f=h.right-h.left,g=h.bottom-h.top;if(!(f<=0||g{if(!u.vertical)return!1;let d=u.a.x;return d<=h.left+$r||d>=h.right-$r?!1:Do(u.a.y,u.b.y,h.top,h.bottom)>=as},"verticalSegmentIntersectsTitle"),a=s((u,h)=>{if(!u.horizontal)return!1;let d=u.a.y;return d<=h.top+$r||d>=h.bottom-$r?!1:Do(u.a.x,u.b.x,h.left,h.right)>=as},"horizontalSegmentIntersectsTitle"),o=[...t.values()].map(i).filter(u=>!!u);if(o.length===0)return;let l=0;for(let u of e){if(u.isLayoutOnly)continue;let h=Ui(u.points??[]);for(let d of yi(h))for(let p of o)if(n(d,p.rect))l=Math.max(l,p.rect.right-d.a.x+4);else if(a(d,p.rect)){let f=Math.min(d.a.x,d.b.x);l=Math.max(l,p.rect.right-f+4)}}if(!(l<=$r))for(let u of o){let h=u.node.x,d=u.node.width;typeof h!="number"||typeof d!="number"||!Number.isFinite(h)||!Number.isFinite(d)||d<=0||(u.node.x=h-l/2,u.node.width=d+l,u.node.groupTitleRect={...u.rect,left:u.rect.left-l,right:u.rect.right-l})}}function LPe(e,t){let{realNodeRects:r}=sf(t.values()),i=e.filter(g=>!g.isLayoutOnly),n=s((g,m=new Map)=>Ui(m.get(g)??g.points??[]),"replacementPointsFor"),a=s((g=new Map)=>{let m=0;for(let y=0;yi.reduce((m,y)=>m+Al(n(y,g)),0),"totalBends"),l=s(g=>{let m=n(g);if(m.length<4)return;let y=m[m.length-2],k=m[m.length-1];if(!(!ya(y,k,$r)&&!va(y,k,$r)))return{tailStart:y,terminal:k}},"terminalTailFor"),u=s((g,m)=>{let y=n(g);if(y.length<3)return;let k=y[0],C=y[1],S;if(ya(k,C,$r))S={x:C.x,y:m.tailStart.y};else if(va(k,C,$r))S={x:m.tailStart.x,y:C.y};else return;let E=Zu(Ui([k,C,S,m.tailStart,m.terminal]));return yi(E).length===E.length-1?E:void 0},"candidateWithDestinationTail"),h=s((g,m)=>{let y=[g.start,g.end].filter(k=>!!k);for(let k of yi(m))if(La(k.a,k.b,r,y,-2))return!0;return!1},"pathHasNodeHit"),d=s((g,m,y)=>{for(let k of i)if(k!==g){for(let C of yi(m))for(let S of yi(n(k,y)))if(Qu(C,S,.5)>=as)return!0}return!1},"pathHasSharedTrack"),p=s((g,m,y)=>!h(g,m)&&!d(g,m,y),"candidateIsSafe"),f=s(()=>{let g=new Map;for(let m of i){let y=m.end;if(!y||!t.has(y)||n(m).length<4)continue;let k=g.get(y)??[];k.push(m),g.set(y,k)}return g},"edgesByDestination");for(let g=0;g<4;g++){let m=a();if(m===0)return;let y=o(),k,C=m,S=y;for(let E of f().values())for(let _=0;_=m||j>C||j===C&&I>=S||(k=N,C=j,S=I)}if(!k)return;for(let[E,_]of k)E.points=_}}function IPe(e,t){let{realNodeRects:r,labelNodeRects:i}=sf(t.values()),n=e.filter(C=>!C.isLayoutOnly),a=s((C,S=new Map)=>Ui(S.get(C)??C.points??[]),"replacementPointsFor"),o=s((C=new Map)=>{let S=0;for(let E=0;En.reduce((S,E)=>S+Al(a(E,C)),0),"totalBends"),u=s(C=>{let S=C.start,E=C.end,_=S?t.get(S):void 0,b=E?t.get(E):void 0,v=_?cl(_):void 0,x=b?cl(b):void 0;return v&&x?{src:v,dst:x}:void 0},"endpointRectsFor"),h=s((C,S,E)=>{if(E.index<=0||E.index+1>=S.length-1)return;let _=u(C);if(_){if(E.vertical){let b=E.a.x,v=Math.min(_.src.left,_.dst.left),x=Math.max(_.src.right,_.dst.right),w=bx+$r?"right":void 0;return w?{edge:C,points:S,segmentIndex:E.index,axis:"vertical",side:w,coord:b,min:Math.min(E.a.y,E.b.y),max:Math.max(E.a.y,E.b.y)}:void 0}if(E.horizontal){let b=E.a.y,v=Math.min(_.src.top,_.dst.top),x=Math.max(_.src.bottom,_.dst.bottom),w=bx+$r?"bottom":void 0;return w?{edge:C,points:S,segmentIndex:E.index,axis:"horizontal",side:w,coord:b,min:Math.min(E.a.x,E.b.x),max:Math.max(E.a.x,E.b.x)}:void 0}}},"externalRailForSegment"),d=s(()=>{let C=[];for(let S of n){let E=a(S);for(let _ of yi(E)){let b=h(S,E,_);b&&C.push(b)}}return C},"collectExternalRails"),p=s((C,S)=>C.edge!==S.edge&&C.axis===S.axis&&C.side===S.side&&Do(C.min,C.max,S.min,S.max)>=as,"railsInteract"),f=s(C=>{let S=[],E=new Set;for(let _ of C){if(E.has(_))continue;let b=[_],v=[];for(E.add(_);b.length>0;){let x=b.pop();v.push(x);for(let w of C)!E.has(w)&&p(x,w)&&(E.add(w),b.push(w))}v.length>1&&S.push(v)}return S},"connectedComponents"),g=s(C=>{let S=[];for(let E of C)S.some(_=>Math.abs(_-E.coord)<$r)||S.push(E.coord);for(;S.length{let S=C.map(b=>b.coord),E=g(C),_=[];if(C.length<=6){let b=new Array(E.length).fill(!1),v=[],x=s(()=>{if(v.length===C.length){v.some((w,R)=>Math.abs(w-S[R])>=$r)&&_.push([...v]);return}for(let[w,R]of E.entries())b[w]||(b[w]=!0,v.push(R),x(),v.pop(),b[w]=!1)},"visit");return x(),_}for(let b=0;b{let E=new Map;for(let[b,v]of C.entries()){let x=S[b],w=E.get(v.edge)??v.points.map(R=>({x:R.x,y:R.y}));v.axis==="vertical"?(w[v.segmentIndex].x=x,w[v.segmentIndex+1].x=x):(w[v.segmentIndex].y=x,w[v.segmentIndex+1].y=x),E.set(v.edge,w)}let _=new Map;for(let[b,v]of E){let x=Zu(Ui(v));if(yi(x).length!==x.length-1)return;_.set(b,x)}return _},"replacementsForAssignment"),k=s(C=>{for(let[S,E]of C){let _=[S.start,S.end].filter(b=>!!b);for(let b of yi(E))if(La(b.a,b.b,r,_,-2)||La(b.a,b.b,i,[],-2))return!1}for(let S=0;S=as)return!1}}return!0},"candidateIsSafe");for(let C=0;C<4;C++){let S=o();if(S===0)return;let E,_=S,b=l(),v=Number.POSITIVE_INFINITY;for(let x of f(d()))for(let w of m(x)){let R=y(x,w);if(!R||!k(R))continue;let $=o(R);if($>=S)continue;let M=l(R),N=x.reduce((j,I,z)=>j+Math.abs(w[z]-I.coord),0);$>_||$===_&&(M>b||M===b&&N>=v)||(E=R,_=$,b=M,v=N)}if(!E)return;for(let[x,w]of E)x.points=w}}function MPe(e,t){let{realNodeRects:r,labelNodeRects:i}=sf(t.values()),n=e.filter(f=>!f.isLayoutOnly),a=s((f,g,m)=>Ui(f===g?m??[]:f.points??[]),"pointsFor"),o=s(f=>yi(f).reduce((g,m)=>{let y=m.a.x-m.b.x,k=m.a.y-m.b.y;return g+Math.hypot(y,k)},0),"pathLength"),l=s((f,g)=>{let m=0;for(let y=0;y{if(f.horizontal){let m=f.a.y;return(Math.abs(m-g.top)<1||Math.abs(m-g.bottom)<1)&&Do(f.a.x,f.b.x,g.left,g.right)>=as}if(f.vertical){let m=f.a.x;return(Math.abs(m-g.left)<1||Math.abs(m-g.right)<1)&&Do(f.a.y,f.b.y,g.top,g.bottom)>=as}return!1},"segmentRunsAlongRectBorder"),h=s(f=>{let g=[f.start,f.end].filter(y=>!!y),m=[];for(let y of g){let k=t.get(y),C=k?cl(k):void 0;C&&m.push(C)}return m},"endpointRectsFor"),d=s((f,g)=>{if(g+3>=f.length)return[];let m=f[g],y=f[g+1],k=f[g+2],C=f[g+3],S=ya(m,y,$r)&&va(y,k,$r)&&ya(k,C,$r),E=va(m,y,$r)&&ya(y,k,$r)&&va(k,C,$r);if(!S&&!E)return[];if(!(S?Math.sign(y.x-m.x)!==Math.sign(C.x-k.x):Math.sign(y.y-m.y)!==Math.sign(C.y-k.y)))return[];let _=Ii(m,C,$r)||Oi(m,C,$r)?[]:[{x:m.x,y:C.y},{x:C.x,y:m.y}],b=_.length===0?[[...f.slice(0,g+1),...f.slice(g+3)]]:_.map(x=>[...f.slice(0,g+1),x,...f.slice(g+3)]),v=new Set;return b.map(x=>Zu(Ui(x))).filter(x=>{if(yi(x).length!==x.length-1||!x.some(R=>nc(R,C,$r)))return!1;let w=x.map(R=>`${R.x.toFixed(3)},${R.y.toFixed(3)}`).join("|");return v.has(w)?!1:(v.add(w),!0)})},"shortcutCandidatesAt"),p=s((f,g,m)=>{let y=[f.start,f.end].filter(C=>!!C),k=h(f);for(let C of yi(g))if(La(C.a,C.b,r,y,-2)||La(C.a,C.b,i,[],-2)||k.some(S=>u(C,S)))return!1;for(let C of n)if(C!==f){for(let S of yi(g))for(let E of yi(a(C)))if(Qu(S,E,.5)>=as)return!1}return l(f,g)<=m},"candidateIsSafe");for(let f=0;f<8;f++){let g=l(),m,y,k=g,C=Number.POSITIVE_INFINITY,S=Number.POSITIVE_INFINITY;for(let E of n){let _=a(E),b=Al(_,$r),v=o(_);for(let x=0;x<=_.length-4;x++)for(let w of d(_,x)){let R=Al(w,$r),$=o(w);if(!(Rk||M===k&&(R>C||R===C&&$>=S)||(m=E,y=w,k=M,C=R,S=$)}}if(!m||!y)return;m.points=y}}function DPe(e,t){let r=[];for(let xe of t.values()){if(xe.isGroup||xe.isEdgeLabel)continue;let $e=xe.x??0,Me=xe.y??0,He=cl(xe);He&&r.push({id:String(xe.id??""),cx:$e,cy:Me,rect:He})}if(r.length===0)return;let i=new Map(r.map(xe=>[xe.id,xe])),n=r.map(xe=>({id:xe.id,rect:xe.rect})),a=["top","bottom","left","right"],o={top:Math.min(...r.map(xe=>xe.rect.top))-20,bottom:Math.max(...r.map(xe=>xe.rect.bottom))+20,left:Math.min(...r.map(xe=>xe.rect.left))-20,right:Math.max(...r.map(xe=>xe.rect.right))+20},l=e.filter(xe=>!xe.isLayoutOnly),u=new Map(l.map((xe,$e)=>[xe,$e])),h=s(xe=>{let $e=xe==="left"||xe==="top"?-1:1,Me=[];for(let He=0;He<=2;He++)Me.push(o[xe]+$e*20*He);return Me},"outwardTracksForSide"),d=s((xe,$e=new Map)=>Ui($e.get(xe)??xe.points??[]),"replacementPointsFor"),p=s((xe,$e)=>{let Me=0;for(let He of xe)for(let Ge of $e)yd(He.a,He.b,Ge.a,Ge.b,$r)&&Me++;return Me},"crossingCountBetweenSegments"),f=s((xe,$e)=>p(yi(xe),yi($e)),"crossingCountBetweenPaths"),g=s((xe=new Map)=>{let $e=0,Me=[],He=new Set,Ge=[],ze=s(Ve=>{He.has(Ve)||(He.add(Ve),Ge.push(Ve))},"addEdge");for(let Ve=0;Ve0&&($e+=Ie,Me.push({first:et,second:be,count:Ie}),ze(et),ze(be))}}return Ge.sort((Ve,et)=>(u.get(Ve)??0)-(u.get(et)??0)),{count:$e,pairs:Me,edgeSet:He,edges:Ge}},"crossingSnapshot"),m=s((xe,$e)=>{let Me=new Set($e.keys());if(Me.size===0)return xe.count;let He=0;for(let ze of xe.pairs)(Me.has(ze.first)||Me.has(ze.second))&&(He+=ze.count);let Ge=0;for(let ze=0;ze{let $e=new Map;for(let Ge of xe.pairs){let ze=$e.get(Ge.first)??new Set;ze.add(Ge.second),$e.set(Ge.first,ze);let Ve=$e.get(Ge.second)??new Set;Ve.add(Ge.first),$e.set(Ge.second,Ve)}let Me=[],He=new Set;for(let Ge of xe.edges){if(He.has(Ge))continue;let ze=[Ge],Ve=[];for(He.add(Ge);ze.length>0;){let et=ze.pop();Ve.push(et);for(let he of $e.get(et)??[])He.has(he)||(He.add(he),ze.push(he))}Ve.sort((et,he)=>(u.get(et)??0)-(u.get(he)??0)),Ve.length>1&&Me.push(Ve)}return Me},"crossingComponents"),k=s(xe=>[xe.start,xe.end].filter($e=>!!$e),"endpointIdsFor"),C=s(xe=>{let $e=[];for(let Me of y(xe)){let He=new Set(Me),Ge=new Set(Me.flatMap(Ve=>k(Ve))),ze=[...Me];for(let Ve of l)He.has(Ve)||k(Ve).some(et=>Ge.has(et))&&ze.push(Ve);ze.sort((Ve,et)=>(u.get(Ve)??0)-(u.get(et)??0)),$e.push(ze)}return $e},"pairSearchGroups"),S=s((xe,$e,Me)=>m(xe,new Map([[$e,Me]])),"crossingCountWithSingleReplacement"),E=s(xe=>{let $e=new Map;for(let Me of xe.pairs)$e.set(Me.first,($e.get(Me.first)??0)+Me.count),$e.set(Me.second,($e.get(Me.second)??0)+Me.count);return $e},"currentCrossingsByEdge"),_=s(xe=>xe.slice(1).reduce(($e,Me,He)=>{let Ge=xe[He];return $e+Math.abs(Me.x-Ge.x)+Math.abs(Me.y-Ge.y)},0),"pathLength"),b=s((xe=new Map)=>l.reduce(($e,Me)=>$e+Al(d(Me,xe)),0),"totalBends"),v=s((xe=new Map)=>l.reduce(($e,Me)=>$e+_(d(Me,xe)),0),"totalLength"),x=s((xe,$e,Me=new Map)=>{let He=yi($e);for(let Ge of l)if(Ge!==xe){for(let ze of He)for(let Ve of yi(d(Ge,Me)))if(Qu(ze,Ve,.5)>=as)return!0}return!1},"pathHasSegmentConflict"),w=s((xe,$e)=>{let Me=[xe.start,xe.end].filter(He=>!!He);for(let He of yi($e))if(La(He.a,He.b,n,Me,-2))return!0;return!1},"pathHitsNode"),R=s((xe,$e)=>{let Me=Zu(Ui($e));yi(Me).length===Me.length-1&&xe.push(Me)},"pushOrthogonalCandidate"),$=s(xe=>xe==="left"||xe==="right","sideIsHorizontal"),M=s((xe,$e,Me)=>{switch($e){case"left":return Math.min(xe.x,Me.x)-20;case"right":return Math.max(xe.x,Me.x)+20;case"top":return Math.min(xe.y,Me.y)-20;case"bottom":return Math.max(xe.y,Me.y)+20}},"localTrackForSameSide"),N=s((xe,$e,Me,He)=>{let Ge=Me==="left"||Me==="top"?-1:1,ze=[M($e,Me,He),o[Me]];for(let Ve of ze)for(let et=0;et<=2;et++)R(xe,KV($e,Me,He,Ve+Ge*20*et))},"addSameSideCandidates"),j=s((xe,$e,Me,He,Ge)=>{for(let ze of h(Me))for(let Ve of h(Ge))R(xe,[$e,{x:ze,y:$e.y},{x:ze,y:Ve},{x:He.x,y:Ve},He])},"addHorizontalToVerticalCandidates"),I=s((xe,$e,Me,He,Ge)=>{for(let ze of h(Me))for(let Ve of h(Ge))R(xe,[$e,{x:$e.x,y:ze},{x:Ve,y:ze},{x:Ve,y:He.y},He])},"addVerticalToHorizontalCandidates"),z=s((xe,$e,Me,He,Ge)=>{let ze=[...h("top"),...h("bottom")];for(let Ve of h(Me))for(let et of h(Ge))for(let he of ze)R(xe,[$e,{x:Ve,y:$e.y},{x:Ve,y:he},{x:et,y:he},{x:et,y:He.y},He])},"addHorizontalPairCandidates"),F=s((xe,$e,Me,He,Ge)=>{let ze=[...h("left"),...h("right")];for(let Ve of h(Me))for(let et of h(Ge))for(let he of ze)R(xe,[$e,{x:$e.x,y:Ve},{x:he,y:Ve},{x:he,y:et},{x:He.x,y:et},He])},"addVerticalPairCandidates"),O=s(xe=>{let $e=new Set;return xe.map(Me=>Ui(Me)).filter(Me=>{let He=Me.map(Ge=>`${Ge.x.toFixed(3)},${Ge.y.toFixed(3)}`).join("|");return $e.has(He)||Me.length<2?!1:($e.add(He),!0)})},"dedupeCandidatePaths"),B=s((xe,$e,Me,He)=>{let Ge=[],ze=XV(xe,$e,Me,He,20,$r);ze&&R(Ge,ze),$e===He&&N(Ge,xe,$e,Me);let Ve=$($e),et=$(He);return Ve&&!et?j(Ge,xe,$e,Me,He):!Ve&&et?I(Ge,xe,$e,Me,He):Ve?z(Ge,xe,$e,Me,He):F(Ge,xe,$e,Me,He),O(Ge)},"buildCandidatesForSides"),Y=s((xe,$e,Me,He)=>{let Ge=[...h("left"),...h("right")],ze=[...h("top"),...h("bottom")];for(let Ve of a){let et=Wg(He,Ve),he=Ve==="top"||Ve==="bottom"?h(Ve):ze;for(let ye of Ge){R(xe,[$e,Me,{x:ye,y:Me.y},{x:ye,y:et.y},et]);for(let be of he)R(xe,[$e,Me,{x:ye,y:Me.y},{x:ye,y:be},{x:et.x,y:be},et])}}},"addVerticalDepartureOuterTrackCandidates"),U=s((xe,$e,Me,He)=>{let Ge=[...h("left"),...h("right")],ze=[...h("top"),...h("bottom")];for(let Ve of a){let et=Wg(He,Ve),he=Ve==="left"||Ve==="right"?h(Ve):Ge;for(let ye of ze){R(xe,[$e,Me,{x:Me.x,y:ye},{x:et.x,y:ye},et]);for(let be of he)R(xe,[$e,Me,{x:Me.x,y:ye},{x:be,y:ye},{x:be,y:et.y},et])}}},"addHorizontalDepartureOuterTrackCandidates"),H=s(xe=>{let $e=xe.start,Me=xe.end,He=Me?i.get(Me):void 0;if(!$e||!He)return[];let Ge=Ui(xe.points??[]);if(Ge.length<4)return[];let ze=Ge[0],Ve=Ge[1],et=[];return va(ze,Ve,$r)?Y(et,ze,Ve,He):ya(ze,Ve,$r)&&U(et,ze,Ve,He),et},"terminalPreservingOuterTrackCandidates"),J=s(xe=>{let $e=xe.start,Me=xe.end,He=$e?i.get($e):void 0,Ge=Me?i.get(Me):void 0;if(!He||!Ge)return[];let ze=[];for(let Ve of a){let et=Wg(He,Ve);for(let he of a)ze.push(...B(et,Ve,Wg(Ge,he),he))}return ze.push(...H(xe)),ze},"candidatePathsFor"),te=s(()=>new Map(l.map(xe=>[xe,yi(d(xe))])),"currentSegmentsByEdge"),ie=s((xe,$e,Me)=>{let He=new Set;for(let Ge of l){if(Ge===xe)continue;let ze=Me.get(Ge)??yi(d(Ge));$e.some(Ve=>ze.some(et=>Qu(Ve,et,.5)>=as))&&He.add(Ge)}return He},"sharedTrackConflictsFor"),ue=s((xe,$e,Me,He)=>{let Ge=new Set;return J(xe).map(ze=>Zu(Ui(ze))).filter(ze=>{if(w(xe,ze))return!1;let Ve=ze.map(et=>`${et.x.toFixed(3)},${et.y.toFixed(3)}`).join("|");return Ge.has(Ve)||ze.length<2?!1:(Ge.add(Ve),!0)}).map(ze=>{let Ve=yi(ze),et=0;for(let he of l)he!==xe&&(et+=p(Ve,Me.get(he)??yi(d(he))));return{candidate:ze,candidateSegments:Ve,crossings:$e.count-(He.get(xe)??0)+et,bends:Al(ze,$r),totalBends:Al(ze),length:_(ze)}}).filter(({crossings:ze})=>ze<=$e.count).sort((ze,Ve)=>ze.crossings-Ve.crossings||ze.bends-Ve.bends||ze.length-Ve.length).slice(0,48).map(ze=>({path:ze.candidate,segments:ze.candidateSegments,sharedTrackConflicts:ie(xe,ze.candidateSegments,Me),totalBends:ze.totalBends,length:ze.length}))},"pairCandidatesFor"),se=s((xe,$e,Me,He,Ge,ze)=>{let Ve=0;for(let he of xe.pairs)(he.first===$e||he.second===$e||he.first===He||he.second===He)&&(Ve+=he.count);let et=p(Me.segments,Ge.segments);for(let he of l){if(he===$e||he===He)continue;let ye=ze.get(he)??yi(d(he));et+=p(Me.segments,ye)+p(Ge.segments,ye)}return xe.count-Ve+et},"pairCrossingCount"),le=s((xe,$e)=>{for(let Me of xe.sharedTrackConflicts)if(Me!==$e)return!1;return!0},"conflictsOnlyWith"),Te=s((xe,$e)=>xe.segments.some(Me=>$e.segments.some(He=>Qu(Me,He,.5)>=as)),"candidatesShareTrack"),oe=s((xe,$e,Me,He)=>le($e,Me.edge)&&le(He,xe.edge)&&!Te($e,He),"pairCandidatesAreCompatible"),fe=s((xe,$e,Me,He,Ge)=>{let ze=se(xe.current,$e.edge,Me,He.edge,Ge,xe.baseSegments);if(!(ze>=xe.current.count))return{replacements:new Map([[$e.edge,Me.path],[He.edge,Ge.path]]),crossings:ze,bends:xe.currentBends-(xe.baseBendsByEdge.get($e.edge)??0)-(xe.baseBendsByEdge.get(He.edge)??0)+Me.totalBends+Ge.totalBends,length:xe.currentLength-(xe.baseLengthByEdge.get($e.edge)??0)-(xe.baseLengthByEdge.get(He.edge)??0)+Me.length+Ge.length}},"scorePairReplacement"),ge=s((xe,$e)=>xe.crossings<$e.crossings||xe.crossings===$e.crossings&&(xe.bends<$e.bends||xe.bends===$e.bends&&xe.length<$e.length),"pairScoreIsBetter"),ke=s((xe,$e,Me,He)=>{let Ge=He;for(let ze of $e.candidates)for(let Ve of Me.candidates){if(!oe($e,ze,Me,Ve))continue;let et=fe(xe,$e,ze,Me,Ve);et&&ge(et,Ge)&&(Ge=et)}return Ge},"bestScoreForOptionPair"),Ne=s(xe=>{let $e=b(),Me=v(),He=te(),Ge=E(xe),ze=new Map(l.map(Ie=>[Ie,Al(d(Ie))])),Ve=new Map(l.map(Ie=>[Ie,_(d(Ie))])),et=new Map,he=C(xe);for(let Ie of he)for(let pe of Ie){if(et.has(pe))continue;let Qe=ue(pe,xe,He,Ge);Qe.length>0&&et.set(pe,{edge:pe,candidates:Qe})}let ye={replacements:new Map,crossings:xe.count,bends:$e,length:Me},be={current:xe,currentBends:$e,currentLength:Me,baseBendsByEdge:ze,baseLengthByEdge:Ve,baseSegments:He};for(let Ie of he){let pe=new Set(Ie.filter(W=>xe.edgeSet.has(W))),Qe=Ie.map(W=>et.get(W)).filter(W=>!!W);for(let W=0;W0?ye.replacements:void 0},"bestPairedReplacement");for(let xe=0;xe<4;xe++){let $e=g(),Me=$e.count;if(Me===0)return;let He,Ge,ze=Me,Ve=Number.POSITIVE_INFINITY;for(let he of $e.edges){let ye=Al(d(he),$r);for(let be of J(he)){let Ie=w(he,be),pe=!Ie&&x(he,be),Qe=S($e,he,be),W=Al(be,$r);Ie||pe||!(Qeze||Qe===ze&&W>=Ve||(He=he,Ge=be,ze=Qe,Ve=W)}}if(He&&Ge){He.points=Ge;continue}let et=Ne($e);if(!et)return;for(let[he,ye]of et)he.points=ye}}var $r,as,yi,vA,RAt=G(()=>{"use strict";Ju(),$r=.001,as=8,yi=M2,vA=s((e,t)=>Ii(e,t,$r)||Oi(e,t,$r),"orthogonallyAligned"),s(RPe,"separateSharedRenderedTerminalLanes"),s($Pe,"collapseRedundantRectangularDoglegs"),s(mU,"liftObstacleHuggingSameSideRails"),s(yU,"liftTopLaneTitleBandsAboveRails"),s(vU,"shiftLeftLaneTitleBandsLeftOfRails"),s(LPe,"swapDestinationTerminalTailsToReduceCrossings"),s(IPe,"reassignCrossingExternalRailChannels"),s(MPe,"shortcutRedundantOrthogonalJogs"),s(DPe,"resolveRenderedOrthogonalCrossings")});function NPe(e,t){let{nodeInfoById:r,realNodeRects:i}=hA(t),n=["top","bottom","left","right"],a=20,o={top:Math.min(...i.map(y=>y.rect.top))-a,bottom:Math.max(...i.map(y=>y.rect.bottom))+a,left:Math.min(...i.map(y=>y.rect.left))-a,right:Math.max(...i.map(y=>y.rect.right))+a},l=s((y,k,C,S)=>{let E=[],_=XV(y,k,C,S,a,vd);return _&&E.push(_),k===S&&E.push(KV(y,k,C,o[k])),E},"buildOrthogonalPathCandidates"),u=s((y,k)=>{for(let C=0;C{let S=0,E=M2(y,vd),_=k.start,b=k.end;for(let v of e){if(v===k||v.isLayoutOnly)continue;let x=v.start,w=v.end;if(!C&&_&&b&&(x===_||x===b||w===_||w===b))continue;let R=v.points;if(!(!R||R.length<2))for(let $ of E)for(let M of M2(R,vd)){if(ZV($.a,$.b,M.a,M.b,vd,vd)){S++;continue}Qu($,M,vd)>=OPe&&S++}}return S},"pathConflictCount"),d=4,p=s((y,k)=>{let C=Math.abs(y.y-k.rect.top),S=Math.abs(y.y-k.rect.bottom),E=Math.abs(y.x-k.rect.left),_=Math.abs(y.x-k.rect.right),b="top",v=C;return S{let S=f.get(y)??[];S.push({side:k,edgeId:C}),f.set(y,S)},"addFaceClaim");for(let y of e){if(y.isLayoutOnly)continue;let k=y.points??[];if(k.length<1)continue;let C=y.id??"",S=y.start,E=y.end;if(S){let _=r.get(S);_&&g(S,p(k[0],_),C)}if(E){let _=r.get(E);_&&g(E,p(k[k.length-1],_),C)}}let m=s((y,k,C)=>f.get(y)?.some(S=>S.edgeId!==C&&S.side===k)??!1,"faceIsClaimed");for(let y of e){if(y.isLayoutOnly)continue;let k=y.points;if(!k||k.length<2)continue;let C=Al(k,vd);if(C0){let B=h(F,y,!0);if(B>$||B===$&&O>=M)continue;$=B,M=O,R=F;continue}h(F,y)>w||OI.edgeId!==v));let j=f.get(E);j&&f.set(E,j.filter(I=>I.edgeId!==v)),g(S,p(R[0],_),v),g(E,p(R[R.length-1],b),v)}}}var vd,OPe,$At=G(()=>{"use strict";Ju(),vd=.001,OPe=8,s(NPe,"simplifyDetouredEdges")});function bU(e,t){let r=t?0:e.length-1,i=t?1:-1,n=e[r],a=e[r+i];if(!n||!a)return;let o=a.x-n.x,l=a.y-n.y;if(!(Math.abs(o)+Math.abs(l)a&&uA(e,BPe(a)))}function bA(e,t){let r=[];for(let m of e){if(m.isLayoutOnly)continue;let y=m.points;if(!(!y||y.length<2))for(let k=0;k{let k=GV(y,a);for(let{nodeId:C,rect:S}of i)if(C!==m&&uA(k,S))return!0;return!1},"labelOverlapsForeignNode"),h=s((m,y)=>{let k=GV(y,a);for(let C of r)if(C.edgeId!==m&&cA(C.p1,C.p2,k))return!0;return!1},"labelOverlapsForeignEdge"),d=s((m,y,k)=>u(m,k)||h(y,k),"labelOverlapsAnything"),p=[],f=s(m=>{for(let{id:y,rect:k}of n)if(oPe(k,m))return y},"findContainingLane"),g=s((m,y)=>p.some(k=>k.labelId!==m&&uA(y,k.rect)),"overlapsPlacedLabel");for(let m of e){if(m.isLayoutOnly)continue;let y=m.labelNodeId;if(!y)continue;let k=t.get(y);if(!k)continue;let C=m.points;if(!C||C.length<2)continue;let S=k.width??0,E=k.height??0;if(S<=0||E<=0)continue;let _=[];for(let H=0;H=No&&ue>=No||_.push({idx:H,length:ie+ue,orientation:ie>=No?"horizontal":"vertical",midX:(J.x+te.x)/2,midY:(J.y+te.y)/2})}if(_.length===0)continue;let b=_.length>=3?_.filter(H=>H.idx>0&&H.idx<_.length-1):_,v=b.length>0?b:_,x=S>=E?"horizontal":"vertical",w=s(H=>[...H].sort((J,te)=>{let ie=J.orientation===x,ue=te.orientation===x;if(ie!==ue)return ie?-1:1;let se=J.length>=(J.orientation==="horizontal"?S:E)+2,le=te.length>=(te.orientation==="horizontal"?S:E)+2;return se!==le?se?-1:1:te.length-J.length}),"rankSegments"),R=_[0],$=_[_.length-1],M=[.5,.25,.75,.05,.95,.15,.85,.1,.9],N=s((H,J)=>{let te=C[H.idx],ie=C[H.idx+1];return{midX:te.x+(ie.x-te.x)*J,midY:te.y+(ie.y-te.y)*J}},"anchorAtT"),j=s((H,J,te)=>Math.min(te,Math.max(J,H)),"clamp"),I=s((H,J)=>H.midX>=J.left-No&&H.midX<=J.right+No&&H.midY>=J.top-No&&H.midY<=J.bottom+No,"pointInsideRectInclusive"),z=s(H=>{let J=D2(H.midX,H.midY,S,E),te=f(J);if(te)return{laneId:te,anchor:H,rect:J};let ie=n.find(({rect:ge})=>I(H,ge));if(!ie)return;let ue=ie.rect.left+S/2+o,se=ie.rect.right-S/2-o,le=ie.rect.top+E/2+o,Te=ie.rect.bottom-E/2-o;if(ue>se||le>Te)return;let oe={midX:j(H.midX,ue,se),midY:j(H.midY,le,Te)},fe=D2(oe.midX,oe.midY,S,E);return I(H,fe)?{laneId:ie.id,anchor:oe,rect:fe}:void 0},"placementForAnchor"),F=s((H,J,te)=>H.orientation==="horizontal"?Math.abs(J.midX-te.x):Math.abs(J.midY-te.y),"distanceAlongSegment"),O=s((H,J)=>{let te=(H.orientation==="horizontal"?S/2:E/2)+l;if(H===R){let ie=C[H.idx];if(F(H,J,ie)+No{let J=w(H);for(let te of J)for(let ie of M){let ue=N(te,ie);if(!O(te,ue))continue;let se=z(ue);if(se&&!xU(se.rect,C)&&!g(y,se.rect)&&!d(y,m.id,se.rect))return{laneId:se.laneId,anchor:se.anchor}}},"tryPool"),Y=s((H,J,te=!1)=>{let ie=w(H);for(let ue of ie){let se={midX:ue.midX,midY:ue.midY};if(J&&!O(ue,se))continue;let le=z(se);if(le&&!xU(le.rect,C)&&!g(y,le.rect)&&!u(y,le.rect)&&(te||!h(m.id,le.rect)))return{laneId:le.laneId,anchor:le.anchor}}},"findLaneContainingFallback"),U=B(v)??(v.length<_.length?B(_):void 0)??Y(_,!0)??Y(_,!1)??Y(_,!1,!0);if(U){k.x=U.anchor.midX,k.y=U.anchor.midY,k.parentId=U.laneId;let H=D2(U.anchor.midX,U.anchor.midY,S,E),J=p.findIndex(te=>te.labelId===y);J>=0?p[J]={labelId:y,rect:H}:p.push({labelId:y,rect:H})}}}var No,TU,C3,LAt=G(()=>{"use strict";Ju(),No=.001,TU=10,C3=7,s(bU,"markerClearanceRectFor"),s(BPe,"normalizeRect"),s(xU,"labelOverlapsOwnMarker"),s(bA,"anchorLabelsToPolyline")});function kU(e,t){return e{let d=kU(l,u),p=0,f=s(g=>{if(!g)return;let m=n.get(g);if(!m)return;let y=h==="x"?m.w/2:m.h/2;y>p&&(p=y)},"consider");f(o.labelNodeId);for(let g of e){if(g===o||g.isLayoutOnly)continue;let m=g.start,y=g.end;!m||!y||kU(m,y)===d&&f(g.labelNodeId)}return p>0?p+zPe:0},"labelClearanceFor");for(let o of e){if(o.isLayoutOnly)continue;let l=o.points;if(!YV(l,xA))continue;let u=QV(o,r,xA);if(!u)continue;let{srcId:h,dstId:d,srcInfo:p,dstInfo:f,collinearX:g,collinearY:m}=u;if(g===m)continue;let y,k;if(g){let _=f.cy>p.cy;y={x:p.cx,y:_?p.rect.bottom:p.rect.top},k={x:f.cx,y:_?f.rect.top:f.rect.bottom}}else{let _=f.cx>p.cx;y={x:_?p.rect.right:p.rect.left,y:p.cy},k={x:_?f.rect.left:f.rect.right,y:f.cy}}if(La(y,k,i,[h,d],1))continue;let C=a(o,h,d,g?"x":"y"),S=C>CU?C:CU,E=[0,S,-S];for(let _ of E){let b={...y},v={...k};if(g){if(b.x+=_,v.x+=_,b.x<=p.rect.left||b.x>=p.rect.right||v.x<=f.rect.left||v.x>=f.rect.right)continue}else if(b.y+=_,v.y+=_,b.y<=p.rect.top||b.y>=p.rect.bottom||v.y<=f.rect.top||v.y>=f.rect.bottom)continue;if(!La(b,v,i,[h,d],1)&&!dA(b,v,e,o,{epsilon:xA})){o.points=[b,v];break}}}}var xA,FPe,CU,zPe,IAt=G(()=>{"use strict";Ju(),xA=1e-6,FPe=8,CU=FPe/2,zPe=3,s(kU,"pairKey"),s(PPe,"straightenCollinearSiblingDetours")});function wU(e,t){let{realNodeRects:r,labelNodeRects:i}=sf(t.values()),n=s((y,k)=>M2(k,.001).map(C=>({...C,edge:y,interior:C.index>=1&&C.index<=k.length-3})),"segmentsFor"),a=s(()=>{let y=[];for(let k of e){if(k.isLayoutOnly)continue;let C=k.points;!C||C.length<2||y.push(...n(k,Ui(C)))}return y},"allSegments"),o=s((y,k)=>y.horizontal&&k.horizontal?Do(y.a.x,y.b.x,k.a.x,k.b.x)>=8&&Math.abs(y.a.y-k.a.y)<7:y.vertical&&k.vertical?Do(y.a.y,y.b.y,k.a.y,k.b.y)>=8&&Math.abs(y.a.x-k.a.x)<7:!1,"hasCrowdedParallelTrack"),l=s((y,k)=>{let C=y.start,S=y.end,E=n(y,k);if(E.length!==k.length-1)return!1;let _=[C,S].filter(v=>!!v),b=y.labelNodeId?[y.labelNodeId]:[];for(let v of E)if(La(v.a,v.b,r,_,-2)||La(v.a,v.b,i,b,-2))return!1;for(let v of e){if(v===y||v.isLayoutOnly)continue;let x=v.points;if(!(!x||x.length<2)){for(let w of E)for(let R of n(v,Ui(x)))if(o(w,R)||yd(w.a,w.b,R.a,R.b,.001))return!1}}return!0},"candidateIsSafe"),u=s((y,k)=>{let C=Ui(y.edge.points??[]);if(C.length<4||y.index>=C.length-1)return;let S=C.map(E=>({...E}));if(y.horizontal)S[y.index].y+=k,S[y.index+1].y+=k;else if(y.vertical)S[y.index].x+=k,S[y.index+1].x+=k;else return;return n(y.edge,S).length===S.length-1?S:void 0},"shiftedCandidate"),h=s((y,k)=>({x:y.x??(k.left+k.right)/2,y:y.y??(k.top+k.bottom)/2}),"nodeCenter"),d=s(y=>{let k=y.edge,C=Ui(k.points??[]);if(C.length!==4||y.index!==1)return;let S=k.start?t.get(k.start):void 0,E=k.end?t.get(k.end):void 0,_=S?cl(S):void 0,b=E?cl(E):void 0,v=C.slice(y.index+2);if(!(!S||!E||!_||!b||v.length===0))return{sourceCenter:h(S,_),targetCenter:h(E,b),sourceRect:_,tail:v}},"sourceDetourContextFor"),p=s((y,k,C,S,E,_)=>{let b=S.y>=C.y,v=b?E.bottom:E.top,x=v+(b?20:-20);if(b&&y.b.y<=x+.001||!b&&y.b.y>=x-.001)return;let w=y.a.x+k;return Ui([{x:C.x,y:v},{x:C.x,y:x},{x:w,y:x},{x:w,y:y.b.y},..._],.001)},"verticalSourceDetour"),f=s((y,k,C,S,E,_)=>{let b=S.x>=C.x,v=b?E.right:E.left,x=v+(b?20:-20);if(b&&y.b.x<=x+.001||!b&&y.b.x>=x-.001)return;let w=y.a.y+k;return Ui([{x:v,y:C.y},{x,y:C.y},{x,y:w},{x:y.b.x,y:w},..._],.001)},"horizontalSourceDetour"),g=s((y,k)=>{let C=d(y);if(C){if(y.vertical)return p(y,k,C.sourceCenter,C.targetCenter,C.sourceRect,C.tail);if(y.horizontal)return f(y,k,C.sourceCenter,C.targetCenter,C.sourceRect,C.tail)}},"sourceDetourCandidate"),m=[-7,7,-14,14,-21,21];for(let y=0;y<12;y++){let k=a(),C=!1;for(let S=0;Sx.interior);for(let x of v){for(let w of m){let R=u(x,w);if(R&&l(x.edge,R)){x.edge.points=R,C=!0;break}let $=g(x,w);if($&&l(x.edge,$)){x.edge.points=$,C=!0;break}}if(C)break}}if(!C)return}}var MAt=G(()=>{"use strict";Ju(),s(wU,"nudgeSharedInteriorSubpaths")});function qPe(e,t,r,i){let n=t.x-e.x,a=t.y-e.y,o=i.x-r.x,l=i.y-r.y,u=n*l-a*o;if(Math.abs(u)<1e-10)return!1;let h=r.x-e.x,d=r.y-e.y,p=(h*l-d*o)/u,f=(h*a-d*n)/u,g=.01;return p>g&&p<1-g&&f>g&&f<1-g}function WPe(e){let t=e.nodes??[],r=e.edges??[],i=[];if(!r.length||!t.length)return i;let n=lPe(t),a=1,o=[];for(let u of r){if(u.isLayoutOnly)continue;let h=u.points;if(!h||h.length<2)continue;let d=u.start,p=u.end,f=u.labelNodeId,g=u.id??`${d}->${p}`;for(let m of n)if(!(m.nodeId===d||m.nodeId===p)&&!(f&&m.nodeId===f)){for(let y=0;y0){let u=i.filter(d=>d.type==="edge-node-overlap").length,h=i.filter(d=>d.type==="edge-edge-crossing").length;Ee.warn(`[SWIMLANE_VALIDATE] ${i.length} issue(s) detected: ${u} edge-node overlap(s), ${h} edge crossing(s)`);for(let d of i)Ee.warn(`[SWIMLANE_VALIDATE] ${d.type}: ${d.detail}`)}return i}var DAt=G(()=>{"use strict";Nt(),Ju(),s(qPe,"segmentsIntersect"),s(WPe,"validateSwimlanesLayout")});function jPe(e,t){let r=e.nodes??[],i=e.edges??[],n=r.filter(l=>!l.isGroup);if((t==="LR"||t==="RL")&&n.length>0&&!wPe(e,t)||t==="BT"&&n.length>0&&!CPe(e))return;for(let l of i){if(l.isLayoutOnly)continue;let u=l.points;!u||u.length<2||(l.points=Zu(pA(u)))}NPe(i,r),PPe(i,r),EPe(i,r);let a=new Map;for(let l of r)a.set(String(l.id),l);bA(i,a),pPe(i,a),APe(i,a),wU(i,a),RPe(i,a),$Pe(i,a),mU(i,a),LPe(i,a);let o=s(()=>{DPe(i,a),IPe(i,a),MPe(i,a),bA(i,a),hU(i,a),mU(i,a),bA(i,a),hU(i,a)},"finalizeRenderedEdges");o(),wU(i,a),o(),yU(i,a),vU(i,a),yU(i,a),vU(i,a)}var NAt=G(()=>{"use strict";EAt(),Ju(),SAt(),_At(),AAt(),RAt(),$At(),LAt(),IAt(),MAt(),DAt(),s(jPe,"postProcessSwimlaneLayout")});function of(e){let t=new Map(e.nodeById),r=new Set,i=[];for(let n of e.edges){if(!t.has(n.src)||!t.has(n.dst))continue;let a=`${n.id}:${n.src}->${n.dst}`;r.has(a)||(r.add(a),i.push(n))}return{nodes:[...t.keys()],edges:i,layout:e.layout,nodeById:t}}function EU(e,t){return e.edges.filter(r=>r.dst===t)}function VPe(e){let t=new Map;for(let r of e.nodes)t.set(r,[]);for(let r of e.edges)t.get(r.src).push(r.dst);return t}function SU(e){let t=VPe(e);for(let r of t.values())r.sort((i,n)=>i.localeCompare(n));return t}function _U(e){let t=new Map;for(let r of e.nodes)t.set(r,0);for(let r of e.edges)t.set(r.dst,(t.get(r.dst)??0)+1);return t}function AU(e){return[...e.entries()].filter(([,t])=>t===0).map(([t])=>t).sort((t,r)=>t.localeCompare(r))}function TA(e,t=()=>!0){let r=new Map,i=new Map;for(let n of e.nodes)r.set(n,[]),i.set(n,[]);for(let n of e.edges)t(n)&&(i.get(n.src).push(n.dst),r.get(n.dst).push(n.src));return{preds:r,succs:i}}function RU(e,t,r,i){let n=0;for(let o of e.nodes)i?.skipGroups&&e.nodeById.get(o)?.isGroup||(n=Math.max(n,r[o]??0));let a=Array.from({length:n+1},()=>[]);for(let o of t)i?.skipGroups&&e.nodeById.get(o)?.isGroup||a[Math.max(0,r[o]??0)].push(o);return a}function w3(e){let t=_U(e),r=AU(t),i=[],n=SU(e);for(;r.length;){let a=r.shift();i.push(a);for(let o of n.get(a)??[])if(t.set(o,(t.get(o)??0)-1),(t.get(o)??0)===0){let l=0;for(;l{if(n-i<=1)return 0;let a=i+n>>1,o=r(i,a)+r(a,n),l=i,u=a,h=i;for(;l=n||l{"use strict";s(of,"normalizeGraph"),s(EU,"incoming"),s(VPe,"buildSuccessorMap"),s(SU,"buildSortedSuccessorMap"),s(_U,"buildInDegreeMap"),s(AU,"sortedZeroInDegreeNodes"),s(TA,"buildPredecessorSuccessorMaps"),s(RU,"buildLayersFromRanks"),s(w3,"topoSortIfAcyclic"),s(N2,"buildLayerIndex"),s($U,"countInversions")});function UPe(e){let t=of(e),r=new Map;for(let h of t.nodes)r.set(h,[]);for(let h of t.edges)r.get(h.src).push(h);for(let h of r.values())h.sort((d,p)=>d.dst===p.dst?d.id.localeCompare(p.id):d.dst.localeCompare(p.dst));let i=Object.create(null);for(let h of t.nodes)i[h]=0;let n=[],a=s(h=>{i[h]=1;for(let d of r.get(h)??[]){let p=d.dst;i[p]===0?a(p):i[p]===1&&n.push(d)}i[h]=2},"dfs"),o=[...t.nodes].sort((h,d)=>h.localeCompare(d));for(let h of o)i[h]===0&&a(h);let l=new Set(n.map(h=>`${h.id}:${h.src}->${h.dst}`)),u=t.edges.map(h=>l.has(`${h.id}:${h.src}->${h.dst}`)?{id:h.id,src:h.dst,dst:h.src,weight:h.weight,ref:h.ref}:h);return{acyclic:{nodes:[...t.nodes],edges:u,layout:t.layout,nodeById:new Map(t.nodeById)},reversed:n}}var OAt=G(()=>{"use strict";bd(),s(UPe,"removeCycles_DFS")});function YPe(e){let t=new Map,r=s(i=>{if(t.has(i))return t.get(i);let n=e.nodeById.get(i);if(!n)return t.set(i,null),null;let a=n.parentId;if(!a)return t.set(i,null),null;let o=r(a)??a;return t.set(i,o),o},"resolve");for(let i of e.nodes)r(i);return t}function xd(e){let t=YPe(e);return r=>t.get(r)??null}function kA(e){let t=[];for(let r of e.layout.nodes??[])r.isGroup&&!r.parentId&&t.push(r.id);return[...new Set(t)].reverse()}function LU(e,t){let r=kA(e);if(!t||t.length===0)return r;let i=new Set(r),n=new Set,a=[];for(let o of t)!i.has(o)||n.has(o)||(n.add(o),a.push(o));for(let o of r)n.has(o)||a.push(o);return a}var lf=G(()=>{"use strict";s(YPe,"buildTopLaneMap"),s(xd,"createTopLaneResolver"),s(kA,"buildTopLaneOrder"),s(LU,"resolveTopLaneOrder")}),HPe,E3,IU,S3=G(()=>{"use strict";HPe={EPSILON:1e-6},E3={GRAVITY_ITERATIONS:8,MAX_CROSSING_OPTIMIZATION_PASSES:4,DEFAULT_COMPACT_SINGLE_INPUT:!0},IU={DEFAULT_LAYER_GAP:100,DEFAULT_NODE_GAP:40}});function GPe(e,t){let r=of(e),i=t?.laneOf??(()=>null),n=t?.rankHint,{preds:a}=TA(r);for(let b of a.values())b.sort((v,x)=>v.localeCompare(x));let o=w3(r)??[...r.nodes].sort((b,v)=>b.localeCompare(v)),l=new Map;for(let[b,v]of o.entries())l.set(v,b);let u=new Map,h=new Map;for(let b of r.nodes)h.set(b,[]);for(let b of o){let v=(a.get(b)??[]).filter(x=>u.has(x));if(v.length>0){let x=XPe(b,v,{laneOf:i,rankHint:n,topoIndex:l});u.set(b,x),h.get(x).push(b)}else u.has(b)||u.set(b,null)}for(let b of r.nodes)u.has(b)||u.set(b,null);let d=new Set;for(let b of r.nodes)(u.get(b)??null)===null&&d.add(b);let p=[...d].sort((b,v)=>{let x=l.get(b)??0,w=l.get(v)??0;return x===w?b.localeCompare(v):x-w}),f=KPe(r),g=new Map;for(let[b,v]of f.entries())g.set(b,[...v].sort((x,w)=>x.localeCompare(w)));let m=QPe(g),y=ZPe(g),k=new Map;for(let b of r.nodes)k.set(b,[]);for(let b of y)for(let v of b.nodes){let x=k.get(v);x?x.push(b.id):k.set(v,[b.id])}let C=[],S=[],E=new Set,_=s(b=>{if(!E.has(b)){E.add(b),C.push(b);for(let v of h.get(b)??[])_(v);S.push(b)}},"walk");for(let b of p)_(b);for(let b of o)_(b);return{parent:u,children:h,roots:p,componentOf:m,blocks:y,nodeBlocks:k,adjacency:g,preorder:C,postorder:S,topologicalOrder:o}}function XPe(e,t,r){let i=r.laneOf(e);return[...t].sort((n,a)=>{let o=r.laneOf(n),l=r.laneOf(a),u=o!=null&&o===i,h=l!=null&&l===i;if(u!==h)return u?-1:1;let d=r.rankHint?.[n],p=r.rankHint?.[a];if(d!=null&&p!=null&&d!==p)return p-d;let f=r.topoIndex.get(n)??0,g=r.topoIndex.get(a)??0;return f!==g?f-g:n.localeCompare(a)})[0]}function KPe(e){let t=new Map;for(let r of e.nodes)t.set(r,new Set);for(let r of e.edges)t.get(r.src).add(r.dst),t.get(r.dst).add(r.src);return t}function QPe(e){let t=new Map,r=0;for(let i of e.keys()){if(t.has(i))continue;let n=[i];for(;n.length>0;){let a=n.pop();if(!t.has(a)){t.set(a,r);for(let o of e.get(a)??[])t.has(o)||n.push(o)}}r++}return t}function ZPe(e){let t=new Map,r=new Map,i=[],n=[],a=0,o=s((l,u)=>{t.set(l,++a),r.set(l,a);for(let h of e.get(l)??[])h!==u&&(t.has(h)?(t.get(h)??0)<(t.get(l)??0)&&(i.push([l,h]),r.set(l,Math.min(r.get(l)??a,t.get(h)??a))):(i.push([l,h]),o(h,l),r.set(l,Math.min(r.get(l)??a,r.get(h)??a)),(r.get(h)??0)>=(t.get(l)??0)&&n.push(JPe(l,h,i,n.length))))},"visit");for(let l of e.keys())t.has(l)||o(l,null);return n}function JPe(e,t,r,i){let n=[],a=new Set;for(;r.length>0;){let o=r.pop();if(n.push(o),a.add(o[0]),a.add(o[1]),o[0]===e&&o[1]===t||o[0]===t&&o[1]===e)break}return{id:i,edges:n,nodes:[...a]}}var BAt=G(()=>{"use strict";bd(),s(GPe,"buildDrivingTree"),s(XPe,"chooseParent"),s(KPe,"buildAdjacency"),s(QPe,"assignComponents"),s(ZPe,"computeBlocks"),s(JPe,"popBlock")});function eFe(e,t,r){let i=[...e.nodes],n=new Map;for(let[S,E]of i.entries())n.set(E,S);let a=i.length,o=new Array(a).fill(-1),l=new Array(a).fill(0),u=[],h=new Set;for(let S of i){let E=r.parent.get(S)??null,_=n.get(S);_!=null&&E==null&&(o[_]=-1,l[_]=0,h.has(S)||(h.add(S),u.push(S)))}for(;u.length>0;){let S=u.shift(),E=n.get(S);if(E==null)continue;let _=r.children.get(S)??[];for(let b of _){if(h.has(b))continue;let v=n.get(b);v!=null&&(o[v]=E,l[v]=l[E]+1,h.add(b),u.push(b))}}for(let S of i){if(h.has(S))continue;let E=n.get(S);E!=null&&(o[E]=-1,l[E]=0,h.add(S))}let d=Math.max(1,Math.ceil(Math.log2(Math.max(1,a)))+1),p=Array.from({length:d},()=>new Array(a).fill(-1));for(let S=0;S{if(S===-1||E===-1)return-1;l[S]>b&1&&(S=p[b][S],S===-1))return-1;if(S===E)return S;for(let b=d-1;b>=0;b--){let v=p[b][S],x=p[b][E];v===-1||x===-1||v!==x&&(S=v,E=x)}return p[0][S]},"lcaIndex"),g=Array.from({length:a},()=>new Map);for(let S of e.edges){let E=S.src,_=S.dst,b=t[E],v=t[_];if(b==null||v==null||(b>v&&([E,_]=[_,E],[b,v]=[v,b]),b==null||v==null||b===v))continue;let x=n.get(E),w=n.get(_);if(x==null||w==null)continue;let R=f(x,w);if(R===-1)continue;let $=g[R];for(let M=b;M{if(E.size!==0)for(let[_,b]of E)S.set(_,(S.get(_)??0)+b)},"mergeInto"),k=new Set,C=s(S=>{let E=n.get(S);k.add(S);let _=E==null?void 0:g[E],b=_?new Map(_):new Map,v=r.children.get(S)??[];for(let x of v){let w=C(x),R=t[S];if(R!=null){let $=m.get(S);$||($=new Map,m.set(S,$));let M=w.get(R)??0,N=t[x];N!=null&&N>R&&(M+=1),$.set(x,M)}y(b,w)}return b},"dfs");for(let S of r.roots)k.has(S)||C(S);for(let S of i)k.has(S)||C(S);return m}var PAt=G(()=>{"use strict";s(eFe,"computeSubtreeCrossCounts")});function tFe(e,t,r){let i=new Map,n=s(a=>{let o=r[a]??0,l=[...t.get(a)??[]];l.sort(MU(r));for(let u of l){n(u);let h=i.get(u);h!=null&&(o=Math.min(o,h))}i.set(a,o)},"annotate");for(let a of e)n(a);return i}function MU(e){return(t,r)=>{let i=e[t]??0,n=e[r]??0;return i===n?t.localeCompare(r):i-n}}function rFe(e,t,r,i){let n=0;for(let u of t){let h=r[u]??0;h>n&&(n=h)}let a=Array.from({length:n+1},()=>[]),o=new Set,l=s(u=>{if(o.has(u))return;o.add(u);let h=r[u]??0;a[h]||(a[h]=[]),a[h].push(u);for(let d of i(u))l(d)},"emit");for(let u of e)l(u);for(let u of t)if(!o.has(u)){let h=r[u]??0;a[h]||(a[h]=[]),a[h].push(u),o.add(u)}return a}function iFe(e){let t=[];for(let r of e){let i=new Set,n=[];for(let a of r)i.has(a)||(i.add(a),n.push(a));t.push(n)}return t}var FAt=G(()=>{"use strict";s(tFe,"annotateMinimumLayers"),s(MU,"compareByRankThenId"),s(rFe,"emitNodesInTreeOrder"),s(iFe,"deduplicateLayers")});function nFe(e,t,r,i){return n=>{let a=e.get(n)??[];if(a.length===0)return[];let o=t[n]??0,l=[],u=[],h=r.get(n);for(let d of a){let p=i.get(d)??o;p>o?l.push({child:d,min:p}):u.push(d)}return l.sort((d,p)=>d.min===p.min?d.child.localeCompare(p.child):d.min-p.min),u.sort((d,p)=>{let f=h?.get(d)??0,g=h?.get(p)??0;if(f!==g)return f-g;let m=i.get(d)??o,y=i.get(p)??o;return m!==y?m-y:d.localeCompare(p)}),[...l.map(d=>d.child),...u]}}function CA(e,t,r){let i=GPe(e,{rankHint:t,laneOf:r}),{children:n,roots:a}=i;for(let p of e.nodes)n.has(p)||n.set(p,[]);let o=eFe(e,t,i),l=[...a].sort(MU(t)),u=tFe(l,n,t),h=nFe(n,t,o,u),d=rFe(l,e.nodes,t,h);return d=iFe(d),d}var aFe=G(()=>{"use strict";BAt(),PAt(),FAt(),s(nFe,"createChildOrderer"),s(CA,"buildMultitreeLayerOrder")});function sFe(e,t,r){let i=new Set(e),n=new Set(t),a=N2(t),o=[];for(let l of r)i.has(l.src)&&n.has(l.dst)&&o.push(a.get(l.dst));return $U(o)}function DU(e,t,r){let i=[];for(let a of t){let o=r[a.src],l=r[a.dst];if(o==null||l==null||o===l)continue;let u=a.src,h=a.dst,d=o,p=l;o>l&&(u=a.dst,h=a.src,d=l,p=o);for(let f=d;f(r[f]??0)-(r[p]??0));for(let p of d){let f=r[p]??0;if(f===0)continue;let g=0;for(let C of i.get(p)??[])g=Math.max(g,(r[C]??0)+1);if(g>=f)continue;let m=f;r[p]=g;let y=CA(e,r,n),k=DU(y,e.edges,r);k{"use strict";bd(),S3(),lf(),aFe(),s(sFe,"countCrossingsBetweenAdjacent"),s(DU,"totalCrossings"),s(oFe,"optimizeRanksByCrossings")});function lFe(e,t){let r=xd(e),i=[...e.nodes].sort((n,a)=>(t[n]??0)-(t[a]??0)||n.localeCompare(a));for(let n of i){let a=r(n);if(!a)continue;let o=e.edges.filter(y=>y.src===n);if(o.length===0)continue;let l=!1,u=0;for(let y of o){let k=r(y.dst);k==null||k===a?l=!0:u++}if(u===0||l)continue;let h=0,d=!1;for(let y of e.edges){if(y.dst!==n)continue;let k=r(y.src);k&&(k===a?d=!0:h++)}if(h>0||!d)continue;let p=t[n]??0,f=p+u,g=0;for(let y of e.edges)y.dst===n&&(g=Math.max(g,(t[y.src]??0)+1));let m=Math.max(p,g,f);m!==p&&(t[n]=m)}}var qAt=G(()=>{"use strict";lf(),s(lFe,"adjustCrossLaneSources")});function cFe(e,t){let r=of(e),i=w3(r)??[...r.nodes].sort(),n=t?.compactSingleInput??!1,a=xd(r),o=Object.create(null);for(let l of i){let u=EU(r,l),h=t?.ignoreCrossLaneEdges?u.filter(d=>{let p=a(d.src),f=a(l);return!p||!f?!0:p===f}):u;if(h.length===0)o[l]=0;else if(n&&h.length===1){let d=h[0].src,p=a(d),f=a(l);p!==f?o[l]=o[d]??0:o[l]=(o[d]??0)+1}else{let d=-1/0;for(let p of h)d=Math.max(d,(o[p.src]??0)+1);o[l]=d===-1/0?0:d}}return(t?.optimizeRanksByCrossings??!1)&&(o=oFe(r,o)),t?.ignoreCrossLaneEdges&&lFe(r,o),{layers:CA(r,o,a),rankOf:o,dummy:new Set}}var WAt=G(()=>{"use strict";bd(),lf(),zAt(),qAt(),aFe(),s(cFe,"assignLayers_LongestPath")});function uFe(e,t){let r=of(e),i={...cFe(r,{compactSingleInput:t?.compactSingleInput,ignoreCrossLaneEdges:t?.ignoreCrossLaneEdges,optimizeRanksByCrossings:t?.optimizeRanksByCrossings}).rankOf},n=xd(r),{preds:a,succs:o}=TA(r,f=>{if(t?.ignoreCrossLaneEdges){let g=n(f.src),m=n(f.dst);if(g&&m&&g!==m)return!1}return!0}),l=w3(r)??[...r.nodes],u=[...l].reverse(),h=s((f,g)=>{let m=0;for(let C of a.get(f)??[])m=Math.max(m,(i[C]??0)+1);let y=Number.POSITIVE_INFINITY,k=o.get(f)??[];return k.length>0&&(y=Math.min(...k.map(C=>(i[C]??0)-1))),Number.isFinite(y)||(y=Math.max(m,g)),Math.min(Math.max(g,m),y)},"clampFeasible"),d=E3.GRAVITY_ITERATIONS,p=s(f=>{let g=!1;for(let m of f){let y=a.get(m)??[],k=o.get(m)??[];if(y.length===0&&k.length===0)continue;let C=y.length>0?y.reduce((b,v)=>b+(i[v]??0)+1,0)/y.length:i[m]??0,S=k.length>0?k.reduce((b,v)=>b+(i[v]??0)-1,0)/k.length:i[m]??0,E=Math.round((C+S)/2),_=h(m,E);_!==i[m]&&(i[m]=_,g=!0)}return g},"relaxOrder");for(let f=0;f0){let m=Math.min(...g.map(y=>(i[y]??0)-1));(i[f]??0)>m&&(i[f]=m)}}return{layers:RU(r,l,i),rankOf:i,dummy:new Set}}var jAt=G(()=>{"use strict";bd(),lf(),S3(),WAt(),s(uFe,"assignLayers_Gravity")});function hFe(e){let t=_U(e),r=SU(e),i=AU(t),n=[];for(;i.length>0;){let a=[];for(let o of i){n.push(o);for(let l of r.get(o)??[])t.set(l,(t.get(l)??0)-1),(t.get(l)??0)===0&&a.push(l)}i=a.sort((o,l)=>o.localeCompare(l))}return n.length===e.nodes.length?n:null}function dFe(e,t){let r=of(e),i=t?.direction==="LR"?hFe(r)??[...r.nodes].sort():w3(r)??[...r.nodes].sort(),n=xd(r),a=s(h=>n(h)??h,"laneOf"),o=Object.create(null),l=new Map,u=s((h,d)=>t?.ignoreCrossLaneEdges??!0?a(h)===a(d)?1:0:1,"edgeWeight");for(let h of i){if(r.nodeById.get(h)?.isGroup)continue;let d=EU(r,h),p=0;if(d.length>0)for(let y of d){let k=y.src,C=o[k]??0;p=Math.max(p,C+u(k,h))}let f=a(h),g=l.get(f)??0,m=Math.max(p,g);o[h]=m,l.set(f,m+1)}return{layers:RU(r,i,o,{skipGroups:!0}),rankOf:o,dummy:new Set}}var VAt=G(()=>{"use strict";bd(),lf(),s(hFe,"topoSortByGenerationIfAcyclic"),s(dFe,"assignLayers_LaneAwareCompact")});function pFe(e,t){let r=of(t),{rankOf:i}=e,n=e.layers.map(f=>[...f]),a=new Set(e.dummy?[...e.dummy]:[]),o=0,l=new Map(r.nodeById),u=s(f=>{let g=`placeholder-${o++}`,m={id:g,isGroup:!1,isDummy:!0,width:0,height:0};for(l.set(g,m),a.add(g);n.length<=f;)n.push([]);return n[f].push(g),i[g]=f,g},"addDummyAt"),h=[...r.edges].sort((f,g)=>f.id===g.id?f.src===g.src?f.dst.localeCompare(g.dst):f.src.localeCompare(g.src):f.id.localeCompare(g.id)),d=[];for(let f of h){let g=i[f.src]??0,m=i[f.dst]??0;if(m-g<=1){d.push(f);continue}let y=f.src;for(let C=g+1,S=0;C!r.nodes.includes(f))],edges:d,layout:r.layout,nodeById:l};return{layering:{layers:n,rankOf:i,dummy:a},graphWithDummies:p}}var UAt=G(()=>{"use strict";bd(),s(pFe,"makeProperLayering")});function NU(e){let t=e.length;if(t===0)return Number.POSITIVE_INFINITY;let r=[...e].sort((i,n)=>i-n);return t%2===1?r[(t-1)/2]:.5*(r[t/2-1]+r[t/2])}function OU(e){return e.length===0?Number.POSITIVE_INFINITY:e.reduce((t,r)=>t+r,0)/e.length}function fFe(e,t,r,i){let n=new Map;for(let a of e)n.set(a,[]);for(let a of r)i==="down"?t.has(a.src)&&n.has(a.dst)&&n.get(a.dst).push(t.get(a.src)):t.has(a.dst)&&n.has(a.src)&&n.get(a.src).push(t.get(a.dst));return n}function gFe(e,t,r){let i=r.get(e)??0,n=r.get(t)??0;return i!==n?i-n:e.localeCompare(t)}function BU(e,t,r){let i=new Set(e),n=new Set(t),a=N2(e),o=N2(t),l=[];for(let h of r)i.has(h.src)&&n.has(h.dst)&&l.push({u:a.get(h.src),v:o.get(h.dst)});l.sort((h,d)=>h.u===d.u?h.v-d.v:h.u-d.u);let u=l.map(h=>h.v);return $U(u)}function wA(e,t,r){return[...e].sort((i,n)=>{let a=NU(t.get(i)??[]),o=NU(t.get(n)??[]);return a===o?gFe(i,n,r):isFinite(a)?isFinite(o)?a-o:-1:1})}function PU(e,t,r,i,n,a){let o=N2(e),l=N2(t),u=fFe(t,o,r,i);if(!n||!a||a.length===0)return wA(t,u,l);let h=new Map;for(let f of t){let g=n(f),m=h.get(g)??[];m.push(f),h.set(g,m)}let d=[];for(let f of a){let g=h.get(f);if(!g||g.length===0)continue;let m=wA(g,u,l);d.push(...m)}let p=h.get(null);if(p&&p.length>0){let f=wA(p,u,l);for(let g of f){let m=OU(u.get(g)??[]),y=d.length;if(isFinite(m))for(let[k,C]of d.entries()){let S=OU(u.get(C)??[]);if(mo.has(y.src)&&l.has(y.dst)),d=u?r.filter(y=>l.has(y.src)&&u.has(y.dst)):void 0,p=s(y=>{let k=BU(e,y,h);return d&&i&&(k+=BU(y,i,d)),k},"crossingScore"),f=n?new Map:null;if(n&&f)for(let y of t)f.set(y,n(y));let g=!0,m=p(a);for(;g;){g=!1;for(let y=0;y+1[...l]),n=t.edges,a=xd(t),o=LU(t,r?.laneOrder);for(let l=0;l<3;l++){for(let u=1;u=0;u--)i[u]=PU(i[u+1],i[u],n,"up",a,o),i[u]=FU(i[u+1],i[u],n,i[u-1],a)}return{layers:i}}var YAt=G(()=>{"use strict";bd(),lf(),s(NU,"median"),s(OU,"barycenter"),s(fFe,"neighborPositionsFor"),s(gFe,"currentOrderTieBreak"),s(BU,"countCrossingsBetweenAdjacent"),s(wA,"sortByHeuristic"),s(PU,"reorderLayer"),s(FU,"transposeImprove"),s(mFe,"orderLayers")});function yFe(e,t,r){let i=r?.layerGap??IU.DEFAULT_LAYER_GAP,n=r?.nodeGap??IU.DEFAULT_NODE_GAP,a=r?.laneGap??n*2,o=r?.direction??"TB",l=o==="LR"||o==="RL",u=e.layers,h=Object.create(null),d=Object.create(null),p=s($=>t.nodeById.get($),"getNode"),f=s($=>p($)?.width??0,"getWidth"),g=s($=>p($)?.height??0,"getHeight"),m=xd(t),y=LU(t,r?.laneOrder),k=u.map($=>$.reduce((M,N)=>Math.max(M,g(N)),0)),C=[];if(l)for(let $=0;$+1Math.max(B,f(Y)),0),N=u[$+1].reduce((B,Y)=>Math.max(B,f(Y)),0),j=k[$],I=k[$+1],z=j/2+I/2,F=(M+N)/2,O=Math.max(0,F-z-i);C.push(O)}let S=new Set;for(let $ of u)for(let M of $)S.add(m(M));let E=S.has(null),_=y.filter($=>S.has($)),b=[...E?[null]:[],..._],v=Object.create(null);for(let $ of _)v[$]=0;E&&(v.null=0);for(let $ of u){let M=Object.create(null),N=[];for(let j of $){let I=m(j);I===null?N.push(j):(M[I]||=[]).push(j)}for(let[j,I]of Object.entries(M)){let z=I.reduce((F,O)=>F+f(O),0)+n*Math.max(0,I.length-1);v[j]=Math.max(v[j]??0,z)}if(E&&N.length){let j=N.reduce((I,z)=>I+f(z),0)+n*Math.max(0,N.length-1);v.null=Math.max(v.null??0,j)}}let x=new Map;{let $=b.map(N=>(N===null?v.null:v[N])??0),M=-($.reduce((N,j)=>N+j,0)+a*Math.max(0,b.length-1))/2;for(let N=0;Nf(H)),Y=B.reduce((H,J)=>H+J,0)+n*(F.length-1),U=O-Y/2;for(let[H,J]of F.entries()){let te=B[H];h[J]=U+te/2,d[J]=w+N/2,U+=te+n}}}let I=C[$]??0;w+=N+i+I}let R=new Map;for(let $ of t.edges){let M=$.ref.id;R.has(M)||R.set(M,[]),R.get(M).push($)}for(let[,$]of R){if($.length===0)continue;let M=$[0].ref,N=M.start,j=M.end;if(N==null||j==null)continue;let I=Math.round(((h[N]??0)+(h[j]??0))/2),z=new Set;for(let F of $)z.add(F.src),z.add(F.dst);for(let F of z)F===N||F===j||t.nodeById.get(F)?.isDummy&&(h[F]=I)}return{x:h,y:d}}var HAt=G(()=>{"use strict";S3(),lf(),s(yFe,"assignCoordinates")});function vFe(e){let t=2166136261;for(let r=0;r>>0}function bFe(e){let t=e>>>0;return()=>{t+=1831565813;let r=t;return r=Math.imul(r^r>>>15,r|1),r^=r+Math.imul(r^r>>>7,r|61),((r^r>>>14)>>>0)/4294967296}}function xFe(e,t){let r=[...e],i=bFe(t);for(let n=r.length-1;n>0;n--){let a=Math.floor(i()*(n+1));[r[n],r[a]]=[r[a],r[n]]}return r}function TFe(e,t){let r=0;for(let[i,n]of e.entries())r+=Math.abs(i-(t.get(n)??i));return r}function zU(e,t){let r=new Map;for(let[n,a]of e.entries())r.set(a,n);let i=0;for(let{a:n,b:a,weight:o}of t){let l=r.get(n),u=r.get(a);l==null||u==null||(i+=o*Math.abs(l-u))}return i}function kFe(e){let t=kA(e);if(t.length<2)return[];let r=new Map(t.map((a,o)=>[a,o])),i=xd(e),n=new Map;for(let a of e.layout.edges??[]){if(a.isLayoutOnly)continue;let o=typeof a.start=="string"?a.start:void 0,l=typeof a.end=="string"?a.end:void 0;if(!o||!l||!e.nodeById.has(o)||!e.nodeById.has(l))continue;let u=i(o),h=i(l);if(!u||!h||u===h)continue;let d=r.get(u),p=r.get(h);if(d==null||p==null)continue;let[f,g]=d<=p?[u,h]:[h,u],m=`${f}\0${g}`,y=n.get(m);y?y.weight++:n.set(m,{a:f,b:g,weight:1})}return[...n.values()]}function qU(e,t,r){let i=[...e],n=zU(i,t),a=!0,o=0,l=Math.max(1,i.length);for(;a&&on.a===a.a?n.b.localeCompare(a.b):n.a.localeCompare(a.a)).map(({a:n,b:a,weight:o})=>`${n}:${a}:${o}`).join("|");return vFe(`${e.join("|")}#${i}#${r}`)}function EFe(e,t={}){let r=kA(e);if(r.length<2)return r;let i=kFe(e);if(i.length===0)return r;let n=new Map(r.map((l,u)=>[l,u])),a=qU(r,i,n),o=Math.max(0,t.restarts??WU);for(let l=0;l{"use strict";lf(),WU=8,s(vFe,"hashString"),s(bFe,"mulberry32"),s(xFe,"deterministicShuffle"),s(TFe,"sourceDistance"),s(zU,"laneArrangementCost"),s(kFe,"buildWeightedLaneEdges"),s(qU,"greedySwitch"),s(CFe,"isBetterCandidate"),s(wFe,"seedForRestart"),s(EFe,"optimizeTopLaneOrder")});function SFe(e,t){let r=t?.ignoreCrossLaneEdges??!0,i=t?.optimizeRanksByCrossings??!0,n=of(e),a=t?.automaticLaneOrdering?EFe(n,{restarts:WU}):void 0,o=UPe(n),l=o.acyclic,u=r?dFe(l,{compactSingleInput:t?.compactSingleInput??E3.DEFAULT_COMPACT_SINGLE_INPUT,ignoreCrossLaneEdges:!0,direction:t?.direction}):uFe(l,{compactSingleInput:t?.compactSingleInput??E3.DEFAULT_COMPACT_SINGLE_INPUT,ignoreCrossLaneEdges:!1,optimizeRanksByCrossings:i}),{layering:h,graphWithDummies:d}=pFe(u,l),p=mFe(h,d,{laneOrder:a}),f=yFe(p,d,{layerGap:t?.layerGap,nodeGap:t?.nodeGap,direction:t?.direction,laneOrder:a});return{acyclic:l,reversed:o.reversed,layering:h,ordered:p,coordinates:f}}var XAt=G(()=>{"use strict";bd(),OAt(),jAt(),VAt(),UAt(),YAt(),HAt(),S3(),GAt(),s(SFe,"sugiyamaLayout")});function jU(e,t,r){let i=e.x??0,n=e.y??0,a=t.x-i,o=t.y-n,l=Math.abs(a),u=Math.abs(o);return lbi&&u*3>=l?o>0?"bottom":"top":l>bi?a>0?"right":"left":r}function VU(e,t){return Math.abs(e.to-t.from)he.isGroup&&!he.parentId);for(let he of h){let ye={id:he.id},be=s(Ie=>{o.set(Ie.id,ye),r.filter(pe=>pe.parentId===Ie.id).forEach(be)},"assignLane");be(he)}let d=r.filter(he=>!he.isGroup&&!he.isEdgeLabel).map(he=>{let ye=he.width??10,be=he.height??10,Ie=he.x??0,pe=he.y??0,Qe=AFe;return{nodeId:he.id,minX:Ie-ye/2-Qe,maxX:Ie+ye/2+Qe,minY:pe-be/2-Qe,maxY:pe+be/2+Qe,visualXHalfExtent:u?be/2+Qe:ye/2+Qe}}),p=s((he,ye,be,Ie)=>{let pe=l.find(Qe=>Qe.orientation===he&&Math.abs(Qe.coord-ye)<1);return pe||(pe={id:`pipe-${he}-${ye.toFixed(0)}`,orientation:he,coord:ye,spanMin:be,spanMax:Ie,tracks:[]},l.push(pe)),pe.spanMin=Math.min(pe.spanMin,be),pe.spanMax=Math.max(pe.spanMax,Ie),pe},"getOrAddPipe"),f=s((he,ye)=>{let be=he.width??10,Ie=he.height??10,pe=he.x??0,Qe=he.y??0;switch(ye){case"top":return{x:pe,y:Qe-Ie/2};case"bottom":return{x:pe,y:Qe+Ie/2};case"left":return{x:pe-be/2,y:Qe};case"right":return{x:pe+be/2,y:Qe}}},"portForSide"),g=s((he,ye,be)=>f(he,jU(he,ye,be?"bottom":"top")),"getOrthogonalPort"),m=[],y=[],k=new Set,C=1e3,S=s((he,ye,be)=>{if(m.length===0)return 0;let Ie=Math.abs(ye.y-be.y)K||de.from-bi<=W&&de.to+bi>=W&&(Qe+=C)}else if(pe){let W=ye.x,Ae=Math.min(ye.y,be.y)-bi,K=Math.max(ye.y,be.y)+bi;if(K<=Ae)return 0;for(let de of m)de.edgeIndex===he||de.orientation!=="horizontal"||de.pipe.coordK||de.from-bi<=W&&de.to+bi>=W&&(Qe+=C)}return Qe},"crossingPenalty"),E=n.map((he,ye)=>{if(!he.start||!he.end)return{idx:ye,crossLane:0,dx:0,dy:0};let be=a.get(he.start),Ie=a.get(he.end),pe=o.get(he.start),Qe=o.get(he.end),W=pe&&Qe&&pe.id!==Qe.id?1:0,Ae=be&&Ie?Math.abs((Ie.x??0)-(be.x??0)):0,K=be&&Ie?Math.abs((Ie.y??0)-(be.y??0)):0;return{idx:ye,crossLane:W,dx:Ae,dy:K}}).sort((he,ye)=>{if(he.crossLane!==ye.crossLane)return ye.crossLane-he.crossLane;let be=he.dx+he.dy,Ie=ye.dx+ye.dy;return Math.abs(be-Ie)>1?be-Ie:he.idx-ye.idx}).map(he=>he.idx),_=s((he,ye,be,Ie)=>{let pe=Math.min(he.x,ye.x),Qe=Math.max(he.x,ye.x),W=Math.min(he.y,ye.y),Ae=Math.max(he.y,ye.y);return!!d.find(K=>be&&K.nodeId===be||Ie&&K.nodeId===Ie?!1:Math.abs(he.x-ye.x)>bi?K.minYhe.y&&K.maxX>pe&&K.minXhe.x&&K.maxY>W&&K.minYjU(he,ye,"bottom"),"determineSide"),w=new Map;for(let[he,ye]of n.entries()){if(!ye.start||!ye.end||ye.start===ye.end||ye.points&&ye.points.length>0)continue;let be=a.get(ye.start),Ie=a.get(ye.end);if(!be||!Ie)continue;let pe=(Ie.x??0)-(be.x??0),Qe=(Ie.y??0)-(be.y??0);w.set(he,{edgeIdx:he,srcId:ye.start,dstId:ye.end,srcSide:x(be,{x:Ie.x??0,y:Ie.y??0}),dstSide:x(Ie,{x:be.x??0,y:be.y??0}),absDx:Math.abs(pe),absDy:Math.abs(Qe),dxSign:Math.sign(pe),dySign:Math.sign(Qe)})}let R=s(he=>he.srcSide==="top"||he.srcSide==="bottom"?he.absDx===0?1/0:he.absDy/he.absDx:he.absDy===0?1/0:he.absDx/he.absDy,"preferenceStrength"),$=s(he=>he.srcSide==="top"||he.srcSide==="bottom"?he.dxSign>=0?"right":"left":he.dySign>=0?"bottom":"top","secondarySide"),M=new Map;for(let he of w.values()){let ye=`${he.srcId}:${he.srcSide}`;M.has(ye)||M.set(ye,[]),M.get(ye).push(he)}let N=new Map,j=s((he,ye)=>`${he}:${ye}`,"loadKey");for(let he of w.values())N.set(j(he.srcId,he.srcSide),(N.get(j(he.srcId,he.srcSide))??0)+1),N.set(j(he.dstId,he.dstSide),(N.get(j(he.dstId,he.dstSide))??0)+1);for(let he of M.values())if(!(he.length<2)){he.sort((ye,be)=>{let Ie=R(ye),pe=R(be);return Math.abs(Ie-pe)>1e-9?pe-Ie:ye.edgeIdx-be.edgeIdx});for(let ye=1;ye=pe||(N.set(j(be.srcId,be.srcSide),pe-1),N.set(j(be.srcId,Ie),Qe+1),be.srcSide=Ie)}}let I=s(he=>{let ye=he?.shape;return ye==="question"||ye==="diamond"},"isDiamondNode"),z=new Map;for(let he of w.values())z.has(he.dstId)||z.set(he.dstId,new Set),z.get(he.dstId).add(he.dstSide);for(let he of w.values()){if(!I(a.get(he.srcId)))continue;let ye=z.get(he.srcId);if(!ye?.has(he.srcSide))continue;let be=$(he);if(ye.has(be)||(N.get(j(he.srcId,be))??0)>0)continue;let Ie=N.get(j(he.srcId,he.srcSide))??0;N.set(j(he.srcId,he.srcSide),Math.max(0,Ie-1)),N.set(j(he.srcId,be),1),he.srcSide=be}for(let he of w.values()){let{edgeIdx:ye,srcId:be,dstId:Ie,srcSide:pe,dstSide:Qe}=he,W=a.get(be),Ae=a.get(Ie),K=`${be}:${pe}:src`,de=pe==="top"||pe==="bottom"?Ae.x??0:Ae.y??0;b.has(K)||b.set(K,[]),b.get(K).push({edgeIdx:ye,oppositeCoord:de});let ee=`${Ie}:${Qe}:dst`,ve=Qe==="top"||Qe==="bottom"?W.x??0:W.y??0;b.has(ee)||b.set(ee,[]),b.get(ee).push({edgeIdx:ye,oppositeCoord:ve})}let F=new Map,O=8;for(let[he,ye]of b){if(ye.length<2)continue;ye.sort((X,me)=>X.oppositeCoord-me.oppositeCoord);let be=he.split(":"),Ie=be.slice(0,-2).join(":"),pe=be[be.length-2],Qe=be[be.length-1],W=a.get(Ie);if(!W)continue;let Ae=pe==="left"||pe==="right"?W.height??10:W.width??10,K=W.shape,de=K==="question"||K==="diamond"?Ae*.3:Ae,ee=Math.min(20,Math.max(O,de/(ye.length+1))),ve=-(ee*(ye.length-1))/2;for(let[X,me]of ye.entries()){let Ce=ve+X*ee,Be=`${me.edgeIdx}:${Qe}`;F.set(Be,Ce)}}let B=s(he=>!!n[he]?.labelNodeId,"edgeHasLabelNode"),Y=s((he,ye)=>he?(b.get(`${he}:${ye}:src`)??[]).some(({edgeIdx:be})=>B(be))||(b.get(`${he}:${ye}:dst`)??[]).some(({edgeIdx:be})=>B(be)):!1,"faceHasLabelNode"),U=s((he,ye,be)=>ye==="top"||ye==="bottom"?{x:he.x+be,y:he.y}:{x:he.x,y:he.y+be},"applyPortOffset"),H=s((he,ye,be)=>{let Ie=w.get(he),pe={x:be.x??0,y:be.y??0},Qe={x:ye.x??0,y:ye.y??0},W=Ie?.srcSide??x(ye,pe),Ae=Ie?.dstSide??x(be,Qe),K=Ie?f(ye,Ie.srcSide):g(ye,pe,!0),de=Ie?f(be,Ie.dstSide):g(be,Qe,!1),ee=F.get(`${he}:src`),ve=F.get(`${he}:dst`);return ee!==void 0&&(K=U(K,W,ee)),ve!==void 0&&(de=U(de,Ae,ve)),{pSrcPort:K,pDstPort:de,srcSide:W,dstSide:Ae}},"portsForEdge");for(let he of E){let ye=n[he];if(y[he]=[],!ye.start||!ye.end||ye.points&&ye.points.length>0||ye.start===ye.end)continue;let be=a.get(ye.start),Ie=a.get(ye.end);if(!be||!Ie)continue;let{pSrcPort:pe,pDstPort:Qe,srcSide:W,dstSide:Ae}=H(he,be,Ie),K={...pe},de={...Qe},ee=W==="top"||W==="bottom",ve=Ae==="top"||Ae==="bottom";if(ee){let vt=pe.y>(be.y??0);K.y=vt?pe.y+Rl:pe.y-Rl}else{let vt=pe.x>(be.x??0);K.x=vt?pe.x+Rl:pe.x-Rl}if(ve){let vt=Qe.y>(Ie.y??0);de.y=vt?Qe.y+Rl:Qe.y-Rl}else{let vt=Qe.x>(Ie.x??0);de.x=vt?Qe.x+Rl:Qe.x-Rl}let X=s((vt,wt)=>{for(let Wt of d)if(!wt.includes(Wt.nodeId)&&vt.x>Wt.minX&&vt.xWt.minY&&vt.y{if(Ir){let ki=vt.y>(wt.y??0);return{x:(Wt.x??0)>=vt.x?kr.maxX+cf:kr.minX-cf,y:ki?kr.maxY+Vg:kr.minY-Vg,leavesPositiveSide:ki}}let Lr=vt.x>(wt.x??0),di=(Wt.y??0)>=vt.y;return{x:Lr?kr.maxX+cf:kr.minX-cf,y:di?kr.maxY+Vg:kr.minY-Vg,leavesPositiveSide:Lr}},"obstacleDetour"),Ce=[],Be=[ye.start,ye.end],Re=X(K,Be);if(Re.inside&&Re.obstacle){let vt=Re.obstacle;if(ee){let wt=me(pe,be,Ie,vt,!0);K.x=wt.x,K.y=wt.y;let Wt=wt.leavesPositiveSide?Math.min(vt.minY-2,pe.y+Rl):Math.max(vt.maxY+2,pe.y-Rl);Ce=[{x:pe.x,y:Wt},{x:wt.x,y:Wt},{x:wt.x,y:wt.y}]}else{let wt=me(pe,be,Ie,vt,!1),Wt=wt.leavesPositiveSide?Math.min(vt.minX-2,pe.x+Rl):Math.max(vt.maxX+2,pe.x-Rl);K.x=wt.x,K.y=wt.y,Ce=[{x:Wt,y:pe.y},{x:Wt,y:wt.y},{x:wt.x,y:wt.y}]}}let Ke=[],De=X(de,Be);if(De.inside&&De.obstacle){let vt=De.obstacle;if(ve){let wt=me(Qe,Ie,be,vt,!0);de.x=wt.x,de.y=wt.y,Ke=[{x:wt.x,y:wt.y},{x:Qe.x,y:wt.y}]}else{let wt=me(Qe,Ie,be,vt,!1);de.x=wt.x,de.y=wt.y,Ke=[{x:wt.x,y:wt.y},{x:wt.x,y:Qe.y}]}}if(Ce.length===0&&Ke.length===0){let vt=cf,wt=Math.abs(K.x-de.x)1||Lr>1,ki=v.get(ye.start??"")??0,qr=v.get(ye.end??"")??0,fi=Ir>1&&Y(ye.start,W)||Lr>1&&Y(ye.end,Ae),gt=Ir<=1||ki<=2,Mt=Lr<=1||qr<=2;if((wt||Wt)&&!kr&&(!di||di&&!fi&>&&Mt)&&!_(pe,Qe,ye.start,ye.end)){ye.points=[{...pe},{...K},{...de},{...Qe}],k.add(he);let kt=Wt?"horizontal":"vertical",Qt=Wt?pe.y:pe.x,Pe=Wt?Math.min(pe.x,Qe.x):Math.min(pe.y,Qe.y),ii=Wt?Math.max(pe.x,Qe.x):Math.max(pe.y,Qe.y),Ye={id:`fast-path-${kt}-${Qt.toFixed(0)}-${he}`,orientation:kt,coord:Qt,spanMin:Pe,spanMax:ii,tracks:[]};m.push({edgeIndex:he,segmentIndex:0,orientation:kt,pipe:Ye,trackIndex:0,from:Pe,to:ii});continue}}let Ze=p("vertical",K.x,K.y,K.y);K.x=Ze.coord;let Fe=p("vertical",de.x,de.y,de.y);de.x=Fe.coord;let Je=Math.min(K.x,de.x)-50,Le=Math.max(K.x,de.x)+50,ct=Math.min(K.y,de.y)-50,qe=Math.max(K.y,de.y)+50;for(let vt of d){let wt=Math.min(K.x,de.x),Wt=Math.max(K.x,de.x),kr=Math.min(K.y,de.y),Ir=Math.max(K.y,de.y);vt.minXwt&&vt.minYkr&&(Je=Math.min(Je,vt.minX-_3),Le=Math.max(Le,vt.maxX+_3),ct=Math.min(ct,vt.minY-_3),qe=Math.max(qe,vt.maxY+_3))}for(let vt of d){if(vt.maxXLe||vt.maxYqe)continue;let wt=cf;p("horizontal",vt.minY-wt,Je,Le),p("horizontal",vt.maxY+wt,Je,Le);let Wt=Vg;p("vertical",vt.minX-Wt,ct,qe),p("vertical",vt.maxX+Wt,ct,qe)}p("horizontal",K.y,Je,Le),p("horizontal",de.y,Je,Le);let mt=l.filter(vt=>vt.orientation==="horizontal"&&vt.coord>=ct&&vt.coord<=qe),St=l.filter(vt=>vt.orientation==="vertical"&&vt.coord>=Je&&vt.coord<=Le),Lt=s((vt,wt)=>`${vt.toFixed(1)},${wt.toFixed(1)}`,"getKey"),Bt=Lt(K.x,K.y),qt=Lt(de.x,de.y),tr=new Map,Et=new Map,_t=new Map,Kt=new Set,$t=[];tr.set(Bt,0),_t.set(Bt,"n"),$t.push({key:Bt,f:Math.hypot(de.x-K.x,de.y-K.y),pt:K}),Kt.add(Bt);let Ot=[],Jt=s((vt,wt)=>_(vt,wt,ye.start,ye.end),"checkSegmentBlocked"),Br={x:de.x,y:K.y},At=Jt(K,Br),jr=Jt(Br,de),br=At||jr,Hr={x:K.x,y:de.y},Ei=Jt(K,Hr),Li=Jt(Hr,de);if(br?Ei||Li||(Math.abs(K.x-de.x)0;){$t.sort((qr,fi)=>qr.f-fi.f);let vt=$t.shift();if(Kt.delete(vt.key),vt.key===qt){let qr=qt,fi=de;for(Ot=[fi];Et.has(qr);){let gt=Et.get(qr);Ot.unshift(gt),fi=gt,qr=Lt(gt.x,gt.y)}break}let wt=vt.pt.x,Wt=vt.pt.y,kr=St.sort((qr,fi)=>qr.coord-fi.coord),Ir=kr.findIndex(qr=>Math.abs(qr.coord-wt)<1),Lr=mt.sort((qr,fi)=>qr.coord-fi.coord),di=Lr.findIndex(qr=>Math.abs(qr.coord-Wt)<1),ki=[];Ir>0&&ki.push({x:kr[Ir-1].coord,y:Wt}),Ir>=0&&Ir0&&ki.push({x:wt,y:Lr[di-1].coord}),di>=0&&diAn.nodeId===ye.start||An.nodeId===ye.end?!1:fi!==gt?An.minYWt&&An.maxX>fi&&An.minXwt&&An.maxY>Mt&&An.minY10&&Ao<-5||Na<-10&&Ao>5)&&(Ye=Math.abs(Ao)*100),(ti>10&&ka<-5||ti<-10&&ka>5)&&(Ye+=Math.abs(ka)*50);let Qo=0,Zo=_t.get(vt.key)??"n",nn=Math.abs(ka)>bi?"h":"v";Zo!=="n"&&Zo!==nn&&(Qo=50);let fs=Pe+ii+Ye+Qo,gs=(tr.get(vt.key)??1/0)+fs,ms=Math.abs(de.x-qr.x)+Math.abs(de.y-qr.y);if(gs<(tr.get(Qt)??1/0))if(Et.set(Qt,vt.pt),tr.set(Qt,gs),_t.set(Qt,nn),!Kt.has(Qt))$t.push({key:Qt,f:gs+ms,pt:qr}),Kt.add(Qt);else{let An=$t.findIndex(We=>We.key===Qt);An!==-1&&($t[An].f=gs+ms)}}}if(Ot.length===0&&(Ot=[K,{x:K.x,y:de.y},de]),Ot.length>4){let vt=Ot[0],wt=Ot[Ot.length-1],Wt=Math.min(vt.x,wt.x),kr=Math.max(vt.x,wt.x),Ir=Math.min(vt.y,wt.y),Lr=Math.max(vt.y,wt.y);for(let Mt of Ot)Wt=Math.min(Wt,Mt.x),kr=Math.max(kr,Mt.x),Ir=Math.min(Ir,Mt.y),Lr=Math.max(Lr,Mt.y);let di=kr>Math.max(vt.x,wt.x),ki=WtYe.minXkt&&Ye.minYQt);if(ii.length>0){let Ye=Math.max(vt.x,wt.x);for(let ti of ii){let Na=(ti.minX+ti.maxX)/2;if(ti.visualXHalfExtent===void 0||isNaN(ti.visualXHalfExtent))continue;let ka=Na+ti.visualXHalfExtent+Mt;Ye=Math.max(Ye,ka)}isNaN(Ye)||(kr=Ye)}}if(ki){let kt=d.filter(Qt=>Qt.minXMath.min(vt.y,wt.y));if(kt.length>0){let Qt=Math.min(vt.x,wt.x);for(let Pe of kt){let ii=(Pe.minX+Pe.maxX)/2-Pe.visualXHalfExtent-Mt;Qt=Math.min(Qt,ii)}Wt=Qt}}}let qr=s(Mt=>{let kt=wt.y>vt.y,Qt=d.filter(Ye=>{let ti=Math.min(vt.x,wt.x)Ye.minX,Na=Math.min(vt.y,wt.y)Ye.minY;return ti&&Na}),Pe=Qt;if(u&&Qt.length>0){let Ye=Qt.filter(ti=>ti.minXMt);Ye.length>0&&(Pe=Ye)}if(Pe.length===0)return wt.y;let ii=cf;if(kt){let Ye=Math.max(...Pe.map(ti=>ti.maxY))+ii;if(Yeti.minY))-ii;if(Ye>wt.y+bi)return Ye}return wt.y},"findBestReturnY"),fi=s(Mt=>{let kt=qr(Mt),Qt={x:Mt,y:vt.y},Pe={x:Mt,y:kt},ii={x:wt.x,y:kt},Ye=Jt(vt,Qt),ti=Jt(Qt,Pe),Na=Jt(Pe,ii),ka=kt!==wt.y?Jt(ii,wt):!1;return!Ye&&!ti&&!Na&&!ka?Math.abs(kt-wt.y)=3){let vt=Cr[Cr.length-1],wt=Cr[Cr.length-2],Wt=Cr[Cr.length-3],kr=Math.abs(Wt.y-wt.y)Math.abs(vt.x-Wt.x)&&Cr.splice(-2,1)}else if(Ir){let Lr=Math.sign(wt.y-Wt.y),di=Math.sign(vt.y-Wt.y);Lr!==0&&Lr===di&&Math.abs(wt.y-Wt.y)>Math.abs(vt.y-Wt.y)&&Cr.splice(-2,1)}}let Ti=[Cr[0]];for(let vt=1;vtwt.x,Lr=kr.x>Wt.x;if(Ir!==Lr){Ti.push(Wt);continue}continue}if(Math.abs(wt.x-Wt.x)wt.y,Lr=kr.y>Wt.y;if(Ir!==Lr){Ti.push(Wt);continue}continue}Ti.push(Wt)}Ti.push(Cr[Cr.length-1]);for(let vt=0;vthe.from{let pe=!Ie.segments.some(W=>(W.edgeIndex!==ye.edgeIndex||W.segmentIndex!==ye.segmentIndex)&&J(W,he)),Qe=!be.segments.some(W=>(W.edgeIndex!==he.edgeIndex||W.segmentIndex!==he.segmentIndex)&&J(W,ye));return pe&&Qe?(he.trackIndex=Ie.index,ye.trackIndex=be.index,be.segments=[...be.segments.filter(W=>W.edgeIndex!==he.edgeIndex||W.segmentIndex!==he.segmentIndex),{edgeIndex:ye.edgeIndex,segmentIndex:ye.segmentIndex,from:ye.from,to:ye.to}],Ie.segments=[...Ie.segments.filter(W=>W.edgeIndex!==ye.edgeIndex||W.segmentIndex!==ye.segmentIndex),{edgeIndex:he.edgeIndex,segmentIndex:he.segmentIndex,from:he.from,to:he.to}],!0):!1},"trySwapSegmentsAcrossTracks"),ie=s(he=>{let ye=he.tracks.length;return he.tracks[ye]={index:ye,coord:he.coord,segments:[]},ye},"createNewTrack"),ue=s((he,ye)=>{let be=he.pipe.tracks[he.trackIndex];be.segments=be.segments.filter(Ie=>Ie.edgeIndex!==he.edgeIndex||Ie.segmentIndex!==he.segmentIndex),he.trackIndex=ye,he.pipe.tracks[ye].segments.push({edgeIndex:he.edgeIndex,segmentIndex:he.segmentIndex,from:he.from,to:he.to})},"moveSegmentToTrack"),se=s((he,ye)=>{let be=y[he.edgeIndex];for(let Ie of be){let pe=m[Ie];pe.pipe===he.pipe&&ue(pe,ye)}},"moveSegmentChainToTrack"),le=s(he=>{let ye=y[he.edgeIndex],be=ye.indexOf(m.indexOf(he)),Ie=[];return be>0&&Ie.push(m[ye[be-1]]),be{if(he.orientation===ye.orientation)return!1;let be=he.orientation==="horizontal"?he:ye,Ie=he.orientation==="horizontal"?ye:he;return Ie.pipe.coord>be.from&&Ie.pipe.coordIe.from&&be.pipe.coord{for(let be of he.tracks)if(!be.segments.some(Ie=>(Ie.edgeIndex!==ye.edgeIndex||Ie.segmentIndex!==ye.segmentIndex)&&J(Ie,ye)))return be.index;return-1},"findAvailableTrack"),fe=s((he,ye)=>{if(he.trackIndex===ye.trackIndex)return J(he,ye);let be=le(he),Ie=le(ye);return be.some(pe=>Ie.some(Qe=>Te(pe,Qe)))},"segmentsConflict"),ge=s((he,ye,be)=>{if(te(he,ye,he.pipe.tracks[he.trackIndex],ye.pipe.tracks[ye.trackIndex]))return;let Ie=oe(he.pipe,ye);be(ye,Ie!==-1?Ie:ie(he.pipe))},"resolveTrackConflict"),ke=s(he=>{let ye=0;for(let be=0;be{if(Ne.has(he))return Ne.get(he);let ye=y[he];if(ye.length===0){let W={dest:0,deviation:0,base:0,delta:0};return Ne.set(he,W),W}let be=m[ye[0]].pipe.coord,Ie=be;for(let W=1;WMath.abs(de-be)?K:de;break}}let pe=Math.abs(Ie-be),Qe={dest:Ie,deviation:pe,base:be,delta:Ie-be};return Ne.set(he,Qe),Qe},"getDestInfo"),$e=s(()=>{let he=0,ye=new Map;for(let[Ie,pe]of n.entries())y[Ie].length!==0&&pe.start&&(ye.has(pe.start)||ye.set(pe.start,[]),ye.get(pe.start).push(Ie));let be=s(Ie=>{let pe=n[Ie];if(!pe.start||!pe.end)return 0;let Qe=a.get(pe.start),W=a.get(pe.end);if(!Qe||!W)return 0;let Ae=(W.x??0)-(Qe.x??0),K=(W.y??0)-(Qe.y??0);return Math.abs(Ae)+Math.abs(K)},"getEdgeDistance");for(let Ie of ye.values()){Ie.sort((Qe,W)=>{let Ae=xe(Qe),K=xe(W);if(Math.abs(Ae.deviation-K.deviation)>1)return Ae.deviation-K.deviation;if(Math.abs(Ae.dest-K.dest)>1)return Ae.dest-K.dest;let de=be(Qe),ee=be(W);if(Math.abs(de-ee)>1)return ee-de;let ve=y[Qe].length,X=y[W].length;if(ve!==X)return ve-X;if(ve===1){let me=y[Qe][0],Ce=y[W][0];if(m[me]&&m[Ce]){let Be=m[me],Re=m[Ce],Ke=Math.abs(Be.to-Be.from),De=Math.abs(Re.to-Re.from);if(Math.abs(Ke-De)>1)return Ke-De}}return 0});let pe=Ie.map(Qe=>m[y[Qe][0]]);he+=ke(pe)}return he},"fixSourceHandleCrossings"),Me=s(()=>{let he=0,ye=new Map;for(let[be,Ie]of n.entries())y[be].length!==0&&Ie.end&&(ye.has(Ie.end)||ye.set(Ie.end,[]),ye.get(Ie.end).push(be));for(let be of ye.values()){be.sort((pe,Qe)=>{let W=s(de=>{let ee=y[de];if(ee.length<2)return 0;let ve=m[ee[ee.length-2]];return Math.abs(ve.to-ve.from)},"getDist"),Ae=W(pe),K=W(Qe);return Math.abs(Ae-K)>.1?Ae-K:pe-Qe});let Ie=be.map(pe=>m[y[pe][y[pe].length-1]]);he+=ke(Ie)}return he},"fixTargetHandleCrossings"),He=s(()=>{let he=0;for(let ye of l){let be=[];for(let Ie of ye.tracks)for(let pe of Ie.segments){let Qe=y[pe.edgeIndex].find(W=>m[W].segmentIndex===pe.segmentIndex);Qe!==void 0&&be.push(m[Qe])}be.sort((Ie,pe)=>Ie.edgeIndex-pe.edgeIndex||Ie.segmentIndex-pe.segmentIndex);for(let Ie=0;Ie{Ie.segments.forEach(pe=>{ye.push({edgeIndex:pe.edgeIndex,segmentIndex:pe.segmentIndex,trackIndex:Ie.index,from:pe.from,to:pe.to})})}),ye.sort((Ie,pe)=>Ie.from-pe.from);let be=[];if(ye.length>0){let Ie=[ye[0]],pe=ye[0].to;for(let Qe=1;Qepe.add(me.trackIndex));let Qe=new Map;Ie.forEach(me=>{let Ce=xe(me.edgeIndex);Qe.set(me.trackIndex,(Qe.get(me.trackIndex)??0)+Ce.delta)});let W=[...pe].filter(me=>(Qe.get(me)??0)<-1),Ae=[...pe].filter(me=>(Qe.get(me)??0)>1),K=[...pe].filter(me=>Math.abs(Qe.get(me)??0)<=1);W.sort((me,Ce)=>(Qe.get(Ce)??0)-(Qe.get(me)??0)),Ae.sort((me,Ce)=>(Qe.get(me)??0)-(Qe.get(Ce)??0));let de=s((me,Ce)=>{Ie.filter(Be=>Be.trackIndex===me).forEach(Be=>{let Re=k.has(Be.edgeIndex)?he.coord:Ce;Ve.set(`${Be.edgeIndex}-${Be.segmentIndex}`,Re)})},"assignCoord"),ee=0;for(let me of W)ee++,de(me,he.coord-ee*EA);if(K.length===0&&pe.size>0){let me=[...pe].sort((Re,Ke)=>Math.abs(Qe.get(Re)??0)-Math.abs(Qe.get(Ke)??0))[0],Ce=W.indexOf(me);Ce!==-1&&W.splice(Ce,1);let Be=Ae.indexOf(me);Be!==-1&&Ae.splice(Be,1),K.push(me)}let ve=0;for(let me of K){if(ve===0)de(me,he.coord);else{let Ce=ve%2===1?1:-1,Be=Math.ceil(ve/2);de(me,he.coord+Ce*Be*EA*.5)}ve++}let X=0;for(let me of Ae)X++,de(me,he.coord+X*EA)}}for(let[he,ye]of n.entries()){let be=y[he]??[];if(be.length===0)continue;let Ie=[],pe=a.get(ye.start),Qe=a.get(ye.end),{pSrcPort:W,pDstPort:Ae}=H(he,pe,Qe),K=be.map(ve=>{let X=m[ve],me=Ve.get(`${X.edgeIndex}-${X.segmentIndex}`)??X.pipe.coord;return{orient:X.orientation,coord:me,from:X.from,to:X.to}});Ie.push(W);for(let ve=0;vebi&&Ie.push(jg(X,Ce)),Ke&&Re.orient===X.orient)if(Math.abs(X.coord-Re.coord)>bi){let De=X.orient==="vertical"?(Ce+Re.from)/2:VU(X,Re);Ie.push(jg(X,De),jg(Re,De))}else(ve===0||ve===K.length-2)&&Ie.push(jg(X,VU(X,Re)));else if(Ke)Ie.push(jg(X,Re.coord));else{let De=Math.abs(X.from-Ce)bi||Math.abs(de.y-Ae.y)>bi)&&Ie.push(Ae);let ee=[];Ie.length>0&&ee.push(Ie[0]);for(let ve=1;vebi||Math.abs(X.y-me.y)>bi)&&ee.push(X)}ye.points=ee}for(let he of n){let ye=he.__originalEdge;ye&&he.points&&(ye.points=he.points)}e.edges=(e.edges??[]).filter(he=>!he.isLayoutOnly);let et=s((he,ye)=>{let be=ye.x??0,Ie=ye.y??0,pe=ye.width??0,Qe=ye.height??0;if(pe<=0||Qe<=0)return he;let W=be-pe/2,Ae=be+pe/2,K=Ie-Qe/2,de=Ie+Qe/2;if(he.xAe||he.yde)return he;let ee=he.x-W,ve=Ae-he.x,X=he.y-K,me=de-he.y,Ce=Math.min(ee,ve,X,me);return Ce===ee?{x:W,y:he.y}:Ce===ve?{x:Ae,y:he.y}:Ce===X?{x:he.x,y:K}:{x:he.x,y:de}},"nodeBoundaryClamp");for(let he of e.edges){let ye=he.points;if(!ye||ye.length<2)continue;let be=he.start,Ie=he.end,pe=be?a.get(be):void 0,Qe=Ie?a.get(Ie):void 0;pe&&(ye[0]=et(ye[0],pe)),Qe&&(ye[ye.length-1]=et(ye[ye.length-1],Qe))}return e}var bi,AFe,cf,Vg,_3,Rl,EA,KAt=G(()=>{"use strict";S3(),bi=HPe.EPSILON,AFe=8,cf=15,Vg=15,_3=25,Rl=20,EA=10,s(jU,"chooseOrthogonalSide"),s(VU,"sharedLineEndpointCoord"),s(jg,"pointOnLine"),s(_Fe,"routeEdgesOrthogonal")});function RFe(e){return e.direction??"TB"}function $Fe(e){let t=rPe(e),r=e.config.flowchart?.nodeSpacing??40,i=e.config.flowchart?.rankSpacing??100,n=e.config.swimlane?.ignoreCrossLaneEdges??!0,a=e.config.swimlane?.optimizeRanksByCrossings??!0,o=e.config.swimlane?.automaticLaneOrdering??!1,l=RFe(e),{ordered:u,coordinates:h}=SFe(t,{nodeGap:r,layerGap:i,ignoreCrossLaneEdges:n,optimizeRanksByCrossings:a,automaticLaneOrdering:o,direction:l});iPe(t,u,h,{nodeGap:r,layerGap:i});for(let d of e.edges??[])delete d.points;_Fe(e,l);for(let d of e.edges??[])(!d.curve||d.curve==="basis")&&(d.curve="rounded");return jPe(e,l),WPe(e),l}var QAt=G(()=>{"use strict";NAt(),nPe(),XAt(),KAt(),s(RFe,"getSwimlaneDirection"),s($Fe,"runSwimlaneLayoutCore")}),LFe={};Mr(LFe,{render:()=>MFe});function IFe(e){tPe(e);let t=aPe(e);e.nodes=t.nodes,e.edges=t.edges}var MFe,ZAt=G(()=>{"use strict";LV(),CAt(),nPe(),wAt(),QAt(),s(IFe,"prepareSwimlaneLayout"),MFe=nA({prepareLayout:IFe,runLayoutCore:$Fe,afterPaint:JBe})});function SA(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,i=Array(t);r=e.length?{done:!0}:{done:!1,value:e[i++]}},"n"),e:s(function(u){throw u},"e"),f:n}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var a,o=!0,l=!1;return{s:s(function(){r=r.call(e)},"s"),n:s(function(){var u=r.next();return o=u.done,u},"n"),e:s(function(u){l=!0,a=u},"e"),f:s(function(){try{o||r.return==null||r.return()}finally{if(l)throw a}},"f")}}function UU(e,t,r){return(t=YU(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function BFe(e){if(typeof Symbol<"u"&&e[Symbol.iterator]!=null||e["@@iterator"]!=null)return Array.from(e)}function PFe(e,t){var r=e==null?null:typeof Symbol<"u"&&e[Symbol.iterator]||e["@@iterator"];if(r!=null){var i,n,a,o,l=[],u=!0,h=!1;try{if(a=(r=r.call(e)).next,t===0){if(Object(r)!==r)return;u=!1}else for(;!(u=(i=a.call(r)).done)&&(l.push(i.value),l.length!==t);u=!0);}catch(d){h=!0,n=d}finally{try{if(!u&&r.return!=null&&(o=r.return(),Object(o)!==o))return}finally{if(h)throw n}}return l}}function FFe(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function zFe(){throw new TypeError(`Invalid attempt to spread non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function Ia(e,t){return DFe(e)||PFe(e,t)||_A(e,t)||FFe()}function A3(e){return NFe(e)||BFe(e)||_A(e)||zFe()}function qFe(e,t){if(typeof e!="object"||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var i=r.call(e,t);if(typeof i!="object")return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}function YU(e){var t=qFe(e,"string");return typeof t=="symbol"?t:t+""}function Wa(e){"@babel/helpers - typeof";return Wa=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Wa(e)}function _A(e,t){if(e){if(typeof e=="string")return SA(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?SA(e,t):void 0}}function O2(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function B2(){if(Xqe)return jY;Xqe=1;function e(t){var r=typeof t;return t!=null&&(r=="object"||r=="function")}return s(e,"isObject"),jY=e,jY}function WFe(){if(Kqe)return VY;Kqe=1;var e=typeof j3=="object"&&j3&&j3.Object===Object&&j3;return VY=e,VY}function R3(){if(Qqe)return UY;Qqe=1;var e=WFe(),t=typeof self=="object"&&self&&self.Object===Object&&self,r=e||t||Function("return this")();return UY=r,UY}function jFe(){if(Zqe)return YY;Zqe=1;var e=R3(),t=s(function(){return e.Date.now()},"now");return YY=t,YY}function VFe(){if(Jqe)return HY;Jqe=1;var e=/\s/;function t(r){for(var i=r.length;i--&&e.test(r.charAt(i)););return i}return s(t,"trimmedEndIndex"),HY=t,HY}function UFe(){if(eWe)return GY;eWe=1;var e=VFe(),t=/^\s+/;function r(i){return i&&i.slice(0,e(i)+1).replace(t,"")}return s(r,"baseTrim"),GY=r,GY}function AA(){if(tWe)return XY;tWe=1;var e=R3(),t=e.Symbol;return XY=t,XY}function YFe(){if(rWe)return KY;rWe=1;var e=AA(),t=Object.prototype,r=t.hasOwnProperty,i=t.toString,n=e?e.toStringTag:void 0;function a(o){var l=r.call(o,n),u=o[n];try{o[n]=void 0;var h=!0}catch{}var d=i.call(o);return h&&(l?o[n]=u:delete o[n]),d}return s(a,"getRawTag"),KY=a,KY}function HFe(){if(iWe)return QY;iWe=1;var e=Object.prototype,t=e.toString;function r(i){return t.call(i)}return s(r,"objectToString"),QY=r,QY}function HU(){if(nWe)return ZY;nWe=1;var e=AA(),t=YFe(),r=HFe(),i="[object Null]",n="[object Undefined]",a=e?e.toStringTag:void 0;function o(l){return l==null?l===void 0?n:i:a&&a in Object(l)?t(l):r(l)}return s(o,"baseGetTag"),ZY=o,ZY}function GFe(){if(aWe)return JY;aWe=1;function e(t){return t!=null&&typeof t=="object"}return s(e,"isObjectLike"),JY=e,JY}function P2(){if(sWe)return eH;sWe=1;var e=HU(),t=GFe(),r="[object Symbol]";function i(n){return typeof n=="symbol"||t(n)&&e(n)==r}return s(i,"isSymbol"),eH=i,eH}function XFe(){if(oWe)return tH;oWe=1;var e=UFe(),t=B2(),r=P2(),i=NaN,n=/^[-+]0x[0-9a-f]+$/i,a=/^0b[01]+$/i,o=/^0o[0-7]+$/i,l=parseInt;function u(h){if(typeof h=="number")return h;if(r(h))return i;if(t(h)){var d=typeof h.valueOf=="function"?h.valueOf():h;h=t(d)?d+"":d}if(typeof h!="string")return h===0?h:+h;h=e(h);var p=a.test(h);return p||o.test(h)?l(h.slice(2),p?2:8):n.test(h)?i:+h}return s(u,"toNumber"),tH=u,tH}function KFe(){if(lWe)return rH;lWe=1;var e=B2(),t=jFe(),r=XFe(),i="Expected a function",n=Math.max,a=Math.min;function o(l,u,h){var d,p,f,g,m,y,k=0,C=!1,S=!1,E=!0;if(typeof l!="function")throw new TypeError(i);u=r(u)||0,e(h)&&(C=!!h.leading,S="maxWait"in h,f=S?n(r(h.maxWait)||0,u):f,E="trailing"in h?!!h.trailing:E);function _(j){var I=d,z=p;return d=p=void 0,k=j,g=l.apply(z,I),g}s(_,"invokeFunc");function b(j){return k=j,m=setTimeout(w,u),C?_(j):g}s(b,"leadingEdge");function v(j){var I=j-y,z=j-k,F=u-I;return S?a(F,f-z):F}s(v,"remainingWait");function x(j){var I=j-y,z=j-k;return y===void 0||I>=u||I<0||S&&z>=f}s(x,"shouldInvoke");function w(){var j=t();if(x(j))return R(j);m=setTimeout(w,v(j))}s(w,"timerExpired");function R(j){return m=void 0,E&&d?_(j):(d=p=void 0,g)}s(R,"trailingEdge");function $(){m!==void 0&&clearTimeout(m),k=0,d=y=p=m=void 0}s($,"cancel");function M(){return m===void 0?g:R(t())}s(M,"flush");function N(){var j=t(),I=x(j);if(d=arguments,p=this,y=j,I){if(m===void 0)return b(y);if(S)return clearTimeout(m),m=setTimeout(w,u),_(y)}return m===void 0&&(m=setTimeout(w,u)),g}return s(N,"debounced"),N.cancel=$,N.flush=M,N}return s(o,"debounce"),rH=o,rH}function QFe(e,t,r,i,n){var a=n*Math.PI/180,o=Math.cos(a)*(e-r)-Math.sin(a)*(t-i)+r,l=Math.sin(a)*(e-r)+Math.cos(a)*(t-i)+i;return{x:o,y:l}}function ZFe(e,t,r){if(r===0)return e;var i=(t.x1+t.x2)/2,n=(t.y1+t.y2)/2,a=t.w/t.h,o=1/a,l=QFe(e.x,e.y,i,n,r),u=pWe(l.x,l.y,i,n,a,o);return{x:u.x,y:u.y}}function JFe(){return wWe||(wWe=1,(function(e,t){(function(){var r,i,n,a,o,l,u,h,d,p,f,g,m,y,k;n=Math.floor,p=Math.min,i=s(function(C,S){return CS?1:0},"defaultCmp"),d=s(function(C,S,E,_,b){var v;if(E==null&&(E=0),b==null&&(b=i),E<0)throw new Error("lo must be non-negative");for(_==null&&(_=C.length);E<_;)v=n((E+_)/2),b(S,C[v])<0?_=v:E=v+1;return[].splice.apply(C,[E,E-E].concat(S)),S},"insort"),l=s(function(C,S,E){return E==null&&(E=i),C.push(S),y(C,0,C.length-1,E)},"heappush"),o=s(function(C,S){var E,_;return S==null&&(S=i),E=C.pop(),C.length?(_=C[0],C[0]=E,k(C,0,S)):_=E,_},"heappop"),h=s(function(C,S,E){var _;return E==null&&(E=i),_=C[0],C[0]=S,k(C,0,E),_},"heapreplace"),u=s(function(C,S,E){var _;return E==null&&(E=i),C.length&&E(C[0],S)<0&&(_=[C[0],S],S=_[0],C[0]=_[1],k(C,0,E)),S},"heappushpop"),a=s(function(C,S){var E,_,b,v,x,w;for(S==null&&(S=i),v=(function(){w=[];for(var R=0,$=n(C.length/2);0<=$?R<$:R>$;0<=$?R++:R--)w.push(R);return w}).apply(this).reverse(),x=[],_=0,b=v.length;_M;0<=M?++w:--w)N.push(o(C,E));return N},"nsmallest"),y=s(function(C,S,E,_){var b,v,x;for(_==null&&(_=i),b=C[E];E>S;){if(x=E-1>>1,v=C[x],_(b,v)<0){C[E]=v,E=x;continue}break}return C[E]=b},"_siftdown"),k=s(function(C,S,E){var _,b,v,x,w;for(E==null&&(E=i),b=C.length,w=S,v=C[S],_=2*S+1;_-1}return s(t,"listCacheHas"),mG=t,mG}function xze(){if(TVe)return yG;TVe=1;var e=M3();function t(r,i){var n=this.__data__,a=e(n,r);return a<0?(++this.size,n.push([r,i])):n[a][1]=i,this}return s(t,"listCacheSet"),yG=t,yG}function Tze(){if(kVe)return vG;kVe=1;var e=mze(),t=yze(),r=vze(),i=bze(),n=xze();function a(o){var l=-1,u=o==null?0:o.length;for(this.clear();++l-1&&i%1==0&&i0;){var d=n.shift();t(d),a.add(d.id()),l&&i(n,a,d)}return e}function JU(e,t,r){if(r.isParent())for(var i=r._private.children,n=0;n0&&arguments[0]!==void 0?arguments[0]:zUe,t=arguments.length>1?arguments[1]:void 0,r=0;r0?N=I:M=I;while(Math.abs(j)>o&&++z=a?S($,z):F===0?z:_($,M,M+h)}s(b,"getTForX");var v=!1;function x(){v=!0,(e!==t||r!==i)&&E()}s(x,"precompute");var w=s(function($){return v||x(),e===t&&r===i?$:$===0?0:$===1?1:k(b($),t,i)},"f");w.getControlPoints=function(){return[{x:e,y:t},{x:r,y:i}]};var R="generateBezier("+[e,t,r,i]+")";return w.toString=function(){return R},w}function sY(e,t,r,i,n){if(i===1||t===r)return r;var a=n(t,r,i);return e==null||((e.roundValue||e.color)&&(a=Math.round(a)),e.min!==void 0&&(a=Math.max(a,e.min)),e.max!==void 0&&(a=Math.min(a,e.max))),a}function oY(e,t){return e.pfValue!=null||e.value!=null?e.pfValue!=null&&(t==null||t.type.units!=="%")?e.pfValue:e.value:e}function Hg(e,t,r,i,n){var a=n!=null?n.type:null;r<0?r=0:r>1&&(r=1);var o=oY(e,n),l=oY(t,n);if(ir(o)&&ir(l))return sY(a,o,l,r,i);if(mn(o)&&mn(l)){for(var u=[],h=0;h0?(g==="spring"&&m.push(o.duration),o.easingImpl=pk[g].apply(null,m)):o.easingImpl=pk[g]}var y=o.easingImpl,k;if(o.duration===0?k=1:k=(r-u)/o.duration,o.applying&&(k=o.progress),k<0?k=0:k>1&&(k=1),o.delay==null){var C=o.startPosition,S=o.position;if(S&&n&&!e.locked()){var E={};z2(C.x,S.x)&&(E.x=Hg(C.x,S.x,k,y)),z2(C.y,S.y)&&(E.y=Hg(C.y,S.y,k,y)),e.position(E)}var _=o.startPan,b=o.pan,v=a.pan,x=b!=null&&i;x&&(z2(_.x,b.x)&&(v.x=Hg(_.x,b.x,k,y)),z2(_.y,b.y)&&(v.y=Hg(_.y,b.y,k,y)),e.emit("pan"));var w=o.startZoom,R=o.zoom,$=R!=null&&i;$&&(z2(w,R)&&(a.zoom=G2(a.minZoom,Hg(w,R,k,y),a.maxZoom)),e.emit("zoom")),(x||$)&&e.emit("viewport");var M=o.style;if(M&&M.length>0&&n){for(var N=0;N=0;v--){var x=b[v];x()}b.splice(0,b.length)},"callbacks"),S=g.length-1;S>=0;S--){var E=g[S],_=E._private;if(_.stopped){g.splice(S,1),_.hooked=!1,_.playing=!1,_.started=!1,C(_.frames);continue}!_.playing&&!_.applying||(_.playing&&_.applying&&(_.applying=!1),_.started||Yze(d,E,e),Uze(d,E,e,p),_.applying&&(_.applying=!1),C(_.frames),_.step!=null&&_.step(e),E.completed()&&(g.splice(S,1),_.hooked=!1,_.playing=!1,_.started=!1,C(_.completes)),y=!0)}return!p&&g.length===0&&m.length===0&&i.push(d),y}s(n,"stepOne");for(var a=!1,o=0;o0?t.notify("draw",r):t.notify("draw")),r.unmerge(i),t.emit("step")}function cY(e){this.options=Ur({},aYe,sYe,e)}function uY(e){this.options=Ur({},oYe,e)}function hY(e){this.options=Ur({},lYe,e)}function F3(e){this.options=Ur({},cYe,e),this.options.layout=this;var t=this.options.eles.nodes(),r=this.options.eles.edges(),i=r.filter(function(n){var a=n.source().data("id"),o=n.target().data("id"),l=t.some(function(h){return h.data("id")===a}),u=t.some(function(h){return h.data("id")===o});return!l||!u});this.options.eles=this.options.eles.not(i)}function dY(e){this.options=Ur({},CYe,e)}function OA(e){this.options=Ur({},wYe,e)}function pY(e){this.options=Ur({},EYe,e)}function fY(e){this.options=Ur({},SYe,e)}function gY(e){this.options=e,this.notifications=0}function mY(e,t){t.radius===0?e.lineTo(t.cx,t.cy):e.arc(t.cx,t.cy,t.radius,t.startAngle,t.endAngle,t.counterClockwise)}function BA(e,t,r,i){var n=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0;return i===0||t.radius===0?{cx:t.x,cy:t.y,radius:0,startX:t.x,startY:t.y,stopX:t.x,stopY:t.y,startAngle:void 0,endAngle:void 0,counterClockwise:void 0}:(DYe(e,t,r,i,n),{cx:SX,cy:_X,radius:pm,startX:$Ye,startY:LYe,stopX:AX,stopY:RX,startAngle:Gc.ang+Math.PI/2*hm,endAngle:Il.ang-Math.PI/2*hm,counterClockwise:v8})}function yY(e){var t=[];if(e!=null){for(var r=0;r5&&arguments[5]!==void 0?arguments[5]:5,o=Math.min(a,i/2,n/2);e.beginPath(),e.moveTo(t+o,r),e.lineTo(t+i-o,r),e.quadraticCurveTo(t+i,r,t+i,r+o),e.lineTo(t+i,r+n-o),e.quadraticCurveTo(t+i,r+n,t+i-o,r+n),e.lineTo(t+o,r+n),e.quadraticCurveTo(t,r+n,t,r+n-o),e.lineTo(t,r+o),e.quadraticCurveTo(t,r,t+o,r),e.closePath()}function xY(e,t,r){var i=e.createShader(t);if(e.shaderSource(i,r),e.compileShader(i),!e.getShaderParameter(i,e.COMPILE_STATUS))throw new Error(e.getShaderInfoLog(i));return i}function Jze(e,t,r){var i=xY(e,e.VERTEX_SHADER,t),n=xY(e,e.FRAGMENT_SHADER,r),a=e.createProgram();if(e.attachShader(a,i),e.attachShader(a,n),e.linkProgram(a),!e.getProgramParameter(a,e.LINK_STATUS))throw new Error("Could not initialize shaders");return a}function eqe(e,t,r){r===void 0&&(r=t);var i=e.makeOffscreenCanvas(t,r),n=i.context=i.getContext("2d");return i.clear=function(){return n.clearRect(0,0,i.width,i.height)},i.clear(),i}function PA(e){var t=e.pixelRatio,r=e.cy.zoom(),i=e.cy.pan();return{zoom:r*t,pan:{x:i.x*t,y:i.y*t}}}function tqe(e){var t=e.pixelRatio,r=e.cy.zoom();return r*t}function rqe(e,t,r,i,n){var a=i*r+t.x,o=n*r+t.y;return o=Math.round(e.canvasHeight-o),[a,o]}function iqe(e,t){return t.picking?!0:e.pstyle("background-fill").value!=="solid"||e.pstyle("background-image").strValue!=="none"?!1:e.pstyle("border-width").value===0||e.pstyle("border-opacity").value===0?!0:e.pstyle("border-style").value==="solid"}function nqe(e,t){if(e.length!==t.length)return!1;for(var r=0;r>0&255)/255,r[1]=(e>>8&255)/255,r[2]=(e>>16&255)/255,r[3]=(e>>24&255)/255,r}function aqe(e){return e[0]+(e[1]<<8)+(e[2]<<16)+(e[3]<<24)}function sqe(e,t){var r=e.createTexture();return r.buffer=function(i){e.bindTexture(e.TEXTURE_2D,r),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_NEAREST),e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,i),e.generateMipmap(e.TEXTURE_2D),e.bindTexture(e.TEXTURE_2D,null)},r.deleteTexture=function(){e.deleteTexture(r)},r}function TY(e,t){switch(t){case"float":return[1,e.FLOAT,4];case"vec2":return[2,e.FLOAT,4];case"vec3":return[3,e.FLOAT,4];case"vec4":return[4,e.FLOAT,4];case"int":return[1,e.INT,4];case"ivec2":return[2,e.INT,4]}}function kY(e,t,r){switch(t){case e.FLOAT:return new Float32Array(r);case e.INT:return new Int32Array(r)}}function oqe(e,t,r,i,n,a){switch(t){case e.FLOAT:return new Float32Array(r.buffer,a*i,n);case e.INT:return new Int32Array(r.buffer,a*i,n)}}function lqe(e,t,r,i){var n=TY(e,t),a=Ia(n,2),o=a[0],l=a[1],u=kY(e,l,i),h=e.createBuffer();return e.bindBuffer(e.ARRAY_BUFFER,h),e.bufferData(e.ARRAY_BUFFER,u,e.STATIC_DRAW),l===e.FLOAT?e.vertexAttribPointer(r,o,l,!1,0,0):l===e.INT&&e.vertexAttribIPointer(r,o,l,0,0),e.enableVertexAttribArray(r),e.bindBuffer(e.ARRAY_BUFFER,null),h}function jc(e,t,r,i){var n=TY(e,r),a=Ia(n,3),o=a[0],l=a[1],u=a[2],h=kY(e,l,t*o),d=o*u,p=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,p),e.bufferData(e.ARRAY_BUFFER,t*d,e.DYNAMIC_DRAW),e.enableVertexAttribArray(i),l===e.FLOAT?e.vertexAttribPointer(i,o,l,!1,d,0):l===e.INT&&e.vertexAttribIPointer(i,o,l,d,0),e.vertexAttribDivisor(i,1),e.bindBuffer(e.ARRAY_BUFFER,null);for(var f=new Array(t),g=0;gjX?(mqe(e),t.call(e,a)):(yqe(e),_Y(e,a,hv.SCREEN)))}}{var r=e.matchCanvasSize;e.matchCanvasSize=function(a){r.call(e,a),e.pickingFrameBuffer.setFramebufferAttachmentSizes(e.canvasWidth,e.canvasHeight),e.pickingFrameBuffer.needsDraw=!0}}e.findNearestElements=function(a,o,l,u){return Cqe(e,a,o)};{var i=e.invalidateCachedZSortedEles;e.invalidateCachedZSortedEles=function(){i.call(e),e.pickingFrameBuffer.needsDraw=!0}}{var n=e.notify;e.notify=function(a,o){n.call(e,a,o),a==="viewport"||a==="bounds"?e.pickingFrameBuffer.needsDraw=!0:a==="background"&&e.drawing.invalidate(o,{type:"node-body"})}}}function mqe(e){var t=e.data.contexts[e.WEBGL];t.clear(t.COLOR_BUFFER_BIT|t.DEPTH_BUFFER_BIT)}function yqe(e){var t=s(function(r){r.save(),r.setTransform(1,0,0,1,0,0),r.clearRect(0,0,e.canvasWidth,e.canvasHeight),r.restore()},"clear");t(e.data.contexts[e.NODE]),t(e.data.contexts[e.DRAG])}function vqe(e){var t=e.canvasWidth,r=e.canvasHeight,i=PA(e),n=i.pan,a=i.zoom,o=FA();z3(o,o,[n.x,n.y]),zA(o,o,[a,a]);var l=FA();dqe(l,t,r);var u=FA();return hqe(u,l,o),u}function SY(e,t){var r=e.canvasWidth,i=e.canvasHeight,n=PA(e),a=n.pan,o=n.zoom;t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,r,i),t.translate(a.x,a.y),t.scale(o,o)}function bqe(e,t){e.drawSelectionRectangle(t,function(r){return SY(e,r)})}function xqe(e){var t=e.data.contexts[e.NODE];t.save(),SY(e,t),t.strokeStyle="rgba(0, 0, 0, 0.3)",t.beginPath(),t.moveTo(-1e3,0),t.lineTo(1e3,0),t.stroke(),t.beginPath(),t.moveTo(0,-1e3),t.lineTo(0,1e3),t.stroke(),t.restore()}function Tqe(e){var t=s(function(i,n,a){for(var o=i.atlasManager.getAtlasCollection(n),l=e.data.contexts[e.NODE],u=o.atlases,h=0;h=0&&_.add(x)}return _}function Cqe(e,t,r){var i=kqe(e,t,r),n=e.getCachedZSortedEles(),a,o,l=Oo(i),u;try{for(l.s();!(u=l.n()).done;){var h=u.value,d=n[h];if(!a&&d.isNode()&&(a=d),!o&&d.isEdge()&&(o=d),a&&o)break}}catch(p){l.e(p)}finally{l.f()}return[a,o].filter(Boolean)}function qA(e,t,r){var i=e.drawing;t+=1,r.isNode()?(i.drawNode(r,t,"node-underlay"),i.drawNode(r,t,"node-body"),i.drawTexture(r,t,"label"),i.drawNode(r,t,"node-overlay")):(i.drawEdgeLine(r,t),i.drawEdgeArrow(r,t,"source"),i.drawEdgeArrow(r,t,"target"),i.drawTexture(r,t,"label"),i.drawTexture(r,t,"edge-source-label"),i.drawTexture(r,t,"edge-target-label"))}function _Y(e,t,r){var i;e.webglDebug&&(i=performance.now());var n=e.drawing,a=0;if(r.screen&&e.data.canvasNeedsRedraw[e.SELECT_BOX]&&bqe(e,t),e.data.canvasNeedsRedraw[e.NODE]||r.picking){var o=e.data.contexts[e.WEBGL];r.screen?(o.clearColor(0,0,0,0),o.enable(o.BLEND),o.blendFunc(o.ONE,o.ONE_MINUS_SRC_ALPHA)):o.disable(o.BLEND),o.clear(o.COLOR_BUFFER_BIT|o.DEPTH_BUFFER_BIT),o.viewport(0,0,o.canvas.width,o.canvas.height);var l=vqe(e),u=e.getCachedZSortedEles();if(a=u.length,n.startFrame(l,r),r.screen){for(var h=0;h{"use strict";for(s(SA,"_arrayLikeToArray"),s(DFe,"_arrayWithHoles"),s(NFe,"_arrayWithoutHoles"),s(Td,"_classCallCheck"),s(OFe,"_defineProperties"),s(kd,"_createClass"),s(Oo,"_createForOfIteratorHelper"),s(UU,"_defineProperty$1"),s(BFe,"_iterableToArray"),s(PFe,"_iterableToArrayLimit"),s(FFe,"_nonIterableRest"),s(zFe,"_nonIterableSpread"),s(Ia,"_slicedToArray"),s(A3,"_toConsumableArray"),s(qFe,"_toPrimitive"),s(YU,"_toPropertyKey"),s(Wa,"_typeof"),s(_A,"_unsupportedIterableToArray"),ja=typeof window>"u"?null:window,MY=ja?ja.navigator:null,ja&&ja.document,_qe=Wa(""),DY=Wa({}),Aqe=Wa(function(){}),Rqe=typeof HTMLElement>"u"?"undefined":Wa(HTMLElement),q2=s(function(e){return e&&e.instanceString&&ta(e.instanceString)?e.instanceString():null},"instanceStr"),zr=s(function(e){return e!=null&&Wa(e)==_qe},"string"),ta=s(function(e){return e!=null&&Wa(e)===Aqe},"fn"),mn=s(function(e){return!ul(e)&&(Array.isArray?Array.isArray(e):e!=null&&e instanceof Array)},"array"),Fi=s(function(e){return e!=null&&Wa(e)===DY&&!mn(e)&&e.constructor===Object},"plainObject"),$qe=s(function(e){return e!=null&&Wa(e)===DY},"object"),ir=s(function(e){return e!=null&&Wa(e)===Wa(1)&&!isNaN(e)},"number"),Lqe=s(function(e){return ir(e)&&Math.floor(e)===e},"integer"),q3=s(function(e){if(Rqe!=="undefined")return e!=null&&e instanceof HTMLElement},"htmlElement"),ul=s(function(e){return W2(e)||NY(e)},"elementOrCollection"),W2=s(function(e){return q2(e)==="collection"&&e._private.single},"element"),NY=s(function(e){return q2(e)==="collection"&&!e._private.single},"collection"),WA=s(function(e){return q2(e)==="core"},"core"),OY=s(function(e){return q2(e)==="stylesheet"},"stylesheet"),Iqe=s(function(e){return q2(e)==="event"},"event"),Cd=s(function(e){return e==null?!0:!!(e===""||e.match(/^\s+$/))},"emptyString"),Mqe=s(function(e){return typeof HTMLElement>"u"?!1:e instanceof HTMLElement},"domElement"),Dqe=s(function(e){return Fi(e)&&ir(e.x1)&&ir(e.x2)&&ir(e.y1)&&ir(e.y2)},"boundingBox"),Nqe=s(function(e){return $qe(e)&&ta(e.then)},"promise"),Oqe=s(function(){return MY&&MY.userAgent.match(/msie|trident|edge/i)},"ms"),Xg=s(function(e,t){t||(t=s(function(){if(arguments.length===1)return arguments[0];if(arguments.length===0)return"undefined";for(var i=[],n=0;nt?1:0},"ascending"),jqe=s(function(e,t){return-1*FY(e,t)},"descending"),Ur=Object.assign!=null?Object.assign.bind(Object):function(e){for(var t=arguments,r=1;r1&&(y-=1),y<1/6?g+(m-g)*6*y:y<1/2?m:y<2/3?g+(m-g)*(2/3-y)*6:g}s(h,"hue2rgb");var d=new RegExp("^"+Fqe+"$").exec(e);if(d){if(r=parseInt(d[1]),r<0?r=(360- -1*r%360)%360:r>360&&(r=r%360),r/=360,i=parseFloat(d[2]),i<0||i>100||(i=i/100,n=parseFloat(d[3]),n<0||n>100)||(n=n/100,a=d[4],a!==void 0&&(a=parseFloat(a),a<0||a>1)))return;if(i===0)o=l=u=Math.round(n*255);else{var p=n<.5?n*(1+i):n+i-n*i,f=2*n-p;o=Math.round(255*h(f,p,r+1/3)),l=Math.round(255*h(f,p,r)),u=Math.round(255*h(f,p,r-1/3))}t=[o,l,u,a]}return t},"hsl2tuple"),Yqe=s(function(e){var t,r=new RegExp("^"+Bqe+"$").exec(e);if(r){t=[];for(var i=[],n=1;n<=3;n++){var a=r[n];if(a[a.length-1]==="%"&&(i[n]=!0),a=parseFloat(a),i[n]&&(a=a/100*255),a<0||a>255)return;t.push(Math.floor(a))}var o=i[1]||i[2]||i[3],l=i[1]&&i[2]&&i[3];if(o&&!l)return;var u=r[4];if(u!==void 0){if(u=parseFloat(u),u<0||u>1)return;t.push(u)}}return t},"rgb2tuple"),Hqe=s(function(e){return Gqe[e.toLowerCase()]},"colorname2tuple"),zY=s(function(e){return(mn(e)?e:null)||Hqe(e)||Vqe(e)||Yqe(e)||Uqe(e)},"color2tuple"),Gqe={transparent:[0,0,0,0],aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],grey:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},qY=s(function(e){for(var t=e.map,r=e.keys,i=r.length,n=0;n1&&arguments[1]!==void 0?arguments[1]:hf,r=t,i;i=e.next(),!i.done;)r=r*nH+i.value|0;return r},"hashIterableInts"),V2=s(function(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:hf;return t*nH+e|0},"hashInt"),U2=s(function(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:Kg;return(t<<5)+t+e|0},"hashIntAlt"),hWe=s(function(e,t){return e*2097152+t},"combineHashes"),Ed=s(function(e){return e[0]*2097152+e[1]},"combineHashesArray"),U3=s(function(e,t){return[V2(e[0],t[0]),U2(e[1],t[1])]},"hashArrays"),sH=s(function(e,t){var r={value:0,done:!1},i=0,n=e.length,a={next:s(function(){return i=0;i--)e[i]===t&&e.splice(i,1)},"removeFromArray"),HA=s(function(e){e.splice(0,e.length)},"clearArray"),bWe=s(function(e,t){for(var r=0;r"u"?"undefined":Wa(Set))!==TWe?Set:kWe,H3=s(function(e,t){var r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0;if(e===void 0||t===void 0||!WA(e)){Kn("An element must have a core reference and parameters set");return}var i=t.group;if(i==null&&(t.data&&t.data.source!=null&&t.data.target!=null?i="edges":i="nodes"),i!=="nodes"&&i!=="edges"){Kn("An element must be of type `nodes` or `edges`; you specified `"+i+"`");return}this.length=1,this[0]=this;var n=this._private={cy:e,single:!0,data:t.data||{},position:t.position||{x:0,y:0},autoWidth:void 0,autoHeight:void 0,autoPadding:void 0,compoundBoundsClean:!1,listeners:[],group:i,style:{},rstyle:{},styleCxts:[],styleKeys:{},removed:!0,selected:!!t.selected,selectable:t.selectable===void 0?!0:!!t.selectable,locked:!!t.locked,grabbed:!1,grabbable:t.grabbable===void 0?!0:!!t.grabbable,pannable:t.pannable===void 0?i==="edges":!!t.pannable,active:!1,classes:new Qg,animation:{current:[],queue:[]},rscratch:{},scratch:t.scratch||{},edges:[],children:[],parent:t.parent&&t.parent.isNode()?t.parent:null,traversalCache:{},backgrounding:!1,bbCache:null,bbCacheShift:{x:0,y:0},bodyBounds:null,overlayBounds:null,labelBounds:{all:null,source:null,target:null,main:null},arrowBounds:{source:null,target:null,"mid-source":null,"mid-target":null}};if(n.position.x==null&&(n.position.x=0),n.position.y==null&&(n.position.y=0),t.renderedPosition){var a=t.renderedPosition,o=e.pan(),l=e.zoom();n.position={x:(a.x-o.x)/l,y:(a.y-o.y)/l}}var u=[];mn(t.classes)?u=t.classes:zr(t.classes)&&(u=t.classes.split(/\s+/));for(var h=0,d=u.length;h0;){var b=C.pop(),v=y(b),x=b.id();if(p[x]=v,v!==1/0)for(var w=b.neighborhood().intersect(g),R=0;R0)for(F.unshift(z);d[B];){var Y=d[B];F.unshift(Y.edge),F.unshift(Y.node),O=Y.node,B=O.id()}return o.spawn(F)},"pathTo")}},"dijkstra")},$We={kruskal:s(function(e){e=e||function(S){return 1};for(var t=this.byGroup(),r=t.nodes,i=t.edges,n=r.length,a=new Array(n),o=r,l=s(function(S){for(var E=0;E0;){if(_(),v++,E===h){for(var x=[],w=n,R=h,$=k[R];x.unshift(w),$!=null&&x.unshift($),w=y[R],w!=null;)R=w.id(),$=k[R];return{found:!0,distance:d[E],path:this.spawn(x),steps:v}}f[E]=!0;for(var M=S._private.edges,N=0;N$&&(g[R]=$,C[R]=w,S[R]=_),!n){var M=w*h+x;!n&&g[M]>$&&(g[M]=$,C[M]=x,S[M]=_)}}}for(var N=0;N1&&arguments[1]!==void 0?arguments[1]:a,Ge=S(Me),ze=[],Ve=Ge;;){if(Ve==null)return t.spawn();var et=C(Ve),he=et.edge,ye=et.pred;if(ze.unshift(Ve[0]),Ve.same(He)&&ze.length>0)break;he!=null&&ze.unshift(he),Ve=ye}return l.spawn(ze)},"pathTo"),b=0;b=0;h--){var d=u[h],p=d[1],f=d[2];(t[p]===o&&t[f]===l||t[p]===l&&t[f]===o)&&u.splice(h,1)}for(var g=0;gi;){var n=Math.floor(Math.random()*t.length);t=PWe(n,e,t),r--}return t},"contractUntil"),FWe={kargerStein:s(function(){var e=this,t=this.byGroup(),r=t.nodes,i=t.edges;i.unmergeBy(function(O){return O.isLoop()});var n=r.length,a=i.length,o=Math.ceil(Math.pow(Math.log(n)/Math.LN2,2)),l=Math.floor(n/BWe);if(n<2){Kn("At least 2 nodes are required for Karger-Stein algorithm");return}for(var u=[],h=0;h1&&arguments[1]!==void 0?arguments[1]:0,r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,i=1/0,n=t;n1&&arguments[1]!==void 0?arguments[1]:0,r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,i=-1/0,n=t;n1&&arguments[1]!==void 0?arguments[1]:0,r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,i=0,n=0,a=t;a1&&arguments[1]!==void 0?arguments[1]:0,r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,i=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!0,n=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,a=arguments.length>5&&arguments[5]!==void 0?arguments[5]:!0;i?e=e.slice(t,r):(r0&&e.splice(0,t));for(var o=0,l=e.length-1;l>=0;l--){var u=e[l];a?isFinite(u)||(e[l]=-1/0,o++):e.splice(l,1)}n&&e.sort(function(p,f){return p-f});var h=e.length,d=Math.floor(h/2);return h%2!==0?e[d+1+o]:(e[d-1+o]+e[d+o])/2},"median"),UWe=s(function(e){return Math.PI*e/180},"deg2rad"),X3=s(function(e,t){return Math.atan2(t,e)-Math.PI/2},"getAngleFromDisp"),KA=Math.log2||function(e){return Math.log(e)/Math.log(2)},QA=s(function(e){return e>0?1:e<0?-1:0},"signum"),pf=s(function(e,t){return Math.sqrt(ff(e,t))},"dist"),ff=s(function(e,t){var r=t.x-e.x,i=t.y-e.y;return r*r+i*i},"sqdist"),YWe=s(function(e){for(var t=e.length,r=0,i=0;i=e.x1&&e.y2>=e.y1)return{x1:e.x1,y1:e.y1,x2:e.x2,y2:e.y2,w:e.x2-e.x1,h:e.y2-e.y1};if(e.w!=null&&e.h!=null&&e.w>=0&&e.h>=0)return{x1:e.x1,y1:e.y1,x2:e.x1+e.w,y2:e.y1+e.h,w:e.w,h:e.h}}},"makeBoundingBox"),GWe=s(function(e){return{x1:e.x1,x2:e.x2,w:e.w,y1:e.y1,y2:e.y2,h:e.h}},"copyBoundingBox"),XWe=s(function(e){e.x1=1/0,e.y1=1/0,e.x2=-1/0,e.y2=-1/0,e.w=0,e.h=0},"clearBoundingBox"),KWe=s(function(e,t){e.x1=Math.min(e.x1,t.x1),e.x2=Math.max(e.x2,t.x2),e.w=e.x2-e.x1,e.y1=Math.min(e.y1,t.y1),e.y2=Math.max(e.y2,t.y2),e.h=e.y2-e.y1},"updateBoundingBox"),yH=s(function(e,t,r){e.x1=Math.min(e.x1,t),e.x2=Math.max(e.x2,t),e.w=e.x2-e.x1,e.y1=Math.min(e.y1,r),e.y2=Math.max(e.y2,r),e.h=e.y2-e.y1},"expandBoundingBoxByPoint"),K3=s(function(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0;return e.x1-=t,e.x2+=t,e.y1-=t,e.y2+=t,e.w=e.x2-e.x1,e.h=e.y2-e.y1,e},"expandBoundingBox"),Q3=s(function(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:[0],r,i,n,a;if(t.length===1)r=i=n=a=t[0];else if(t.length===2)r=n=t[0],a=i=t[1];else if(t.length===4){var o=Ia(t,4);r=o[0],i=o[1],n=o[2],a=o[3]}return e.x1-=a,e.x2+=i,e.y1-=r,e.y2+=n,e.w=e.x2-e.x1,e.h=e.y2-e.y1,e},"expandBoundingBoxSides"),vH=s(function(e,t){e.x1=t.x1,e.y1=t.y1,e.x2=t.x2,e.y2=t.y2,e.w=e.x2-e.x1,e.h=e.y2-e.y1},"assignBoundingBox"),ZA=s(function(e,t){return!(e.x1>t.x2||t.x1>e.x2||e.x2t.y2||t.y1>e.y2)},"boundingBoxesIntersect"),_d=s(function(e,t,r){return e.x1<=t&&t<=e.x2&&e.y1<=r&&r<=e.y2},"inBoundingBox"),bH=s(function(e,t){return _d(e,t.x,t.y)},"pointInBoundingBox"),xH=s(function(e,t){return _d(e,t.x1,t.y1)&&_d(e,t.x2,t.y2)},"boundingBoxInBoundingBox"),QWe=(gH=Math.hypot)!==null&&gH!==void 0?gH:function(e,t){return Math.sqrt(e*e+t*t)},s(tze,"inflatePolygon"),s(rze,"miterBox"),TH=s(function(e,t,r,i,n,a,o){var l=arguments.length>7&&arguments[7]!==void 0?arguments[7]:"auto",u=l==="auto"?Rd(n,a):l,h=n/2,d=a/2;u=Math.min(u,h,d);var p=u!==h,f=u!==d,g;if(p){var m=r-h+u-o,y=i-d-o,k=r+h-u+o,C=y;if(g=Ad(e,t,r,i,m,y,k,C,!1),g.length>0)return g}if(f){var S=r+h+o,E=i-d+u-o,_=S,b=i+d-u+o;if(g=Ad(e,t,r,i,S,E,_,b,!1),g.length>0)return g}if(p){var v=r-h+u-o,x=i+d+o,w=r+h-u+o,R=x;if(g=Ad(e,t,r,i,v,x,w,R,!1),g.length>0)return g}if(f){var $=r-h-o,M=i-d+u-o,N=$,j=i+d-u+o;if(g=Ad(e,t,r,i,$,M,N,j,!1),g.length>0)return g}var I;{var z=r-h+u,F=i-d+u;if(I=X2(e,t,r,i,z,F,u+o),I.length>0&&I[0]<=z&&I[1]<=F)return[I[0],I[1]]}{var O=r+h-u,B=i-d+u;if(I=X2(e,t,r,i,O,B,u+o),I.length>0&&I[0]>=O&&I[1]<=B)return[I[0],I[1]]}{var Y=r+h-u,U=i+d-u;if(I=X2(e,t,r,i,Y,U,u+o),I.length>0&&I[0]>=Y&&I[1]>=U)return[I[0],I[1]]}{var H=r-h+u,J=i+d-u;if(I=X2(e,t,r,i,H,J,u+o),I.length>0&&I[0]<=H&&I[1]>=J)return[I[0],I[1]]}return[]},"roundRectangleIntersectLine"),ZWe=s(function(e,t,r,i,n,a,o){var l=o,u=Math.min(r,n),h=Math.max(r,n),d=Math.min(i,a),p=Math.max(i,a);return u-l<=e&&e<=h+l&&d-l<=t&&t<=p+l},"inLineVicinity"),JWe=s(function(e,t,r,i,n,a,o,l,u){var h={x1:Math.min(r,o,n)-u,x2:Math.max(r,o,n)+u,y1:Math.min(i,l,a)-u,y2:Math.max(i,l,a)+u};return!(eh.x2||th.y2)},"inBezierVicinity"),eje=s(function(e,t,r,i){r-=i;var n=t*t-4*e*r;if(n<0)return[];var a=Math.sqrt(n),o=2*e,l=(-t+a)/o,u=(-t-a)/o;return[l,u]},"solveQuadratic"),tje=s(function(e,t,r,i,n){var a=1e-5;e===0&&(e=a),t/=e,r/=e,i/=e;var o,l,u,h,d,p,f,g;if(l=(3*r-t*t)/9,u=-(27*i)+t*(9*r-2*(t*t)),u/=54,o=l*l*l+u*u,n[1]=0,f=t/3,o>0){d=u+Math.sqrt(o),d=d<0?-Math.pow(-d,1/3):Math.pow(d,1/3),p=u-Math.sqrt(o),p=p<0?-Math.pow(-p,1/3):Math.pow(p,1/3),n[0]=-f+d+p,f+=(d+p)/2,n[4]=n[2]=-f,f=Math.sqrt(3)*(-p+d)/2,n[3]=f,n[5]=-f;return}if(n[5]=n[3]=0,o===0){g=u<0?-Math.pow(-u,1/3):Math.pow(u,1/3),n[0]=-f+2*g,n[4]=n[2]=-(g+f);return}l=-l,h=l*l*l,h=Math.acos(u/Math.sqrt(h)),g=2*Math.sqrt(l),n[0]=-f+g*Math.cos(h/3),n[2]=-f+g*Math.cos((h+2*Math.PI)/3),n[4]=-f+g*Math.cos((h+4*Math.PI)/3)},"solveCubic"),rje=s(function(e,t,r,i,n,a,o,l){var u=1*r*r-4*r*n+2*r*o+4*n*n-4*n*o+o*o+i*i-4*i*a+2*i*l+4*a*a-4*a*l+l*l,h=9*r*n-3*r*r-3*r*o-6*n*n+3*n*o+9*i*a-3*i*i-3*i*l-6*a*a+3*a*l,d=3*r*r-6*r*n+r*o-r*e+2*n*n+2*n*e-o*e+3*i*i-6*i*a+i*l-i*t+2*a*a+2*a*t-l*t,p=1*r*n-r*r+r*e-n*e+i*a-i*i+i*t-a*t,f=[];tje(u,h,d,p,f);for(var g=1e-7,m=[],y=0;y<6;y+=2)Math.abs(f[y+1])=0&&f[y]<=1&&m.push(f[y]);m.push(1),m.push(0);for(var k=-1,C,S,E,_=0;_=0?Eu?(e-n)*(e-n)+(t-a)*(t-a):h-p},"sqdistToFiniteLine"),Bo=s(function(e,t,r){for(var i,n,a,o,l,u=0,h=0;h=e&&e>=a||i<=e&&e<=a)l=(e-i)/(a-i)*(o-n)+n,l>t&&u++;else continue;return u%2!==0},"pointInsidePolygonPoints"),rh=s(function(e,t,r,i,n,a,o,l,u){var h=new Array(r.length),d;l[0]!=null?(d=Math.atan(l[1]/l[0]),l[0]<0?d=d+Math.PI/2:d=-d-Math.PI/2):d=l;for(var p=Math.cos(-d),f=Math.sin(-d),g=0;g0){var y=J3(h,-u);m=Z3(y)}else m=h;return Bo(e,t,m)},"pointInsidePolygon"),nje=s(function(e,t,r,i,n,a,o,l){for(var u=new Array(r.length*2),h=0;h=0&&y<=1&&C.push(y),k>=0&&k<=1&&C.push(k),C.length===0)return[];var S=C[0]*l[0]+e,E=C[0]*l[1]+t;if(C.length>1){if(C[0]==C[1])return[S,E];var _=C[1]*l[0]+e,b=C[1]*l[1]+t;return[S,E,_,b]}else return[S,E]},"intersectLineCircle"),JA=s(function(e,t,r){return t<=e&&e<=r||r<=e&&e<=t?e:e<=t&&t<=r||r<=t&&t<=e?t:r},"midOfThree"),Ad=s(function(e,t,r,i,n,a,o,l,u){var h=e-n,d=r-e,p=o-n,f=t-a,g=i-t,m=l-a,y=p*f-m*h,k=d*f-g*h,C=m*d-p*g;if(C!==0){var S=y/C,E=k/C,_=.001,b=0-_,v=1+_;return b<=S&&S<=v&&b<=E&&E<=v?[e+S*d,t+S*g]:u?[e+S*d,t+S*g]:[]}else return y===0||k===0?JA(e,r,o)===o?[o,l]:JA(e,r,n)===n?[n,a]:JA(n,o,r)===r?[r,i]:[]:[]},"finiteLinesIntersect"),sje=s(function(e,t,r,i,n){var a=[],o=i/2,l=n/2,u=t,h=r;a.push({x:u+o*e[0],y:h+l*e[1]});for(var d=1;d0){var m=J3(d,-l);f=Z3(m)}else f=d}else f=r;for(var y,k,C,S,E=0;E2){for(var g=[h[0],h[1]],m=Math.pow(g[0]-e,2)+Math.pow(g[1]-t,2),y=1;yh&&(h=S)},"set"),get:s(function(C){return u[C]},"get")},p=0;p0?j=N.edgesTo(M)[0]:j=M.edgesTo(N)[0];var I=i(j);M=M.id(),b[M]>b[R]+I&&(b[M]=b[R]+I,v.nodes.indexOf(M)<0?v.push(M):v.updateItem(M),_[M]=0,E[M]=[]),b[M]==b[R]+I&&(_[M]=_[M]+_[R],E[M].push(R))}else for(var z=0;z0;){for(var Y=S.pop(),U=0;U0&&o.push(r[l]);o.length!==0&&n.push(i.collection(o))}return n},"assign"),Tje=s(function(e,t){for(var r=0;r5&&arguments[5]!==void 0?arguments[5]:wje,o=i,l,u,h=0;h=2?Q2(e,t,r,0,$H,Eje):Q2(e,t,r,0,RH)},"euclidean"),squaredEuclidean:s(function(e,t,r){return Q2(e,t,r,0,$H)},"squaredEuclidean"),manhattan:s(function(e,t,r){return Q2(e,t,r,0,RH)},"manhattan"),max:s(function(e,t,r){return Q2(e,t,r,-1/0,Sje)},"max")},rm["squared-euclidean"]=rm.squaredEuclidean,rm.squaredeuclidean=rm.squaredEuclidean,s($3,"clusteringDistance"),_je=Cs({k:2,m:2,sensitivityThreshold:1e-4,distance:"euclidean",maxIterations:10,attributes:[],testMode:!1,testCentroids:null}),n8=s(function(e){return _je(e)},"setOptions"),tk=s(function(e,t,r,i,n){var a=n!=="kMedoids",o=a?function(d){return r[d]}:function(d){return i[d](r)},l=s(function(d){return i[d](t)},"getQ"),u=r,h=t;return $3(e,i.length,o,l,u,h)},"getDist"),a8=s(function(e,t,r){for(var i=r.length,n=new Array(i),a=new Array(i),o=new Array(t),l=null,u=0;ur)return!1}return!0},"haveMatricesConverged"),$je=s(function(e,t,r){for(var i=0;io&&(o=t[u][h],l=h);n[l].push(e[u])}for(var d=0;d=n.threshold||n.mode==="dendrogram"&&e.length===1)return!1;var g=t[a],m=t[i[a]],y;n.mode==="dendrogram"?y={left:g,right:m,key:g.key}:y={value:g.value.concat(m.value),key:g.key},e[g.index]=y,e.splice(m.index,1),t[g.key]=y;for(var k=0;kr[m.key][C.key]&&(l=r[m.key][C.key])):n.linkage==="max"?(l=r[g.key][C.key],r[g.key][C.key]0&&i.push(n);return i},"findExemplars"),PH=s(function(e,t,r){for(var i=[],n=0;no&&(a=u,o=t[n*e+u])}a>0&&i.push(a)}for(var h=0;hu&&(l=h,u=d)}r[n]=a[l]}return i=PH(e,t,r),i},"assign"),FH=s(function(e){for(var t=this.cy(),r=this.nodes(),i=Wje(e),n={},a=0;a=$?(M=$,$=j,N=I):j>M&&(M=j);for(var z=0;z0?1:0;v[w%i.minIterations*o+H]=J,U+=J}if(U>0&&(w>=i.minIterations-1||w==i.maxIterations-1)){for(var te=0,ie=0;ie1||b>1)&&(o=!0),d[S]=[],C.outgoers().forEach(function(x){x.isEdge()&&d[S].push(x.id())})}else p[S]=[void 0,C.target().id()]}):a.forEach(function(C){var S=C.id();if(C.isNode()){var E=C.degree(!0);E%2&&(l?u?o=!0:u=S:l=S),d[S]=[],C.connectedEdges().forEach(function(_){return d[S].push(_.id())})}else p[S]=[C.source().id(),C.target().id()]});var f={found:!1,trail:void 0};if(o)return f;if(u&&l)if(n){if(h&&u!=h)return f;h=u}else{if(h&&u!=h&&l!=h)return f;h||(h=u)}else h||(h=a[0].id());var g=s(function(C){for(var S=C,E=[C],_,b,v;d[S].length;)_=d[S].shift(),b=p[_][0],v=p[_][1],S!=v?(d[v]=d[v].filter(function(x){return x!=_}),S=v):!n&&S!=b&&(d[b]=d[b].filter(function(x){return x!=_}),S=b),E.unshift(_),E.unshift(S);return E},"walk"),m=[],y=[];for(y=g(h);y.length!=1;)d[y[0]].length==0?(m.unshift(a.getElementById(y.shift())),m.unshift(a.getElementById(y.shift()))):y=g(y.shift()).concat(y);m.unshift(a.getElementById(y.shift()));for(var k in d)if(d[k].length)return f;return f.found=!0,f.trail=this.spawn(m,!0),f},"hierholzer")},rk=s(function(){var e=this,t={},r=0,i=0,n=[],a=[],o={},l=s(function(d,p){for(var f=a.length-1,g=[],m=e.spawn();a[f].x!=d||a[f].y!=p;)g.push(a.pop().edge),f--;g.push(a.pop().edge),g.forEach(function(y){var k=y.connectedNodes().intersection(e);m.merge(y),k.forEach(function(C){var S=C.id(),E=C.connectedEdges().intersection(e);m.merge(C),t[S].cutVertex?m.merge(E.filter(function(_){return _.isLoop()})):m.merge(E)})}),n.push(m)},"buildComponent"),u=s(function(d,p,f){d===f&&(i+=1),t[p]={id:r,low:r++,cutVertex:!1};var g=e.getElementById(p).connectedEdges().intersection(e);if(g.size()===0)n.push(e.spawn(e.getElementById(p)));else{var m,y,k,C;g.forEach(function(S){m=S.source().id(),y=S.target().id(),k=m===p?y:m,k!==f&&(C=S.id(),o[C]||(o[C]=!0,a.push({x:p,y:k,edge:S})),k in t?t[p].low=Math.min(t[p].low,t[k].id):(u(d,k,p),t[p].low=Math.min(t[p].low,t[k].low),t[p].id<=t[k].low&&(t[p].cutVertex=!0,l(p,k))))})}},"biconnectedSearch");e.forEach(function(d){if(d.isNode()){var p=d.id();p in t||(i=0,u(p,p),t[p].cutVertex=i>1)}});var h=Object.keys(t).filter(function(d){return t[d].cutVertex}).map(function(d){return e.getElementById(d)});return{cut:e.spawn(h),components:n}},"hopcroftTarjanBiconnected"),Kje={hopcroftTarjanBiconnected:rk,htbc:rk,htb:rk,hopcroftTarjanBiconnectedComponents:rk},ik=s(function(){var e=this,t={},r=0,i=[],n=[],a=e.spawn(e),o=s(function(l){n.push(l),t[l]={index:r,low:r++,explored:!1};var u=e.getElementById(l).connectedEdges().intersection(e);if(u.forEach(function(g){var m=g.target().id();m!==l&&(m in t||o(m),t[m].explored||(t[l].low=Math.min(t[l].low,t[m].low)))}),t[l].index===t[l].low){for(var h=e.spawn();;){var d=n.pop();if(h.merge(e.getElementById(d)),t[d].low=t[l].index,t[d].explored=!0,d===l)break}var p=h.edgesWith(h),f=h.merge(p);i.push(f),a=a.difference(f)}},"stronglyConnectedSearch");return e.forEach(function(l){if(l.isNode()){var u=l.id();u in t||o(u)}}),{cut:a,components:i}},"tarjanStronglyConnected"),Qje={tarjanStronglyConnected:ik,tsc:ik,tscc:ik,tarjanStronglyConnectedComponents:ik},zH={},[Y2,RWe,$We,IWe,DWe,OWe,FWe,uje,em,tm,i8,Cje,Oje,zje,Hje,Xje,Kje,Qje].forEach(function(e){Ur(zH,e)}),qH=0,WH=1,jH=2,sc=s(function(e){if(!(this instanceof sc))return new sc(e);this.id="Thenable/1.0.7",this.state=qH,this.fulfillValue=void 0,this.rejectReason=void 0,this.onFulfilled=[],this.onRejected=[],this.proxy={then:this.then.bind(this)},typeof e=="function"&&e.call(this,this.fulfill.bind(this),this.reject.bind(this))},"api"),sc.prototype={fulfill:s(function(e){return VH(this,WH,"fulfillValue",e)},"fulfill"),reject:s(function(e){return VH(this,jH,"rejectReason",e)},"reject"),then:s(function(e,t){var r=this,i=new sc;return r.onFulfilled.push(HH(e,i,"fulfill")),r.onRejected.push(HH(t,i,"reject")),UH(r),i.proxy},"then")},VH=s(function(e,t,r,i){return e.state===qH&&(e.state=t,e[r]=i,UH(e)),e},"deliver"),UH=s(function(e){e.state===WH?YH(e,"onFulfilled",e.fulfillValue):e.state===jH&&YH(e,"onRejected",e.rejectReason)},"execute"),YH=s(function(e,t,r){if(e[t].length!==0){var i=e[t];e[t]=[];var n=s(function(){for(var a=0;a0},"animatedImpl")},"animated"),clearQueue:s(function(){return s(function(){var e=this,t=e.length!==void 0,r=t?e:[e],i=this._private.cy||this;if(!i.styleEnabled())return this;for(var n=0;n0&&this.spawn(i).updateStyle().emit("class"),t},"classes"),addClass:s(function(e){return this.toggleClass(e,!0)},"addClass"),hasClass:s(function(e){var t=this[0];return t!=null&&t._private.classes.has(e)},"hasClass"),toggleClass:s(function(e,t){mn(e)||(e=e.match(/\S+/g)||[]);for(var r=this,i=t===void 0,n=[],a=0,o=r.length;a0&&this.spawn(n).updateStyle().emit("class"),r},"toggleClass"),removeClass:s(function(e){return this.toggleClass(e,!1)},"removeClass"),flashClass:s(function(e,t){var r=this;if(t==null)t=250;else if(t===0)return r;return r.addClass(e),setTimeout(function(){r.removeClass(e)},t),r},"flashClass")},nk.className=nk.classNames=nk.classes,zi={metaChar:"[\\!\\\"\\#\\$\\%\\&\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]",comparatorOp:"=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=",boolOp:"\\?|\\!|\\^",string:`"(?:\\\\"|[^"])*"|'(?:\\\\'|[^'])*'`,number:Va,meta:"degree|indegree|outdegree",separator:"\\s*,\\s*",descendant:"\\s+",child:"\\s+>\\s+",subject:"\\$",group:"node|edge|\\*",directedEdge:"\\s+->\\s+",undirectedEdge:"\\s+<->\\s+"},zi.variable="(?:[\\w-.]|(?:\\\\"+zi.metaChar+"))+",zi.className="(?:[\\w-]|(?:\\\\"+zi.metaChar+"))+",zi.value=zi.string+"|"+zi.number,zi.id=zi.variable,(function(){var e,t,r;for(e=zi.comparatorOp.split("|"),r=0;r=0)&&t!=="="&&(zi.comparatorOp+="|\\!"+t)})(),ln=s(function(){return{checks:[]}},"newQuery"),Sr={GROUP:0,COLLECTION:1,FILTER:2,DATA_COMPARE:3,DATA_EXIST:4,DATA_BOOL:5,META_COMPARE:6,STATE:7,ID:8,CLASS:9,UNDIRECTED_EDGE:10,DIRECTED_EDGE:11,NODE_SOURCE:12,NODE_TARGET:13,NODE_NEIGHBOR:14,CHILD:15,DESCENDANT:16,PARENT:17,ANCESTOR:18,COMPOUND_SPLIT:19,TRUE:20},c8=[{selector:":selected",matches:s(function(e){return e.selected()},"matches")},{selector:":unselected",matches:s(function(e){return!e.selected()},"matches")},{selector:":selectable",matches:s(function(e){return e.selectable()},"matches")},{selector:":unselectable",matches:s(function(e){return!e.selectable()},"matches")},{selector:":locked",matches:s(function(e){return e.locked()},"matches")},{selector:":unlocked",matches:s(function(e){return!e.locked()},"matches")},{selector:":visible",matches:s(function(e){return e.visible()},"matches")},{selector:":hidden",matches:s(function(e){return!e.visible()},"matches")},{selector:":transparent",matches:s(function(e){return e.transparent()},"matches")},{selector:":grabbed",matches:s(function(e){return e.grabbed()},"matches")},{selector:":free",matches:s(function(e){return!e.grabbed()},"matches")},{selector:":removed",matches:s(function(e){return e.removed()},"matches")},{selector:":inside",matches:s(function(e){return!e.removed()},"matches")},{selector:":grabbable",matches:s(function(e){return e.grabbable()},"matches")},{selector:":ungrabbable",matches:s(function(e){return!e.grabbable()},"matches")},{selector:":animated",matches:s(function(e){return e.animated()},"matches")},{selector:":unanimated",matches:s(function(e){return!e.animated()},"matches")},{selector:":parent",matches:s(function(e){return e.isParent()},"matches")},{selector:":childless",matches:s(function(e){return e.isChildless()},"matches")},{selector:":child",matches:s(function(e){return e.isChild()},"matches")},{selector:":orphan",matches:s(function(e){return e.isOrphan()},"matches")},{selector:":nonorphan",matches:s(function(e){return e.isChild()},"matches")},{selector:":compound",matches:s(function(e){return e.isNode()?e.isParent():e.source().isParent()||e.target().isParent()},"matches")},{selector:":loop",matches:s(function(e){return e.isLoop()},"matches")},{selector:":simple",matches:s(function(e){return e.isSimple()},"matches")},{selector:":active",matches:s(function(e){return e.active()},"matches")},{selector:":inactive",matches:s(function(e){return!e.active()},"matches")},{selector:":backgrounding",matches:s(function(e){return e.backgrounding()},"matches")},{selector:":nonbackgrounding",matches:s(function(e){return!e.backgrounding()},"matches")}].sort(function(e,t){return jqe(e.selector,t.selector)}),aUe=(function(){for(var e={},t,r=0;r0&&h.edgeCount>0)return Ji("The selector `"+e+"` is invalid because it uses both a compound selector and an edge selector"),!1;if(h.edgeCount>1)return Ji("The selector `"+e+"` is invalid because it uses multiple edge selectors"),!1;h.edgeCount===1&&Ji("The selector `"+e+"` is deprecated. Edge selectors do not take effect on changes to source and target nodes after an edge is added, for performance reasons. Use a class or data selector on edges instead, updating the class or data of an edge when your app detects a change in source or target nodes.")}return!0},"parse"),hUe=s(function(){if(this.toStringCache!=null)return this.toStringCache;for(var e=s(function(u){return u??""},"clean"),t=s(function(u){return zr(u)?'"'+u+'"':e(u)},"cleanVal"),r=s(function(u){return" "+u+" "},"space"),i=s(function(u,h){var d=u.type,p=u.value;switch(d){case Sr.GROUP:{var f=e(p);return f.substring(0,f.length-1)}case Sr.DATA_COMPARE:{var g=u.field,m=u.operator;return"["+g+r(e(m))+t(p)+"]"}case Sr.DATA_BOOL:{var y=u.operator,k=u.field;return"["+e(y)+k+"]"}case Sr.DATA_EXIST:{var C=u.field;return"["+C+"]"}case Sr.META_COMPARE:{var S=u.operator,E=u.field;return"[["+E+r(e(S))+t(p)+"]]"}case Sr.STATE:return p;case Sr.ID:return"#"+p;case Sr.CLASS:return"."+p;case Sr.PARENT:case Sr.CHILD:return n(u.parent,h)+r(">")+n(u.child,h);case Sr.ANCESTOR:case Sr.DESCENDANT:return n(u.ancestor,h)+" "+n(u.descendant,h);case Sr.COMPOUND_SPLIT:{var _=n(u.left,h),b=n(u.subject,h),v=n(u.right,h);return _+(_.length>0?" ":"")+b+v}case Sr.TRUE:return""}},"checkToString"),n=s(function(u,h){return u.checks.reduce(function(d,p,f){return d+(h===u&&f===0?"$":"")+i(p,h)},"")},"queryToString"),a="",o=0;o1&&o=0&&(t=t.replace("!",""),d=!0),t.indexOf("@")>=0&&(t=t.replace("@",""),h=!0),(n||o||h)&&(l=!n&&!a?"":""+e,u=""+r),h&&(e=l=l.toLowerCase(),r=u=u.toLowerCase()),t){case"*=":i=l.indexOf(u)>=0;break;case"$=":i=l.indexOf(u,l.length-u.length)>=0;break;case"^=":i=l.indexOf(u)===0;break;case"=":i=e===r;break;case">":p=!0,i=e>r;break;case">=":p=!0,i=e>=r;break;case"<":p=!0,i=e1&&arguments[1]!==void 0?arguments[1]:!0;return IA(this,e,t,JU)},s(eY,"addParent"),sm.forEachUp=function(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return IA(this,e,t,eY)},s(jze,"addParentAndChildren"),sm.forEachUpAndDown=function(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return IA(this,e,t,jze)},sm.ancestors=sm.parents,ak=bUe={data:en.data({field:"data",bindingEvent:"data",allowBinding:!0,allowSetting:!0,settingEvent:"data",settingTriggersEvent:!0,triggerFnName:"trigger",allowGetting:!0,immutableKeys:{id:!0,source:!0,target:!0,parent:!0},updateStyle:!0}),removeData:en.removeData({field:"data",event:"data",triggerFnName:"trigger",triggerEvent:!0,immutableKeys:{id:!0,source:!0,target:!0,parent:!0},updateStyle:!0}),scratch:en.data({field:"scratch",bindingEvent:"scratch",allowBinding:!0,allowSetting:!0,settingEvent:"scratch",settingTriggersEvent:!0,triggerFnName:"trigger",allowGetting:!0,updateStyle:!0}),removeScratch:en.removeData({field:"scratch",event:"scratch",triggerFnName:"trigger",triggerEvent:!0,updateStyle:!0}),rscratch:en.data({field:"rscratch",allowBinding:!1,allowSetting:!0,settingTriggersEvent:!1,allowGetting:!0}),removeRscratch:en.removeData({field:"rscratch",triggerEvent:!1}),id:s(function(){var e=this[0];if(e)return e._private.data.id},"id")},ak.attr=ak.data,ak.removeAttr=ak.removeData,xUe=bUe,sk={},s(MA,"defineDegreeFunction"),Ur(sk,{degree:MA(function(e,t){return t.source().same(t.target())?2:1}),indegree:MA(function(e,t){return t.target().same(e)?1:0}),outdegree:MA(function(e,t){return t.source().same(e)?1:0})}),s(Ug,"defineDegreeBoundsFunction"),Ur(sk,{minDegree:Ug("degree",function(e,t){return et}),minIndegree:Ug("indegree",function(e,t){return et}),minOutdegree:Ug("outdegree",function(e,t){return et})}),Ur(sk,{totalDegree:s(function(e){for(var t=0,r=this.nodes(),i=0;i0,p=d;d&&(h=h[0]);var f=p?h.position():{x:0,y:0};t!==void 0?u.position(e,t+f[e]):n!==void 0&&u.position({x:n.x+f.x,y:n.y+f.y})}else{var g=r.position(),m=o?r.parent():null,y=m&&m.length>0,k=y;y&&(m=m[0]);var C=k?m.position():{x:0,y:0};return n={x:g.x-C.x,y:g.y-C.y},e===void 0?n:n[e]}else if(!a)return;return this},"relativePosition")},Yc.modelPosition=Yc.point=Yc.position,Yc.modelPositions=Yc.points=Yc.positions,Yc.renderedPoint=Yc.renderedPosition,Yc.relativePoint=Yc.relativePosition,kUe=TUe,om=s(function(e){switch(e){case"left":case"right-inside":return"left";case"right":case"left-inside":return"right";default:return"center"}},"labelHalign"),lm=s(function(e){switch(e){case"top":case"bottom-inside":return"top";case"bottom":case"top-inside":return"bottom";default:return"center"}},"labelValign"),CUe=s(function(e){switch(e){case"left":return"right";case"right":return"left";case"left-inside":return"left";case"right-inside":return"right";default:return"center"}},"labelJustification"),Z2=yf={},yf.renderedBoundingBox=function(e){var t=this.boundingBox(e),r=this.cy(),i=r.zoom(),n=r.pan(),a=t.x1*i+n.x,o=t.x2*i+n.x,l=t.y1*i+n.y,u=t.y2*i+n.y;return{x1:a,x2:o,y1:l,y2:u,w:o-a,h:u-l}},yf.dirtyCompoundBoundsCache=function(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!1,t=this.cy();return!t.styleEnabled()||!t.hasCompoundNodes()?this:(this.forEachUp(function(r){if(r.isParent()){var i=r._private;i.compoundBoundsClean=!1,i.bbCache=null,e||r.emitAndNotify("bounds")}}),this)},yf.updateCompoundBounds=function(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!1,t=this.cy();if(!t.styleEnabled()||!t.hasCompoundNodes())return this;if(!e&&t.batching())return this;function r(o){if(!o.isParent())return;var l=o._private,u=o.children(),h=o.pstyle("compound-sizing-wrt-labels").value==="include",d={width:{val:o.pstyle("min-width").pfValue,left:o.pstyle("min-width-bias-left"),right:o.pstyle("min-width-bias-right")},height:{val:o.pstyle("min-height").pfValue,top:o.pstyle("min-height-bias-top"),bottom:o.pstyle("min-height-bias-bottom")}},p=u.boundingBox({includeLabels:h,includeOverlays:!1,useCache:!1}),f=l.position;(p.w===0||p.h===0)&&(p={w:o.pstyle("width").pfValue,h:o.pstyle("height").pfValue},p.x1=f.x-p.w/2,p.x2=f.x+p.w/2,p.y1=f.y-p.h/2,p.y2=f.y+p.h/2);function g(R,$,M){var N=0,j=0,I=$+M;return R>0&&I>0&&(N=$/I*R,j=M/I*R),{biasDiff:N,biasComplementDiff:j}}s(g,"computeBiasValues");function m(R,$,M,N){if(M.units==="%")switch(N){case"width":return R>0?M.pfValue*R:0;case"height":return $>0?M.pfValue*$:0;case"average":return R>0&&$>0?M.pfValue*(R+$)/2:0;case"min":return R>0&&$>0?R>$?M.pfValue*$:M.pfValue*R:0;case"max":return R>0&&$>0?R>$?M.pfValue*R:M.pfValue*$:0;default:return 0}else return M.units==="px"?M.pfValue:0}s(m,"computePaddingValues");var y=d.width.left.value;d.width.left.units==="px"&&d.width.val>0&&(y=y*100/d.width.val);var k=d.width.right.value;d.width.right.units==="px"&&d.width.val>0&&(k=k*100/d.width.val);var C=d.height.top.value;d.height.top.units==="px"&&d.height.val>0&&(C=C*100/d.height.val);var S=d.height.bottom.value;d.height.bottom.units==="px"&&d.height.val>0&&(S=S*100/d.height.val);var E=g(d.width.val-p.w,y,k),_=E.biasDiff,b=E.biasComplementDiff,v=g(d.height.val-p.h,C,S),x=v.biasDiff,w=v.biasComplementDiff;l.autoPadding=m(p.w,p.h,o.pstyle("padding"),o.pstyle("padding-relative-to").value),l.autoWidth=Math.max(p.w,d.width.val),f.x=(-_+p.x1+p.x2+b)/2,l.autoHeight=Math.max(p.h,d.height.val),f.y=(-x+p.y1+p.y2+w)/2}s(r,"update");for(var i=0;ie.x2?i:e.x2,e.y1=re.y2?n:e.y2,e.w=e.x2-e.x1,e.h=e.y2-e.y1)},"updateBounds"),Dd=s(function(e,t){return t==null?e:oc(e,t.x1,t.y1,t.x2,t.y2)},"updateBoundsFromBox"),J2=s(function(e,t,r){return co(e,t,r)},"prefixedProperty"),ok=s(function(e,t,r){if(!t.cy().headless()){var i=t._private,n=i.rstyle,a=n.arrowWidth/2,o=t.pstyle(r+"-arrow-shape").value,l,u;if(o!=="none"){r==="source"?(l=n.srcX,u=n.srcY):r==="target"?(l=n.tgtX,u=n.tgtY):(l=n.midX,u=n.midY);var h=i.arrowBounds=i.arrowBounds||{},d=h[r]=h[r]||{};d.x1=l-a,d.y1=u-a,d.x2=l+a,d.y2=u+a,d.w=d.x2-d.x1,d.h=d.y2-d.y1,K3(d,1),oc(e,d.x1,d.y1,d.x2,d.y2)}}},"updateBoundsFromArrow"),d8=s(function(e,t,r){if(!t.cy().headless()){var i;r?i=r+"-":i="";var n=t._private,a=n.rstyle,o=t.pstyle(i+"label").strValue;if(o){var l=t.pstyle("text-halign"),u=t.pstyle("text-valign"),h=J2(a,"labelWidth",r),d=J2(a,"labelHeight",r),p=J2(a,"labelX",r),f=J2(a,"labelY",r),g=t.pstyle(i+"text-margin-x").pfValue,m=t.pstyle(i+"text-margin-y").pfValue,y=t.isEdge(),k=t.pstyle(i+"text-rotation"),C=t.pstyle("text-outline-width").pfValue,S=t.pstyle("text-border-width").pfValue,E=S/2,_=t.pstyle("text-background-padding").pfValue,b=2,v=d,x=h,w=x/2,R=v/2,$,M,N,j;if(y)$=p-w,M=p+w,N=f-R,j=f+R;else{switch(om(l.value)){case"left":$=p-x,M=p;break;case"center":$=p-w,M=p+w;break;case"right":$=p,M=p+x;break}switch(lm(u.value)){case"top":N=f-v,j=f;break;case"center":N=f-R,j=f+R;break;case"bottom":N=f,j=f+v;break}}var I=g-Math.max(C,E)-_-b,z=g+Math.max(C,E)+_+b,F=m-Math.max(C,E)-_-b,O=m+Math.max(C,E)+_+b;$+=I,M+=z,N+=F,j+=O;var B=r||"main",Y=n.labelBounds,U=Y[B]=Y[B]||{};U.x1=$,U.y1=N,U.x2=M,U.y2=j,U.w=M-$,U.h=j-N,U.leftPad=I,U.rightPad=z,U.topPad=F,U.botPad=O;var H=y&&k.strValue==="autorotate",J=k.pfValue!=null&&k.pfValue!==0;if(H||J){var te=H?J2(n.rstyle,"labelAngle",r):k.pfValue,ie=Math.cos(te),ue=Math.sin(te),se=($+M)/2,le=(N+j)/2;if(!y){switch(om(l.value)){case"left":se=M;break;case"right":se=$;break}switch(lm(u.value)){case"top":le=j;break;case"bottom":le=N;break}}var Te=s(function($e,Me){return $e=$e-se,Me=Me-le,{x:$e*ie-Me*ue+se,y:$e*ue+Me*ie+le}},"rotate"),oe=Te($,N),fe=Te($,j),ge=Te(M,N),ke=Te(M,j);$=Math.min(oe.x,fe.x,ge.x,ke.x),M=Math.max(oe.x,fe.x,ge.x,ke.x),N=Math.min(oe.y,fe.y,ge.y,ke.y),j=Math.max(oe.y,fe.y,ge.y,ke.y)}var Ne=B+"Rot",xe=Y[Ne]=Y[Ne]||{};xe.x1=$,xe.y1=N,xe.x2=M,xe.y2=j,xe.w=M-$,xe.h=j-N,oc(e,$,N,M,j),oc(n.labelBounds.all,$,N,M,j)}return e}},"updateBoundsFromLabel"),XG=s(function(e,t){if(!t.cy().headless()){var r=t.pstyle("outline-opacity").value,i=t.pstyle("outline-width").value,n=t.pstyle("outline-offset").value,a=i+n;KG(e,t,r,a,"outside",a/2)}},"updateBoundsFromOutline"),KG=s(function(e,t,r,i,n,a){if(!(r===0||i<=0||n==="inside")){var o=t.cy(),l=o.renderer(),u=l.nodeShapes[l.getNodeShape(t)];if(u){var h=t.position(),d=h.x,p=h.y,f=t.width(),g=t.height();if(u.hasMiterBounds){n==="center"&&(i/=2);var m=u.miterBounds(d,p,f,g,i);Dd(e,m)}else a!=null&&a>0&&Q3(e,[a,a,a,a])}}},"updateBoundsFromMiter"),wUe=s(function(e,t){if(!t.cy().headless()){var r=t.pstyle("border-opacity").value,i=t.pstyle("border-width").pfValue,n=t.pstyle("border-position").value;KG(e,t,r,i,n)}},"updateBoundsFromMiterBorder"),EUe=s(function(e,t){var r=e._private.cy,i=r.styleEnabled(),n=r.headless(),a=uo(),o=e._private,l=e.isNode(),u=e.isEdge(),h,d,p,f,g,m,y=o.rstyle,k=l&&i?e.pstyle("bounds-expansion").pfValue:[0],C=s(function(xe){return xe.pstyle("display").value!=="none"},"isDisplayed"),S=!i||C(e)&&(!u||C(e.source())&&C(e.target()));if(S){var E=0,_=0;i&&t.includeOverlays&&(E=e.pstyle("overlay-opacity").value,E!==0&&(_=e.pstyle("overlay-padding").value));var b=0,v=0;i&&t.includeUnderlays&&(b=e.pstyle("underlay-opacity").value,b!==0&&(v=e.pstyle("underlay-padding").value));var x=Math.max(_,v),w=0,R=0;if(i&&(w=e.pstyle("width").pfValue,R=w/2),l&&t.includeNodes){var $=e.position();g=$.x,m=$.y;var M=e.outerWidth(),N=M/2,j=e.outerHeight(),I=j/2;h=g-N,d=g+N,p=m-I,f=m+I,oc(a,h,p,d,f),i&&XG(a,e),i&&t.includeOutlines&&!n&&XG(a,e),i&&wUe(a,e)}else if(u&&t.includeEdges)if(i&&!n){var z=e.pstyle("curve-style").strValue;if(h=Math.min(y.srcX,y.midX,y.tgtX),d=Math.max(y.srcX,y.midX,y.tgtX),p=Math.min(y.srcY,y.midY,y.tgtY),f=Math.max(y.srcY,y.midY,y.tgtY),h-=R,d+=R,p-=R,f+=R,oc(a,h,p,d,f),z==="haystack"){var F=y.haystackPts;if(F&&F.length===2){if(h=F[0].x,p=F[0].y,d=F[1].x,f=F[1].y,h>d){var O=h;h=d,d=O}if(p>f){var B=p;p=f,f=B}oc(a,h-R,p-R,d+R,f+R)}}else if(z==="bezier"||z==="unbundled-bezier"||wd(z,"segments")||wd(z,"taxi")){var Y;switch(z){case"bezier":case"unbundled-bezier":Y=y.bezierPts;break;case"segments":case"taxi":case"round-segments":case"round-taxi":Y=y.linePts;break}if(Y!=null)for(var U=0;Ud){var se=h;h=d,d=se}if(p>f){var le=p;p=f,f=le}h-=R,d+=R,p-=R,f+=R,oc(a,h,p,d,f)}if(i&&t.includeEdges&&u&&(ok(a,e,"mid-source"),ok(a,e,"mid-target"),ok(a,e,"source"),ok(a,e,"target")),i){var Te=e.pstyle("ghost").value==="yes";if(Te){var oe=e.pstyle("ghost-offset-x").pfValue,fe=e.pstyle("ghost-offset-y").pfValue;oc(a,a.x1+oe,a.y1+fe,a.x2+oe,a.y2+fe)}}var ge=o.bodyBounds=o.bodyBounds||{};vH(ge,a),Q3(ge,k),K3(ge,1),i&&(h=a.x1,d=a.x2,p=a.y1,f=a.y2,oc(a,h-x,p-x,d+x,f+x));var ke=o.overlayBounds=o.overlayBounds||{};vH(ke,a),Q3(ke,k),K3(ke,1);var Ne=o.labelBounds=o.labelBounds||{};Ne.all!=null?XWe(Ne.all):Ne.all=uo(),i&&t.includeLabels&&(t.includeMainLabels&&d8(a,e,null),u&&(t.includeSourceLabels&&d8(a,e,"source"),t.includeTargetLabels&&d8(a,e,"target")))}return a.x1=Ll(a.x1),a.y1=Ll(a.y1),a.x2=Ll(a.x2),a.y2=Ll(a.y2),a.w=Ll(a.x2-a.x1),a.h=Ll(a.y2-a.y1),a.w>0&&a.h>0&&S&&(Q3(a,k),K3(a,1)),a},"boundingBoxImpl"),QG=s(function(e){var t=0,r=s(function(n){return(n?1:0)<=0;l--)o(l);return this},Nd.removeAllListeners=function(){return this.removeListener("*")},Nd.emit=Nd.trigger=function(e,t,r){var i=this.listeners,n=i.length;return this.emitting++,mn(t)||(t=[t]),qUe(this,function(a,o){r!=null&&(i=[{event:o.event,type:o.type,namespace:o.namespace,callback:r}],n=i.length);for(var l=s(function(){var h=i[u];if(h.type===o.type&&(!h.namespace||h.namespace===o.namespace||h.namespace===FUe)&&a.eventMatches(a.context,h,o)){var d=[o];t!=null&&bWe(d,t),a.beforeEmit(a.context,h,o),h.conf&&h.conf.one&&(a.listeners=a.listeners.filter(function(g){return g!==h}));var p=a.callbackContext(a.context,h,o),f=h.callback.apply(p,d);a.afterEmit(a.context,h,o),f===!1&&(o.stopPropagation(),o.preventDefault())}},"_loop2"),u=0;u1&&!a){var o=this.length-1,l=this[o],u=l._private.data.id;this[o]=void 0,this[e]=l,n.set(u,{ele:l,index:e})}return this.length--,this},"unmergeAt"),unmergeOne:s(function(e){e=e[0];var t=this._private,r=e._private.data.id,i=t.map,n=i.get(r);if(!n)return this;var a=n.index;return this.unmergeAt(a),this},"unmergeOne"),unmerge:s(function(e){var t=this._private.cy;if(!e)return this;if(e&&zr(e)){var r=e;e=t.mutableElements().filter(r)}for(var i=0;i=0;t--){var r=this[t];e(r)&&this.unmergeAt(t)}return this},"unmergeBy"),map:s(function(e,t){for(var r=[],i=this,n=0;nr&&(r=l,i=o)}return{value:r,ele:i}},"max"),min:s(function(e,t){for(var r=1/0,i,n=this,a=0;a=0&&n"u"?"undefined":Wa(Symbol))!=e&&Wa(Symbol.iterator)!=e;t&&(hk[Symbol.iterator]=function(){var r=this,i={value:void 0,done:!1},n=0,a=this.length;return UU({next:s(function(){return n1&&arguments[1]!==void 0?arguments[1]:!0,r=this[0],i=r.cy();if(i.styleEnabled()&&r){r._private.styleDirty&&(r._private.styleDirty=!1,i.style().apply(r));var n=r._private.style[e];return n??(t?i.style().getDefaultProperty(e):null)}},"parsedStyle"),numericStyle:s(function(e){var t=this[0];if(t.cy().styleEnabled()&&t){var r=t.pstyle(e);return r.pfValue!==void 0?r.pfValue:r.value}},"numericStyle"),numericStyleUnits:s(function(e){var t=this[0];if(t.cy().styleEnabled()&&t)return t.pstyle(e).units},"numericStyleUnits"),renderedStyle:s(function(e){var t=this.cy();if(!t.styleEnabled())return this;var r=this[0];if(r)return t.style().getRenderedStyle(r,e)},"renderedStyle"),style:s(function(e,t){var r=this.cy();if(!r.styleEnabled())return this;var i=!1,n=r.style();if(Fi(e)){var a=e;n.applyBypass(this,a,i),this.emitAndNotify("style")}else if(zr(e))if(t===void 0){var o=this[0];return o?n.getStylePropertyValue(o,e):void 0}else n.applyBypass(this,e,t,i),this.emitAndNotify("style");else if(e===void 0){var l=this[0];return l?n.getRawStyle(l):void 0}return this},"style"),removeStyle:s(function(e){var t=this.cy();if(!t.styleEnabled())return this;var r=!1,i=t.style(),n=this;if(e===void 0)for(var a=0;a0&&t.push(d[0]),t.push(l[0])}return this.spawn(t,!0).filter(e)},"neighborhood"),closedNeighborhood:s(function(e){return this.neighborhood().add(this).filter(e)},"closedNeighborhood"),openNeighborhood:s(function(e){return this.neighborhood(e)},"openNeighborhood")}),Ys.neighbourhood=Ys.neighborhood,Ys.closedNeighbourhood=Ys.closedNeighborhood,Ys.openNeighbourhood=Ys.openNeighborhood,Ur(Ys,{source:$l(s(function(e){var t=this[0],r;return t&&(r=t._private.source||t.cy().collection()),r&&e?r.filter(e):r},"sourceImpl"),"source"),target:$l(s(function(e){var t=this[0],r;return t&&(r=t._private.target||t.cy().collection()),r&&e?r.filter(e):r},"targetImpl"),"target"),sources:iY({attr:"source"}),targets:iY({attr:"target"})}),s(iY,"defineSourceFunction"),Ur(Ys,{edgesWith:$l(nY(),"edgesWith"),edgesTo:$l(nY({thisIsSrc:!0}),"edgesTo")}),s(nY,"defineEdgesWithFunction"),Ur(Ys,{connectedEdges:$l(function(e){for(var t=[],r=this,i=0;i0);return a},"components"),component:s(function(){var e=this[0];return e.cy().mutableElements().components(e)[0]},"component")}),Ys.componentsOf=Ys.components,Es=s(function(e,t){var r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!1,i=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!1;if(e===void 0){Kn("A collection must have a reference to the core");return}var n=new th,a=!1;if(!t)t=[];else if(t.length>0&&Fi(t[0])&&!W2(t[0])){a=!0;for(var o=[],l=new Qg,u=0,h=t.length;u0&&arguments[0]!==void 0?arguments[0]:!0,t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,r=this,i=r.cy(),n=i._private,a=[],o=[],l,u=0,h=r.length;u0){for(var Y=l.length===r.length?r:new Es(i,l),U=0;U0&&arguments[0]!==void 0?arguments[0]:!0,t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,r=this,i=[],n={},a=r._private.cy;function o(I){for(var z=I._private.edges,F=0;F0&&(e?M.emitAndNotify("remove"):t&&M.emit("remove"));for(var N=0;Nd&&Math.abs(m.v)>d;);return f?function(y){return u[y*(u.length-1)|0]}:h},"springRK4Factory")})(),Cn=s(function(e,t,r,i){var n=Vze(e,t,r,i);return function(a,o,l){return a+(o-a)*n(l)}},"cubicBezier"),pk={linear:s(function(e,t,r){return e+(t-e)*r},"linear"),ease:Cn(.25,.1,.25,1),"ease-in":Cn(.42,0,1,1),"ease-out":Cn(0,0,.58,1),"ease-in-out":Cn(.42,0,.58,1),"ease-in-sine":Cn(.47,0,.745,.715),"ease-out-sine":Cn(.39,.575,.565,1),"ease-in-out-sine":Cn(.445,.05,.55,.95),"ease-in-quad":Cn(.55,.085,.68,.53),"ease-out-quad":Cn(.25,.46,.45,.94),"ease-in-out-quad":Cn(.455,.03,.515,.955),"ease-in-cubic":Cn(.55,.055,.675,.19),"ease-out-cubic":Cn(.215,.61,.355,1),"ease-in-out-cubic":Cn(.645,.045,.355,1),"ease-in-quart":Cn(.895,.03,.685,.22),"ease-out-quart":Cn(.165,.84,.44,1),"ease-in-out-quart":Cn(.77,0,.175,1),"ease-in-quint":Cn(.755,.05,.855,.06),"ease-out-quint":Cn(.23,1,.32,1),"ease-in-out-quint":Cn(.86,0,.07,1),"ease-in-expo":Cn(.95,.05,.795,.035),"ease-out-expo":Cn(.19,1,.22,1),"ease-in-out-expo":Cn(1,0,0,1),"ease-in-circ":Cn(.6,.04,.98,.335),"ease-out-circ":Cn(.075,.82,.165,1),"ease-in-out-circ":Cn(.785,.135,.15,.86),spring:s(function(e,t,r){if(r===0)return pk.linear;var i=QUe(e,t,r);return function(n,a,o){return n+(a-n)*i(o)}},"spring"),"cubic-bezier":Cn},s(sY,"getEasedValue"),s(oY,"getValue"),s(Hg,"ease"),s(Uze,"step$1"),s(z2,"valid"),s(Yze,"startAnimation"),s(lY,"stepAll"),ZUe={animate:en.animate(),animation:en.animation(),animated:en.animated(),clearQueue:en.clearQueue(),delay:en.delay(),delayAnimation:en.delayAnimation(),stop:en.stop(),addToAnimationPool:s(function(e){var t=this;t.styleEnabled()&&t._private.aniEles.merge(e)},"addToAnimationPool"),stopAnimationLoop:s(function(){this._private.animationsRunning=!1},"stopAnimationLoop"),startAnimationLoop:s(function(){var e=this;if(e._private.animationsRunning=!0,!e.styleEnabled())return;function t(){e._private.animationsRunning&&V3(s(function(i){lY(i,e),t()},"animationStep"))}s(t,"headlessStep");var r=e.renderer();r&&r.beforeRender?r.beforeRender(s(function(i,n){lY(n,e)},"rendererAnimationStep"),r.beforeRenderPriorities.animations):t()},"startAnimationLoop")},JUe={qualifierCompare:s(function(e,t){return e==null||t==null?e==null&&t==null:e.sameText(t)},"qualifierCompare"),eventMatches:s(function(e,t,r){var i=t.qualifier;return i!=null?e!==r.target&&W2(r.target)&&i.matches(r.target):!0},"eventMatches"),addEventFields:s(function(e,t){t.cy=e,t.target=e},"addEventFields"),callbackContext:s(function(e,t,r){return t.qualifier!=null?r.target:e},"callbackContext")},fk=s(function(e){return zr(e)?new Ld(e):e},"argSelector"),mX={createEmitter:s(function(){var e=this._private;return e.emitter||(e.emitter=new O3(JUe,this)),this},"createEmitter"),emitter:s(function(){return this._private.emitter},"emitter"),on:s(function(e,t,r){return this.emitter().on(e,fk(t),r),this},"on"),removeListener:s(function(e,t,r){return this.emitter().removeListener(e,fk(t),r),this},"removeListener"),removeAllListeners:s(function(){return this.emitter().removeAllListeners(),this},"removeAllListeners"),one:s(function(e,t,r){return this.emitter().one(e,fk(t),r),this},"one"),once:s(function(e,t,r){return this.emitter().one(e,fk(t),r),this},"once"),emit:s(function(e,t){return this.emitter().emit(e,t),this},"emit"),emitAndNotify:s(function(e,t){return this.emit(e),this.notify(e,t),this},"emitAndNotify")},en.eventAliasesOn(mX),p8={png:s(function(e){var t=this._private.renderer;return e=e||{},t.png(e)},"png"),jpg:s(function(e){var t=this._private.renderer;return e=e||{},e.bg=e.bg||"#fff",t.jpg(e)},"jpg")},p8.jpeg=p8.jpg,gk={layout:s(function(e){var t=this;if(e==null){Kn("Layout options must be specified to make a layout");return}if(e.name==null){Kn("A `name` must be specified to make a layout");return}var r=e.name,i=t.extension("layout",r);if(i==null){Kn("No such layout `"+r+"` found. Did you forget to import it and `cytoscape.use()` it?");return}var n;zr(e.eles)?n=t.$(e.eles):n=e.eles!=null?e.eles:t.$();var a=new i(Ur({},e,{cy:t,eles:n}));return a},"layout")},gk.createLayout=gk.makeLayout=gk.layout,eYe={notify:s(function(e,t){var r=this._private;if(this.batching()){r.batchNotifications=r.batchNotifications||{};var i=r.batchNotifications[e]=r.batchNotifications[e]||this.collection();t!=null&&i.merge(t);return}if(r.notificationsEnabled){var n=this.renderer();this.destroyed()||!n||n.notify(e,t)}},"notify"),notifications:s(function(e){var t=this._private;return e===void 0?t.notificationsEnabled:(t.notificationsEnabled=!!e,this)},"notifications"),noNotifications:s(function(e){this.notifications(!1),e(),this.notifications(!0)},"noNotifications"),batching:s(function(){return this._private.batchCount>0},"batching"),startBatch:s(function(){var e=this._private;return e.batchCount==null&&(e.batchCount=0),e.batchCount===0&&(e.batchStyleEles=this.collection(),e.batchNotifications={}),e.batchCount++,this},"startBatch"),endBatch:s(function(){var e=this._private;if(e.batchCount===0)return this;if(e.batchCount--,e.batchCount===0){e.batchStyleEles.updateStyle();var t=this.renderer();Object.keys(e.batchNotifications).forEach(function(r){var i=e.batchNotifications[r];i.empty()?t.notify(r):t.notify(r,i)})}return this},"endBatch"),batch:s(function(e){return this.startBatch(),e(),this.endBatch(),this},"batch"),batchData:s(function(e){var t=this;return this.batch(function(){for(var r=Object.keys(e),i=0;i0;)t.removeChild(t.childNodes[0]);e._private.renderer=null,e.mutableElements().forEach(function(r){var i=r._private;i.rscratch={},i.rstyle={},i.animation.current=[],i.animation.queue=[]})},"destroyRenderer"),onRender:s(function(e){return this.on("render",e)},"onRender"),offRender:s(function(e){return this.off("render",e)},"offRender")},f8.invalidateDimensions=f8.resize,mk={collection:s(function(e,t){return zr(e)?this.$(e):ul(e)?e.collection():mn(e)?(t||(t={}),new Es(this,e,t.unique,t.removed)):new Es(this)},"collection"),nodes:s(function(e){var t=this.$(function(r){return r.isNode()});return e?t.filter(e):t},"nodes"),edges:s(function(e){var t=this.$(function(r){return r.isEdge()});return e?t.filter(e):t},"edges"),$:s(function(e){var t=this._private.elements;return e?t.filter(e):t.spawnSelf()},"$"),mutableElements:s(function(){return this._private.elements},"mutableElements")},mk.elements=mk.filter=mk.$,os={},tv="t",rYe="f",os.apply=function(e){for(var t=this,r=t._private,i=r.cy,n=i.collection(),a=0;a0;if(f||p&&g){var m=void 0;f&&g||f?m=h.properties:g&&(m=h.mappedProperties);for(var y=0;y1&&(_=1),l.color){var v=i.valueMin[0],x=i.valueMax[0],w=i.valueMin[1],R=i.valueMax[1],$=i.valueMin[2],M=i.valueMax[2],N=i.valueMin[3]==null?1:i.valueMin[3],j=i.valueMax[3]==null?1:i.valueMax[3],I=[Math.round(v+(x-v)*_),Math.round(w+(R-w)*_),Math.round($+(M-$)*_),Math.round(N+(j-N)*_)];a={bypass:i.bypass,name:i.name,value:I,strValue:"rgb("+I[0]+", "+I[1]+", "+I[2]+")"}}else if(l.number){var z=i.valueMin+(i.valueMax-i.valueMin)*_;a=this.parse(i.name,z,i.bypass,f)}else return!1;if(!a)return y(),!1;a.mapping=i,i=a;break}case o.data:{for(var F=i.field.split("."),O=p.data,B=0;B0&&a>0){for(var l={},u=!1,h=0;h0?e.delayAnimation(o).play().promise().then(E):E()}).then(function(){return e.animation({style:l,duration:a,easing:e.pstyle("transition-timing-function").value,queue:!1}).play().promise()}).then(function(){r.removeBypasses(e,n),e.emitAndNotify("style"),i.transitioning=!1})}else i.transitioning&&(this.removeBypasses(e,n),e.emitAndNotify("style"),i.transitioning=!1)},os.checkTrigger=function(e,t,r,i,n,a){var o=this.properties[t],l=n(o);e.removed()||l!=null&&l(r,i,e)&&a(o)},os.checkZOrderTrigger=function(e,t,r,i){var n=this;this.checkTrigger(e,t,r,i,function(a){return a.triggersZOrder},function(){n._private.cy.notify("zorder",e)})},os.checkBoundsTrigger=function(e,t,r,i){this.checkTrigger(e,t,r,i,function(n){return n.triggersBounds},function(n){e.dirtyCompoundBoundsCache(),e.dirtyBoundingBoxCache()})},os.checkConnectedEdgesBoundsTrigger=function(e,t,r,i){this.checkTrigger(e,t,r,i,function(n){return n.triggersBoundsOfConnectedEdges},function(n){e.connectedEdges().forEach(function(a){a.dirtyBoundingBoxCache()})})},os.checkParallelEdgesBoundsTrigger=function(e,t,r,i){this.checkTrigger(e,t,r,i,function(n){return n.triggersBoundsOfParallelEdges},function(n){e.parallelEdges().forEach(function(a){a.dirtyBoundingBoxCache()})})},os.checkTriggers=function(e,t,r,i){e.dirtyStyleCache(),this.checkZOrderTrigger(e,t,r,i),this.checkBoundsTrigger(e,t,r,i),this.checkConnectedEdgesBoundsTrigger(e,t,r,i),this.checkParallelEdgesBoundsTrigger(e,t,r,i)},rv={},rv.applyBypass=function(e,t,r,i){var n=this,a=[],o=!0;if(t==="*"||t==="**"){if(r!==void 0)for(var l=0;ln.length?i=i.substr(n.length):i=""}s(l,"removeSelAndBlockFromRemaining");function u(){a.length>o.length?a=a.substr(o.length):a=""}for(s(u,"removePropAndValFromRem");;){var h=i.match(/^\s*$/);if(h)break;var d=i.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);if(!d){Ji("Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: "+i);break}n=d[0];var p=d[1];if(p!=="core"){var f=new Ld(p);if(f.invalid){Ji("Skipping parsing of block: Invalid selector found in string stylesheet: "+p),l();continue}}var g=d[2],m=!1;a=g;for(var y=[];;){var k=a.match(/^\s*$/);if(k)break;var C=a.match(/^\s*(.+?)\s*:\s*(.+?)(?:\s*;|\s*$)/);if(!C){Ji("Skipping parsing of block: Invalid formatting of style property and value definitions found in:"+g),m=!0;break}o=C[0];var S=C[1],E=C[2],_=t.properties[S];if(!_){Ji("Skipping property: Invalid property name in: "+o),u();continue}var b=r.parse(S,E);if(!b){Ji("Skipping property: Invalid property definition in: "+o),u();continue}y.push({name:S,val:E}),u()}if(m){l();break}r.selector(p);for(var v=0;v=7&&t[0]==="d"&&(d=new RegExp(l.data.regex).exec(t))){if(r)return!1;var f=l.data;return{name:e,value:d,strValue:""+t,mapped:f,field:d[1],bypass:r}}else if(t.length>=10&&t[0]==="m"&&(p=new RegExp(l.mapData.regex).exec(t))){if(r||h.multiple)return!1;var g=l.mapData;if(!(h.color||h.number))return!1;var m=this.parse(e,p[4]);if(!m||m.mapped)return!1;var y=this.parse(e,p[5]);if(!y||y.mapped)return!1;if(m.pfValue===y.pfValue||m.strValue===y.strValue)return Ji("`"+e+": "+t+"` is not a valid mapper because the output range is zero; converting to `"+e+": "+m.strValue+"`"),this.parse(e,m.strValue);if(h.color){var k=m.value,C=y.value,S=k[0]===C[0]&&k[1]===C[1]&&k[2]===C[2]&&(k[3]===C[3]||(k[3]==null||k[3]===1)&&(C[3]==null||C[3]===1));if(S)return!1}return{name:e,value:p,strValue:""+t,mapped:g,field:p[1],fieldMin:parseFloat(p[2]),fieldMax:parseFloat(p[3]),valueMin:m.value,valueMax:y.value,bypass:r}}}if(h.multiple&&i!=="multiple"){var E;if(u?E=t.split(/\s+/):mn(t)?E=t:E=[t],h.evenMultiple&&E.length%2!==0)return null;for(var _=[],b=[],v=[],x="",w=!1,R=0;R0?" ":"")+$.strValue}return h.validate&&!h.validate(_,b)?null:h.singleEnum&&w?_.length===1&&zr(_[0])?{name:e,value:_[0],strValue:_[0],bypass:r}:null:{name:e,value:_,pfValue:v,strValue:x,bypass:r,units:b}}var M=s(function(){for(var Te=0;Teh.max||h.strictMax&&t===h.max))return null;var F={name:e,value:t,strValue:""+t+(N||""),units:N,bypass:r};return h.unitless||N!=="px"&&N!=="em"?F.pfValue=t:F.pfValue=N==="px"||!N?t:this.getEmSizeInPixels()*t,(N==="ms"||N==="s")&&(F.pfValue=N==="ms"?t:1e3*t),(N==="deg"||N==="rad")&&(F.pfValue=N==="rad"?t:UWe(t)),N==="%"&&(F.pfValue=t/100),F}else if(h.propList){var O=[],B=""+t;if(B!=="none"){for(var Y=B.split(/\s*,\s*|\s+/),U=0;U0&&o>0&&!isNaN(r.w)&&!isNaN(r.h)&&r.w>0&&r.h>0){l=Math.min((a-2*t)/r.w,(o-2*t)/r.h),l=l>this._private.maxZoom?this._private.maxZoom:l,l=l=r.minZoom&&(r.maxZoom=t),this},"zoomRange"),minZoom:s(function(e){return e===void 0?this._private.minZoom:this.zoomRange({min:e})},"minZoom"),maxZoom:s(function(e){return e===void 0?this._private.maxZoom:this.zoomRange({max:e})},"maxZoom"),getZoomedViewport:s(function(e){var t=this._private,r=t.pan,i=t.zoom,n,a,o=!1;if(t.zoomingEnabled||(o=!0),ir(e)?a=e:Fi(e)&&(a=e.level,e.position!=null?n=G3(e.position,i,r):e.renderedPosition!=null&&(n=e.renderedPosition),n!=null&&!t.panningEnabled&&(o=!0)),a=a>t.maxZoom?t.maxZoom:a,a=at.maxZoom||!t.zoomingEnabled?a=!0:(t.zoom=l,n.push("zoom"))}if(i&&(!a||!e.cancelOnFailedZoom)&&t.panningEnabled){var u=e.pan;ir(u.x)&&(t.pan.x=u.x,o=!1),ir(u.y)&&(t.pan.y=u.y,o=!1),o||n.push("pan")}return n.length>0&&(n.push("viewport"),this.emit(n.join(" ")),this.notify("viewport")),this},"viewport"),center:s(function(e){var t=this.getCenterPan(e);return t&&(this._private.pan=t,this.emit("pan viewport"),this.notify("viewport")),this},"center"),getCenterPan:s(function(e,t){if(this._private.panningEnabled){if(zr(e)){var r=e;e=this.mutableElements().filter(r)}else ul(e)||(e=this.mutableElements());if(e.length!==0){var i=e.boundingBox(),n=this.width(),a=this.height();t=t===void 0?this._private.zoom:t;var o={x:(n-t*(i.x1+i.x2))/2,y:(a-t*(i.y1+i.y2))/2};return o}}},"getCenterPan"),reset:s(function(){return!this._private.panningEnabled||!this._private.zoomingEnabled?this:(this.viewport({pan:{x:0,y:0},zoom:1}),this)},"reset"),invalidateSize:s(function(){this._private.sizeCache=null},"invalidateSize"),size:s(function(){var e=this._private,t=e.container,r=this;return e.sizeCache=e.sizeCache||(t?(function(){var i=r.window().getComputedStyle(t),n=s(function(a){return parseFloat(i.getPropertyValue(a))},"val");return{width:t.clientWidth-n("padding-left")-n("padding-right"),height:t.clientHeight-n("padding-top")-n("padding-bottom")}})():{width:1,height:1})},"size"),width:s(function(){return this.size().width},"width"),height:s(function(){return this.size().height},"height"),extent:s(function(){var e=this._private.pan,t=this._private.zoom,r=this.renderedExtent(),i={x1:(r.x1-e.x)/t,x2:(r.x2-e.x)/t,y1:(r.y1-e.y)/t,y2:(r.y2-e.y)/t};return i.w=i.x2-i.x1,i.h=i.y2-i.y1,i},"extent"),renderedExtent:s(function(){var e=this.width(),t=this.height();return{x1:0,y1:0,x2:e,y2:t,w:e,h:t}},"renderedExtent"),multiClickDebounceTime:s(function(e){if(e)this._private.multiClickDebounceTime=e;else return this._private.multiClickDebounceTime;return this},"multiClickDebounceTime")},vf.centre=vf.center,vf.autolockNodes=vf.autolock,vf.autoungrabifyNodes=vf.autoungrabify,iv={data:en.data({field:"data",bindingEvent:"data",allowBinding:!0,allowSetting:!0,settingEvent:"data",settingTriggersEvent:!0,triggerFnName:"trigger",allowGetting:!0,updateStyle:!0}),removeData:en.removeData({field:"data",event:"data",triggerFnName:"trigger",triggerEvent:!0,updateStyle:!0}),scratch:en.data({field:"scratch",bindingEvent:"scratch",allowBinding:!0,allowSetting:!0,settingEvent:"scratch",settingTriggersEvent:!0,triggerFnName:"trigger",allowGetting:!0,updateStyle:!0}),removeScratch:en.removeData({field:"scratch",event:"scratch",triggerFnName:"trigger",triggerEvent:!0,updateStyle:!0})},iv.attr=iv.data,iv.removeAttr=iv.removeData,nv=s(function(e){var t=this;e=Ur({},e);var r=e.container;r&&!q3(r)&&q3(r[0])&&(r=r[0]);var i=r?r._cyreg:null;i=i||{},i&&i.cy&&(i.cy.destroy(),i={});var n=i.readies=i.readies||[];r&&(r._cyreg=i),i.cy=t;var a=ja!==void 0&&r!==void 0&&!e.headless,o=e;o.layout=Ur({name:a?"grid":"null"},o.layout),o.renderer=Ur({name:a?"canvas":"null"},o.renderer);var l=s(function(f,g,m){return g!==void 0?g:m!==void 0?m:f},"defVal"),u=this._private={container:r,ready:!1,options:o,elements:new Es(this),listeners:[],aniEles:new Es(this),data:o.data||{},scratch:{},layout:null,renderer:null,destroyed:!1,notificationsEnabled:!0,minZoom:1e-50,maxZoom:1e50,zoomingEnabled:l(!0,o.zoomingEnabled),userZoomingEnabled:l(!0,o.userZoomingEnabled),panningEnabled:l(!0,o.panningEnabled),userPanningEnabled:l(!0,o.userPanningEnabled),boxSelectionEnabled:l(!0,o.boxSelectionEnabled),autolock:l(!1,o.autolock,o.autolockNodes),autoungrabify:l(!1,o.autoungrabify,o.autoungrabifyNodes),autounselectify:l(!1,o.autounselectify),styleEnabled:o.styleEnabled===void 0?a:o.styleEnabled,zoom:ir(o.zoom)?o.zoom:1,pan:{x:Fi(o.pan)&&ir(o.pan.x)?o.pan.x:0,y:Fi(o.pan)&&ir(o.pan.y)?o.pan.y:0},animation:{current:[],queue:[]},hasCompoundNodes:!1,multiClickDebounceTime:l(250,o.multiClickDebounceTime)};this.createEmitter(),this.selectionType(o.selectionType),this.zoomRange({min:o.minZoom,max:o.maxZoom});var h=s(function(f,g){var m=f.some(Nqe);if(m)return nm.all(f).then(g);g(f)},"loadExtData");u.styleEnabled&&t.setStyle([]);var d=Ur({},o,o.renderer);t.initRenderer(d);var p=s(function(f,g,m){t.notifications(!1);var y=t.mutableElements();y.length>0&&y.remove(),f!=null&&(Fi(f)||mn(f))&&t.add(f),t.one("layoutready",function(C){t.notifications(!0),t.emit(C),t.one("load",g),t.emitAndNotify("load")}).one("layoutstop",function(){t.one("done",m),t.emit("done")});var k=Ur({},t._private.options.layout);k.eles=t.elements(),t.layout(k).run()},"setElesAndLayout");h([o.style,o.elements],function(f){var g=f[0],m=f[1];u.styleEnabled&&t.style().append(g),p(m,function(){t.startAnimationLoop(),u.ready=!0,ta(o.ready)&&t.on("ready",o.ready);for(var y=0;y0,l=!!e.boundingBox,u=uo(l?e.boundingBox:structuredClone(t.extent())),h;if(ul(e.roots))h=e.roots;else if(mn(e.roots)){for(var d=[],p=0;p0;){var I=j(),z=R(I,M);if(z)I.outgoers().filter(function(ze){return ze.isNode()&&r.has(ze)}).forEach(N);else if(z===null){Ji("Detected double maximal shift for node `"+I.id()+"`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.");break}}}var F=0;if(e.avoidOverlap)for(var O=0;O0&&C[0].length<=3?ye/2:0),Ie=2*Math.PI/C[et].length*he;return et===0&&C[0].length===1&&(be=1),{x:ke.x+be*Math.cos(Ie),y:ke.y+be*Math.sin(Ie)}}else{var pe=C[et].length,Qe=Math.max(pe===1?0:l?(u.w-e.padding*2-Ne.w)/((e.grid?$e:pe)-1):(u.w-e.padding*2-Ne.w)/((e.grid?$e:pe)+1),F),W={x:ke.x+(he+1-(pe+1)/2)*Qe,y:ke.y+(et+1-(ue+1)/2)*xe};return W}},"getPositionTopBottom"),He={downward:0,leftward:90,upward:180,rightward:-90};Object.keys(He).indexOf(e.direction)===-1&&Kn("Invalid direction '".concat(e.direction,"' specified for breadthfirst layout. Valid values are: ").concat(Object.keys(He).join(", ")));var Ge=s(function(ze){return ZFe(Me(ze),u,He[e.direction])},"getPosition");return r.nodes().layoutPositions(this,e,Ge),this},oYe={fit:!0,padding:30,boundingBox:void 0,avoidOverlap:!0,nodeDimensionsIncludeLabels:!1,spacingFactor:void 0,radius:void 0,startAngle:3/2*Math.PI,sweep:void 0,clockwise:!0,sort:void 0,animate:!1,animationDuration:500,animationEasing:void 0,animateFilter:s(function(e,t){return!0},"animateFilter"),ready:void 0,stop:void 0,transform:s(function(e,t){return t},"transform")},s(uY,"CircleLayout"),uY.prototype.run=function(){var e=this.options,t=e,r=e.cy,i=t.eles,n=t.counterclockwise!==void 0?!t.counterclockwise:t.clockwise,a=i.nodes().not(":parent");t.sort&&(a=a.sort(t.sort));for(var o=uo(t.boundingBox?t.boundingBox:{x1:0,y1:0,w:r.width(),h:r.height()}),l={x:o.x1+o.w/2,y:o.y1+o.h/2},u=t.sweep===void 0?2*Math.PI-2*Math.PI/a.length:t.sweep,h=u/Math.max(1,a.length-1),d,p=0,f=0;f1&&t.avoidOverlap){p*=1.75;var C=Math.cos(h)-Math.cos(0),S=Math.sin(h)-Math.sin(0),E=Math.sqrt(p*p/(C*C+S*S));d=Math.max(E,d)}var _=s(function(b,v){var x=t.startAngle+v*h*(n?1:-1),w=d*Math.cos(x),R=d*Math.sin(x),$={x:l.x+w,y:l.y+R};return $},"getPos");return i.nodes().layoutPositions(this,t,_),this},lYe={fit:!0,padding:30,startAngle:3/2*Math.PI,sweep:void 0,clockwise:!0,equidistant:!1,minNodeSpacing:10,boundingBox:void 0,avoidOverlap:!0,nodeDimensionsIncludeLabels:!1,height:void 0,width:void 0,spacingFactor:void 0,concentric:s(function(e){return e.degree()},"concentric"),levelWidth:s(function(e){return e.maxDegree()/4},"levelWidth"),animate:!1,animationDuration:500,animationEasing:void 0,animateFilter:s(function(e,t){return!0},"animateFilter"),ready:void 0,stop:void 0,transform:s(function(e,t){return t},"transform")},s(hY,"ConcentricLayout"),hY.prototype.run=function(){for(var e=this.options,t=e,r=t.counterclockwise!==void 0?!t.counterclockwise:t.clockwise,i=e.cy,n=t.eles,a=n.nodes().not(":parent"),o=uo(t.boundingBox?t.boundingBox:{x1:0,y1:0,w:i.width(),h:i.height()}),l={x:o.x1+o.w/2,y:o.y1+o.h/2},u=[],h=0,d=0;d0){var b=Math.abs(S[0].value-_.value);b>=k&&(S=[],C.push(S))}S.push(_)}var v=h+t.minNodeSpacing;if(!t.avoidOverlap){var x=C.length>0&&C[0].length>1,w=Math.min(o.w,o.h)/2-v,R=w/(C.length+x?1:0);v=Math.min(v,R)}for(var $=0,M=0;M1&&t.avoidOverlap){var z=Math.cos(I)-Math.cos(0),F=Math.sin(I)-Math.sin(0),O=Math.sqrt(v*v/(z*z+F*F));$=Math.max(O,$)}N.r=$,$+=v}if(t.equidistant){for(var B=0,Y=0,U=0;U=e.numIter||(fYe(i,e),i.temperature=i.temperature*e.coolingFactor,i.temperature=e.animationThreshold&&a(),V3(d)}},"frame");d()}else{for(;h;)h=o(u),u++;CX(i,e),l()}return this},F3.prototype.stop=function(){return this.stopped=!0,this.thread&&this.thread.stop(),this.emit("layoutstop"),this},F3.prototype.destroy=function(){return this.thread&&this.thread.stop(),this},uYe=s(function(e,t,r){for(var i=r.eles.edges(),n=r.eles.nodes(),a=uo(r.boundingBox?r.boundingBox:{x1:0,y1:0,w:e.width(),h:e.height()}),o={isCompound:e.hasCompoundNodes(),layoutNodes:[],idToIndex:{},nodeSize:n.size(),graphSet:[],indexToGraph:[],layoutEdges:[],edgeSize:i.size(),temperature:r.initialTemp,clientWidth:a.w,clientHeight:a.h,boundingBox:a},l=r.eles.components(),u={},h=0;h0){o.graphSet.push(x);for(var h=0;hi.count?0:i.graph},"findLCA"),bX=s(function(e,t,r,i){var n=i.graphSet[r];if(-10)var k=i.nodeOverlap*h,y=Math.sqrt(o*o+l*l),C=k*o/y,S=k*l/y;else var d=xk(e,o,l),p=xk(t,-1*o,-1*l),f=p.x-d.x,g=p.y-d.y,m=f*f+g*g,y=Math.sqrt(m),k=(e.nodeRepulsion+t.nodeRepulsion)/m,C=k*f/y,S=k*g/y;e.isLocked||(e.offsetX-=C,e.offsetY-=S),t.isLocked||(t.offsetX+=C,t.offsetY+=S)}},"nodeRepulsion"),yYe=s(function(e,t,r,i){if(r>0)var n=e.maxX-t.minX;else var n=t.maxX-e.minX;if(i>0)var a=e.maxY-t.minY;else var a=t.maxY-e.minY;return n>=0&&a>=0?Math.sqrt(n*n+a*a):0},"nodesOverlap"),xk=s(function(e,t,r){var i=e.positionX,n=e.positionY,a=e.height||1,o=e.width||1,l=r/t,u=a/o,h={};return t===0&&0r?(h.x=i,h.y=n+a/2,h):0t&&-1*u<=l&&l<=u?(h.x=i-o/2,h.y=n-o*r/2/t,h):0=u)?(h.x=i+a*t/2/r,h.y=n+a/2,h):(0>r&&(l<=-1*u||l>=u)&&(h.x=i-a*t/2/r,h.y=n-a/2),h)},"findClippingPoint"),vYe=s(function(e,t){for(var r=0;rr){var y=t.gravity*f/m,k=t.gravity*g/m;p.offsetX+=y,p.offsetY+=k}}}}},"calculateGravityForces"),xYe=s(function(e,t){var r=[],i=0,n=-1;for(r.push.apply(r,e.graphSet[0]),n+=e.graphSet[0].length;i<=n;){var a=r[i++],o=e.idToIndex[a],l=e.layoutNodes[o],u=l.children;if(0r)var n={x:r*e/i,y:r*t/i};else var n={x:e,y:t};return n},"limitForce"),kX=s(function(e,t){var r=e.parentId;if(r!=null){var i=t.layoutNodes[t.idToIndex[r]],n=!1;if((i.maxX==null||e.maxX+i.padRight>i.maxX)&&(i.maxX=e.maxX+i.padRight,n=!0),(i.minX==null||e.minX-i.padLefti.maxY)&&(i.maxY=e.maxY+i.padBottom,n=!0),(i.minY==null||e.minY-i.padTopk&&(g+=y+t.componentSpacing,f=0,m=0,y=0)}}},"separateComponents"),CYe={fit:!0,padding:30,boundingBox:void 0,avoidOverlap:!0,avoidOverlapPadding:10,nodeDimensionsIncludeLabels:!1,spacingFactor:void 0,condense:!1,rows:void 0,cols:void 0,position:s(function(e){},"position"),sort:void 0,animate:!1,animationDuration:500,animationEasing:void 0,animateFilter:s(function(e,t){return!0},"animateFilter"),ready:void 0,stop:void 0,transform:s(function(e,t){return t},"transform")},s(dY,"GridLayout"),dY.prototype.run=function(){var e=this.options,t=e,r=e.cy,i=t.eles,n=i.nodes().not(":parent");t.sort&&(n=n.sort(t.sort));var a=uo(t.boundingBox?t.boundingBox:{x1:0,y1:0,w:r.width(),h:r.height()});if(a.h===0||a.w===0)i.nodes().layoutPositions(this,t,function(te){return{x:a.x1,y:a.y1}});else{var o=n.size(),l=Math.sqrt(o*a.h/a.w),u=Math.round(l),h=Math.round(a.w/a.h*l),d=s(function(te){if(te==null)return Math.min(u,h);var ie=Math.min(u,h);ie==u?u=te:h=te},"small"),p=s(function(te){if(te==null)return Math.max(u,h);var ie=Math.max(u,h);ie==u?u=te:h=te},"large"),f=t.rows,g=t.cols!=null?t.cols:t.columns;if(f!=null&&g!=null)u=f,h=g;else if(f!=null&&g==null)u=f,h=Math.ceil(o/u);else if(f==null&&g!=null)h=g,u=Math.ceil(o/h);else if(h*u>o){var m=d(),y=p();(m-1)*y>=o?d(m-1):(y-1)*m>=o&&p(y-1)}else for(;h*u=o?p(C+1):d(k+1)}var S=a.w/h,E=a.h/u;if(t.condense&&(S=0,E=0),t.avoidOverlap)for(var _=0;_=h&&(z=0,I++)},"moveToNextCell"),O={},B=0;B(O=ije(e,t,B[Y],B[Y+1],B[Y+2],B[Y+3])))return k(x,O),!0}else if(R.edgeType==="bezier"||R.edgeType==="multibezier"||R.edgeType==="self"||R.edgeType==="compound"){for(var B=R.allpts,Y=0;Y+5(O=rje(e,t,B[Y],B[Y+1],B[Y+2],B[Y+3],B[Y+4],B[Y+5])))return k(x,O),!0}for(var z=z||w.source,F=F||w.target,U=n.getArrowWidth($,M),H=[{name:"source",x:R.arrowStartX,y:R.arrowStartY,angle:R.srcArrowAngle},{name:"target",x:R.arrowEndX,y:R.arrowEndY,angle:R.tgtArrowAngle},{name:"mid-source",x:R.midX,y:R.midY,angle:R.midsrcArrowAngle},{name:"mid-target",x:R.midX,y:R.midY,angle:R.midtgtArrowAngle}],Y=0;Y0&&(C(z),C(F))}s(S,"checkEdge");function E(x,w,R){return co(x,w,R)}s(E,"preprop");function _(x,w){var R=x._private,$=f,M;w?M=w+"-":M="",x.boundingBox();var N=R.labelBounds[w||"main"],j=x.pstyle(M+"label").value,I=x.pstyle("text-events").strValue==="yes";if(!(!I||!j)){var z=E(R.rscratch,"labelX",w),F=E(R.rscratch,"labelY",w),O=E(R.rscratch,"labelAngle",w),B=x.pstyle(M+"text-margin-x").pfValue,Y=x.pstyle(M+"text-margin-y").pfValue,U=N.x1-$-B,H=N.x2+$-B,J=N.y1-$-Y,te=N.y2+$-Y;if(O){var ie=Math.cos(O),ue=Math.sin(O),se=s(function(ke,Ne){return ke=ke-z,Ne=Ne-F,{x:ke*ie-Ne*ue+z,y:ke*ue+Ne*ie+F}},"rotate"),le=se(U,J),Te=se(U,te),oe=se(H,J),fe=se(H,te),ge=[le.x+B,le.y+Y,oe.x+B,oe.y+Y,fe.x+B,fe.y+Y,Te.x+B,Te.y+Y];if(Bo(e,t,ge))return k(x),!0}else if(_d(N,e,t))return k(x),!0}}s(_,"checkLabel");for(var b=o.length-1;b>=0;b--){var v=o[b];v.isNode()?C(v)||_(v):S(v)||_(v)||_(v,"source")||_(v,"target")}return l},bf.getAllInBox=function(e,t,r,i){var n=this.getCachedZSortedEles().interactive,a=this.cy.zoom(),o=2/a,l=[],u=Math.min(e,r),h=Math.max(e,r),d=Math.min(t,i),p=Math.max(t,i);e=u,r=h,t=d,i=p;var f=uo({x1:e,y1:t,x2:r,y2:i}),g=[{x:f.x1,y:f.y1},{x:f.x2,y:f.y1},{x:f.x2,y:f.y2},{x:f.x1,y:f.y2}],m=[[g[0],g[1]],[g[1],g[2]],[g[2],g[3]],[g[3],g[0]]];function y(Ne,xe,$e){return co(Ne,xe,$e)}s(y,"preprop");function k(Ne,xe){var $e=Ne._private,Me=o,He="";Ne.boundingBox();var Ge=$e.labelBounds.main;if(!Ge)return null;var ze=y($e.rscratch,"labelX",xe),Ve=y($e.rscratch,"labelY",xe),et=y($e.rscratch,"labelAngle",xe),he=Ne.pstyle(He+"text-margin-x").pfValue,ye=Ne.pstyle(He+"text-margin-y").pfValue,be=Ge.x1-Me-he,Ie=Ge.x2+Me-he,pe=Ge.y1-Me-ye,Qe=Ge.y2+Me-ye;if(et){var W=Math.cos(et),Ae=Math.sin(et),K=s(function(de,ee){return de=de-ze,ee=ee-Ve,{x:de*W-ee*Ae+ze,y:de*Ae+ee*W+Ve}},"rotate");return[K(be,pe),K(Ie,pe),K(Ie,Qe),K(be,Qe)]}else return[{x:be,y:pe},{x:Ie,y:pe},{x:Ie,y:Qe},{x:be,y:Qe}]}s(k,"getRotatedLabelBox");function C(Ne,xe,$e,Me){function He(Ge,ze,Ve){return(Ve.y-Ge.y)*(ze.x-Ge.x)>(ze.y-Ge.y)*(Ve.x-Ge.x)}return s(He,"ccw"),He(Ne,$e,Me)!==He(xe,$e,Me)&&He(Ne,xe,$e)!==He(Ne,xe,Me)}s(C,"doLinesIntersect");for(var S=0;S0?-(Math.PI-e.ang):Math.PI+e.ang},"invertVec"),DYe=s(function(e,t,r,i,n){if(e!==IYe?$X(t,e,Gc):MYe(Il,Gc),$X(t,r,Il),AYe=Gc.nx*Il.ny-Gc.ny*Il.nx,RYe=Gc.nx*Il.nx-Gc.ny*-Il.ny,Bd=Math.asin(Math.max(-1,Math.min(1,AYe))),Math.abs(Bd)<1e-6){SX=t.x,_X=t.y,pm=av=0;return}hm=1,v8=!1,RYe<0?Bd<0?Bd=Math.PI+Bd:(Bd=Math.PI-Bd,hm=-1,v8=!0):Bd>0&&(hm=-1,v8=!0),t.radius!==void 0?av=t.radius:av=i,dm=Bd/2,b8=Math.min(Gc.len/2,Il.len/2),n?(ih=Math.abs(Math.cos(dm)*av/Math.sin(dm)),ih>b8?(ih=b8,pm=Math.abs(ih*Math.sin(dm)/Math.cos(dm))):pm=av):(ih=Math.min(b8,av),pm=Math.abs(ih*Math.sin(dm)/Math.cos(dm))),AX=t.x+Il.nx*ih,RX=t.y+Il.ny*ih,SX=AX-Il.ny*pm*hm,_X=RX+Il.nx*pm*hm,$Ye=t.x+Gc.nx*ih,LYe=t.y+Gc.ny*ih,IYe=t},"calcCornerArc"),s(mY,"drawPreparedRoundCorner"),s(BA,"getRoundCorner"),sv=.01,NYe=Math.sqrt(2*sv),Gs={},Gs.findMidptPtsEtc=function(e,t){var r=t.posPts,i=t.intersectionPts,n=t.vectorNormInverse,a,o=e.pstyle("source-endpoint"),l=e.pstyle("target-endpoint"),u=o.units!=null&&l.units!=null,h=s(function(_,b,v,x){var w=x-b,R=v-_,$=Math.sqrt(R*R+w*w);return{x:-w/$,y:R/$}},"recalcVectorNormInverse"),d=e.pstyle("edge-distances").value;switch(d){case"node-position":a=r;break;case"intersection":a=i;break;case"endpoints":{if(u){var p=this.manualEndptToPx(e.source()[0],o),f=Ia(p,2),g=f[0],m=f[1],y=this.manualEndptToPx(e.target()[0],l),k=Ia(y,2),C=k[0],S=k[1],E={x1:g,y1:m,x2:C,y2:S};n=h(g,m,C,S),a=E}else Ji("Edge ".concat(e.id()," has edge-distances:endpoints specified without manual endpoints specified via source-endpoint and target-endpoint. Falling back on edge-distances:intersection (default).")),a=i;break}}return{midptPts:a,vectorNormInverse:n}},Gs.findHaystackPoints=function(e){for(var t=0;t0?Math.max(ee-ve,0):Math.min(ee+ve,0)},"subDWH"),j=N($,w),I=N(M,R),z=!1;S===h?C=Math.abs(j)>Math.abs(I)?n:i:S===u||S===l?(C=i,z=!0):(S===a||S===o)&&(C=n,z=!0);var F=C===i,O=F?I:j,B=F?M:$,Y=QA(B),U=!1;!(z&&(_||v))&&(S===l&&B<0||S===u&&B>0||S===a&&B>0||S===o&&B<0)&&(Y*=-1,O=Y*Math.abs(O),U=!0);var H;if(_){var J=b<0?1+b:b;H=J*O}else{var te=b<0?O:0;H=te+b*Y}var ie=s(function(ee){return Math.abs(ee)=Math.abs(O)},"getIsTooClose"),ue=ie(H),se=ie(Math.abs(O)-Math.abs(H)),le=ue||se;if(le&&!U)if(F){var Te=Math.abs(B)<=f/2,oe=Math.abs($)<=g/2;if(Te){var fe=(d.x1+d.x2)/2,ge=d.y1,ke=d.y2;r.segpts=[fe,ge,fe,ke]}else if(oe){var Ne=(d.y1+d.y2)/2,xe=d.x1,$e=d.x2;r.segpts=[xe,Ne,$e,Ne]}else r.segpts=[d.x1,d.y2]}else{var Me=Math.abs(B)<=p/2,He=Math.abs(M)<=m/2;if(Me){var Ge=(d.y1+d.y2)/2,ze=d.x1,Ve=d.x2;r.segpts=[ze,Ge,Ve,Ge]}else if(He){var et=(d.x1+d.x2)/2,he=d.y1,ye=d.y2;r.segpts=[et,he,et,ye]}else r.segpts=[d.x2,d.y1]}else if(F){var be=d.y1+H+(k?f/2*Y:0),Ie=d.x1,pe=d.x2;r.segpts=[Ie,be,pe,be]}else{var Qe=d.x1+H+(k?p/2*Y:0),W=d.y1,Ae=d.y2;r.segpts=[Qe,W,Qe,Ae]}if(r.isRound){var K=e.pstyle("taxi-radius").value,de=e.pstyle("radius-type").value[0]==="arc-radius";r.radii=new Array(r.segpts.length/2).fill(K),r.isArcRadius=new Array(r.segpts.length/2).fill(de)}},Gs.tryToCorrectInvalidPoints=function(e,t){var r=e._private.rscratch;if(r.edgeType==="bezier"){var i=t.srcPos,n=t.tgtPos,a=t.srcW,o=t.srcH,l=t.tgtW,u=t.tgtH,h=t.srcShape,d=t.tgtShape,p=t.srcCornerRadius,f=t.tgtCornerRadius,g=t.srcRs,m=t.tgtRs,y=!ir(r.startX)||!ir(r.startY),k=!ir(r.arrowStartX)||!ir(r.arrowStartY),C=!ir(r.endX)||!ir(r.endY),S=!ir(r.arrowEndX)||!ir(r.arrowEndY),E=3,_=this.getArrowWidth(e.pstyle("width").pfValue,e.pstyle("arrow-scale").value)*this.arrowShapeWidth,b=E*_,v=pf({x:r.ctrlpts[0],y:r.ctrlpts[1]},{x:r.startX,y:r.startY}),x=vO.poolIndex()){var B=F;F=O,O=B}var Y=N.srcPos=F.position(),U=N.tgtPos=O.position(),H=N.srcW=F.outerWidth(),J=N.srcH=F.outerHeight(),te=N.tgtW=O.outerWidth(),ie=N.tgtH=O.outerHeight(),ue=N.srcShape=r.nodeShapes[t.getNodeShape(F)],se=N.tgtShape=r.nodeShapes[t.getNodeShape(O)],le=N.srcCornerRadius=F.pstyle("corner-radius").value==="auto"?"auto":F.pstyle("corner-radius").pfValue,Te=N.tgtCornerRadius=O.pstyle("corner-radius").value==="auto"?"auto":O.pstyle("corner-radius").pfValue,oe=N.tgtRs=O._private.rscratch,fe=N.srcRs=F._private.rscratch;N.dirCounts={north:0,west:0,south:0,east:0,northwest:0,southwest:0,northeast:0,southeast:0};for(var ge=0;ge=NYe||(Ie=Math.sqrt(Math.max(be*be,sv)+Math.max(ye*ye,sv)));var pe=N.vector={x:be,y:ye},Qe=N.vectorNorm={x:pe.x/Ie,y:pe.y/Ie},W={x:-Qe.y,y:Qe.x};N.nodesOverlap=!ir(Ie)||se.checkPoint(He[0],He[1],0,te,ie,U.x,U.y,Te,oe)||ue.checkPoint(ze[0],ze[1],0,H,J,Y.x,Y.y,le,fe),N.vectorNormInverse=W,j={nodesOverlap:N.nodesOverlap,dirCounts:N.dirCounts,calculatedIntersection:!0,hasBezier:N.hasBezier,hasUnbundled:N.hasUnbundled,eles:N.eles,srcPos:U,srcRs:oe,tgtPos:Y,tgtRs:fe,srcW:te,srcH:ie,tgtW:H,tgtH:J,srcIntn:Ve,tgtIntn:Ge,srcShape:se,tgtShape:ue,posPts:{x1:he.x2,y1:he.y2,x2:he.x1,y2:he.y1},intersectionPts:{x1:et.x2,y1:et.y2,x2:et.x1,y2:et.y1},vector:{x:-pe.x,y:-pe.y},vectorNorm:{x:-Qe.x,y:-Qe.y},vectorNormInverse:{x:-W.x,y:-W.y}}}var Ae=Me?j:N;Ne.nodesOverlap=Ae.nodesOverlap,Ne.srcIntn=Ae.srcIntn,Ne.tgtIntn=Ae.tgtIntn,Ne.isRound=xe.startsWith("round"),n&&(F.isParent()||F.isChild()||O.isParent()||O.isChild())&&(F.parents().anySame(O)||O.parents().anySame(F)||F.same(O)&&F.isParent())?t.findCompoundLoopPoints(ke,Ae,ge,$e):F===O?t.findLoopPoints(ke,Ae,ge,$e):xe.endsWith("segments")?t.findSegmentsPoints(ke,Ae):xe.endsWith("taxi")?t.findTaxiPoints(ke,Ae):xe==="straight"||!$e&&N.eles.length%2===1&&ge===Math.floor(N.eles.length/2)?t.findStraightEdgePoints(ke):t.findBezierPoints(ke,Ae,ge,$e,Me),t.findEndpoints(ke),t.tryToCorrectInvalidPoints(ke,Ae),t.checkForInvalidEdgeWarning(ke),t.storeAllpts(ke),t.storeEdgeProjections(ke),t.calculateArrowAngles(ke),t.recalculateEdgeLabelProjections(ke),t.calculateLabelAngles(ke)}},"_loop"),x=0;x0){var Ge=h,ze=ff(Ge,Zg(o)),Ve=ff(Ge,Zg(He)),et=ze;if(Ve2){var he=ff(Ge,{x:He[2],y:He[3]});he0){var me=d,Ce=ff(me,Zg(o)),Be=ff(me,Zg(X)),Re=Ce;if(Be2){var Ke=ff(me,{x:X[2],y:X[3]});Ke=g||v){y={cp:E,segment:b};break}}if(y)break}var x=y.cp,w=y.segment,R=(g-k)/w.length,$=w.t1-w.t0,M=f?w.t0+$*R:w.t1-$*R;M=G2(0,M,1),t=Jg(x.p0,x.p1,x.p2,M),p=OYe(x.p0,x.p1,x.p2,M);break}case"straight":case"segments":case"haystack":{for(var N=0,j,I,z,F,O=i.allpts.length,B=0;B+3=g));B+=2);var Y=g-I,U=Y/j;U=G2(0,U,1),t=HWe(z,F,U),p=IX(z,F);break}}o("labelX",d,t.x),o("labelY",d,t.y),o("labelAutoAngle",d,p)}},"calculateEndProjection");h("source"),h("target"),this.applyLabelDimensions(e)}},Xc.applyLabelDimensions=function(e){this.applyPrefixedLabelDimensions(e),e.isEdge()&&(this.applyPrefixedLabelDimensions(e,"source"),this.applyPrefixedLabelDimensions(e,"target"))},Xc.applyPrefixedLabelDimensions=function(e,t){var r=e._private,i=this.getLabelText(e,t),n=df(i,e._private.labelDimsKey);if(co(r.rscratch,"prefixedLabelDimsKey",t)!==n){Uc(r.rscratch,"prefixedLabelDimsKey",t,n);var a=this.calculateLabelDimensions(e,i),o=e.pstyle("line-height").pfValue,l=e.pstyle("font-size").pfValue,u=e.pstyle("text-wrap").strValue,h=co(r.rscratch,"labelWrapCachedLines",t)||[],d=u!=="wrap"?1:Math.max(h.length,1),p=l*o,f=a.width,g=a.height+(d-1)*(o-1)*l;Uc(r.rstyle,"labelWidth",t,f),Uc(r.rscratch,"labelWidth",t,f),Uc(r.rstyle,"labelHeight",t,g),Uc(r.rscratch,"labelHeight",t,g),Uc(r.rscratch,"labelLineHeight",t,p),Uc(r.rscratch,"labelActualDescent",t,a.labelActualDescent)}},Xc.getLabelText=function(e,t){var r=e._private,i=t?t+"-":"",n=e.pstyle(i+"label").strValue,a=e.pstyle("text-transform").value,o=s(function(J,te){return te?(Uc(r.rscratch,J,t,te),te):co(r.rscratch,J,t)},"rscratch");if(!n)return"";a=="none"||(a=="uppercase"?n=n.toUpperCase():a=="lowercase"&&(n=n.toLowerCase()));var l=e.pstyle("text-wrap").value;if(l==="wrap"){var u=o("labelKey");if(u!=null&&o("labelWrapKey")===u)return o("labelWrapCachedText");for(var h="​",d=n.split(` +`),p=e.pstyle("text-max-width").pfValue,f=e.pstyle("text-overflow-wrap").value,g=f==="anywhere",m=[],y=/[\s\u200b]+|$/g,k=0;kp){var b=C.matchAll(y),v="",x=0,w=Oo(b),R;try{for(w.s();!(R=w.n()).done;){var $=R.value,M=$[0],N=C.substring(x,$.index);x=$.index+M.length;var j=v.length===0?N:v+N+M,I=this.calculateLabelDimensions(e,j),z=I.width;z<=p?v+=N+M:(v&&m.push(v),v=N+M)}}catch(J){w.e(J)}finally{w.f()}v.match(/^[\s\u200b]+$/)||m.push(v)}else m.push(C)}o("labelWrapCachedLines",m),n=o("labelWrapCachedText",m.join(` +`)),o("labelWrapKey",u)}else if(l==="ellipsis"){var F=e.pstyle("text-max-width").pfValue,O="",B="…",Y=!1;if(this.calculateLabelDimensions(e,n).widthF)break;O+=n[U],U===n.length-1&&(Y=!0)}return Y||(O+=B),O}return n},Xc.getLabelJustification=function(e){var t=e.pstyle("text-justification").strValue,r=e.pstyle("text-halign").strValue;return t==="auto"?e.isNode()?CUe(r):"center":t},Xc.calculateLabelDimensions=function(e,t){var r=this,i=r.cy.window(),n=i.document,a=0,o=e.pstyle("font-style").strValue,l=e.pstyle("font-size").pfValue,u=e.pstyle("font-family").strValue,h=e.pstyle("font-weight").strValue,d=e.pstyle("text-metrics").strValue||"font",p=this.labelCalcCanvas,f=this.labelCalcCanvasContext;if(!p){p=this.labelCalcCanvas=n.createElement("canvas"),f=this.labelCalcCanvasContext=p.getContext("2d");var g=p.style;g.position="absolute",g.left="-9999px",g.top="-9999px",g.zIndex="-1",g.visibility="hidden",g.pointerEvents="none"}f.font="".concat(o," ").concat(h," ").concat(l,"px ").concat(u);for(var m=0,y=0,k=t.split(` +`),C=k.length,S=0,E=0,_=0;_1&&arguments[1]!==void 0?arguments[1]:!0;if(t.merge(a),o)for(var l=0;l=e.desktopTapThreshold2}var qt=a(K);ct&&(e.hoverData.tapholdCancelled=!0);var tr=s(function(){var br=e.hoverData.dragDelta=e.hoverData.dragDelta||[];br.length===0?(br.push(Je[0]),br.push(Je[1])):(br[0]+=Je[0],br[1]+=Je[1])},"updateDragDelta");ee=!0,n(De,["mousemove","vmousemove","tapdrag"],K,{x:Ce[0],y:Ce[1]});var Et=s(function(br){return{originalEvent:K,type:br,position:{x:Ce[0],y:Ce[1]}}},"makeEvent"),_t=s(function(){e.data.bgActivePosistion=void 0,e.hoverData.selecting||ve.emit(Et("boxstart")),Ke[4]=1,e.hoverData.selecting=!0,e.redrawHint("select",!0),e.redraw()},"goIntoBoxMode");if(e.hoverData.which===3){if(ct){var Kt=Et("cxtdrag");Fe?Fe.emit(Kt):ve.emit(Kt),e.hoverData.cxtDragged=!0,(!e.hoverData.cxtOver||De!==e.hoverData.cxtOver)&&(e.hoverData.cxtOver&&e.hoverData.cxtOver.emit(Et("cxtdragout")),e.hoverData.cxtOver=De,De&&De.emit(Et("cxtdragover")))}}else if(e.hoverData.dragging){if(ee=!0,ve.panningEnabled()&&ve.userPanningEnabled()){var $t;if(e.hoverData.justStartedPan){var Ot=e.hoverData.mdownPos;$t={x:(Ce[0]-Ot[0])*X,y:(Ce[1]-Ot[1])*X},e.hoverData.justStartedPan=!1}else $t={x:Je[0]*X,y:Je[1]*X};ve.panBy($t),ve.emit(Et("dragpan")),e.hoverData.dragged=!0}Ce=e.projectIntoViewport(K.clientX,K.clientY)}else if(Ke[4]==1&&(Fe==null||Fe.pannable())){if(ct){if(!e.hoverData.dragging&&ve.boxSelectionEnabled()&&(qt||!ve.panningEnabled()||!ve.userPanningEnabled()))_t();else if(!e.hoverData.selecting&&ve.panningEnabled()&&ve.userPanningEnabled()){var Jt=o(Fe,e.hoverData.downs);Jt&&(e.hoverData.dragging=!0,e.hoverData.justStartedPan=!0,Ke[4]=0,e.data.bgActivePosistion=Zg(Be),e.redrawHint("select",!0),e.redraw())}Fe&&Fe.pannable()&&Fe.active()&&Fe.unactivate()}}else{if(Fe&&Fe.pannable()&&Fe.active()&&Fe.unactivate(),(!Fe||!Fe.grabbed())&&De!=Ze&&(Ze&&n(Ze,["mouseout","tapdragout"],K,{x:Ce[0],y:Ce[1]}),De&&n(De,["mouseover","tapdragover"],K,{x:Ce[0],y:Ce[1]}),e.hoverData.last=De),Fe)if(ct){if(ve.boxSelectionEnabled()&&qt)Fe&&Fe.grabbed()&&(y(Le),Fe.emit(Et("freeon")),Le.emit(Et("free")),e.dragData.didDrag&&(Fe.emit(Et("dragfreeon")),Le.emit(Et("dragfree")))),_t();else if(Fe&&Fe.grabbed()&&e.nodeIsDraggable(Fe)){var Br=!e.dragData.didDrag;Br&&e.redrawHint("eles",!0),e.dragData.didDrag=!0,e.hoverData.draggingEles||g(Le,{inDragLayer:!0});var At={x:0,y:0};if(ir(Je[0])&&ir(Je[1])&&(At.x+=Je[0],At.y+=Je[1],Br)){var jr=e.hoverData.dragDelta;jr&&ir(jr[0])&&ir(jr[1])&&(At.x+=jr[0],At.y+=jr[1])}e.hoverData.draggingEles=!0,Le.silentShift(At).emit(Et("position")).emit(Et("drag")),e.redrawHint("drag",!0),e.redraw()}}else tr();ee=!0}if(Ke[2]=Ce[0],Ke[3]=Ce[1],ee)return K.stopPropagation&&K.stopPropagation(),K.preventDefault&&K.preventDefault(),!1}},"mousemoveHandler"),!1);var $,M,N;e.registerBinding(t,"mouseup",s(function(K){if(!(e.hoverData.which===1&&K.which!==1&&e.hoverData.capture)){var de=e.hoverData.capture;if(de){e.hoverData.capture=!1;var ee=e.cy,ve=e.projectIntoViewport(K.clientX,K.clientY),X=e.selection,me=e.findNearestElement(ve[0],ve[1],!0,!1),Ce=e.dragData.possibleDragElements,Be=e.hoverData.down,Re=a(K);e.data.bgActivePosistion&&(e.redrawHint("select",!0),e.redraw()),e.hoverData.tapholdCancelled=!0,e.data.bgActivePosistion=void 0,Be&&Be.unactivate();var Ke=s(function(ct){return{originalEvent:K,type:ct,position:{x:ve[0],y:ve[1]}}},"makeEvent");if(e.hoverData.which===3){var De=Ke("cxttapend");if(Be?Be.emit(De):ee.emit(De),!e.hoverData.cxtDragged){var Ze=Ke("cxttap");Be?Be.emit(Ze):ee.emit(Ze)}e.hoverData.cxtDragged=!1,e.hoverData.which=null}else if(e.hoverData.which===1){if(n(me,["mouseup","tapend","vmouseup"],K,{x:ve[0],y:ve[1]}),!e.dragData.didDrag&&!e.hoverData.dragged&&!e.hoverData.selecting&&!e.hoverData.isOverThresholdDrag&&(n(Be,["click","tap","vclick"],K,{x:ve[0],y:ve[1]}),M=!1,K.timeStamp-N<=ee.multiClickDebounceTime()?($&&clearTimeout($),M=!0,N=null,n(Be,["dblclick","dbltap","vdblclick"],K,{x:ve[0],y:ve[1]})):($=setTimeout(function(){M||n(Be,["oneclick","onetap","voneclick"],K,{x:ve[0],y:ve[1]})},ee.multiClickDebounceTime()),N=K.timeStamp)),Be==null&&!e.dragData.didDrag&&!e.hoverData.selecting&&!e.hoverData.dragged&&!a(K)&&(ee.$(r).unselect(["tapunselect"]),Ce.length>0&&e.redrawHint("eles",!0),e.dragData.possibleDragElements=Ce=ee.collection()),me==Be&&!e.dragData.didDrag&&!e.hoverData.selecting&&me!=null&&me._private.selectable&&(e.hoverData.dragging||(ee.selectionType()==="additive"||Re?me.selected()?me.unselect(["tapunselect"]):me.select(["tapselect"]):Re||(ee.$(r).unmerge(me).unselect(["tapunselect"]),me.select(["tapselect"]))),e.redrawHint("eles",!0)),e.hoverData.selecting){var Fe=ee.collection(e.getAllInBox(X[0],X[1],X[2],X[3]));e.redrawHint("select",!0),Fe.length>0&&e.redrawHint("eles",!0),ee.emit(Ke("boxend"));var Je=s(function(ct){return ct.selectable()&&!ct.selected()},"eleWouldBeSelected");ee.selectionType()==="additive"||Re||ee.$(r).unmerge(Fe).unselect(),Fe.emit(Ke("box")).stdFilter(Je).select().emit(Ke("boxselect")),e.redraw()}if(e.hoverData.dragging&&(e.hoverData.dragging=!1,e.redrawHint("select",!0),e.redrawHint("eles",!0),e.redraw()),!X[4]){e.redrawHint("drag",!0),e.redrawHint("eles",!0);var Le=Be&&Be.grabbed();y(Ce),Le&&(Be.emit(Ke("freeon")),Ce.emit(Ke("free")),e.dragData.didDrag&&(Be.emit(Ke("dragfreeon")),Ce.emit(Ke("dragfree"))))}}X[4]=0,e.hoverData.down=null,e.hoverData.cxtStarted=!1,e.hoverData.draggingEles=!1,e.hoverData.selecting=!1,e.hoverData.isOverThresholdDrag=!1,e.dragData.didDrag=!1,e.hoverData.dragged=!1,e.hoverData.dragDelta=[],e.hoverData.mdownPos=null,e.hoverData.mdownGPos=null,e.hoverData.which=null}}},"mouseupHandler"),!1);var j=[],I=4,z,F=1e5,O=s(function(K,de){for(var ee=0;ee=I){var ve=j;if(z=O(ve,5),!z){var X=Math.abs(ve[0]);z=B(ve)&&X>5}if(z)for(var me=0;me5&&(ee=QA(ee)*5),Ze=ee/-250,z&&(Ze/=F,Ze*=3),Ze=Ze*e.wheelSensitivity;var Fe=K.deltaMode===1;Fe&&(Ze*=33);var Je=Ce.zoom()*Math.pow(10,Ze);K.type==="gesturechange"&&(Je=e.gestureStartZoom*K.scale),Ce.zoom({level:Je,renderedPosition:{x:De[0],y:De[1]}}),Ce.emit({type:K.type==="gesturechange"?"pinchzoom":"scrollzoom",originalEvent:K,position:{x:Ke[0],y:Ke[1]}})}}}},"wheelHandler");e.registerBinding(e.container,"wheel",Y,!0),e.registerBinding(t,"scroll",s(function(K){e.scrollingPage=!0,clearTimeout(e.scrollingPageTimeout),e.scrollingPageTimeout=setTimeout(function(){e.scrollingPage=!1},250)},"scrollHandler"),!0),e.registerBinding(e.container,"gesturestart",s(function(K){e.gestureStartZoom=e.cy.zoom(),e.hasTouchStarted||K.preventDefault()},"gestureStartHandler"),!0),e.registerBinding(e.container,"gesturechange",function(K){e.hasTouchStarted||Y(K)},!0),e.registerBinding(e.container,"mouseout",s(function(K){var de=e.projectIntoViewport(K.clientX,K.clientY);e.cy.emit({originalEvent:K,type:"mouseout",position:{x:de[0],y:de[1]}})},"mouseOutHandler"),!1),e.registerBinding(e.container,"mouseover",s(function(K){var de=e.projectIntoViewport(K.clientX,K.clientY);e.cy.emit({originalEvent:K,type:"mouseover",position:{x:de[0],y:de[1]}})},"mouseOverHandler"),!1);var U,H,J,te,ie,ue,se,le,Te,oe,fe,ge,ke,Ne=s(function(K,de,ee,ve){return Math.sqrt((ee-K)*(ee-K)+(ve-de)*(ve-de))},"distance"),xe=s(function(K,de,ee,ve){return(ee-K)*(ee-K)+(ve-de)*(ve-de)},"distanceSq"),$e;e.registerBinding(e.container,"touchstart",$e=s(function(K){if(e.hasTouchStarted=!0,!!w(K)){C(),e.touchData.capture=!0,e.data.bgActivePosistion=void 0;var de=e.cy,ee=e.touchData.now,ve=e.touchData.earlier;if(K.touches[0]){var X=e.projectIntoViewport(K.touches[0].clientX,K.touches[0].clientY);ee[0]=X[0],ee[1]=X[1]}if(K.touches[1]){var X=e.projectIntoViewport(K.touches[1].clientX,K.touches[1].clientY);ee[2]=X[0],ee[3]=X[1]}if(K.touches[2]){var X=e.projectIntoViewport(K.touches[2].clientX,K.touches[2].clientY);ee[4]=X[0],ee[5]=X[1]}var me=s(function(Bt){return{originalEvent:K,type:Bt,position:{x:ee[0],y:ee[1]}}},"makeEvent");if(K.touches[1]){e.touchData.singleTouchMoved=!0,y(e.dragData.touchDragEles);var Ce=e.findContainerClientCoords();Te=Ce[0],oe=Ce[1],fe=Ce[2],ge=Ce[3],U=K.touches[0].clientX-Te,H=K.touches[0].clientY-oe,J=K.touches[1].clientX-Te,te=K.touches[1].clientY-oe,ke=0<=U&&U<=fe&&0<=J&&J<=fe&&0<=H&&H<=ge&&0<=te&&te<=ge;var Be=de.pan(),Re=de.zoom();ie=Ne(U,H,J,te),ue=xe(U,H,J,te),se=[(U+J)/2,(H+te)/2],le=[(se[0]-Be.x)/Re,(se[1]-Be.y)/Re];var Ke=200,De=Ke*Ke;if(ue=1){for(var mt=e.touchData.startPosition=[null,null,null,null,null,null],St=0;St=e.touchTapThreshold2}if(de&&e.touchData.cxt){K.preventDefault();var St=K.touches[0].clientX-Te,Lt=K.touches[0].clientY-oe,Bt=K.touches[1].clientX-Te,qt=K.touches[1].clientY-oe,tr=xe(St,Lt,Bt,qt),Et=tr/ue,_t=150,Kt=_t*_t,$t=1.5,Ot=$t*$t;if(Et>=Ot||tr>=Kt){e.touchData.cxt=!1,e.data.bgActivePosistion=void 0,e.redrawHint("select",!0);var Jt=Re("cxttapend");e.touchData.start?(e.touchData.start.unactivate().emit(Jt),e.touchData.start=null):ve.emit(Jt)}}if(de&&e.touchData.cxt){var Jt=Re("cxtdrag");e.data.bgActivePosistion=void 0,e.redrawHint("select",!0),e.touchData.start?e.touchData.start.emit(Jt):ve.emit(Jt),e.touchData.start&&(e.touchData.start._private.grabbed=!1),e.touchData.cxtDragged=!0;var Br=e.findNearestElement(X[0],X[1],!0,!0);(!e.touchData.cxtOver||Br!==e.touchData.cxtOver)&&(e.touchData.cxtOver&&e.touchData.cxtOver.emit(Re("cxtdragout")),e.touchData.cxtOver=Br,Br&&Br.emit(Re("cxtdragover")))}else if(de&&K.touches[2]&&ve.boxSelectionEnabled())K.preventDefault(),e.data.bgActivePosistion=void 0,this.lastThreeTouch=+new Date,e.touchData.selecting||ve.emit(Re("boxstart")),e.touchData.selecting=!0,e.touchData.didSelect=!0,ee[4]=1,!ee||ee.length===0||ee[0]===void 0?(ee[0]=(X[0]+X[2]+X[4])/3,ee[1]=(X[1]+X[3]+X[5])/3,ee[2]=(X[0]+X[2]+X[4])/3+1,ee[3]=(X[1]+X[3]+X[5])/3+1):(ee[2]=(X[0]+X[2]+X[4])/3,ee[3]=(X[1]+X[3]+X[5])/3),e.redrawHint("select",!0),e.redraw();else if(de&&K.touches[1]&&!e.touchData.didSelect&&ve.zoomingEnabled()&&ve.panningEnabled()&&ve.userZoomingEnabled()&&ve.userPanningEnabled()){K.preventDefault(),e.data.bgActivePosistion=void 0,e.redrawHint("select",!0);var At=e.dragData.touchDragEles;if(At){e.redrawHint("drag",!0);for(var jr=0;jr0&&!e.hoverData.draggingEles&&!e.swipePanning&&e.data.bgActivePosistion!=null&&(e.data.bgActivePosistion=void 0,e.redrawHint("select",!0),e.redraw())}},"touchmoveHandler"),!1);var He;e.registerBinding(t,"touchcancel",He=s(function(K){var de=e.touchData.start;e.touchData.capture=!1,de&&de.unactivate()},"touchcancelHandler"));var Ge,ze,Ve,et;if(e.registerBinding(t,"touchend",Ge=s(function(K){var de=e.touchData.start,ee=e.touchData.capture;if(ee)K.touches.length===0&&(e.touchData.capture=!1),K.preventDefault();else return;var ve=e.selection;e.swipePanning=!1,e.hoverData.draggingEles=!1;var X=e.cy,me=X.zoom(),Ce=e.touchData.now,Be=e.touchData.earlier;if(K.touches[0]){var Re=e.projectIntoViewport(K.touches[0].clientX,K.touches[0].clientY);Ce[0]=Re[0],Ce[1]=Re[1]}if(K.touches[1]){var Re=e.projectIntoViewport(K.touches[1].clientX,K.touches[1].clientY);Ce[2]=Re[0],Ce[3]=Re[1]}if(K.touches[2]){var Re=e.projectIntoViewport(K.touches[2].clientX,K.touches[2].clientY);Ce[4]=Re[0],Ce[5]=Re[1]}var Ke=s(function(_t){return{originalEvent:K,type:_t,position:{x:Ce[0],y:Ce[1]}}},"makeEvent");de&&de.unactivate();var De;if(e.touchData.cxt){if(De=Ke("cxttapend"),de?de.emit(De):X.emit(De),!e.touchData.cxtDragged){var Ze=Ke("cxttap");de?de.emit(Ze):X.emit(Ze)}e.touchData.start&&(e.touchData.start._private.grabbed=!1),e.touchData.cxt=!1,e.touchData.start=null,e.redraw();return}if(!K.touches[2]&&X.boxSelectionEnabled()&&e.touchData.selecting){e.touchData.selecting=!1;var Fe=X.collection(e.getAllInBox(ve[0],ve[1],ve[2],ve[3]));ve[0]=void 0,ve[1]=void 0,ve[2]=void 0,ve[3]=void 0,ve[4]=0,e.redrawHint("select",!0),X.emit(Ke("boxend"));var Je=s(function(_t){return _t.selectable()&&!_t.selected()},"eleWouldBeSelected");Fe.emit(Ke("box")).stdFilter(Je).select().emit(Ke("boxselect")),Fe.nonempty()&&e.redrawHint("eles",!0),e.redraw()}if(de?.unactivate(),K.touches[2])e.data.bgActivePosistion=void 0,e.redrawHint("select",!0);else if(!K.touches[1]&&!K.touches[0]&&!K.touches[0]){e.data.bgActivePosistion=void 0,e.redrawHint("select",!0);var Le=e.dragData.touchDragEles;if(de!=null){var ct=de._private.grabbed;y(Le),e.redrawHint("drag",!0),e.redrawHint("eles",!0),ct&&(de.emit(Ke("freeon")),Le.emit(Ke("free")),e.dragData.didDrag&&(de.emit(Ke("dragfreeon")),Le.emit(Ke("dragfree")))),n(de,["touchend","tapend","vmouseup","tapdragout"],K,{x:Ce[0],y:Ce[1]}),de.unactivate(),e.touchData.start=null}else{var qe=e.findNearestElement(Ce[0],Ce[1],!0,!0);n(qe,["touchend","tapend","vmouseup","tapdragout"],K,{x:Ce[0],y:Ce[1]})}var mt=e.touchData.startPosition[0]-Ce[0],St=mt*mt,Lt=e.touchData.startPosition[1]-Ce[1],Bt=Lt*Lt,qt=St+Bt,tr=qt*me*me;e.touchData.singleTouchMoved||(de||X.$(":selected").unselect(["tapunselect"]),n(de,["tap","vclick"],K,{x:Ce[0],y:Ce[1]}),ze=!1,K.timeStamp-et<=X.multiClickDebounceTime()?(Ve&&clearTimeout(Ve),ze=!0,et=null,n(de,["dbltap","vdblclick"],K,{x:Ce[0],y:Ce[1]})):(Ve=setTimeout(function(){ze||n(de,["onetap","voneclick"],K,{x:Ce[0],y:Ce[1]})},X.multiClickDebounceTime()),et=K.timeStamp)),de!=null&&!e.dragData.didDrag&&de._private.selectable&&tr"u"){var he=[],ye=s(function(K){return{clientX:K.clientX,clientY:K.clientY,force:1,identifier:K.pointerId,pageX:K.pageX,pageY:K.pageY,radiusX:K.width/2,radiusY:K.height/2,screenX:K.screenX,screenY:K.screenY,target:K.target}},"makeTouch"),be=s(function(K){return{event:K,touch:ye(K)}},"makePointer"),Ie=s(function(K){he.push(be(K))},"addPointer"),pe=s(function(K){for(var de=0;de0)return U[0]}return null},"getCurveT"),g=Object.keys(p),m=0;m0?f:TH(n,a,e,t,r,i,o,l)},"intersectLine"),checkPoint:s(function(e,t,r,i,n,a,o,l){l=l==="auto"?Rd(i,n):l;var u=2*l;if(rh(e,t,this.points,a,o,i,n-u,[0,-1],r)||rh(e,t,this.points,a,o,i-u,n,[0,-1],r))return!0;var h=i/2+2*r,d=n/2+2*r,p=[a-h,o-d,a-h,o,a+h,o,a+h,o-d];return!!(Bo(e,t,p)||gf(e,t,u,u,a+i/2-l,o+n/2-l,r)||gf(e,t,u,u,a-i/2+l,o+n/2-l,r))},"checkPoint")}},nh.registerNodeShapes=function(){var e=this.nodeShapes={},t=this;this.generateEllipse(),this.generatePolygon("triangle",ho(3,0)),this.generateRoundPolygon("round-triangle",ho(3,0)),this.generatePolygon("rectangle",ho(4,0)),e.square=e.rectangle,this.generateRoundRectangle(),this.generateCutRectangle(),this.generateBarrel(),this.generateBottomRoundrectangle();{var r=[0,1,1,0,0,-1,-1,0];this.generatePolygon("diamond",r),this.generateRoundPolygon("round-diamond",r)}this.generatePolygon("pentagon",ho(5,0)),this.generateRoundPolygon("round-pentagon",ho(5,0)),this.generatePolygon("hexagon",ho(6,0)),this.generateRoundPolygon("round-hexagon",ho(6,0)),this.generatePolygon("heptagon",ho(7,0)),this.generateRoundPolygon("round-heptagon",ho(7,0)),this.generatePolygon("octagon",ho(8,0)),this.generateRoundPolygon("round-octagon",ho(8,0));var i=new Array(20);{var n=e8(5,0),a=e8(5,Math.PI/5),o=.5*(3-Math.sqrt(5));o*=1.57;for(var l=0;l=e.deqFastCost*C)break}else if(o){if(y>=e.deqCost*d||y>=e.deqAvgCost*h)break}else if(k>=e.deqNoDrawCost*T8)break;var S=e.deq(t,g,f);if(S.length>0)for(var E=0;E0&&(e.onDeqd(t,p),!o&&e.shouldRedraw(t,p,g,f)&&i())},"dequeue"),a=e.priority||YA;r.beforeRender(n,a(t))}},"setupDequeueingImpl")},"setupDequeueing")},zYe=(function(){function e(t){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:Y3;Td(this,e),this.idsByKey=new th,this.keyForId=new th,this.cachesByLvl=new th,this.lvls=[],this.getKey=t,this.doesEleInvalidateKey=r}return s(e,"ElementTextureCacheLookup"),kd(e,[{key:"getIdsFor",value:s(function(t){t==null&&Kn("Can not get id list for null key");var r=this.idsByKey,i=this.idsByKey.get(t);return i||(i=new Qg,r.set(t,i)),i},"getIdsFor")},{key:"addIdForKey",value:s(function(t,r){t!=null&&this.getIdsFor(t).add(r)},"addIdForKey")},{key:"deleteIdForKey",value:s(function(t,r){t!=null&&this.getIdsFor(t).delete(r)},"deleteIdForKey")},{key:"getNumberOfIdsForKey",value:s(function(t){return t==null?0:this.getIdsFor(t).size},"getNumberOfIdsForKey")},{key:"updateKeyMappingFor",value:s(function(t){var r=t.id(),i=this.keyForId.get(r),n=this.getKey(t);this.deleteIdForKey(i,r),this.addIdForKey(n,r),this.keyForId.set(r,n)},"updateKeyMappingFor")},{key:"deleteKeyMappingFor",value:s(function(t){var r=t.id(),i=this.keyForId.get(r);this.deleteIdForKey(i,r),this.keyForId.delete(r)},"deleteKeyMappingFor")},{key:"keyHasChangedFor",value:s(function(t){var r=t.id(),i=this.keyForId.get(r),n=this.getKey(t);return i!==n},"keyHasChangedFor")},{key:"isInvalid",value:s(function(t){return this.keyHasChangedFor(t)||this.doesEleInvalidateKey(t)},"isInvalid")},{key:"getCachesAt",value:s(function(t){var r=this.cachesByLvl,i=this.lvls,n=r.get(t);return n||(n=new th,r.set(t,n),i.push(t)),n},"getCachesAt")},{key:"getCache",value:s(function(t,r){return this.getCachesAt(r).get(t)},"getCache")},{key:"get",value:s(function(t,r){var i=this.getKey(t),n=this.getCache(i,r);return n!=null&&this.updateKeyMappingFor(t),n},"get")},{key:"getForCachedKey",value:s(function(t,r){var i=this.keyForId.get(t.id()),n=this.getCache(i,r);return n},"getForCachedKey")},{key:"hasCache",value:s(function(t,r){return this.getCachesAt(r).has(t)},"hasCache")},{key:"has",value:s(function(t,r){var i=this.getKey(t);return this.hasCache(i,r)},"has")},{key:"setCache",value:s(function(t,r,i){i.key=t,this.getCachesAt(r).set(t,i)},"setCache")},{key:"set",value:s(function(t,r,i){var n=this.getKey(t);this.setCache(n,r,i),this.updateKeyMappingFor(t)},"set")},{key:"deleteCache",value:s(function(t,r){this.getCachesAt(r).delete(t)},"deleteCache")},{key:"delete",value:s(function(t,r){var i=this.getKey(t);this.deleteCache(i,r)},"_delete")},{key:"invalidateKey",value:s(function(t){var r=this;this.lvls.forEach(function(i){return r.deleteCache(t,i)})},"invalidateKey")},{key:"invalidate",value:s(function(t){var r=t.id(),i=this.keyForId.get(r);this.deleteKeyMappingFor(t);var n=this.doesEleInvalidateKey(t);return n&&this.invalidateKey(i),n||this.getNumberOfIdsForKey(i)===0},"invalidate")}])})(),WX=25,wk=50,Ek=-4,k8=3,jX=7.99,qYe=8,WYe=1024,jYe=1024,VYe=1024,UYe=.2,YYe=.8,HYe=10,GYe=.15,XYe=.1,KYe=.9,QYe=.9,ZYe=100,JYe=1,mm={dequeue:"dequeue",downscale:"downscale",highQuality:"highQuality"},eHe=Cs({getKey:null,doesEleInvalidateKey:Y3,drawElement:null,getBoundingBox:null,getRotationPoint:null,getRotationOffset:null,isVisible:cH,allowEdgeTxrCaching:!0,allowParentTxrCaching:!0}),cv=s(function(e,t){var r=this;r.renderer=e,r.onDequeues=[];var i=eHe(t);Ur(r,i),r.lookup=new zYe(i.getKey,i.doesEleInvalidateKey),r.setupDequeueing()},"ElementTextureCache"),Ua=cv.prototype,Ua.reasons=mm,Ua.getTextureQueue=function(e){var t=this;return t.eleImgCaches=t.eleImgCaches||{},t.eleImgCaches[e]=t.eleImgCaches[e]||[]},Ua.getRetiredTextureQueue=function(e){var t=this,r=t.eleImgCaches.retired=t.eleImgCaches.retired||{},i=r[e]=r[e]||[];return i},Ua.getElementQueue=function(){var e=this,t=e.eleCacheQueue=e.eleCacheQueue||new H2(function(r,i){return i.reqs-r.reqs});return t},Ua.getElementKeyToQueue=function(){var e=this,t=e.eleKeyToCacheQueue=e.eleKeyToCacheQueue||{};return t},Ua.getElement=function(e,t,r,i,n){var a=this,o=this.renderer,l=o.cy.zoom(),u=this.lookup;if(!t||t.w===0||t.h===0||isNaN(t.w)||isNaN(t.h)||!e.visible()||e.removed()||!a.allowEdgeTxrCaching&&e.isEdge()||!a.allowParentTxrCaching&&e.isParent())return null;if(i==null&&(i=Math.ceil(KA(l*r))),i=jX||i>k8)return null;var h=Math.pow(2,i),d=t.h*h,p=t.w*h,f=o.eleTextBiggerThanMin(e,h);if(!this.isVisible(e,f))return null;var g=u.get(e,i);if(g&&g.invalidated&&(g.invalidated=!1,g.texture.invalidatedWidth-=g.width),g)return g;var m;if(d<=WX?m=WX:d<=wk?m=wk:m=Math.ceil(d/wk)*wk,d>VYe||p>jYe)return null;var y=a.getTextureQueue(m),k=y[y.length-2],C=s(function(){return a.recycleTexture(m,p)||a.addTexture(m,p)},"addNewTxr");k||(k=y[y.length-1]),k||(k=C()),k.width-k.usedWidthi;M--)R=a.getElement(e,t,r,M,mm.downscale);$()}else return a.queueElement(e,v.level-1),v;else{var N;if(!E&&!_&&!b)for(var j=i-1;j>=Ek;j--){var I=u.get(e,j);if(I){N=I;break}}if(S(N))return a.queueElement(e,i),N;k.context.translate(k.usedWidth,0),k.context.scale(h,h),this.drawElement(k.context,e,t,f,!1),k.context.scale(1/h,1/h),k.context.translate(-k.usedWidth,0)}return g={x:k.usedWidth,texture:k,level:i,scale:h,width:p,height:d,scaledLabelShown:f},k.usedWidth+=Math.ceil(p+qYe),k.eleCaches.push(g),u.set(e,i,g),a.checkTextureFullness(k),g},Ua.invalidateElements=function(e){for(var t=0;t=UYe*e.width&&this.retireTexture(e)},Ua.checkTextureFullness=function(e){var t=this,r=t.getTextureQueue(e.height);e.usedWidth/e.width>YYe&&e.fullnessChecks>=HYe?Sd(r,e):e.fullnessChecks++},Ua.retireTexture=function(e){var t=this,r=e.height,i=t.getTextureQueue(r),n=this.lookup;Sd(i,e),e.retired=!0;for(var a=e.eleCaches,o=0;o=t)return o.retired=!1,o.usedWidth=0,o.invalidatedWidth=0,o.fullnessChecks=0,HA(o.eleCaches),o.context.setTransform(1,0,0,1,0,0),o.context.clearRect(0,0,o.width,o.height),Sd(n,o),i.push(o),o}},Ua.queueElement=function(e,t){var r=this,i=r.getElementQueue(),n=r.getElementKeyToQueue(),a=this.getKey(e),o=n[a];if(o)o.level=Math.max(o.level,t),o.eles.merge(e),o.reqs++,i.updateItem(o);else{var l={eles:e.spawn().merge(e),level:t,reqs:1,key:a};i.push(l),n[a]=l}},Ua.dequeue=function(e){for(var t=this,r=t.getElementQueue(),i=t.getElementKeyToQueue(),n=[],a=t.lookup,o=0;o0;o++){var l=r.pop(),u=l.key,h=l.eles[0],d=a.hasCache(h,l.level);if(i[u]=null,!d){n.push(l);var p=t.getBoundingBox(h);t.getElement(h,p,e,l.level,mm.dequeue)}}return n},Ua.removeFromQueue=function(e){var t=this,r=t.getElementQueue(),i=t.getElementKeyToQueue(),n=this.getKey(e),a=i[n];a!=null&&(a.eles.length===1?(a.reqs=UA,r.updateItem(a),r.pop(),i[n]=null):a.eles.unmerge(e))},Ua.onDequeue=function(e){this.onDequeues.push(e)},Ua.offDequeue=function(e){Sd(this.onDequeues,e)},Ua.setupDequeueing=qX.setupDequeueing({deqRedrawThreshold:ZYe,deqCost:GYe,deqAvgCost:XYe,deqNoDrawCost:KYe,deqFastCost:QYe,deq:s(function(e,t,r){return e.dequeue(t,r)},"deq"),onDeqd:s(function(e,t){for(var r=0;r=rHe||r>Sk)return null}i.validateLayersElesOrdering(r,e);var u=i.layersByLevel,h=Math.pow(2,r),d=u[r]=u[r]||[],p,f=i.levelIsComplete(r,e),g,m=s(function(){var R=s(function(j){if(i.validateLayersElesOrdering(j,e),i.levelIsComplete(j,e))return g=u[j],!0},"canUseAsTmpLvl"),$=s(function(j){if(!g)for(var I=r+j;uv<=I&&I<=Sk&&!R(I);I+=j);},"checkLvls");$(1),$(-1);for(var M=d.length-1;M>=0;M--){var N=d[M];N.invalid&&Sd(d,N)}},"checkTempLevels");if(!f)m();else return d;var y=s(function(){if(!p){p=uo();for(var R=0;RUX||N>UX)return null;var j=M*N;if(j>uHe)return null;var I=i.makeLayer(p,r);if($!=null){var z=d.indexOf($)+1;d.splice(z,0,I)}else(R.insert===void 0||R.insert)&&d.unshift(I);return I},"makeLayer");if(i.skipping&&!l)return null;for(var C=null,S=e.length/tHe,E=!l,_=0;_=S||!xH(C.bb,b.boundingBox()))&&(C=k({insert:!0,after:C}),!C))return null;g||E?i.queueLayer(C,b):i.drawEleInLayer(C,b,r,t),C.eles.push(b),x[r]=C}return g||(E?null:d)},Ss.getEleLevelForLayerLevel=function(e,t){return e},Ss.drawEleInLayer=function(e,t,r,i){var n=this,a=this.renderer,o=e.context,l=t.boundingBox();l.w===0||l.h===0||!t.visible()||(r=n.getEleLevelForLayerLevel(r,i),a.setImgSmoothing(o,!1),a.drawCachedElement(o,t,null,null,r,hHe),a.setImgSmoothing(o,!0))},Ss.levelIsComplete=function(e,t){var r=this,i=r.layersByLevel[e];if(!i||i.length===0)return!1;for(var n=0,a=0;a0||o.invalid)return!1;n+=o.eles.length}return n===t.length},Ss.validateLayersElesOrdering=function(e,t){var r=this.layersByLevel[e];if(r)for(var i=0;i0){t=!0;break}}return t},Ss.invalidateElements=function(e){var t=this;e.length!==0&&(t.lastInvalidationTime=eh(),!(e.length===0||!t.haveLayers())&&t.updateElementsInLayers(e,s(function(r,i,n){t.invalidateLayer(r)},"invalAssocLayers")))},Ss.invalidateLayer=function(e){if(this.lastInvalidationTime=eh(),!e.invalid){var t=e.level,r=e.eles,i=this.layersByLevel[t];Sd(i,e),e.elesQueue=[],e.invalid=!0,e.replacement&&(e.replacement.invalid=!0);for(var n=0;n3&&arguments[3]!==void 0?arguments[3]:!0,n=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,a=arguments.length>5&&arguments[5]!==void 0?arguments[5]:!0,o=this,l=t._private.rscratch;if(!(a&&!t.visible())&&!(l.badLine||l.allpts==null||isNaN(l.allpts[0]))){var u;r&&(u=r,e.translate(-u.x1,-u.y1));var h=a?t.pstyle("opacity").value:1,d=a?t.pstyle("line-opacity").value:1,p=t.pstyle("curve-style").value,f=t.pstyle("line-style").value,g=t.pstyle("width").pfValue,m=t.pstyle("line-cap").value,y=t.pstyle("line-outline-width").value,k=t.pstyle("line-outline-color").value,C=h*d,S=h*d,E=s(function(){var I=arguments.length>0&&arguments[0]!==void 0?arguments[0]:C;p==="straight-triangle"?(o.eleStrokeStyle(e,t,I),o.drawEdgeTrianglePath(t,e,l.allpts)):(e.lineWidth=g,e.lineCap=m,o.eleStrokeStyle(e,t,I),o.drawEdgePath(t,e,l.allpts,f),e.lineCap="butt")},"drawLine"),_=s(function(){var I=arguments.length>0&&arguments[0]!==void 0?arguments[0]:C;if(e.lineWidth=g+y,e.lineCap=m,y>0)o.colorStrokeStyle(e,k[0],k[1],k[2],I);else{e.lineCap="butt";return}p==="straight-triangle"?o.drawEdgeTrianglePath(t,e,l.allpts):(o.drawEdgePath(t,e,l.allpts,f),e.lineCap="butt")},"drawLineOutline"),b=s(function(){n&&o.drawEdgeOverlay(e,t)},"drawOverlay"),v=s(function(){n&&o.drawEdgeUnderlay(e,t)},"drawUnderlay"),x=s(function(){var I=arguments.length>0&&arguments[0]!==void 0?arguments[0]:S;o.drawArrowheads(e,t,I)},"drawArrows"),w=s(function(){o.drawElementText(e,t,null,i)},"drawText");e.lineJoin="round";var R=t.pstyle("ghost").value==="yes";if(R){var $=t.pstyle("ghost-offset-x").pfValue,M=t.pstyle("ghost-offset-y").pfValue,N=t.pstyle("ghost-opacity").value,j=C*N;e.translate($,M),E(j),x(j),e.translate(-$,-M)}else _();v(),E(),x(),b(),w(),r&&e.translate(u.x1,u.y1)}},XX=s(function(e){if(!["overlay","underlay"].includes(e))throw new Error("Invalid state");return function(t,r){if(r.visible()){var i=r.pstyle("".concat(e,"-opacity")).value;if(i!==0){var n=this,a=n.usePaths(),o=r._private.rscratch,l=r.pstyle("".concat(e,"-padding")).pfValue,u=2*l,h=r.pstyle("".concat(e,"-color")).value;t.lineWidth=u,o.edgeType==="self"&&!a?t.lineCap="butt":t.lineCap="round",n.colorStrokeStyle(t,h[0],h[1],h[2],i),n.drawEdgePath(r,t,o.allpts,"solid")}}}},"drawEdgeOverlayUnderlay"),ah.drawEdgeOverlay=XX("overlay"),ah.drawEdgeUnderlay=XX("underlay"),ah.drawEdgePath=function(e,t,r,i){var n=e._private.rscratch,a=t,o,l=!1,u=this.usePaths(),h=e.pstyle("line-dash-pattern").pfValue,d=e.pstyle("line-dash-offset").pfValue;if(u){var p=r.join("$"),f=n.pathCacheKey&&n.pathCacheKey===p;f?(o=t=n.pathCache,l=!0):(o=t=new Path2D,n.pathCacheKey=p,n.pathCache=o)}if(a.setLineDash)switch(i){case"dotted":a.setLineDash([1,1]);break;case"dashed":a.setLineDash(h),a.lineDashOffset=d;break;case"solid":a.setLineDash([]);break}if(!l&&!n.badLine)switch(t.beginPath&&t.beginPath(),t.moveTo(r[0],r[1]),n.edgeType){case"bezier":case"self":case"compound":case"multibezier":for(var g=2;g+35&&arguments[5]!==void 0?arguments[5]:!0,o=this;if(i==null){if(a&&!o.eleTextBiggerThanMin(t))return}else if(i===!1)return;if(t.isNode()){var l=t.pstyle("label");if(!l||!l.value)return;var u=o.getLabelJustification(t),h=t.pstyle("text-metrics").strValue==="glyph";e.textAlign=u,e.textBaseline=h?"alphabetic":"bottom"}else{var d=t.element()._private.rscratch.badLine,p=t.pstyle("label"),f=t.pstyle("source-label"),g=t.pstyle("target-label");if(d||(!p||!p.value)&&(!f||!f.value)&&(!g||!g.value))return;e.textAlign="center",e.textBaseline="bottom"}var m=!r,y;r&&(y=r,e.translate(-y.x1,-y.y1)),n==null?(o.drawText(e,t,null,m,a),t.isEdge()&&(o.drawText(e,t,"source",m,a),o.drawText(e,t,"target",m,a))):o.drawText(e,t,n,m,a),r&&e.translate(y.x1,y.y1)},xf.getFontCache=function(e){var t;this.fontCaches=this.fontCaches||[];for(var r=0;r2&&arguments[2]!==void 0?arguments[2]:!0,i=t.pstyle("font-style").strValue,n=t.pstyle("font-size").pfValue+"px",a=t.pstyle("font-family").strValue,o=t.pstyle("font-weight").strValue,l=r?t.effectiveOpacity()*t.pstyle("text-opacity").value:1,u=t.pstyle("text-outline-opacity").value*l,h=t.pstyle("color").value,d=t.pstyle("text-outline-color").value;e.font=i+" "+o+" "+n+" "+a,e.lineJoin="round",this.colorFillStyle(e,h[0],h[1],h[2],l),this.colorStrokeStyle(e,d[0],d[1],d[2],u)},s(Zze,"circle"),s(bY,"roundRect"),xf.getTextAngle=function(e,t){var r,i=e._private,n=i.rscratch,a=t?t+"-":"",o=e.pstyle(a+"text-rotation");if(o.strValue==="autorotate"){var l=co(n,"labelAngle",t);r=e.isEdge()?l:0}else o.strValue==="none"?r=0:r=o.pfValue;return r},xf.drawText=function(e,t,r){var i=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!0,n=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,a=t._private,o=a.rscratch,l=n?t.effectiveOpacity():1;if(!(n&&(l===0||t.pstyle("text-opacity").value===0))){r==="main"&&(r=null);var u=co(o,"labelX",r),h=co(o,"labelY",r),d,p,f=this.getLabelText(t,r);if(f!=null&&f!==""&&!isNaN(u)&&!isNaN(h)){this.setupTextStyle(e,t,n);var g=r?r+"-":"",m=co(o,"labelWidth",r),y=co(o,"labelHeight",r),k=co(o,"labelActualDescent",r),C=t.pstyle(g+"text-margin-x").pfValue,S=t.pstyle(g+"text-margin-y").pfValue,E=t.isEdge(),_=t.pstyle("text-halign").value,b=t.pstyle("text-valign").value;E&&(_="center",b="center"),u+=C,h+=S;var v;i?v=this.getTextAngle(t,r):v=0,v!==0&&(d=u,p=h,e.translate(d,p),e.rotate(v),u=0,h=0);var x=om(_),w=lm(b);switch(w){case"top":break;case"center":h+=y/2;break;case"bottom":h+=y;break}var R=t.pstyle("text-background-opacity").value,$=t.pstyle("text-border-opacity").value,M=t.pstyle("text-border-width").pfValue,N=t.pstyle("text-background-padding").pfValue,j=t.pstyle("text-background-shape").strValue,I=j==="round-rectangle"||j==="roundrectangle",z=j==="circle",F=2;if(R>0||M>0&&$>0){var O=e.fillStyle,B=e.strokeStyle,Y=e.lineWidth,U=t.pstyle("text-background-color").value,H=t.pstyle("text-border-color").value,J=t.pstyle("text-border-style").value,te=R>0,ie=M>0&&$>0,ue=u-N;switch(x){case"left":ue-=m;break;case"center":ue-=m/2;break}var se=h-y-N,le=m+2*N,Te=y+2*N;if(te&&(e.fillStyle="rgba(".concat(U[0],",").concat(U[1],",").concat(U[2],",").concat(R*l,")")),ie&&(e.strokeStyle="rgba(".concat(H[0],",").concat(H[1],",").concat(H[2],",").concat($*l,")"),e.lineWidth=M,e.setLineDash))switch(J){case"dotted":e.setLineDash([1,1]);break;case"dashed":e.setLineDash([4,2]);break;case"double":e.lineWidth=M/4,e.setLineDash([]);break;default:e.setLineDash([]);break}if(I?(e.beginPath(),bY(e,ue,se,le,Te,F)):z?(e.beginPath(),Zze(e,ue,se,le,Te)):(e.beginPath(),e.rect(ue,se,le,Te)),te&&e.fill(),ie&&e.stroke(),ie&&J==="double"){var oe=M/2;e.beginPath(),I?bY(e,ue+oe,se+oe,le-2*oe,Te-2*oe,F):e.rect(ue+oe,se+oe,le-2*oe,Te-2*oe),e.stroke()}e.fillStyle=O,e.strokeStyle=B,e.lineWidth=Y,e.setLineDash&&e.setLineDash([])}var fe=2*t.pstyle("text-outline-width").pfValue;if(fe>0&&(e.lineWidth=fe),h-=k,t.pstyle("text-wrap").value==="wrap"){var ge=co(o,"labelWrapCachedLines",r),ke=co(o,"labelLineHeight",r),Ne=m/2,xe=this.getLabelJustification(t);switch(xe==="auto"||(x==="left"?xe==="left"?u+=-m:xe==="center"&&(u+=-Ne):x==="center"?xe==="left"?u+=-Ne:xe==="right"&&(u+=Ne):x==="right"&&(xe==="center"?u+=Ne:xe==="right"&&(u+=m))),w){case"top":h-=(ge.length-1)*ke;break;case"center":case"bottom":h-=(ge.length-1)*ke;break}for(var $e=0;$e0&&e.strokeText(ge[$e],u,h),e.fillText(ge[$e],u,h),h+=ke}else fe>0&&e.strokeText(f,u,h),e.fillText(f,u,h);v!==0&&(e.rotate(-v),e.translate(-d,-p))}}},Pd={},Pd.drawNode=function(e,t,r){var i=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!0,n=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,a=arguments.length>5&&arguments[5]!==void 0?arguments[5]:!0,o=this,l,u,h=t._private,d=h.rscratch,p=t.position();if(!(!ir(p.x)||!ir(p.y))&&!(a&&!t.visible())){var f=a?t.effectiveOpacity():1,g=o.usePaths(),m,y=!1,k=t.padding();l=t.width()+2*k,u=t.height()+2*k;var C;r&&(C=r,e.translate(-C.x1,-C.y1));for(var S=t.pstyle("background-image"),E=S.value,_=new Array(E.length),b=new Array(E.length),v=0,x=0;x0&&arguments[0]!==void 0?arguments[0]:j;o.eleFillStyle(e,t,K)},"setupShapeColor"),oe=s(function(){var K=arguments.length>0&&arguments[0]!==void 0?arguments[0]:H;o.colorStrokeStyle(e,I[0],I[1],I[2],K)},"setupBorderColor"),fe=s(function(){var K=arguments.length>0&&arguments[0]!==void 0?arguments[0]:ue;o.colorStrokeStyle(e,te[0],te[1],te[2],K)},"setupOutlineColor"),ge=s(function(K,de,ee,ve){var X=o.nodePathCache=o.nodePathCache||[],me=oH(ee==="polygon"?ee+","+ve.join(","):ee,""+de,""+K,""+le),Ce=X[me],Be,Re=!1;return Ce!=null?(Be=Ce,Re=!0,d.pathCache=Be):(Be=new Path2D,X[me]=d.pathCache=Be),{path:Be,cacheHit:Re}},"getPath"),ke=t.pstyle("shape").strValue,Ne=t.pstyle("shape-polygon-points").pfValue;if(g){e.translate(p.x,p.y);var xe=ge(l,u,ke,Ne);m=xe.path,y=xe.cacheHit}var $e=s(function(){if(!y){var K=p;g&&(K={x:0,y:0}),o.nodeShapes[o.getNodeShape(t)].draw(m||e,K.x,K.y,l,u,le,d)}g?e.fill(m):e.fill()},"drawShape"),Me=s(function(){for(var K=arguments.length>0&&arguments[0]!==void 0?arguments[0]:f,de=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,ee=h.backgrounding,ve=0,X=0;X0&&arguments[0]!==void 0?arguments[0]:!1,de=arguments.length>1&&arguments[1]!==void 0?arguments[1]:f;o.hasPie(t)&&(o.drawPie(e,t,de),K&&(g||o.nodeShapes[o.getNodeShape(t)].draw(e,p.x,p.y,l,u,le,d)))},"drawPie"),Ge=s(function(){var K=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!1,de=arguments.length>1&&arguments[1]!==void 0?arguments[1]:f;o.hasStripe(t)&&(e.save(),g?e.clip(d.pathCache):(o.nodeShapes[o.getNodeShape(t)].draw(e,p.x,p.y,l,u,le,d),e.clip()),o.drawStripe(e,t,de),e.restore(),K&&(g||o.nodeShapes[o.getNodeShape(t)].draw(e,p.x,p.y,l,u,le,d)))},"drawStripe"),ze=s(function(){var K=arguments.length>0&&arguments[0]!==void 0?arguments[0]:f,de=(M>0?M:-M)*K,ee=M>0?0:255;M!==0&&(o.colorFillStyle(e,ee,ee,ee,de),g?e.fill(m):e.fill())},"darken"),Ve=s(function(){if(N>0){if(e.lineWidth=N,e.lineCap=O,e.lineJoin=F,e.setLineDash)switch(z){case"dotted":e.setLineDash([1,1]);break;case"dashed":e.setLineDash(Y),e.lineDashOffset=U;break;case"solid":case"double":e.setLineDash([]);break}if(B!=="center"){if(e.save(),e.lineWidth*=2,B==="inside")g?e.clip(m):e.clip();else{var K=new Path2D;K.rect(-l/2-N,-u/2-N,l+2*N,u+2*N),K.addPath(m),e.clip(K,"evenodd")}g?e.stroke(m):e.stroke(),e.restore()}else g?e.stroke(m):e.stroke();if(z==="double"){e.lineWidth=N/3;var de=e.globalCompositeOperation;e.globalCompositeOperation="destination-out",g?e.stroke(m):e.stroke(),e.globalCompositeOperation=de}e.setLineDash&&e.setLineDash([])}},"drawBorder"),et=s(function(){if(J>0){if(e.lineWidth=J,e.lineCap="butt",e.setLineDash)switch(ie){case"dotted":e.setLineDash([1,1]);break;case"dashed":e.setLineDash([4,2]);break;case"solid":case"double":e.setLineDash([]);break}var K=p;g&&(K={x:0,y:0});var de=o.getNodeShape(t),ee=N;B==="inside"&&(ee=0),B==="outside"&&(ee*=2);var ve=(l+ee+(J+se))/l,X=(u+ee+(J+se))/u,me=l*ve,Ce=u*X,Be=o.nodeShapes[de].points,Re;if(g){var Ke=ge(me,Ce,de,Be);Re=Ke.path}if(de==="ellipse")o.drawEllipsePath(Re||e,K.x,K.y,me,Ce);else if(["round-diamond","round-heptagon","round-hexagon","round-octagon","round-pentagon","round-polygon","round-triangle","round-tag"].includes(de)){var De=0,Ze=0,Fe=0;de==="round-diamond"?De=(ee+se+J)*1.4:de==="round-heptagon"?(De=(ee+se+J)*1.075,Fe=-(ee/2+se+J)/35):de==="round-hexagon"?De=(ee+se+J)*1.12:de==="round-pentagon"?(De=(ee+se+J)*1.13,Fe=-(ee/2+se+J)/15):de==="round-tag"?(De=(ee+se+J)*1.12,Ze=(ee/2+J+se)*.07):de==="round-triangle"&&(De=(ee+se+J)*(Math.PI/2),Fe=-(ee+se/2+J)/Math.PI),De!==0&&(ve=(l+De)/l,me=l*ve,["round-hexagon","round-tag"].includes(de)||(X=(u+De)/u,Ce=u*X)),le=le==="auto"?CH(me,Ce):le;for(var Je=me/2,Le=Ce/2,ct=le+(ee+J+se)/2,qe=new Array(Be.length/2),mt=new Array(Be.length/2),St=0;St0){if(i=i||r.position(),n==null||a==null){var f=r.padding();n=r.width()+2*f,a=r.height()+2*f}o.colorFillStyle(t,h[0],h[1],h[2],u),o.nodeShapes[d].draw(t,i.x,i.y,n+l*2,a+l*2,p),t.fill()}}}},"drawNodeOverlayUnderlay"),Pd.drawNodeOverlay=KX("overlay"),Pd.drawNodeUnderlay=KX("underlay"),Pd.hasPie=function(e){return e=e[0],e._private.hasPie},Pd.hasStripe=function(e){return e=e[0],e._private.hasStripe},Pd.drawPie=function(e,t,r,i){t=t[0],i=i||t.position();var n=t.cy().style(),a=t.pstyle("pie-size"),o=t.pstyle("pie-hole"),l=t.pstyle("pie-start-angle").pfValue,u=i.x,h=i.y,d=t.width(),p=t.height(),f=Math.min(d,p)/2,g,m=0,y=this.usePaths();if(y&&(u=0,h=0),a.units==="%"?f=f*a.pfValue:a.pfValue!==void 0&&(f=a.pfValue/2),o.units==="%"?g=f*o.pfValue:o.pfValue!==void 0&&(g=o.pfValue/2),!(g>=f))for(var k=1;k<=n.pieBackgroundN;k++){var C=t.pstyle("pie-"+k+"-background-size").value,S=t.pstyle("pie-"+k+"-background-color").value,E=t.pstyle("pie-"+k+"-background-opacity").value*r,_=C/100;_+m>1&&(_=1-m);var b=1.5*Math.PI+2*Math.PI*m;b+=l;var v=2*Math.PI*_,x=b+v;C===0||m>=1||m+_>1||(g===0?(e.beginPath(),e.moveTo(u,h),e.arc(u,h,f,b,x),e.closePath()):(e.beginPath(),e.arc(u,h,f,b,x),e.arc(u,h,g,x,b,!0),e.closePath()),this.colorFillStyle(e,S[0],S[1],S[2],E),e.fill(),m+=_)}},Pd.drawStripe=function(e,t,r,i){t=t[0],i=i||t.position();var n=t.cy().style(),a=i.x,o=i.y,l=t.width(),u=t.height(),h=0,d=this.usePaths();e.save();var p=t.pstyle("stripe-direction").value,f=t.pstyle("stripe-size");switch(p){case"vertical":break;case"righward":e.rotate(-Math.PI/2);break}var g=l,m=u;f.units==="%"?(g=g*f.pfValue,m=m*f.pfValue):f.pfValue!==void 0&&(g=f.pfValue,m=f.pfValue),d&&(a=0,o=0),o-=g/2,a-=m/2;for(var y=1;y<=n.stripeBackgroundN;y++){var k=t.pstyle("stripe-"+y+"-background-size").value,C=t.pstyle("stripe-"+y+"-background-color").value,S=t.pstyle("stripe-"+y+"-background-opacity").value*r,E=k/100;E+h>1&&(E=1-h),!(k===0||h>=1||h+E>1)&&(e.beginPath(),e.rect(a,o+m*h,g,m*E),e.closePath(),this.colorFillStyle(e,C[0],C[1],C[2],S),e.fill(),h+=E)}e.restore()},po={},bHe=100,po.getPixelRatio=function(){var e=this.data.contexts[0];if(this.forcedPixelRatio!=null)return this.forcedPixelRatio;var t=this.cy.window(),r=e.backingStorePixelRatio||e.webkitBackingStorePixelRatio||e.mozBackingStorePixelRatio||e.msBackingStorePixelRatio||e.oBackingStorePixelRatio||e.backingStorePixelRatio||1;return(t.devicePixelRatio||1)/r},po.paintCache=function(e){for(var t=this.paintCaches=this.paintCaches||[],r=!0,i,n=0;nt.minMbLowQualFrames&&(t.motionBlurPxRatio=t.mbPxRBlurry)),t.clearingMotionBlur&&(t.motionBlurPxRatio=1),t.textureDrawLastFrame&&!p&&(d[t.NODE]=!0,d[t.SELECT_BOX]=!0);var S=r.style(),E=r.zoom(),_=o!==void 0?o:E,b=r.pan(),v={x:b.x,y:b.y},x={zoom:E,pan:{x:b.x,y:b.y}},w=t.prevViewport,R=w===void 0||x.zoom!==w.zoom||x.pan.x!==w.pan.x||x.pan.y!==w.pan.y;!R&&!(y&&!m)&&(t.motionBlurPxRatio=1),l&&(v=l),_*=u,v.x*=u,v.y*=u;var $=t.getCachedZSortedEles();function M(oe,fe,ge,ke,Ne){var xe=oe.globalCompositeOperation;oe.globalCompositeOperation="destination-out",t.colorFillStyle(oe,255,255,255,t.motionBlurTransparency),oe.fillRect(fe,ge,ke,Ne),oe.globalCompositeOperation=xe}s(M,"mbclear");function N(oe,fe){var ge,ke,Ne,xe;!t.clearingMotionBlur&&(oe===h.bufferContexts[t.MOTIONBLUR_BUFFER_NODE]||oe===h.bufferContexts[t.MOTIONBLUR_BUFFER_DRAG])?(ge={x:b.x*g,y:b.y*g},ke=E*g,Ne=t.canvasWidth*g,xe=t.canvasHeight*g):(ge=v,ke=_,Ne=t.canvasWidth,xe=t.canvasHeight),oe.setTransform(1,0,0,1,0,0),fe==="motionBlur"?M(oe,0,0,Ne,xe):!i&&(fe===void 0||fe)&&oe.clearRect(0,0,Ne,xe),n||(oe.translate(ge.x,ge.y),oe.scale(ke,ke)),l&&oe.translate(l.x,l.y),o&&oe.scale(o,o)}if(s(N,"setContextTransform"),p||(t.textureDrawLastFrame=!1),p){if(t.textureDrawLastFrame=!0,!t.textureCache){t.textureCache={},t.textureCache.bb=r.mutableElements().boundingBox(),t.textureCache.texture=t.data.bufferCanvases[t.TEXTURE_BUFFER];var j=t.data.bufferContexts[t.TEXTURE_BUFFER];j.setTransform(1,0,0,1,0,0),j.clearRect(0,0,t.canvasWidth*t.textureMult,t.canvasHeight*t.textureMult),t.render({forcedContext:j,drawOnlyNodeLayer:!0,forcedPxRatio:u*t.textureMult});var x=t.textureCache.viewport={zoom:r.zoom(),pan:r.pan(),width:t.canvasWidth,height:t.canvasHeight};x.mpan={x:(0-x.pan.x)/x.zoom,y:(0-x.pan.y)/x.zoom}}d[t.DRAG]=!1,d[t.NODE]=!1;var I=h.contexts[t.NODE],z=t.textureCache.texture,x=t.textureCache.viewport;I.setTransform(1,0,0,1,0,0),f?M(I,0,0,x.width,x.height):I.clearRect(0,0,x.width,x.height);var F=S.core("outside-texture-bg-color").value,O=S.core("outside-texture-bg-opacity").value;t.colorFillStyle(I,F[0],F[1],F[2],O),I.fillRect(0,0,x.width,x.height);var E=r.zoom();N(I,!1),I.clearRect(x.mpan.x,x.mpan.y,x.width/x.zoom/u,x.height/x.zoom/u),I.drawImage(z,x.mpan.x,x.mpan.y,x.width/x.zoom/u,x.height/x.zoom/u)}else t.textureOnViewport&&!i&&(t.textureCache=null);var B=r.extent(),Y=t.pinching||t.hoverData.dragging||t.swipePanning||t.data.wheelZooming||t.hoverData.draggingEles||t.cy.animated(),U=t.hideEdgesOnViewport&&Y,H=[];if(H[t.NODE]=!d[t.NODE]&&f&&!t.clearedForMotionBlur[t.NODE]||t.clearingMotionBlur,H[t.NODE]&&(t.clearedForMotionBlur[t.NODE]=!0),H[t.DRAG]=!d[t.DRAG]&&f&&!t.clearedForMotionBlur[t.DRAG]||t.clearingMotionBlur,H[t.DRAG]&&(t.clearedForMotionBlur[t.DRAG]=!0),d[t.NODE]||n||a||H[t.NODE]){var J=f&&!H[t.NODE]&&g!==1,I=i||(J?t.data.bufferContexts[t.MOTIONBLUR_BUFFER_NODE]:h.contexts[t.NODE]),te=f&&!J?"motionBlur":void 0;N(I,te),U?t.drawCachedNodes(I,$.nondrag,u,B):t.drawLayeredElements(I,$.nondrag,u,B),t.debug&&t.drawDebugPoints(I,$.nondrag),!n&&!f&&(d[t.NODE]=!1)}if(!a&&(d[t.DRAG]||n||H[t.DRAG])){var J=f&&!H[t.DRAG]&&g!==1,I=i||(J?t.data.bufferContexts[t.MOTIONBLUR_BUFFER_DRAG]:h.contexts[t.DRAG]);N(I,f&&!J?"motionBlur":void 0),U?t.drawCachedNodes(I,$.drag,u,B):t.drawCachedElements(I,$.drag,u,B),t.debug&&t.drawDebugPoints(I,$.drag),!n&&!f&&(d[t.DRAG]=!1)}if(this.drawSelectionRectangle(e,N),f&&g!==1){var ie=h.contexts[t.NODE],ue=t.data.bufferCanvases[t.MOTIONBLUR_BUFFER_NODE],se=h.contexts[t.DRAG],le=t.data.bufferCanvases[t.MOTIONBLUR_BUFFER_DRAG],Te=s(function(oe,fe,ge){oe.setTransform(1,0,0,1,0,0),ge||!C?oe.clearRect(0,0,t.canvasWidth,t.canvasHeight):M(oe,0,0,t.canvasWidth,t.canvasHeight);var ke=g;oe.drawImage(fe,0,0,t.canvasWidth*ke,t.canvasHeight*ke,0,0,t.canvasWidth,t.canvasHeight)},"drawMotionBlur");(d[t.NODE]||H[t.NODE])&&(Te(ie,ue,H[t.NODE]),d[t.NODE]=!1),(d[t.DRAG]||H[t.DRAG])&&(Te(se,le,H[t.DRAG]),d[t.DRAG]=!1)}t.prevViewport=x,t.clearingMotionBlur&&(t.clearingMotionBlur=!1,t.motionBlurCleared=!0,t.motionBlur=!0),f&&(t.motionBlurTimeout=setTimeout(function(){t.motionBlurTimeout=null,t.clearedForMotionBlur[t.NODE]=!1,t.clearedForMotionBlur[t.DRAG]=!1,t.motionBlur=!1,t.clearingMotionBlur=!p,t.mbFrames=0,d[t.NODE]=!0,d[t.DRAG]=!0,t.redraw()},bHe)),i||r.emit("render")},po.drawSelectionRectangle=function(e,t){var r=this,i=r.cy,n=r.data,a=i.style(),o=e.drawOnlyNodeLayer,l=e.drawAllLayers,u=n.canvasNeedsRedraw,h=e.forcedContext;if(r.showFps||!o&&u[r.SELECT_BOX]&&!l){var d=h||n.contexts[r.SELECT_BOX];if(t(d),r.selection[4]==1&&(r.hoverData.selecting||r.touchData.selecting)){var p=r.cy.zoom(),f=a.core("selection-box-border-width").value/p;d.lineWidth=f,d.fillStyle="rgba("+a.core("selection-box-color").value[0]+","+a.core("selection-box-color").value[1]+","+a.core("selection-box-color").value[2]+","+a.core("selection-box-opacity").value+")",d.fillRect(r.selection[0],r.selection[1],r.selection[2]-r.selection[0],r.selection[3]-r.selection[1]),f>0&&(d.strokeStyle="rgba("+a.core("selection-box-border-color").value[0]+","+a.core("selection-box-border-color").value[1]+","+a.core("selection-box-border-color").value[2]+","+a.core("selection-box-opacity").value+")",d.strokeRect(r.selection[0],r.selection[1],r.selection[2]-r.selection[0],r.selection[3]-r.selection[1]))}if(n.bgActivePosistion&&!r.hoverData.selecting){var p=r.cy.zoom(),g=n.bgActivePosistion;d.fillStyle="rgba("+a.core("active-bg-color").value[0]+","+a.core("active-bg-color").value[1]+","+a.core("active-bg-color").value[2]+","+a.core("active-bg-opacity").value+")",d.beginPath(),d.arc(g.x,g.y,a.core("active-bg-size").pfValue/p,0,2*Math.PI),d.fill()}var m=r.lastRedrawTime;if(r.showFps&&m){m=Math.round(m);var y=Math.round(1e3/m),k="1 frame = "+m+" ms = "+y+" fps";if(d.setTransform(1,0,0,1,0,0),d.fillStyle="rgba(255, 0, 0, 0.75)",d.strokeStyle="rgba(255, 0, 0, 0.75)",d.font="30px Arial",!_k){var C=d.measureText(k);_k=C.actualBoundingBoxAscent}d.fillText(k,0,_k);var S=60;d.strokeRect(0,_k+10,250,20),d.fillRect(0,_k+10,250*Math.min(y/S,1),20)}l||(u[r.SELECT_BOX]=!1)}},s(xY,"compileShader"),s(Jze,"createProgram"),s(eqe,"createTextureCanvas"),s(PA,"getEffectivePanZoom"),s(tqe,"getEffectiveZoom"),s(rqe,"modelToRenderedPosition"),s(iqe,"isSimpleShape"),s(nqe,"arrayEqual"),s(uf,"toWebGLColor"),s(Gg,"indexToVec4"),s(aqe,"vec4ToIndex"),s(sqe,"createTexture"),s(TY,"getTypeInfo"),s(kY,"createTypedArray"),s(oqe,"createTypedArrayView"),s(lqe,"createBufferStaticDraw"),s(jc,"createBufferDynamicDraw"),s(cqe,"create3x3MatrixBufferDynamicDraw"),s(uqe,"createPickingFrameBuffer"),QX=typeof Float32Array<"u"?Float32Array:Array,Math.hypot||(Math.hypot=function(){for(var e=0,t=arguments.length;t--;)e+=arguments[t]*arguments[t];return Math.sqrt(e)}),s(FA,"create"),s(CY,"identity"),s(hqe,"multiply"),s(z3,"translate"),s(wY,"rotate"),s(zA,"scale"),s(dqe,"projection"),xHe=(function(){function e(t,r,i,n){Td(this,e),this.debugID=Math.floor(Math.random()*1e4),this.r=t,this.texSize=r,this.texRows=i,this.texHeight=Math.floor(r/i),this.enableWrapping=!0,this.locked=!1,this.texture=null,this.needsBuffer=!0,this.freePointer={x:0,row:0},this.keyToLocation=new Map,this.canvas=n(t,r,r),this.scratch=n(t,r,this.texHeight,"scratch")}return s(e,"Atlas"),kd(e,[{key:"lock",value:s(function(){this.locked=!0},"lock")},{key:"getKeys",value:s(function(){return new Set(this.keyToLocation.keys())},"getKeys")},{key:"getScale",value:s(function(t){var r=t.w,i=t.h,n=this.texHeight,a=this.texSize,o=n/i,l=r*o,u=i*o;return l>a&&(o=a/r,l=r*o,u=i*o),{scale:o,texW:l,texH:u}},"getScale")},{key:"draw",value:s(function(t,r,i){var n=this;if(this.locked)throw new Error("can't draw, atlas is locked");var a=this.texSize,o=this.texRows,l=this.texHeight,u=this.getScale(r),h=u.scale,d=u.texW,p=u.texH,f=s(function(C,S){if(i&&S){var E=S.context,_=C.x,b=C.row,v=_,x=l*b;E.save(),E.translate(v,x),E.scale(h,h),i(E,r),E.restore()}},"drawAt"),g=[null,null],m=s(function(){f(n.freePointer,n.canvas),g[0]={x:n.freePointer.x,y:n.freePointer.row*l,w:d,h:p},g[1]={x:n.freePointer.x+d,y:n.freePointer.row*l,w:0,h:p},n.freePointer.x+=d,n.freePointer.x==a&&(n.freePointer.x=0,n.freePointer.row++)},"drawNormal"),y=s(function(){var C=n.scratch,S=n.canvas;C.clear(),f({x:0,row:0},C);var E=a-n.freePointer.x,_=d-E,b=l;{var v=n.freePointer.x,x=n.freePointer.row*l,w=E;S.context.drawImage(C,0,0,w,b,v,x,w,b),g[0]={x:v,y:x,w,h:p}}{var R=E,$=(n.freePointer.row+1)*l,M=_;S&&S.context.drawImage(C,R,0,M,b,0,$,M,b),g[1]={x:0,y:$,w:M,h:p}}n.freePointer.x=_,n.freePointer.row++},"drawWrapped"),k=s(function(){n.freePointer.x=0,n.freePointer.row++},"moveToStartOfNextRow");if(this.freePointer.x+d<=a)m();else{if(this.freePointer.row>=o-1)return!1;this.freePointer.x===a?(k(),m()):this.enableWrapping?y():(k(),m())}return this.keyToLocation.set(t,g),this.needsBuffer=!0,g},"draw")},{key:"getOffsets",value:s(function(t){return this.keyToLocation.get(t)},"getOffsets")},{key:"isEmpty",value:s(function(){return this.freePointer.x===0&&this.freePointer.row===0},"isEmpty")},{key:"canFit",value:s(function(t){if(this.locked)return!1;var r=this.texSize,i=this.texRows,n=this.getScale(t),a=n.texW;return this.freePointer.x+a>r?this.freePointer.row1&&arguments[1]!==void 0?arguments[1]:{},n=i.forceRedraw,a=n===void 0?!1:n,o=i.filterEle,l=o===void 0?function(){return!0}:o,u=i.filterType,h=u===void 0?function(){return!0}:u,d=!1,p=!1,f=Oo(t),g;try{for(f.s();!(g=f.n()).done;){var m=g.value;if(l(m)){var y=Oo(this.renderTypes.values()),k;try{var C=s(function(){var S=k.value,E=S.type;if(h(E)){var _=r.collections.get(S.collection),b=S.getKey(m),v=Array.isArray(b)?b:[b];if(a)v.forEach(function($){return _.markKeyForGC($)}),p=!0;else{var x=S.getID?S.getID(m):m.id(),w=r._key(E,x),R=r.typeAndIdToKey.get(w);R!==void 0&&!nqe(v,R)&&(d=!0,r.typeAndIdToKey.delete(w),R.forEach(function($){return _.markKeyForGC($)}))}}},"_loop2");for(y.s();!(k=y.n()).done;)C()}catch(S){y.e(S)}finally{y.f()}}}}catch(S){f.e(S)}finally{f.f()}return p&&(this.gc(),d=!1),d},"invalidate")},{key:"gc",value:s(function(){var t=Oo(this.collections.values()),r;try{for(t.s();!(r=t.n()).done;){var i=r.value;i.gc()}}catch(n){t.e(n)}finally{t.f()}},"gc")},{key:"getOrCreateAtlas",value:s(function(t,r,i,n){var a=this.renderTypes.get(r),o=this.collections.get(a.collection),l=!1,u=o.draw(n,i,function(p){a.drawClipped?(p.save(),p.beginPath(),p.rect(0,0,i.w,i.h),p.clip(),a.drawElement(p,t,i,!0,!0),p.restore()):a.drawElement(p,t,i,!0,!0),l=!0});if(l){var h=a.getID?a.getID(t):t.id(),d=this._key(r,h);this.typeAndIdToKey.has(d)?this.typeAndIdToKey.get(d).push(n):this.typeAndIdToKey.set(d,[n])}return u},"getOrCreateAtlas")},{key:"getAtlasInfo",value:s(function(t,r){var i=this,n=this.renderTypes.get(r),a=n.getKey(t),o=Array.isArray(a)?a:[a];return o.map(function(l){var u=n.getBoundingBox(t,l),h=i.getOrCreateAtlas(t,r,u,l),d=h.getOffsets(l),p=Ia(d,2),f=p[0],g=p[1];return{atlas:h,tex:f,tex1:f,tex2:g,bb:u}})},"getAtlasInfo")},{key:"getDebugInfo",value:s(function(){var t=[],r=Oo(this.collections),i;try{for(r.s();!(i=r.n()).done;){var n=Ia(i.value,2),a=n[0],o=n[1],l=o.getCounts(),u=l.keyCount,h=l.atlasCount;t.push({type:a,keyCount:u,atlasCount:h})}}catch(d){r.e(d)}finally{r.f()}return t},"getDebugInfo")}])})(),CHe=(function(){function e(t){Td(this,e),this.globalOptions=t,this.atlasSize=t.webglTexSize,this.maxAtlasesPerBatch=t.webglTexPerBatch,this.batchAtlases=[]}return s(e,"AtlasBatchManager"),kd(e,[{key:"getMaxAtlasesPerBatch",value:s(function(){return this.maxAtlasesPerBatch},"getMaxAtlasesPerBatch")},{key:"getAtlasSize",value:s(function(){return this.atlasSize},"getAtlasSize")},{key:"getIndexArray",value:s(function(){return Array.from({length:this.maxAtlasesPerBatch},function(t,r){return r})},"getIndexArray")},{key:"startBatch",value:s(function(){this.batchAtlases=[]},"startBatch")},{key:"getAtlasCount",value:s(function(){return this.batchAtlases.length},"getAtlasCount")},{key:"getAtlases",value:s(function(){return this.batchAtlases},"getAtlases")},{key:"canAddToCurrentBatch",value:s(function(t){return this.batchAtlases.length===this.maxAtlasesPerBatch?this.batchAtlases.includes(t):!0},"canAddToCurrentBatch")},{key:"getAtlasIndexForBatch",value:s(function(t){var r=this.batchAtlases.indexOf(t);if(r<0){if(this.batchAtlases.length===this.maxAtlasesPerBatch)throw new Error("cannot add more atlases to batch");this.batchAtlases.push(t),r=this.batchAtlases.length-1}return r},"getAtlasIndexForBatch")}])})(),wHe=` + float circleSD(vec2 p, float r) { + return distance(vec2(0), p) - r; // signed distance + } +`,EHe=` + float rectangleSD(vec2 p, vec2 b) { + vec2 d = abs(p)-b; + return distance(vec2(0),max(d,0.0)) + min(max(d.x,d.y),0.0); + } +`,SHe=` + float roundRectangleSD(vec2 p, vec2 b, vec4 cr) { + cr.xy = (p.x > 0.0) ? cr.xy : cr.zw; + cr.x = (p.y > 0.0) ? cr.x : cr.y; + vec2 q = abs(p) - b + cr.x; + return min(max(q.x, q.y), 0.0) + distance(vec2(0), max(q, 0.0)) - cr.x; + } +`,_He=` + float ellipseSD(vec2 p, vec2 ab) { + p = abs( p ); // symmetry + + // find root with Newton solver + vec2 q = ab*(p-ab); + float w = (q.x1.0) ? d : -d; + } +`,hv={SCREEN:{name:"screen",screen:!0},PICKING:{name:"picking",picking:!0}},Ak={IGNORE:1,USE_BB:2},E8=0,ZX=1,JX=2,S8=3,ym=4,Rk=5,dv=6,pv=7,AHe=(function(){function e(t,r,i){Td(this,e),this.r=t,this.gl=r,this.maxInstances=i.webglBatchSize,this.atlasSize=i.webglTexSize,this.bgColor=i.bgColor,this.debug=i.webglDebug,this.batchDebugInfo=[],i.enableWrapping=!0,i.createTextureCanvas=eqe,this.atlasManager=new kHe(t,i),this.batchManager=new CHe(i),this.simpleShapeOptions=new Map,this.program=this._createShaderProgram(hv.SCREEN),this.pickingProgram=this._createShaderProgram(hv.PICKING),this.vao=this._createVAO()}return s(e,"ElementDrawingWebGL"),kd(e,[{key:"addAtlasCollection",value:s(function(t,r){this.atlasManager.addAtlasCollection(t,r)},"addAtlasCollection")},{key:"addTextureAtlasRenderType",value:s(function(t,r){this.atlasManager.addRenderType(t,r)},"addTextureAtlasRenderType")},{key:"addSimpleShapeRenderType",value:s(function(t,r){this.simpleShapeOptions.set(t,r)},"addSimpleShapeRenderType")},{key:"invalidate",value:s(function(t){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=r.type,n=this.atlasManager;return i?n.invalidate(t,{filterType:s(function(a){return a===i},"filterType"),forceRedraw:!0}):n.invalidate(t)},"invalidate")},{key:"gc",value:s(function(){this.atlasManager.gc()},"gc")},{key:"_createShaderProgram",value:s(function(t){var r=this.gl,i=`#version 300 es + precision highp float; + + uniform mat3 uPanZoomMatrix; + uniform int uAtlasSize; + + // instanced + in vec2 aPosition; // a vertex from the unit square + + in mat3 aTransform; // used to transform verticies, eg into a bounding box + in int aVertType; // the type of thing we are rendering + + // the z-index that is output when using picking mode + in vec4 aIndex; + + // For textures + in int aAtlasId; // which shader unit/atlas to use + in vec4 aTex; // x/y/w/h of texture in atlas + + // for edges + in vec4 aPointAPointB; + in vec4 aPointCPointD; + in vec2 aLineWidth; // also used for node border width + + // simple shapes + in vec4 aCornerRadius; // for round-rectangle [top-right, bottom-right, top-left, bottom-left] + in vec4 aColor; // also used for edges + in vec4 aBorderColor; // aLineWidth is used for border width + + // output values passed to the fragment shader + out vec2 vTexCoord; + out vec4 vColor; + out vec2 vPosition; + // flat values are not interpolated + flat out int vAtlasId; + flat out int vVertType; + flat out vec2 vTopRight; + flat out vec2 vBotLeft; + flat out vec4 vCornerRadius; + flat out vec4 vBorderColor; + flat out vec2 vBorderWidth; + flat out vec4 vIndex; + + void main(void) { + int vid = gl_VertexID; + vec2 position = aPosition; // TODO make this a vec3, simplifies some code below + + if(aVertType == `.concat(E8,`) { + float texX = aTex.x; // texture coordinates + float texY = aTex.y; + float texW = aTex.z; + float texH = aTex.w; + + if(vid == 1 || vid == 2 || vid == 4) { + texX += texW; + } + if(vid == 2 || vid == 4 || vid == 5) { + texY += texH; + } + + float d = float(uAtlasSize); + vTexCoord = vec2(texX / d, texY / d); // tex coords must be between 0 and 1 + + gl_Position = vec4(uPanZoomMatrix * aTransform * vec3(position, 1.0), 1.0); + } + else if(aVertType == `).concat(ym," || aVertType == ").concat(pv,` + || aVertType == `).concat(Rk," || aVertType == ").concat(dv,`) { // simple shapes + + // the bounding box is needed by the fragment shader + vBotLeft = (aTransform * vec3(0, 0, 1)).xy; // flat + vTopRight = (aTransform * vec3(1, 1, 1)).xy; // flat + vPosition = (aTransform * vec3(position, 1)).xy; // will be interpolated + + // calculations are done in the fragment shader, just pass these along + vColor = aColor; + vCornerRadius = aCornerRadius; + vBorderColor = aBorderColor; + vBorderWidth = aLineWidth; + + gl_Position = vec4(uPanZoomMatrix * aTransform * vec3(position, 1.0), 1.0); + } + else if(aVertType == `).concat(ZX,`) { + vec2 source = aPointAPointB.xy; + vec2 target = aPointAPointB.zw; + + // adjust the geometry so that the line is centered on the edge + position.y = position.y - 0.5; + + // stretch the unit square into a long skinny rectangle + vec2 xBasis = target - source; + vec2 yBasis = normalize(vec2(-xBasis.y, xBasis.x)); + vec2 point = source + xBasis * position.x + yBasis * aLineWidth[0] * position.y; + + gl_Position = vec4(uPanZoomMatrix * vec3(point, 1.0), 1.0); + vColor = aColor; + } + else if(aVertType == `).concat(JX,`) { + vec2 pointA = aPointAPointB.xy; + vec2 pointB = aPointAPointB.zw; + vec2 pointC = aPointCPointD.xy; + vec2 pointD = aPointCPointD.zw; + + // adjust the geometry so that the line is centered on the edge + position.y = position.y - 0.5; + + vec2 p0, p1, p2, pos; + if(position.x == 0.0) { // The left side of the unit square + p0 = pointA; + p1 = pointB; + p2 = pointC; + pos = position; + } else { // The right side of the unit square, use same approach but flip the geometry upside down + p0 = pointD; + p1 = pointC; + p2 = pointB; + pos = vec2(0.0, -position.y); + } + + vec2 p01 = p1 - p0; + vec2 p12 = p2 - p1; + vec2 p21 = p1 - p2; + + // Find the normal vector. + vec2 tangent = normalize(normalize(p12) + normalize(p01)); + vec2 normal = vec2(-tangent.y, tangent.x); + + // Find the vector perpendicular to p0 -> p1. + vec2 p01Norm = normalize(vec2(-p01.y, p01.x)); + + // Determine the bend direction. + float sigma = sign(dot(p01 + p21, normal)); + float width = aLineWidth[0]; + + if(sign(pos.y) == -sigma) { + // This is an intersecting vertex. Adjust the position so that there's no overlap. + vec2 point = 0.5 * width * normal * -sigma / dot(normal, p01Norm); + gl_Position = vec4(uPanZoomMatrix * vec3(p1 + point, 1.0), 1.0); + } else { + // This is a non-intersecting vertex. Treat it like a mitre join. + vec2 point = 0.5 * width * normal * sigma * dot(normal, p01Norm); + gl_Position = vec4(uPanZoomMatrix * vec3(p1 + point, 1.0), 1.0); + } + + vColor = aColor; + } + else if(aVertType == `).concat(S8,` && vid < 3) { + // massage the first triangle into an edge arrow + if(vid == 0) + position = vec2(-0.15, -0.3); + if(vid == 1) + position = vec2( 0.0, 0.0); + if(vid == 2) + position = vec2( 0.15, -0.3); + + gl_Position = vec4(uPanZoomMatrix * aTransform * vec3(position, 1.0), 1.0); + vColor = aColor; + } + else { + gl_Position = vec4(2.0, 0.0, 0.0, 1.0); // discard vertex by putting it outside webgl clip space + } + + vAtlasId = aAtlasId; + vVertType = aVertType; + vIndex = aIndex; + } + `),n=this.batchManager.getIndexArray(),a=`#version 300 es + precision highp float; + + // declare texture unit for each texture atlas in the batch + `.concat(n.map(function(u){return"uniform sampler2D uTexture".concat(u,";")}).join(` + `),` + + uniform vec4 uBGColor; + uniform float uZoom; + + in vec2 vTexCoord; + in vec4 vColor; + in vec2 vPosition; // model coordinates + + flat in int vAtlasId; + flat in vec4 vIndex; + flat in int vVertType; + flat in vec2 vTopRight; + flat in vec2 vBotLeft; + flat in vec4 vCornerRadius; + flat in vec4 vBorderColor; + flat in vec2 vBorderWidth; + + out vec4 outColor; + + `).concat(wHe,` + `).concat(EHe,` + `).concat(SHe,` + `).concat(_He,` + + vec4 blend(vec4 top, vec4 bot) { // blend colors with premultiplied alpha + return vec4( + top.rgb + (bot.rgb * (1.0 - top.a)), + top.a + (bot.a * (1.0 - top.a)) + ); + } + + vec4 distInterp(vec4 cA, vec4 cB, float d) { // interpolate color using Signed Distance + // scale to the zoom level so that borders don't look blurry when zoomed in + // note 1.5 is an aribitrary value chosen because it looks good + return mix(cA, cB, 1.0 - smoothstep(0.0, 1.5 / uZoom, abs(d))); + } + + void main(void) { + if(vVertType == `).concat(E8,`) { + // look up the texel from the texture unit + `).concat(n.map(function(u){return"if(vAtlasId == ".concat(u,") outColor = texture(uTexture").concat(u,", vTexCoord);")}).join(` + else `),` + } + else if(vVertType == `).concat(S8,`) { + // mimics how canvas renderer uses context.globalCompositeOperation = 'destination-out'; + outColor = blend(vColor, uBGColor); + outColor.a = 1.0; // make opaque, masks out line under arrow + } + else if(vVertType == `).concat(ym,` && vBorderWidth == vec2(0.0)) { // simple rectangle with no border + outColor = vColor; // unit square is already transformed to the rectangle, nothing else needs to be done + } + else if(vVertType == `).concat(ym," || vVertType == ").concat(pv,` + || vVertType == `).concat(Rk," || vVertType == ").concat(dv,`) { // use SDF + + float outerBorder = vBorderWidth[0]; + float innerBorder = vBorderWidth[1]; + float borderPadding = outerBorder * 2.0; + float w = vTopRight.x - vBotLeft.x - borderPadding; + float h = vTopRight.y - vBotLeft.y - borderPadding; + vec2 b = vec2(w/2.0, h/2.0); // half width, half height + vec2 p = vPosition - vec2(vTopRight.x - b[0] - outerBorder, vTopRight.y - b[1] - outerBorder); // translate to center + + float d; // signed distance + if(vVertType == `).concat(ym,`) { + d = rectangleSD(p, b); + } else if(vVertType == `).concat(pv,` && w == h) { + d = circleSD(p, b.x); // faster than ellipse + } else if(vVertType == `).concat(pv,`) { + d = ellipseSD(p, b); + } else { + d = roundRectangleSD(p, b, vCornerRadius.wzyx); + } + + // use the distance to interpolate a color to smooth the edges of the shape, doesn't need multisampling + // we must smooth colors inwards, because we can't change pixels outside the shape's bounding box + if(d > 0.0) { + if(d > outerBorder) { + discard; + } else { + outColor = distInterp(vBorderColor, vec4(0), d - outerBorder); + } + } else { + if(d > innerBorder) { + vec4 outerColor = outerBorder == 0.0 ? vec4(0) : vBorderColor; + vec4 innerBorderColor = blend(vBorderColor, vColor); + outColor = distInterp(innerBorderColor, outerColor, d); + } + else { + vec4 outerColor; + if(innerBorder == 0.0 && outerBorder == 0.0) { + outerColor = vec4(0); + } else if(innerBorder == 0.0) { + outerColor = vBorderColor; + } else { + outerColor = blend(vBorderColor, vColor); + } + outColor = distInterp(vColor, outerColor, d - innerBorder); + } + } + } + else { + outColor = vColor; + } + + `).concat(t.picking?`if(outColor.a == 0.0) discard; + else outColor = vIndex;`:"",` + } + `),o=Jze(r,i,a);o.aPosition=r.getAttribLocation(o,"aPosition"),o.aIndex=r.getAttribLocation(o,"aIndex"),o.aVertType=r.getAttribLocation(o,"aVertType"),o.aTransform=r.getAttribLocation(o,"aTransform"),o.aAtlasId=r.getAttribLocation(o,"aAtlasId"),o.aTex=r.getAttribLocation(o,"aTex"),o.aPointAPointB=r.getAttribLocation(o,"aPointAPointB"),o.aPointCPointD=r.getAttribLocation(o,"aPointCPointD"),o.aLineWidth=r.getAttribLocation(o,"aLineWidth"),o.aColor=r.getAttribLocation(o,"aColor"),o.aCornerRadius=r.getAttribLocation(o,"aCornerRadius"),o.aBorderColor=r.getAttribLocation(o,"aBorderColor"),o.uPanZoomMatrix=r.getUniformLocation(o,"uPanZoomMatrix"),o.uAtlasSize=r.getUniformLocation(o,"uAtlasSize"),o.uBGColor=r.getUniformLocation(o,"uBGColor"),o.uZoom=r.getUniformLocation(o,"uZoom"),o.uTextures=[];for(var l=0;l1&&arguments[1]!==void 0?arguments[1]:hv.SCREEN;this.panZoomMatrix=t,this.renderTarget=r,this.batchDebugInfo=[],this.wrappedCount=0,this.simpleCount=0,this.startBatch()},"startFrame")},{key:"startBatch",value:s(function(){this.instanceCount=0,this.batchManager.startBatch()},"startBatch")},{key:"endFrame",value:s(function(){this.endBatch()},"endFrame")},{key:"_isVisible",value:s(function(t,r){return t.visible()?r&&r.isVisible?r.isVisible(t):!0:!1},"_isVisible")},{key:"drawTexture",value:s(function(t,r,i){var n=this.atlasManager,a=this.batchManager,o=n.getRenderTypeOpts(i);if(this._isVisible(t,o)&&!(t.isEdge()&&!this._isValidEdge(t))){if(this.renderTarget.picking&&o.getTexPickingMode){var l=o.getTexPickingMode(t);if(l===Ak.IGNORE)return;if(l==Ak.USE_BB){this.drawPickingRectangle(t,r,i);return}}var u=n.getAtlasInfo(t,i),h=Oo(u),d;try{for(h.s();!(d=h.n()).done;){var p=d.value,f=p.atlas,g=p.tex1,m=p.tex2;a.canAddToCurrentBatch(f)||this.endBatch();for(var y=a.getAtlasIndexForBatch(f),k=0,C=[[g,!0],[m,!1]];k=this.maxInstances&&this.endBatch()}}}}catch($){h.e($)}finally{h.f()}}},"drawTexture")},{key:"setTransformMatrix",value:s(function(t,r,i,n){var a=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,o=0;if(i.shapeProps&&i.shapeProps.padding&&(o=t.pstyle(i.shapeProps.padding).pfValue),n){var l=n.bb,u=n.tex1,h=n.tex2,d=u.w/(u.w+h.w);a||(d=1-d);var p=this._getAdjustedBB(l,o,a,d);this._applyTransformMatrix(r,p,i,t)}else{var f=i.getBoundingBox(t),g=this._getAdjustedBB(f,o,!0,1);this._applyTransformMatrix(r,g,i,t)}},"setTransformMatrix")},{key:"_applyTransformMatrix",value:s(function(t,r,i,n){var a,o;CY(t);var l=i.getRotation?i.getRotation(n):0;if(l!==0){var u=i.getRotationPoint(n),h=u.x,d=u.y;z3(t,t,[h,d]),wY(t,t,l);var p=i.getRotationOffset(n);a=p.x+(r.xOffset||0),o=p.y+(r.yOffset||0)}else a=r.x1,o=r.y1;z3(t,t,[a,o]),zA(t,t,[r.w,r.h])},"_applyTransformMatrix")},{key:"_getAdjustedBB",value:s(function(t,r,i,n){var a=t.x1,o=t.y1,l=t.w,u=t.h,h=t.yOffset;r&&(a-=r,o-=r,l+=2*r,u+=2*r);var d=0,p=l*n;return i&&n<1?l=p:!i&&n<1&&(d=l-p,a+=d,l=p),{x1:a,y1:o,w:l,h:u,xOffset:d,yOffset:h}},"_getAdjustedBB")},{key:"drawPickingRectangle",value:s(function(t,r,i){var n=this.atlasManager.getRenderTypeOpts(i),a=this.instanceCount;this.vertTypeBuffer.getView(a)[0]=ym;var o=this.indexBuffer.getView(a);Gg(r,o);var l=this.colorBuffer.getView(a);uf([0,0,0],1,l);var u=this.transformBuffer.getMatrixView(a);this.setTransformMatrix(t,u,n),this.simpleCount++,this.instanceCount++,this.instanceCount>=this.maxInstances&&this.endBatch()},"drawPickingRectangle")},{key:"drawNode",value:s(function(t,r,i){var n=this.simpleShapeOptions.get(i);if(this._isVisible(t,n)){var a=n.shapeProps,o=this._getVertTypeForShape(t,a.shape);if(o===void 0||n.isSimple&&!n.isSimple(t,this.renderTarget)){this.drawTexture(t,r,i);return}var l=this.instanceCount;if(this.vertTypeBuffer.getView(l)[0]=o,o===Rk||o===dv){var u=n.getBoundingBox(t),h=this._getCornerRadius(t,a.radius,u),d=this.cornerRadiusBuffer.getView(l);d[0]=h,d[1]=h,d[2]=h,d[3]=h,o===dv&&(d[0]=0,d[2]=0)}var p=this.indexBuffer.getView(l);Gg(r,p);var f=this.renderTarget.picking?1:i==="node-body"?t.effectiveOpacity():1,g=this.renderTarget.picking?1:t.pstyle(a.opacity).value*f,m=t.pstyle(a.color).value,y=this.colorBuffer.getView(l);uf(m,g,y);var k=this.lineWidthBuffer.getView(l);if(k[0]=0,k[1]=0,a.border){var C=t.pstyle("border-width").value;if(C>0){var S=t.pstyle("border-color").value,E=f*t.pstyle("border-opacity").value,_=this.borderColorBuffer.getView(l);uf(S,E,_);var b=t.pstyle("border-position").value;if(b==="inside")k[0]=0,k[1]=-C;else if(b==="outside")k[0]=C,k[1]=0;else{var v=C/2;k[0]=v,k[1]=-v}}}var x=this.transformBuffer.getMatrixView(l);this.setTransformMatrix(t,x,n),this.simpleCount++,this.instanceCount++,this.instanceCount>=this.maxInstances&&this.endBatch()}},"drawNode")},{key:"_getVertTypeForShape",value:s(function(t,r){var i=t.pstyle(r).value;switch(i){case"rectangle":return ym;case"ellipse":return pv;case"roundrectangle":case"round-rectangle":return Rk;case"bottom-round-rectangle":return dv;default:return}},"_getVertTypeForShape")},{key:"_getCornerRadius",value:s(function(t,r,i){var n=i.w,a=i.h;if(t.pstyle(r).value==="auto")return Rd(n,a);var o=t.pstyle(r).pfValue,l=n/2,u=a/2;return Math.min(o,u,l)},"_getCornerRadius")},{key:"drawEdgeArrow",value:s(function(t,r,i){if(t.visible()){var n=t._private.rscratch,a,o,l;if(i==="source"?(a=n.arrowStartX,o=n.arrowStartY,l=n.srcArrowAngle):(a=n.arrowEndX,o=n.arrowEndY,l=n.tgtArrowAngle),!(isNaN(a)||a==null||isNaN(o)||o==null||isNaN(l)||l==null)){var u=t.pstyle(i+"-arrow-shape").value;if(u!=="none"){var h=t.pstyle(i+"-arrow-color").value,d=t.pstyle("opacity").value,p=t.pstyle("line-opacity").value,f=d*p,g=t.pstyle("width").pfValue,m=t.pstyle("arrow-scale").value,y=this.r.getArrowWidth(g,m),k=this.instanceCount,C=this.transformBuffer.getMatrixView(k);CY(C),z3(C,C,[a,o]),zA(C,C,[y,y]),wY(C,C,l),this.vertTypeBuffer.getView(k)[0]=S8;var S=this.indexBuffer.getView(k);Gg(r,S);var E=this.colorBuffer.getView(k);uf(h,f,E),this.instanceCount++,this.instanceCount>=this.maxInstances&&this.endBatch()}}}},"drawEdgeArrow")},{key:"drawEdgeLine",value:s(function(t,r){if(t.visible()){var i=this._getEdgePoints(t);if(i){var n=t.pstyle("opacity").value,a=t.pstyle("line-opacity").value,o=t.pstyle("width").pfValue,l=t.pstyle("line-color").value,u=n*a;if(i.length/2+this.instanceCount>this.maxInstances&&this.endBatch(),i.length==4){var h=this.instanceCount;this.vertTypeBuffer.getView(h)[0]=ZX;var d=this.indexBuffer.getView(h);Gg(r,d);var p=this.colorBuffer.getView(h);uf(l,u,p);var f=this.lineWidthBuffer.getView(h);f[0]=o;var g=this.pointAPointBBuffer.getView(h);g[0]=i[0],g[1]=i[1],g[2]=i[2],g[3]=i[3],this.instanceCount++,this.instanceCount>=this.maxInstances&&this.endBatch()}else for(var m=0;m=this.maxInstances&&this.endBatch()}}}},"drawEdgeLine")},{key:"_isValidEdge",value:s(function(t){var r=t._private.rscratch;return!(r.badLine||r.allpts==null||isNaN(r.allpts[0]))},"_isValidEdge")},{key:"_getEdgePoints",value:s(function(t){var r=t._private.rscratch;if(this._isValidEdge(t)){var i=r.allpts;if(i.length==4)return i;var n=this._getNumSegments(t);return this._getCurveSegmentPoints(i,n)}},"_getEdgePoints")},{key:"_getNumSegments",value:s(function(t){var r=15;return Math.min(Math.max(r,5),this.maxInstances)},"_getNumSegments")},{key:"_getCurveSegmentPoints",value:s(function(t,r){if(t.length==4)return t;for(var i=Array((r+1)*2),n=0;n<=r;n++)if(n==0)i[0]=t[0],i[1]=t[1];else if(n==r)i[n*2]=t[t.length-2],i[n*2+1]=t[t.length-1];else{var a=n/r;this._setCurvePoint(t,a,i,n*2)}return i},"_getCurveSegmentPoints")},{key:"_setCurvePoint",value:s(function(t,r,i,n){if(t.length<=2)i[n]=t[0],i[n+1]=t[1];else{for(var a=Array(t.length-2),o=0;o0}},"isLayerVisible"),l=s(function(d){var p=d.pstyle("text-events").strValue==="yes";return p?Ak.USE_BB:Ak.IGNORE},"getTexPickingMode"),u=s(function(d){var p=d.position(),f=p.x,g=p.y,m=d.outerWidth(),y=d.outerHeight();return{w:m,h:y,x1:f-m/2,y1:g-y/2}},"getBBForSimpleShape");r.drawing.addAtlasCollection("node",{texRows:e.webglTexRowsNodes}),r.drawing.addAtlasCollection("label",{texRows:e.webglTexRows}),r.drawing.addTextureAtlasRenderType("node-body",{collection:"node",getKey:t.getStyleKey,getBoundingBox:t.getElementBox,drawElement:t.drawElement}),r.drawing.addSimpleShapeRenderType("node-body",{getBoundingBox:u,isSimple:iqe,shapeProps:{shape:"shape",color:"background-color",opacity:"background-opacity",radius:"corner-radius",border:!0}}),r.drawing.addSimpleShapeRenderType("node-overlay",{getBoundingBox:u,isVisible:o("overlay"),shapeProps:{shape:"overlay-shape",color:"overlay-color",opacity:"overlay-opacity",padding:"overlay-padding",radius:"overlay-corner-radius"}}),r.drawing.addSimpleShapeRenderType("node-underlay",{getBoundingBox:u,isVisible:o("underlay"),shapeProps:{shape:"underlay-shape",color:"underlay-color",opacity:"underlay-opacity",padding:"underlay-padding",radius:"underlay-corner-radius"}}),r.drawing.addTextureAtlasRenderType("label",{collection:"label",getTexPickingMode:l,getKey:_8(t.getLabelKey,null),getBoundingBox:A8(t.getLabelBox,null),drawClipped:!0,drawElement:t.drawLabel,getRotation:n(null),getRotationPoint:t.getLabelRotationPoint,getRotationOffset:t.getLabelRotationOffset,isVisible:a("label")}),r.drawing.addTextureAtlasRenderType("edge-source-label",{collection:"label",getTexPickingMode:l,getKey:_8(t.getSourceLabelKey,"source"),getBoundingBox:A8(t.getSourceLabelBox,"source"),drawClipped:!0,drawElement:t.drawSourceLabel,getRotation:n("source"),getRotationPoint:t.getSourceLabelRotationPoint,getRotationOffset:t.getSourceLabelRotationOffset,isVisible:a("source-label")}),r.drawing.addTextureAtlasRenderType("edge-target-label",{collection:"label",getTexPickingMode:l,getKey:_8(t.getTargetLabelKey,"target"),getBoundingBox:A8(t.getTargetLabelBox,"target"),drawClipped:!0,drawElement:t.drawTargetLabel,getRotation:n("target"),getRotationPoint:t.getTargetLabelRotationPoint,getRotationOffset:t.getTargetLabelRotationOffset,isVisible:a("target-label")});var h=j2(function(){console.log("garbage collect flag set"),r.data.gc=!0},1e4);r.onUpdateEleCalcs(function(d,p){var f=!1;p&&p.length>0&&(f|=r.drawing.invalidate(p)),f&&h()}),gqe(r)},s(fqe,"getBGColor"),s(EY,"getLabelLines"),_8=s(function(e,t){return function(r){var i=e(r),n=EY(r,t);return n.length>1?n.map(function(a,o){return"".concat(i,"_").concat(o)}):i}},"getStyleKeysForLabel"),A8=s(function(e,t){return function(r,i){var n=e(r);if(typeof i=="string"){var a=i.indexOf("_");if(a>0){var o=Number(i.substring(a+1)),l=EY(r,t),u=n.h/l.length,h=u*o,d=n.y1+h;return{x1:n.x1,w:n.w,y1:d,h:u,yOffset:h}}}return n}},"getBoundingBoxForLabel"),s(gqe,"overrideCanvasRendererFunctions"),s(mqe,"clearWebgl"),s(yqe,"clearCanvas"),s(vqe,"createPanZoomMatrix"),s(SY,"setContextTransform"),s(bqe,"drawSelectionRectangle"),s(xqe,"drawAxes"),s(Tqe,"drawAtlases"),s(kqe,"getPickingIndexes"),s(Cqe,"findNearestElementsWebgl"),s(qA,"drawEle"),s(_Y,"renderWebgl"),Fd={},Fd.drawPolygonPath=function(e,t,r,i,n,a){var o=i/2,l=n/2;e.beginPath&&e.beginPath(),e.moveTo(t+o*a[0],r+l*a[1]);for(var u=1;u0&&o>0){g.clearRect(0,0,a,o),g.globalCompositeOperation="source-over";var m=this.getCachedZSortedEles();if(e.full)g.translate(-i.x1*h,-i.y1*h),g.scale(h,h),this.drawElements(g,m),g.scale(1/h,1/h),g.translate(i.x1*h,i.y1*h);else{var y=t.pan(),k={x:y.x*h,y:y.y*h};h*=t.zoom(),g.translate(k.x,k.y),g.scale(h,h),this.drawElements(g,m),g.scale(1/h,1/h),g.translate(-k.x,-k.y)}e.bg&&(g.globalCompositeOperation="destination-over",g.fillStyle=e.bg,g.rect(0,0,a,o),g.fill())}return f},s(wqe,"b64ToBlob"),s(AY,"b64UriToB64"),s(RY,"output"),fv.png=function(e){return RY(e,this.bufferCanvasImage(e),"image/png")},fv.jpg=function(e){return RY(e,this.bufferCanvasImage(e),"image/jpeg")},nK={},nK.nodeShapeImpl=function(e,t,r,i,n,a,o,l){switch(e){case"ellipse":return this.drawEllipsePath(t,r,i,n,a);case"polygon":return this.drawPolygonPath(t,r,i,n,a,o);case"round-polygon":return this.drawRoundPolygonPath(t,r,i,n,a,o,l);case"roundrectangle":case"round-rectangle":return this.drawRoundRectanglePath(t,r,i,n,a,l);case"cutrectangle":case"cut-rectangle":return this.drawCutRectanglePath(t,r,i,n,a,o,l);case"bottomroundrectangle":case"bottom-round-rectangle":return this.drawBottomRoundRectanglePath(t,r,i,n,a,l);case"barrel":return this.drawBarrelPath(t,r,i,n,a)}},RHe=$Y,xi=$Y.prototype,xi.CANVAS_LAYERS=3,xi.SELECT_BOX=0,xi.DRAG=1,xi.NODE=2,xi.WEBGL=3,xi.CANVAS_TYPES=["2d","2d","2d","webgl2"],xi.BUFFER_COUNT=3,xi.TEXTURE_BUFFER=0,xi.MOTIONBLUR_BUFFER_NODE=1,xi.MOTIONBLUR_BUFFER_DRAG=2,s($Y,"CanvasRenderer"),xi.redrawHint=function(e,t){var r=this;switch(e){case"eles":r.data.canvasNeedsRedraw[xi.NODE]=t;break;case"drag":r.data.canvasNeedsRedraw[xi.DRAG]=t;break;case"select":r.data.canvasNeedsRedraw[xi.SELECT_BOX]=t;break;case"gc":r.data.gc=!0;break}},$He=typeof Path2D<"u",xi.path2dEnabled=function(e){if(e===void 0)return this.pathsEnabled;this.pathsEnabled=!!e},xi.usePaths=function(){return $He&&this.pathsEnabled},xi.setImgSmoothing=function(e,t){e.imageSmoothingEnabled!=null?e.imageSmoothingEnabled=t:(e.webkitImageSmoothingEnabled=t,e.mozImageSmoothingEnabled=t,e.msImageSmoothingEnabled=t)},xi.getImgSmoothing=function(e){return e.imageSmoothingEnabled!=null?e.imageSmoothingEnabled:e.webkitImageSmoothingEnabled||e.mozImageSmoothingEnabled||e.msImageSmoothingEnabled},xi.makeOffscreenCanvas=function(e,t){var r;if((typeof OffscreenCanvas>"u"?"undefined":Wa(OffscreenCanvas))!=="undefined")r=new OffscreenCanvas(e,t);else{var i=this.cy.window(),n=i.document;r=n.createElement("canvas"),r.width=e,r.height=t}return r},[GX,Kc,ah,w8,xf,Pd,po,eK,Fd,fv,nK].forEach(function(e){Ur(xi,e)}),LHe=[{name:"null",impl:gY},{name:"base",impl:zX},{name:"canvas",impl:RHe}],IHe=[{type:"layout",extensions:_Ye},{type:"renderer",extensions:LHe}],aK={},sK={},s(LY,"setExtension"),s(IY,"getExtension"),s(Eqe,"setModule"),s(Sqe,"getModule"),L8=s(function(){if(arguments.length===2)return IY.apply(null,arguments);if(arguments.length===3)return LY.apply(null,arguments);if(arguments.length===4)return Sqe.apply(null,arguments);if(arguments.length===5)return Eqe.apply(null,arguments);Kn("Invalid extension access syntax")},"extension"),nv.prototype.extension=L8,IHe.forEach(function(e){e.extensions.forEach(function(t){LY(e.type,t.name,t.impl)})}),$k=s(function(){if(!(this instanceof $k))return new $k;this.length=0},"Stylesheet"),Tf=$k.prototype,Tf.instanceString=function(){return"stylesheet"},Tf.selector=function(e){var t=this.length++;return this[t]={selector:e,properties:[]},this},Tf.css=function(e,t){var r=this.length-1;if(zr(e))this[r].properties.push({name:e,value:t});else if(Fi(e))for(var i=e,n=Object.keys(i),a=0;a{"use strict";s((function(r,i){typeof e=="object"&&typeof t=="object"?t.exports=i():typeof define=="function"&&define.amd?define([],i):typeof e=="object"?e.layoutBase=i():r.layoutBase=i()}),"webpackUniversalModuleDefinition")(e,function(){return(function(r){var i={};function n(a){if(i[a])return i[a].exports;var o=i[a]={i:a,l:!1,exports:{}};return r[a].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return s(n,"__webpack_require__"),n.m=r,n.c=i,n.i=function(a){return a},n.d=function(a,o,l){n.o(a,o)||Object.defineProperty(a,o,{configurable:!1,enumerable:!0,get:l})},n.n=function(a){var o=a&&a.__esModule?s(function(){return a.default},"getDefault"):s(function(){return a},"getModuleExports");return n.d(o,"a",o),o},n.o=function(a,o){return Object.prototype.hasOwnProperty.call(a,o)},n.p="",n(n.s=26)})([(function(r,i,n){"use strict";function a(){}s(a,"LayoutConstants"),a.QUALITY=1,a.DEFAULT_CREATE_BENDS_AS_NEEDED=!1,a.DEFAULT_INCREMENTAL=!1,a.DEFAULT_ANIMATION_ON_LAYOUT=!0,a.DEFAULT_ANIMATION_DURING_LAYOUT=!1,a.DEFAULT_ANIMATION_PERIOD=50,a.DEFAULT_UNIFORM_LEAF_NODE_SIZES=!1,a.DEFAULT_GRAPH_MARGIN=15,a.NODE_DIMENSIONS_INCLUDE_LABELS=!1,a.SIMPLE_NODE_SIZE=40,a.SIMPLE_NODE_HALF_SIZE=a.SIMPLE_NODE_SIZE/2,a.EMPTY_COMPOUND_NODE_SIZE=40,a.MIN_EDGE_LENGTH=1,a.WORLD_BOUNDARY=1e6,a.INITIAL_WORLD_BOUNDARY=a.WORLD_BOUNDARY/1e3,a.WORLD_CENTER_X=1200,a.WORLD_CENTER_Y=900,r.exports=a}),(function(r,i,n){"use strict";var a=n(2),o=n(8),l=n(9);function u(d,p,f){a.call(this,f),this.isOverlapingSourceAndTarget=!1,this.vGraphObject=f,this.bendpoints=[],this.source=d,this.target=p}s(u,"LEdge"),u.prototype=Object.create(a.prototype);for(var h in a)u[h]=a[h];u.prototype.getSource=function(){return this.source},u.prototype.getTarget=function(){return this.target},u.prototype.isInterGraph=function(){return this.isInterGraph},u.prototype.getLength=function(){return this.length},u.prototype.isOverlapingSourceAndTarget=function(){return this.isOverlapingSourceAndTarget},u.prototype.getBendpoints=function(){return this.bendpoints},u.prototype.getLca=function(){return this.lca},u.prototype.getSourceInLca=function(){return this.sourceInLca},u.prototype.getTargetInLca=function(){return this.targetInLca},u.prototype.getOtherEnd=function(d){if(this.source===d)return this.target;if(this.target===d)return this.source;throw"Node is not incident with this edge"},u.prototype.getOtherEndInGraph=function(d,p){for(var f=this.getOtherEnd(d),g=p.getGraphManager().getRoot();;){if(f.getOwner()==p)return f;if(f.getOwner()==g)break;f=f.getOwner().getParent()}return null},u.prototype.updateLength=function(){var d=new Array(4);this.isOverlapingSourceAndTarget=o.getIntersection(this.target.getRect(),this.source.getRect(),d),this.isOverlapingSourceAndTarget||(this.lengthX=d[0]-d[2],this.lengthY=d[1]-d[3],Math.abs(this.lengthX)<1&&(this.lengthX=l.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=l.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY))},u.prototype.updateLengthSimple=function(){this.lengthX=this.target.getCenterX()-this.source.getCenterX(),this.lengthY=this.target.getCenterY()-this.source.getCenterY(),Math.abs(this.lengthX)<1&&(this.lengthX=l.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=l.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY)},r.exports=u}),(function(r,i,n){"use strict";function a(o){this.vGraphObject=o}s(a,"LGraphObject"),r.exports=a}),(function(r,i,n){"use strict";var a=n(2),o=n(10),l=n(13),u=n(0),h=n(16),d=n(4);function p(g,m,y,k){y==null&&k==null&&(k=m),a.call(this,k),g.graphManager!=null&&(g=g.graphManager),this.estimatedSize=o.MIN_VALUE,this.inclusionTreeDepth=o.MAX_VALUE,this.vGraphObject=k,this.edges=[],this.graphManager=g,y!=null&&m!=null?this.rect=new l(m.x,m.y,y.width,y.height):this.rect=new l}s(p,"LNode"),p.prototype=Object.create(a.prototype);for(var f in a)p[f]=a[f];p.prototype.getEdges=function(){return this.edges},p.prototype.getChild=function(){return this.child},p.prototype.getOwner=function(){return this.owner},p.prototype.getWidth=function(){return this.rect.width},p.prototype.setWidth=function(g){this.rect.width=g},p.prototype.getHeight=function(){return this.rect.height},p.prototype.setHeight=function(g){this.rect.height=g},p.prototype.getCenterX=function(){return this.rect.x+this.rect.width/2},p.prototype.getCenterY=function(){return this.rect.y+this.rect.height/2},p.prototype.getCenter=function(){return new d(this.rect.x+this.rect.width/2,this.rect.y+this.rect.height/2)},p.prototype.getLocation=function(){return new d(this.rect.x,this.rect.y)},p.prototype.getRect=function(){return this.rect},p.prototype.getDiagonal=function(){return Math.sqrt(this.rect.width*this.rect.width+this.rect.height*this.rect.height)},p.prototype.getHalfTheDiagonal=function(){return Math.sqrt(this.rect.height*this.rect.height+this.rect.width*this.rect.width)/2},p.prototype.setRect=function(g,m){this.rect.x=g.x,this.rect.y=g.y,this.rect.width=m.width,this.rect.height=m.height},p.prototype.setCenter=function(g,m){this.rect.x=g-this.rect.width/2,this.rect.y=m-this.rect.height/2},p.prototype.setLocation=function(g,m){this.rect.x=g,this.rect.y=m},p.prototype.moveBy=function(g,m){this.rect.x+=g,this.rect.y+=m},p.prototype.getEdgeListToNode=function(g){var m=[],y,k=this;return k.edges.forEach(function(C){if(C.target==g){if(C.source!=k)throw"Incorrect edge source!";m.push(C)}}),m},p.prototype.getEdgesBetween=function(g){var m=[],y,k=this;return k.edges.forEach(function(C){if(!(C.source==k||C.target==k))throw"Incorrect edge source and/or target";(C.target==g||C.source==g)&&m.push(C)}),m},p.prototype.getNeighborsList=function(){var g=new Set,m=this;return m.edges.forEach(function(y){if(y.source==m)g.add(y.target);else{if(y.target!=m)throw"Incorrect incidency!";g.add(y.source)}}),g},p.prototype.withChildren=function(){var g=new Set,m,y;if(g.add(this),this.child!=null)for(var k=this.child.getNodes(),C=0;Cm&&(this.rect.x-=(this.labelWidth-m)/2,this.setWidth(this.labelWidth)),this.labelHeight>y&&(this.labelPos=="center"?this.rect.y-=(this.labelHeight-y)/2:this.labelPos=="top"&&(this.rect.y-=this.labelHeight-y),this.setHeight(this.labelHeight))}}},p.prototype.getInclusionTreeDepth=function(){if(this.inclusionTreeDepth==o.MAX_VALUE)throw"assert failed";return this.inclusionTreeDepth},p.prototype.transform=function(g){var m=this.rect.x;m>u.WORLD_BOUNDARY?m=u.WORLD_BOUNDARY:m<-u.WORLD_BOUNDARY&&(m=-u.WORLD_BOUNDARY);var y=this.rect.y;y>u.WORLD_BOUNDARY?y=u.WORLD_BOUNDARY:y<-u.WORLD_BOUNDARY&&(y=-u.WORLD_BOUNDARY);var k=new d(m,y),C=g.inverseTransformPoint(k);this.setLocation(C.x,C.y)},p.prototype.getLeft=function(){return this.rect.x},p.prototype.getRight=function(){return this.rect.x+this.rect.width},p.prototype.getTop=function(){return this.rect.y},p.prototype.getBottom=function(){return this.rect.y+this.rect.height},p.prototype.getParent=function(){return this.owner==null?null:this.owner.getParent()},r.exports=p}),(function(r,i,n){"use strict";function a(o,l){o==null&&l==null?(this.x=0,this.y=0):(this.x=o,this.y=l)}s(a,"PointD"),a.prototype.getX=function(){return this.x},a.prototype.getY=function(){return this.y},a.prototype.setX=function(o){this.x=o},a.prototype.setY=function(o){this.y=o},a.prototype.getDifference=function(o){return new DimensionD(this.x-o.x,this.y-o.y)},a.prototype.getCopy=function(){return new a(this.x,this.y)},a.prototype.translate=function(o){return this.x+=o.width,this.y+=o.height,this},r.exports=a}),(function(r,i,n){"use strict";var a=n(2),o=n(10),l=n(0),u=n(6),h=n(3),d=n(1),p=n(13),f=n(12),g=n(11);function m(k,C,S){a.call(this,S),this.estimatedSize=o.MIN_VALUE,this.margin=l.DEFAULT_GRAPH_MARGIN,this.edges=[],this.nodes=[],this.isConnected=!1,this.parent=k,C!=null&&C instanceof u?this.graphManager=C:C!=null&&C instanceof Layout&&(this.graphManager=C.graphManager)}s(m,"LGraph"),m.prototype=Object.create(a.prototype);for(var y in a)m[y]=a[y];m.prototype.getNodes=function(){return this.nodes},m.prototype.getEdges=function(){return this.edges},m.prototype.getGraphManager=function(){return this.graphManager},m.prototype.getParent=function(){return this.parent},m.prototype.getLeft=function(){return this.left},m.prototype.getRight=function(){return this.right},m.prototype.getTop=function(){return this.top},m.prototype.getBottom=function(){return this.bottom},m.prototype.isConnected=function(){return this.isConnected},m.prototype.add=function(k,C,S){if(C==null&&S==null){var E=k;if(this.graphManager==null)throw"Graph has no graph mgr!";if(this.getNodes().indexOf(E)>-1)throw"Node already in graph!";return E.owner=this,this.getNodes().push(E),E}else{var _=k;if(!(this.getNodes().indexOf(C)>-1&&this.getNodes().indexOf(S)>-1))throw"Source or target not in graph!";if(!(C.owner==S.owner&&C.owner==this))throw"Both owners must be this graph!";return C.owner!=S.owner?null:(_.source=C,_.target=S,_.isInterGraph=!1,this.getEdges().push(_),C.edges.push(_),S!=C&&S.edges.push(_),_)}},m.prototype.remove=function(k){var C=k;if(k instanceof h){if(C==null)throw"Node is null!";if(!(C.owner!=null&&C.owner==this))throw"Owner graph is invalid!";if(this.graphManager==null)throw"Owner graph manager is invalid!";for(var S=C.edges.slice(),E,_=S.length,b=0;b<_;b++)E=S[b],E.isInterGraph?this.graphManager.remove(E):E.source.owner.remove(E);var v=this.nodes.indexOf(C);if(v==-1)throw"Node not in owner node list!";this.nodes.splice(v,1)}else if(k instanceof d){var E=k;if(E==null)throw"Edge is null!";if(!(E.source!=null&&E.target!=null))throw"Source and/or target is null!";if(!(E.source.owner!=null&&E.target.owner!=null&&E.source.owner==this&&E.target.owner==this))throw"Source and/or target owner is invalid!";var x=E.source.edges.indexOf(E),w=E.target.edges.indexOf(E);if(!(x>-1&&w>-1))throw"Source and/or target doesn't know this edge!";E.source.edges.splice(x,1),E.target!=E.source&&E.target.edges.splice(w,1);var v=E.source.owner.getEdges().indexOf(E);if(v==-1)throw"Not in owner's edge list!";E.source.owner.getEdges().splice(v,1)}},m.prototype.updateLeftTop=function(){for(var k=o.MAX_VALUE,C=o.MAX_VALUE,S,E,_,b=this.getNodes(),v=b.length,x=0;xS&&(k=S),C>E&&(C=E)}return k==o.MAX_VALUE?null:(b[0].getParent().paddingLeft!=null?_=b[0].getParent().paddingLeft:_=this.margin,this.left=C-_,this.top=k-_,new f(this.left,this.top))},m.prototype.updateBounds=function(k){for(var C=o.MAX_VALUE,S=-o.MAX_VALUE,E=o.MAX_VALUE,_=-o.MAX_VALUE,b,v,x,w,R,$=this.nodes,M=$.length,N=0;Nb&&(C=b),Sx&&(E=x),_b&&(C=b),Sx&&(E=x),_=this.nodes.length){var M=0;S.forEach(function(N){N.owner==k&&M++}),M==this.nodes.length&&(this.isConnected=!0)}},r.exports=m}),(function(r,i,n){"use strict";var a,o=n(1);function l(u){a=n(5),this.layout=u,this.graphs=[],this.edges=[]}s(l,"LGraphManager"),l.prototype.addRoot=function(){var u=this.layout.newGraph(),h=this.layout.newNode(null),d=this.add(u,h);return this.setRootGraph(d),this.rootGraph},l.prototype.add=function(u,h,d,p,f){if(d==null&&p==null&&f==null){if(u==null)throw"Graph is null!";if(h==null)throw"Parent node is null!";if(this.graphs.indexOf(u)>-1)throw"Graph already in this graph mgr!";if(this.graphs.push(u),u.parent!=null)throw"Already has a parent!";if(h.child!=null)throw"Already has a child!";return u.parent=h,h.child=u,u}else{f=d,p=h,d=u;var g=p.getOwner(),m=f.getOwner();if(!(g!=null&&g.getGraphManager()==this))throw"Source not in this graph mgr!";if(!(m!=null&&m.getGraphManager()==this))throw"Target not in this graph mgr!";if(g==m)return d.isInterGraph=!1,g.add(d,p,f);if(d.isInterGraph=!0,d.source=p,d.target=f,this.edges.indexOf(d)>-1)throw"Edge already in inter-graph edge list!";if(this.edges.push(d),!(d.source!=null&&d.target!=null))throw"Edge source and/or target is null!";if(!(d.source.edges.indexOf(d)==-1&&d.target.edges.indexOf(d)==-1))throw"Edge already in source and/or target incidency list!";return d.source.edges.push(d),d.target.edges.push(d),d}},l.prototype.remove=function(u){if(u instanceof a){var h=u;if(h.getGraphManager()!=this)throw"Graph not in this graph mgr";if(!(h==this.rootGraph||h.parent!=null&&h.parent.graphManager==this))throw"Invalid parent node!";var d=[];d=d.concat(h.getEdges());for(var p,f=d.length,g=0;g=u.getRight()?h[0]+=Math.min(u.getX()-l.getX(),l.getRight()-u.getRight()):u.getX()<=l.getX()&&u.getRight()>=l.getRight()&&(h[0]+=Math.min(l.getX()-u.getX(),u.getRight()-l.getRight())),l.getY()<=u.getY()&&l.getBottom()>=u.getBottom()?h[1]+=Math.min(u.getY()-l.getY(),l.getBottom()-u.getBottom()):u.getY()<=l.getY()&&u.getBottom()>=l.getBottom()&&(h[1]+=Math.min(l.getY()-u.getY(),u.getBottom()-l.getBottom()));var f=Math.abs((u.getCenterY()-l.getCenterY())/(u.getCenterX()-l.getCenterX()));u.getCenterY()===l.getCenterY()&&u.getCenterX()===l.getCenterX()&&(f=1);var g=f*h[0],m=h[1]/f;h[0]g)return h[0]=d,h[1]=y,h[2]=f,h[3]=$,!1;if(pf)return h[0]=m,h[1]=p,h[2]=w,h[3]=g,!1;if(df?(h[0]=C,h[1]=S,I=!0):(h[0]=k,h[1]=y,I=!0):F===B&&(d>f?(h[0]=m,h[1]=y,I=!0):(h[0]=E,h[1]=S,I=!0)),-O===B?f>d?(h[2]=R,h[3]=$,z=!0):(h[2]=w,h[3]=x,z=!0):O===B&&(f>d?(h[2]=v,h[3]=x,z=!0):(h[2]=M,h[3]=$,z=!0)),I&&z)return!1;if(d>f?p>g?(Y=this.getCardinalDirection(F,B,4),U=this.getCardinalDirection(O,B,2)):(Y=this.getCardinalDirection(-F,B,3),U=this.getCardinalDirection(-O,B,1)):p>g?(Y=this.getCardinalDirection(-F,B,1),U=this.getCardinalDirection(-O,B,3)):(Y=this.getCardinalDirection(F,B,2),U=this.getCardinalDirection(O,B,4)),!I)switch(Y){case 1:J=y,H=d+-b/B,h[0]=H,h[1]=J;break;case 2:H=E,J=p+_*B,h[0]=H,h[1]=J;break;case 3:J=S,H=d+b/B,h[0]=H,h[1]=J;break;case 4:H=C,J=p+-_*B,h[0]=H,h[1]=J;break}if(!z)switch(U){case 1:ie=x,te=f+-j/B,h[2]=te,h[3]=ie;break;case 2:te=M,ie=g+N*B,h[2]=te,h[3]=ie;break;case 3:ie=$,te=f+j/B,h[2]=te,h[3]=ie;break;case 4:te=R,ie=g+-N*B,h[2]=te,h[3]=ie;break}}return!1},o.getCardinalDirection=function(l,u,h){return l>u?h:1+h%4},o.getIntersection=function(l,u,h,d){if(d==null)return this.getIntersection2(l,u,h);var p=l.x,f=l.y,g=u.x,m=u.y,y=h.x,k=h.y,C=d.x,S=d.y,E=void 0,_=void 0,b=void 0,v=void 0,x=void 0,w=void 0,R=void 0,$=void 0,M=void 0;return b=m-f,x=p-g,R=g*f-p*m,v=S-k,w=y-C,$=C*k-y*S,M=b*w-v*x,M===0?null:(E=(x*$-w*R)/M,_=(v*R-b*$)/M,new a(E,_))},o.angleOfVector=function(l,u,h,d){var p=void 0;return l!==h?(p=Math.atan((d-u)/(h-l)),h0?1:o<0?-1:0},a.floor=function(o){return o<0?Math.ceil(o):Math.floor(o)},a.ceil=function(o){return o<0?Math.floor(o):Math.ceil(o)},r.exports=a}),(function(r,i,n){"use strict";function a(){}s(a,"Integer"),a.MAX_VALUE=2147483647,a.MIN_VALUE=-2147483648,r.exports=a}),(function(r,i,n){"use strict";var a=(function(){function p(f,g){for(var m=0;m"u"?"undefined":a(l);return l==null||u!="object"&&u!="function"},r.exports=o}),(function(r,i,n){"use strict";function a(y){if(Array.isArray(y)){for(var k=0,C=Array(y.length);k0&&k;){for(b.push(x[0]);b.length>0&&k;){var w=b[0];b.splice(0,1),_.add(w);for(var R=w.getEdges(),E=0;E-1&&x.splice(j,1)}_=new Set,v=new Map}}return y},m.prototype.createDummyNodesForBendpoints=function(y){for(var k=[],C=y.source,S=this.graphManager.calcLowestCommonAncestor(y.source,y.target),E=0;E0){for(var S=this.edgeToDummyNodes.get(C),E=0;E=0&&k.splice($,1);var M=v.getNeighborsList();M.forEach(function(I){if(C.indexOf(I)<0){var z=S.get(I),F=z-1;F==1&&w.push(I),S.set(I,F)}})}C=C.concat(w),(k.length==1||k.length==2)&&(E=!0,_=k[0])}return _},m.prototype.setGraphManager=function(y){this.graphManager=y},r.exports=m}),(function(r,i,n){"use strict";function a(){}s(a,"RandomSeed"),a.seed=1,a.x=0,a.nextDouble=function(){return a.x=Math.sin(a.seed++)*1e4,a.x-Math.floor(a.x)},r.exports=a}),(function(r,i,n){"use strict";var a=n(4);function o(l,u){this.lworldOrgX=0,this.lworldOrgY=0,this.ldeviceOrgX=0,this.ldeviceOrgY=0,this.lworldExtX=1,this.lworldExtY=1,this.ldeviceExtX=1,this.ldeviceExtY=1}s(o,"Transform"),o.prototype.getWorldOrgX=function(){return this.lworldOrgX},o.prototype.setWorldOrgX=function(l){this.lworldOrgX=l},o.prototype.getWorldOrgY=function(){return this.lworldOrgY},o.prototype.setWorldOrgY=function(l){this.lworldOrgY=l},o.prototype.getWorldExtX=function(){return this.lworldExtX},o.prototype.setWorldExtX=function(l){this.lworldExtX=l},o.prototype.getWorldExtY=function(){return this.lworldExtY},o.prototype.setWorldExtY=function(l){this.lworldExtY=l},o.prototype.getDeviceOrgX=function(){return this.ldeviceOrgX},o.prototype.setDeviceOrgX=function(l){this.ldeviceOrgX=l},o.prototype.getDeviceOrgY=function(){return this.ldeviceOrgY},o.prototype.setDeviceOrgY=function(l){this.ldeviceOrgY=l},o.prototype.getDeviceExtX=function(){return this.ldeviceExtX},o.prototype.setDeviceExtX=function(l){this.ldeviceExtX=l},o.prototype.getDeviceExtY=function(){return this.ldeviceExtY},o.prototype.setDeviceExtY=function(l){this.ldeviceExtY=l},o.prototype.transformX=function(l){var u=0,h=this.lworldExtX;return h!=0&&(u=this.ldeviceOrgX+(l-this.lworldOrgX)*this.ldeviceExtX/h),u},o.prototype.transformY=function(l){var u=0,h=this.lworldExtY;return h!=0&&(u=this.ldeviceOrgY+(l-this.lworldOrgY)*this.ldeviceExtY/h),u},o.prototype.inverseTransformX=function(l){var u=0,h=this.ldeviceExtX;return h!=0&&(u=this.lworldOrgX+(l-this.ldeviceOrgX)*this.lworldExtX/h),u},o.prototype.inverseTransformY=function(l){var u=0,h=this.ldeviceExtY;return h!=0&&(u=this.lworldOrgY+(l-this.ldeviceOrgY)*this.lworldExtY/h),u},o.prototype.inverseTransformPoint=function(l){var u=new a(this.inverseTransformX(l.x),this.inverseTransformY(l.y));return u},r.exports=o}),(function(r,i,n){"use strict";function a(g){if(Array.isArray(g)){for(var m=0,y=Array(g.length);ml.ADAPTATION_LOWER_NODE_LIMIT&&(this.coolingFactor=Math.max(this.coolingFactor*l.COOLING_ADAPTATION_FACTOR,this.coolingFactor-(g-l.ADAPTATION_LOWER_NODE_LIMIT)/(l.ADAPTATION_UPPER_NODE_LIMIT-l.ADAPTATION_LOWER_NODE_LIMIT)*this.coolingFactor*(1-l.COOLING_ADAPTATION_FACTOR))),this.maxNodeDisplacement=l.MAX_NODE_DISPLACEMENT_INCREMENTAL):(g>l.ADAPTATION_LOWER_NODE_LIMIT?this.coolingFactor=Math.max(l.COOLING_ADAPTATION_FACTOR,1-(g-l.ADAPTATION_LOWER_NODE_LIMIT)/(l.ADAPTATION_UPPER_NODE_LIMIT-l.ADAPTATION_LOWER_NODE_LIMIT)*(1-l.COOLING_ADAPTATION_FACTOR)):this.coolingFactor=1,this.initialCoolingFactor=this.coolingFactor,this.maxNodeDisplacement=l.MAX_NODE_DISPLACEMENT),this.maxIterations=Math.max(this.getAllNodes().length*5,this.maxIterations),this.totalDisplacementThreshold=this.displacementThresholdPerNode*this.getAllNodes().length,this.repulsionRange=this.calcRepulsionRange()},p.prototype.calcSpringForces=function(){for(var g=this.getAllEdges(),m,y=0;y0&&arguments[0]!==void 0?arguments[0]:!0,m=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!1,y,k,C,S,E=this.getAllNodes(),_;if(this.useFRGridVariant)for(this.totalIterations%l.GRID_CALCULATION_CHECK_PERIOD==1&&g&&this.updateGrid(),_=new Set,y=0;yb||_>b)&&(g.gravitationForceX=-this.gravityConstant*C,g.gravitationForceY=-this.gravityConstant*S)):(b=m.getEstimatedSize()*this.compoundGravityRangeFactor,(E>b||_>b)&&(g.gravitationForceX=-this.gravityConstant*C*this.compoundGravityConstant,g.gravitationForceY=-this.gravityConstant*S*this.compoundGravityConstant))},p.prototype.isConverged=function(){var g,m=!1;return this.totalIterations>this.maxIterations/3&&(m=Math.abs(this.totalDisplacement-this.oldTotalDisplacement)<2),g=this.totalDisplacement=E.length||b>=E[0].length)){for(var v=0;vd},"_defaultCompareFunction")}]),h})();r.exports=u}),(function(r,i,n){"use strict";var a=(function(){function u(h,d){for(var p=0;p2&&arguments[2]!==void 0?arguments[2]:1,f=arguments.length>3&&arguments[3]!==void 0?arguments[3]:-1,g=arguments.length>4&&arguments[4]!==void 0?arguments[4]:-1;o(this,u),this.sequence1=h,this.sequence2=d,this.match_score=p,this.mismatch_penalty=f,this.gap_penalty=g,this.iMax=h.length+1,this.jMax=d.length+1,this.grid=new Array(this.iMax);for(var m=0;m=0;h--){var d=this.listeners[h];d.event===l&&d.callback===u&&this.listeners.splice(h,1)}},o.emit=function(l,u){for(var h=0;h{"use strict";s((function(r,i){typeof e=="object"&&typeof t=="object"?t.exports=i(NHe()):typeof define=="function"&&define.amd?define(["layout-base"],i):typeof e=="object"?e.coseBase=i(NHe()):r.coseBase=i(r.layoutBase)}),"webpackUniversalModuleDefinition")(e,function(r){return(function(i){var n={};function a(o){if(n[o])return n[o].exports;var l=n[o]={i:o,l:!1,exports:{}};return i[o].call(l.exports,l,l.exports,a),l.l=!0,l.exports}return s(a,"__webpack_require__"),a.m=i,a.c=n,a.i=function(o){return o},a.d=function(o,l,u){a.o(o,l)||Object.defineProperty(o,l,{configurable:!1,enumerable:!0,get:u})},a.n=function(o){var l=o&&o.__esModule?s(function(){return o.default},"getDefault"):s(function(){return o},"getModuleExports");return a.d(l,"a",l),l},a.o=function(o,l){return Object.prototype.hasOwnProperty.call(o,l)},a.p="",a(a.s=7)})([(function(i,n){i.exports=r}),(function(i,n,a){"use strict";var o=a(0).FDLayoutConstants;function l(){}s(l,"CoSEConstants");for(var u in o)l[u]=o[u];l.DEFAULT_USE_MULTI_LEVEL_SCALING=!1,l.DEFAULT_RADIAL_SEPARATION=o.DEFAULT_EDGE_LENGTH,l.DEFAULT_COMPONENT_SEPERATION=60,l.TILE=!0,l.TILING_PADDING_VERTICAL=10,l.TILING_PADDING_HORIZONTAL=10,l.TREE_REDUCTION_ON_INCREMENTAL=!1,i.exports=l}),(function(i,n,a){"use strict";var o=a(0).FDLayoutEdge;function l(h,d,p){o.call(this,h,d,p)}s(l,"CoSEEdge"),l.prototype=Object.create(o.prototype);for(var u in o)l[u]=o[u];i.exports=l}),(function(i,n,a){"use strict";var o=a(0).LGraph;function l(h,d,p){o.call(this,h,d,p)}s(l,"CoSEGraph"),l.prototype=Object.create(o.prototype);for(var u in o)l[u]=o[u];i.exports=l}),(function(i,n,a){"use strict";var o=a(0).LGraphManager;function l(h){o.call(this,h)}s(l,"CoSEGraphManager"),l.prototype=Object.create(o.prototype);for(var u in o)l[u]=o[u];i.exports=l}),(function(i,n,a){"use strict";var o=a(0).FDLayoutNode,l=a(0).IMath;function u(d,p,f,g){o.call(this,d,p,f,g)}s(u,"CoSENode"),u.prototype=Object.create(o.prototype);for(var h in o)u[h]=o[h];u.prototype.move=function(){var d=this.graphManager.getLayout();this.displacementX=d.coolingFactor*(this.springForceX+this.repulsionForceX+this.gravitationForceX)/this.noOfChildren,this.displacementY=d.coolingFactor*(this.springForceY+this.repulsionForceY+this.gravitationForceY)/this.noOfChildren,Math.abs(this.displacementX)>d.coolingFactor*d.maxNodeDisplacement&&(this.displacementX=d.coolingFactor*d.maxNodeDisplacement*l.sign(this.displacementX)),Math.abs(this.displacementY)>d.coolingFactor*d.maxNodeDisplacement&&(this.displacementY=d.coolingFactor*d.maxNodeDisplacement*l.sign(this.displacementY)),this.child==null?this.moveBy(this.displacementX,this.displacementY):this.child.getNodes().length==0?this.moveBy(this.displacementX,this.displacementY):this.propogateDisplacementToChildren(this.displacementX,this.displacementY),d.totalDisplacement+=Math.abs(this.displacementX)+Math.abs(this.displacementY),this.springForceX=0,this.springForceY=0,this.repulsionForceX=0,this.repulsionForceY=0,this.gravitationForceX=0,this.gravitationForceY=0,this.displacementX=0,this.displacementY=0},u.prototype.propogateDisplacementToChildren=function(d,p){for(var f=this.getChild().getNodes(),g,m=0;m0)this.positionNodesRadially(R);else{this.reduceTrees(),this.graphManager.resetAllNodesToApplyGravitation();var x=new Set(this.getAllNodes()),w=this.nodesWithGravity.filter(function(N){return x.has(N)});this.graphManager.setAllNodesToApplyGravitation(w),this.positionNodesRandomly()}}return this.initSpringEmbedder(),this.runSpringEmbedder(),!0},b.prototype.tick=function(){if(this.totalIterations++,this.totalIterations===this.maxIterations&&!this.isTreeGrowing&&!this.isGrowthFinished)if(this.prunedNodesAll.length>0)this.isTreeGrowing=!0;else return!0;if(this.totalIterations%f.CONVERGENCE_CHECK_PERIOD==0&&!this.isTreeGrowing&&!this.isGrowthFinished){if(this.isConverged())if(this.prunedNodesAll.length>0)this.isTreeGrowing=!0;else return!0;this.coolingCycle++,this.layoutQuality==0?this.coolingAdjuster=this.coolingCycle:this.layoutQuality==1&&(this.coolingAdjuster=this.coolingCycle/3),this.coolingFactor=Math.max(this.initialCoolingFactor-Math.pow(this.coolingCycle,Math.log(100*(this.initialCoolingFactor-this.finalTemperature))/Math.log(this.maxCoolingCycle))/100*this.coolingAdjuster,this.finalTemperature),this.animationPeriod=Math.ceil(this.initialAnimationPeriod*Math.sqrt(this.coolingFactor))}if(this.isTreeGrowing){if(this.growTreeIterations%10==0)if(this.prunedNodesAll.length>0){this.graphManager.updateBounds(),this.updateGrid(),this.growTree(this.prunedNodesAll),this.graphManager.resetAllNodesToApplyGravitation();var x=new Set(this.getAllNodes()),w=this.nodesWithGravity.filter(function(M){return x.has(M)});this.graphManager.setAllNodesToApplyGravitation(w),this.graphManager.updateBounds(),this.updateGrid(),this.coolingFactor=f.DEFAULT_COOLING_FACTOR_INCREMENTAL}else this.isTreeGrowing=!1,this.isGrowthFinished=!0;this.growTreeIterations++}if(this.isGrowthFinished){if(this.isConverged())return!0;this.afterGrowthIterations%10==0&&(this.graphManager.updateBounds(),this.updateGrid()),this.coolingFactor=f.DEFAULT_COOLING_FACTOR_INCREMENTAL*((100-this.afterGrowthIterations)/100),this.afterGrowthIterations++}var R=!this.isTreeGrowing&&!this.isGrowthFinished,$=this.growTreeIterations%10==1&&this.isTreeGrowing||this.afterGrowthIterations%10==1&&this.isGrowthFinished;return this.totalDisplacement=0,this.graphManager.updateBounds(),this.calcSpringForces(),this.calcRepulsionForces(R,$),this.calcGravitationalForces(),this.moveNodes(),this.animate(),!1},b.prototype.getPositionsData=function(){for(var x=this.graphManager.getAllNodes(),w={},R=0;R1){var I;for(I=0;I$&&($=Math.floor(j.y)),N=Math.floor(j.x+p.DEFAULT_COMPONENT_SEPERATION)}this.transform(new y(g.WORLD_CENTER_X-j.x/2,g.WORLD_CENTER_Y-j.y/2))},b.radialLayout=function(x,w,R){var $=Math.max(this.maxDiagonalInTree(x),p.DEFAULT_RADIAL_SEPARATION);b.branchRadialLayout(w,null,0,359,0,$);var M=E.calculateBounds(x),N=new _;N.setDeviceOrgX(M.getMinX()),N.setDeviceOrgY(M.getMinY()),N.setWorldOrgX(R.x),N.setWorldOrgY(R.y);for(var j=0;j1;){var ue=ie[0];ie.splice(0,1);var se=Y.indexOf(ue);se>=0&&Y.splice(se,1),J--,U--}w!=null?te=(Y.indexOf(ie[0])+1)%J:te=0;for(var le=Math.abs($-R)/U,Te=te;H!=U;Te=++Te%J){var oe=Y[Te].getOtherEnd(x);if(oe!=w){var fe=(R+H*le)%360,ge=(fe+le)%360;b.branchRadialLayout(oe,x,fe,ge,M+N,N),H++}}},b.maxDiagonalInTree=function(x){for(var w=C.MIN_VALUE,R=0;Rw&&(w=M)}return w},b.prototype.calcRepulsionRange=function(){return 2*(this.level+1)*this.idealEdgeLength},b.prototype.groupZeroDegreeMembers=function(){var x=this,w={};this.memberGroups={},this.idToDummyNode={};for(var R=[],$=this.graphManager.getAllNodes(),M=0;M<$.length;M++){var N=$[M],j=N.getParent();this.getNodeDegreeWithChildren(N)===0&&(j.id==null||!this.getToBeTiled(j))&&R.push(N)}for(var M=0;M"u"&&(w[I]=[]),w[I]=w[I].concat(N)}Object.keys(w).forEach(function(z){if(w[z].length>1){var F="DummyCompound_"+z;x.memberGroups[F]=w[z];var O=w[z][0].getParent(),B=new h(x.graphManager);B.id=F,B.paddingLeft=O.paddingLeft||0,B.paddingRight=O.paddingRight||0,B.paddingBottom=O.paddingBottom||0,B.paddingTop=O.paddingTop||0,x.idToDummyNode[F]=B;var Y=x.getGraphManager().add(x.newGraph(),B),U=O.getChild();U.add(B);for(var H=0;H=0;x--){var w=this.compoundOrder[x],R=w.id,$=w.paddingLeft,M=w.paddingTop;this.adjustLocations(this.tiledMemberPack[R],w.rect.x,w.rect.y,$,M)}},b.prototype.repopulateZeroDegreeMembers=function(){var x=this,w=this.tiledZeroDegreePack;Object.keys(w).forEach(function(R){var $=x.idToDummyNode[R],M=$.paddingLeft,N=$.paddingTop;x.adjustLocations(w[R],$.rect.x,$.rect.y,M,N)})},b.prototype.getToBeTiled=function(x){var w=x.id;if(this.toBeTiled[w]!=null)return this.toBeTiled[w];var R=x.getChild();if(R==null)return this.toBeTiled[w]=!1,!1;for(var $=R.getNodes(),M=0;M<$.length;M++){var N=$[M];if(this.getNodeDegree(N)>0)return this.toBeTiled[w]=!1,!1;if(N.getChild()==null){this.toBeTiled[N.id]=!1;continue}if(!this.getToBeTiled(N))return this.toBeTiled[w]=!1,!1}return this.toBeTiled[w]=!0,!0},b.prototype.getNodeDegree=function(x){for(var w=x.id,R=x.getEdges(),$=0,M=0;Mz&&(z=O.rect.height)}R+=z+x.verticalPadding}},b.prototype.tileCompoundMembers=function(x,w){var R=this;this.tiledMemberPack=[],Object.keys(x).forEach(function($){var M=w[$];R.tiledMemberPack[$]=R.tileNodes(x[$],M.paddingLeft+M.paddingRight),M.rect.width=R.tiledMemberPack[$].width,M.rect.height=R.tiledMemberPack[$].height})},b.prototype.tileNodes=function(x,w){var R=p.TILING_PADDING_VERTICAL,$=p.TILING_PADDING_HORIZONTAL,M={rows:[],rowWidth:[],rowHeight:[],width:0,height:w,verticalPadding:R,horizontalPadding:$};x.sort(function(I,z){return I.rect.width*I.rect.height>z.rect.width*z.rect.height?-1:I.rect.width*I.rect.height0&&(j+=x.horizontalPadding),x.rowWidth[R]=j,x.width0&&(I+=x.verticalPadding);var z=0;I>x.rowHeight[R]&&(z=x.rowHeight[R],x.rowHeight[R]=I,z=x.rowHeight[R]-z),x.height+=z,x.rows[R].push(w)},b.prototype.getShortestRowIndex=function(x){for(var w=-1,R=Number.MAX_VALUE,$=0;$R&&(w=$,R=x.rowWidth[$]);return w},b.prototype.canAddHorizontal=function(x,w,R){var $=this.getShortestRowIndex(x);if($<0)return!0;var M=x.rowWidth[$];if(M+x.horizontalPadding+w<=x.width)return!0;var N=0;x.rowHeight[$]0&&(N=R+x.verticalPadding-x.rowHeight[$]);var j;x.width-M>=w+x.horizontalPadding?j=(x.height+N)/(M+w+x.horizontalPadding):j=(x.height+N)/x.width,N=R+x.verticalPadding;var I;return x.widthN&&w!=R){$.splice(-1,1),x.rows[R].push(M),x.rowWidth[w]=x.rowWidth[w]-N,x.rowWidth[R]=x.rowWidth[R]+N,x.width=x.rowWidth[instance.getLongestRowIndex(x)];for(var j=Number.MIN_VALUE,I=0;I<$.length;I++)$[I].height>j&&(j=$[I].height);w>0&&(j+=x.verticalPadding);var z=x.rowHeight[w]+x.rowHeight[R];x.rowHeight[w]=j,x.rowHeight[R]0)for(var U=M;U<=N;U++)Y[0]+=this.grid[U][j-1].length+this.grid[U][j].length-1;if(N0)for(var U=j;U<=I;U++)Y[3]+=this.grid[M-1][U].length+this.grid[M][U].length-1;for(var H=C.MAX_VALUE,J,te,ie=0;ie{"use strict";s((function(r,i){typeof e=="object"&&typeof t=="object"?t.exports=i(OHe()):typeof define=="function"&&define.amd?define(["cose-base"],i):typeof e=="object"?e.cytoscapeCoseBilkent=i(OHe()):r.cytoscapeCoseBilkent=i(r.coseBase)}),"webpackUniversalModuleDefinition")(e,function(r){return(function(i){var n={};function a(o){if(n[o])return n[o].exports;var l=n[o]={i:o,l:!1,exports:{}};return i[o].call(l.exports,l,l.exports,a),l.l=!0,l.exports}return s(a,"__webpack_require__"),a.m=i,a.c=n,a.i=function(o){return o},a.d=function(o,l,u){a.o(o,l)||Object.defineProperty(o,l,{configurable:!1,enumerable:!0,get:u})},a.n=function(o){var l=o&&o.__esModule?s(function(){return o.default},"getDefault"):s(function(){return o},"getModuleExports");return a.d(l,"a",l),l},a.o=function(o,l){return Object.prototype.hasOwnProperty.call(o,l)},a.p="",a(a.s=1)})([(function(i,n){i.exports=r}),(function(i,n,a){"use strict";var o=a(0).layoutBase.LayoutConstants,l=a(0).layoutBase.FDLayoutConstants,u=a(0).CoSEConstants,h=a(0).CoSELayout,d=a(0).CoSENode,p=a(0).layoutBase.PointD,f=a(0).layoutBase.DimensionD,g={ready:s(function(){},"ready"),stop:s(function(){},"stop"),quality:"default",nodeDimensionsIncludeLabels:!1,refresh:30,fit:!0,padding:10,randomize:!0,nodeRepulsion:4500,idealEdgeLength:50,edgeElasticity:.45,nestingFactor:.1,gravity:.25,numIter:2500,tile:!0,animate:"end",animationDuration:500,tilingPaddingVertical:10,tilingPaddingHorizontal:10,gravityRangeCompound:1.5,gravityCompound:1,gravityRange:3.8,initialEnergyOnIncremental:.5};function m(S,E){var _={};for(var b in S)_[b]=S[b];for(var b in E)_[b]=E[b];return _}s(m,"extend");function y(S){this.options=m(g,S),k(this.options)}s(y,"_CoSELayout");var k=s(function(S){S.nodeRepulsion!=null&&(u.DEFAULT_REPULSION_STRENGTH=l.DEFAULT_REPULSION_STRENGTH=S.nodeRepulsion),S.idealEdgeLength!=null&&(u.DEFAULT_EDGE_LENGTH=l.DEFAULT_EDGE_LENGTH=S.idealEdgeLength),S.edgeElasticity!=null&&(u.DEFAULT_SPRING_STRENGTH=l.DEFAULT_SPRING_STRENGTH=S.edgeElasticity),S.nestingFactor!=null&&(u.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR=l.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR=S.nestingFactor),S.gravity!=null&&(u.DEFAULT_GRAVITY_STRENGTH=l.DEFAULT_GRAVITY_STRENGTH=S.gravity),S.numIter!=null&&(u.MAX_ITERATIONS=l.MAX_ITERATIONS=S.numIter),S.gravityRange!=null&&(u.DEFAULT_GRAVITY_RANGE_FACTOR=l.DEFAULT_GRAVITY_RANGE_FACTOR=S.gravityRange),S.gravityCompound!=null&&(u.DEFAULT_COMPOUND_GRAVITY_STRENGTH=l.DEFAULT_COMPOUND_GRAVITY_STRENGTH=S.gravityCompound),S.gravityRangeCompound!=null&&(u.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR=l.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR=S.gravityRangeCompound),S.initialEnergyOnIncremental!=null&&(u.DEFAULT_COOLING_FACTOR_INCREMENTAL=l.DEFAULT_COOLING_FACTOR_INCREMENTAL=S.initialEnergyOnIncremental),S.quality=="draft"?o.QUALITY=0:S.quality=="proof"?o.QUALITY=2:o.QUALITY=1,u.NODE_DIMENSIONS_INCLUDE_LABELS=l.NODE_DIMENSIONS_INCLUDE_LABELS=o.NODE_DIMENSIONS_INCLUDE_LABELS=S.nodeDimensionsIncludeLabels,u.DEFAULT_INCREMENTAL=l.DEFAULT_INCREMENTAL=o.DEFAULT_INCREMENTAL=!S.randomize,u.ANIMATE=l.ANIMATE=o.ANIMATE=S.animate,u.TILE=S.tile,u.TILING_PADDING_VERTICAL=typeof S.tilingPaddingVertical=="function"?S.tilingPaddingVertical.call():S.tilingPaddingVertical,u.TILING_PADDING_HORIZONTAL=typeof S.tilingPaddingHorizontal=="function"?S.tilingPaddingHorizontal.call():S.tilingPaddingHorizontal},"getUserOptions");y.prototype.run=function(){var S,E,_=this.options,b=this.idToLNode={},v=this.layout=new h,x=this;x.stopped=!1,this.cy=this.options.cy,this.cy.trigger({type:"layoutstart",layout:this});var w=v.newGraphManager();this.gm=w;var R=this.options.eles.nodes(),$=this.options.eles.edges();this.root=w.addRoot(),this.processChildrenList(this.root,this.getTopMostNodes(R),v);for(var M=0;M<$.length;M++){var N=$[M],j=this.idToLNode[N.data("source")],I=this.idToLNode[N.data("target")];if(j!==I&&j.getEdgesBetween(I).length==0){var z=w.add(v.newEdge(),j,I);z.id=N.id()}}var F=s(function(B,Y){typeof B=="number"&&(B=Y);var U=B.data("id"),H=x.idToLNode[U];return{x:H.getRect().getCenterX(),y:H.getRect().getCenterY()}},"getPositions"),O=s(function B(){for(var Y=s(function(){_.fit&&_.cy.fit(_.eles,_.padding),S||(S=!0,x.cy.one("layoutready",_.ready),x.cy.trigger({type:"layoutready",layout:x}))},"afterReposition"),U=x.options.refresh,H,J=0;J0){var I;I=_.getGraphManager().add(_.newGraph(),R),this.processChildrenList(I,w,_)}}},y.prototype.stop=function(){return this.stopped=!0,this};var C=s(function(S){S("layout","cose-bilkent",y)},"register");typeof cytoscape<"u"&&C(cytoscape),i.exports=C})])})});function BHe(e,t){e.forEach(r=>{let i={id:r.id,labelText:r.label,height:r.height,width:r.width,padding:r.padding??0};Object.keys(r).forEach(n=>{["id","label","height","width","padding","x","y"].includes(n)||(i[n]=r[n])}),t.add({group:"nodes",data:i,position:{x:r.x??0,y:r.y??0}})})}function PHe(e,t){e.forEach(r=>{let i={id:r.id,source:r.start,target:r.end};Object.keys(r).forEach(n=>{["id","start","end"].includes(n)||(i[n]=r[n])}),t.add({group:"edges",data:i})})}function FHe(e){return new Promise(t=>{let r=zt("body").append("div").attr("id","cy").attr("style","display:none"),i=Qc({container:document.getElementById("cy"),style:[{selector:"edge",style:{"curve-style":"bezier"}}]});r.remove(),BHe(e.nodes,i),PHe(e.edges,i),i.nodes().forEach(function(a){a.layoutDimensions=()=>{let o=a.data();return{w:o.width,h:o.height}}});let n={name:"cose-bilkent",quality:"proof",styleEnabled:!1,animate:!1};i.layout(n).run(),i.ready(a=>{Ee.info("Cytoscape ready",a),t(i)})})}function zHe(e){return e.nodes().map(t=>{let r=t.data(),i=t.position(),n={id:r.id,x:i.x,y:i.y};return Object.keys(r).forEach(a=>{a!=="id"&&(n[a]=r[a])}),n})}function qHe(e){return e.edges().map(t=>{let r=t.data(),i=t._private.rscratch,n={id:r.id,source:r.source,target:r.target,startX:i.startX,startY:i.startY,midX:i.midX,midY:i.midY,endX:i.endX,endY:i.endY};return Object.keys(r).forEach(a=>{["id","source","target"].includes(a)||(n[a]=r[a])}),n})}var WHe,t8t=G(()=>{"use strict";DHe(),WHe=$o(e8t(),1),pi(),Nt(),Qc.use(WHe.default),s(BHe,"addNodes"),s(PHe,"addEdges"),s(FHe,"createCytoscapeInstance"),s(zHe,"extractPositionedNodes"),s(qHe,"extractPositionedEdges")});async function jHe(e,t){Ee.debug("Starting cose-bilkent layout algorithm");try{VHe(e);let r=await FHe(e),i=zHe(r),n=qHe(r);return Ee.debug(`Layout completed: ${i.length} nodes, ${n.length} edges`),{nodes:i,edges:n}}catch(r){throw Ee.error("Error in cose-bilkent layout algorithm:",r),r}}function VHe(e){if(!e)throw new Error("Layout data is required");if(!e.config)throw new Error("Configuration is required in layout data");if(!e.rootNode)throw new Error("Root node is required");if(!e.nodes||!Array.isArray(e.nodes))throw new Error("No nodes found in layout data");if(!Array.isArray(e.edges))throw new Error("Edges array is required in layout data");return!0}var r8t=G(()=>{"use strict";Nt(),t8t(),s(jHe,"executeCoseBilkentLayout"),s(VHe,"validateLayoutData")}),UHe,i8t=G(()=>{"use strict";r8t(),UHe=s(async(e,t,{insertCluster:r,insertEdge:i,insertEdgeLabel:n,insertMarkers:a,insertNode:o,log:l,positionEdgeLabel:u},{algorithm:h})=>{let d={},p={},f=t.select("g");a(f,e.markers,e.type,e.diagramId);let g=f.insert("g").attr("class","subgraphs"),m=f.insert("g").attr("class","edgePaths"),y=f.insert("g").attr("class","edgeLabels"),k=f.insert("g").attr("class","nodes");l.debug("Inserting nodes into DOM for dimension calculation"),await Promise.all(e.nodes.map(async E=>{if(E.isGroup){let _={...E};p[E.id]=_,d[E.id]=_,await r(g,E)}else{let _={...E};d[E.id]=_;let b=await o(k,E,{config:e.config,dir:e.direction||"TB"}),v=b.node().getBBox();_.width=v.width,_.height=v.height,_.domId=b,l.debug(`Node ${E.id} dimensions: ${v.width}x${v.height}`)}})),l.debug("Running cose-bilkent layout algorithm");let C={...e,nodes:e.nodes.map(E=>{let _=d[E.id];return{...E,width:_.width,height:_.height}})},S=await jHe(C,e.config);l.debug("Positioning nodes based on layout results"),S.nodes.forEach(E=>{let _=d[E.id];_?.domId&&(_.domId.attr("transform",`translate(${E.x}, ${E.y})`),_.x=E.x,_.y=E.y,l.debug(`Positioned node ${_.id} at center (${E.x}, ${E.y})`))}),S.edges.forEach(E=>{let _=e.edges.find(b=>b.id===E.id);_&&(_.points=[{x:E.startX,y:E.startY},{x:E.midX,y:E.midY},{x:E.endX,y:E.endY}])}),l.debug("Inserting and positioning edges"),await Promise.all(e.edges.map(async E=>{let _=await n(y,E),b=d[E.start??""],v=d[E.end??""];if(b&&v){let x=S.edges.find(w=>w.id===E.id);if(x){l.debug("APA01 positionedEdge",x);let w={...E},R=i(m,w,p,e.type,b,v,e.diagramId);u(w,R)}else{let w={...E,points:[{x:b.x||0,y:b.y||0},{x:v.x||0,y:v.y||0}]},R=i(m,w,p,e.type,b,v,e.diagramId);u(w,R)}}})),l.debug("Cose-bilkent rendering completed")},"render")}),YHe={};Mr(YHe,{render:()=>HHe});var HHe,n8t=G(()=>{"use strict";i8t(),HHe=UHe}),gv,oK,GHe,bm,mv,xm=G(()=>{"use strict";i6t(),Nt(),gv={},oK=s(e=>{for(let t of e)gv[t.name]=t},"registerLayoutLoaders"),GHe=s(()=>{oK([{name:"dagre",loader:s(async()=>await Promise.resolve().then(()=>(TAt(),RBe)),"loader")},{name:"swimlane",loader:s(async()=>await Promise.resolve().then(()=>(ZAt(),LFe)),"loader")},{name:"cose-bilkent",loader:s(async()=>await Promise.resolve().then(()=>(n8t(),YHe)),"loader")}])},"registerDefaultLayoutLoaders"),GHe(),bm=s(async(e,t)=>{if(!(e.layoutAlgorithm in gv))throw new Error(`Unknown layout algorithm: ${e.layoutAlgorithm}`);if(e.diagramId)for(let d of e.nodes){let p=d.domId||d.id;d.domId=`${e.diagramId}-${p}`}let r=gv[e.layoutAlgorithm],i=await r.loader(),{theme:n,themeVariables:a}=e.config,{useGradient:o,gradientStart:l,gradientStop:u}=a,h=t.attr("id");if(t.append("defs").append("filter").attr("id",`${h}-drop-shadow`).attr("height","130%").attr("width","130%").append("feDropShadow").attr("dx","4").attr("dy","4").attr("stdDeviation",0).attr("flood-opacity","0.06").attr("flood-color",`${n?.includes("dark")?"#FFFFFF":"#000000"}`),t.append("defs").append("filter").attr("id",`${h}-drop-shadow-small`).attr("height","150%").attr("width","150%").append("feDropShadow").attr("dx","2").attr("dy","2").attr("stdDeviation",0).attr("flood-opacity","0.06").attr("flood-color",`${n?.includes("dark")?"#FFFFFF":"#000000"}`),o){let d=t.append("linearGradient").attr("id",t.attr("id")+"-gradient").attr("gradientUnits","objectBoundingBox").attr("x1","0%").attr("y1","0%").attr("x2","100%").attr("y2","0%");d.append("svg:stop").attr("offset","0%").attr("stop-color",l).attr("stop-opacity",1),d.append("svg:stop").attr("offset","100%").attr("stop-color",u).attr("stop-opacity",1)}return i.render(e,t,Q6e,{algorithm:r.algorithm})},"render"),mv=s((e="",{fallback:t="dagre"}={})=>{if(e in gv)return e;if(t in gv)return Ee.warn(`Layout algorithm ${e} is not registered. Using ${t} as fallback.`),t;throw new Error(`Both layout algorithms ${e} and ${t} are not registered.`)},"getRegisteredLayoutAlgorithm")}),kf,XHe,KHe,Tm=G(()=>{"use strict";vn(),Nt(),kf=s((e,t,r,i)=>{e.attr("class",r);let{width:n,height:a,x:o,y:l}=XHe(e,t);aa(e,a,n,i);let u=KHe(o,l,n,a,t);e.attr("viewBox",u),Ee.debug(`viewBox configured: ${u} with padding: ${t}`)},"setupViewPortForSVG"),XHe=s((e,t)=>{let r=e.node()?.getBBox()||{width:0,height:0,x:0,y:0};return{width:r.width+t*2,height:r.height+t*2,x:r.x,y:r.y}},"calculateDimensionsWithPadding"),KHe=s((e,t,r,i,n)=>`${e-n} ${t-n} ${r} ${i}`,"createViewBox")}),QHe,ZHe,JHe,a8t=G(()=>{"use strict";Er(),Nt(),l2(),xm(),Tm(),Ar(),QHe=s(function(e,t){return t.db.getClasses()},"getClasses"),ZHe=s(async function(e,t,r,i){Ee.info("REF0:"),Ee.info("Drawing state diagram (v2)",t);let{securityLevel:n,flowchart:a,layout:o}=it();i.db.setDiagramId(t),Ee.debug("Before getData: ");let l=i.db.getData();Ee.debug("Data: ",l);let u=bg(t,n),h=i.db.getDirection();l.type=i.type,l.layoutAlgorithm=mv(o),l.layoutAlgorithm==="dagre"&&o==="elk"&&Ee.warn("flowchart-elk was moved to an external package in Mermaid v11. Please refer [release notes](https://github.com/mermaid-js/mermaid/releases/tag/v11.0.0) for more details. This diagram will be rendered using `dagre` layout as a fallback."),l.direction=h,l.nodeSpacing=a?.nodeSpacing||50,l.rankSpacing=a?.rankSpacing||50,l.markers=["point","circle","cross"],l.diagramId=t,Ee.debug("REF1:",l),await bm(l,u);let d=l.config.flowchart?.diagramPadding??8;ai.insertTitle(u,"flowchartTitleText",a?.titleTopMargin||0,i.db.getDiagramTitle()),kf(u,d,"flowchart",a?.useMaxWidth||!1)},"draw"),JHe={getClasses:QHe,draw:ZHe}}),I8,lK,s8t=G(()=>{"use strict";I8=(function(){var e=s(function(fi,gt,Mt,kt){for(Mt=Mt||{},kt=fi.length;kt--;Mt[fi[kt]]=gt);return Mt},"o"),t=[1,4],r=[1,3],i=[1,5],n=[1,8,9,10,11,27,34,36,38,44,60,84,85,86,87,88,89,102,105,106,109,111,114,115,116,121,122,123,124,125],a=[2,2],o=[1,13],l=[1,14],u=[1,15],h=[1,16],d=[1,23],p=[1,25],f=[1,26],g=[1,27],m=[1,50],y=[1,49],k=[1,29],C=[1,30],S=[1,31],E=[1,32],_=[1,33],b=[1,45],v=[1,47],x=[1,43],w=[1,48],R=[1,44],$=[1,51],M=[1,46],N=[1,52],j=[1,53],I=[1,34],z=[1,35],F=[1,36],O=[1,37],B=[1,38],Y=[1,58],U=[1,8,9,10,11,27,32,34,36,38,44,60,84,85,86,87,88,89,102,105,106,109,111,114,115,116,121,122,123,124,125],H=[1,62],J=[1,61],te=[1,63],ie=[8,9,11,75,77,78],ue=[1,79],se=[1,92],le=[1,97],Te=[1,96],oe=[1,93],fe=[1,89],ge=[1,95],ke=[1,91],Ne=[1,98],xe=[1,94],$e=[1,99],Me=[1,90],He=[8,9,10,11,40,75,77,78],Ge=[8,9,10,11,40,46,75,77,78],ze=[8,9,10,11,29,40,44,46,48,50,52,54,56,58,60,63,65,67,68,70,75,77,78,89,102,105,106,109,111,114,115,116],Ve=[8,9,11,44,60,75,77,78,89,102,105,106,109,111,114,115,116],et=[44,60,89,102,105,106,109,111,114,115,116],he=[1,122],ye=[1,123],be=[1,125],Ie=[1,124],pe=[44,60,62,74,89,102,105,106,109,111,114,115,116],Qe=[1,134],W=[1,148],Ae=[1,149],K=[1,150],de=[1,151],ee=[1,136],ve=[1,138],X=[1,142],me=[1,143],Ce=[1,144],Be=[1,145],Re=[1,146],Ke=[1,147],De=[1,152],Ze=[1,153],Fe=[1,132],Je=[1,133],Le=[1,140],ct=[1,135],qe=[1,139],mt=[1,137],St=[8,9,10,11,27,32,34,36,38,44,60,84,85,86,87,88,89,102,105,106,109,111,114,115,116,121,122,123,124,125],Lt=[1,155],Bt=[1,157],qt=[8,9,11],tr=[8,9,10,11,14,44,60,89,105,106,109,111,114,115,116],Et=[1,177],_t=[1,173],Kt=[1,174],$t=[1,178],Ot=[1,175],Jt=[1,176],Br=[77,116,119],At=[8,9,10,11,12,14,27,29,32,44,60,75,84,85,86,87,88,89,90,105,109,111,114,115,116],jr=[10,106],br=[31,49,51,53,55,57,62,64,66,67,69,71,116,117,118],Hr=[1,248],Ei=[1,246],Li=[1,250],Cr=[1,244],Ti=[1,245],vt=[1,247],wt=[1,249],Wt=[1,251],kr=[1,269],Ir=[8,9,11,106],Lr=[8,9,10,11,60,84,105,106,109,110,111,112],di={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,graphConfig:4,document:5,line:6,statement:7,SEMI:8,NEWLINE:9,SPACE:10,EOF:11,GRAPH:12,NODIR:13,DIR:14,FirstStmtSeparator:15,ending:16,endToken:17,spaceList:18,spaceListNewline:19,vertexStatement:20,separator:21,styleStatement:22,linkStyleStatement:23,classDefStatement:24,classStatement:25,clickStatement:26,subgraph:27,textNoTags:28,SQS:29,text:30,SQE:31,end:32,direction:33,acc_title:34,acc_title_value:35,acc_descr:36,acc_descr_value:37,acc_descr_multiline_value:38,shapeData:39,SHAPE_DATA:40,link:41,node:42,styledVertex:43,AMP:44,vertex:45,STYLE_SEPARATOR:46,idString:47,DOUBLECIRCLESTART:48,DOUBLECIRCLEEND:49,PS:50,PE:51,"(-":52,"-)":53,STADIUMSTART:54,STADIUMEND:55,SUBROUTINESTART:56,SUBROUTINEEND:57,VERTEX_WITH_PROPS_START:58,"NODE_STRING[field]":59,COLON:60,"NODE_STRING[value]":61,PIPE:62,CYLINDERSTART:63,CYLINDEREND:64,DIAMOND_START:65,DIAMOND_STOP:66,TAGEND:67,TRAPSTART:68,TRAPEND:69,INVTRAPSTART:70,INVTRAPEND:71,linkStatement:72,arrowText:73,TESTSTR:74,START_LINK:75,edgeText:76,LINK:77,LINK_ID:78,edgeTextToken:79,STR:80,MD_STR:81,textToken:82,keywords:83,STYLE:84,LINKSTYLE:85,CLASSDEF:86,CLASS:87,CLICK:88,DOWN:89,UP:90,textNoTagsToken:91,stylesOpt:92,"idString[vertex]":93,"idString[class]":94,CALLBACKNAME:95,CALLBACKARGS:96,HREF:97,LINK_TARGET:98,"STR[link]":99,"STR[tooltip]":100,alphaNum:101,DEFAULT:102,numList:103,INTERPOLATE:104,NUM:105,COMMA:106,style:107,styleComponent:108,NODE_STRING:109,UNIT:110,BRKT:111,PCT:112,idStringToken:113,MINUS:114,MULT:115,UNICODE_TEXT:116,TEXT:117,TAGSTART:118,EDGE_TEXT:119,alphaNumToken:120,direction_tb:121,direction_bt:122,direction_rl:123,direction_lr:124,direction_td:125,$accept:0,$end:1},terminals_:{2:"error",8:"SEMI",9:"NEWLINE",10:"SPACE",11:"EOF",12:"GRAPH",13:"NODIR",14:"DIR",27:"subgraph",29:"SQS",31:"SQE",32:"end",34:"acc_title",35:"acc_title_value",36:"acc_descr",37:"acc_descr_value",38:"acc_descr_multiline_value",40:"SHAPE_DATA",44:"AMP",46:"STYLE_SEPARATOR",48:"DOUBLECIRCLESTART",49:"DOUBLECIRCLEEND",50:"PS",51:"PE",52:"(-",53:"-)",54:"STADIUMSTART",55:"STADIUMEND",56:"SUBROUTINESTART",57:"SUBROUTINEEND",58:"VERTEX_WITH_PROPS_START",59:"NODE_STRING[field]",60:"COLON",61:"NODE_STRING[value]",62:"PIPE",63:"CYLINDERSTART",64:"CYLINDEREND",65:"DIAMOND_START",66:"DIAMOND_STOP",67:"TAGEND",68:"TRAPSTART",69:"TRAPEND",70:"INVTRAPSTART",71:"INVTRAPEND",74:"TESTSTR",75:"START_LINK",77:"LINK",78:"LINK_ID",80:"STR",81:"MD_STR",84:"STYLE",85:"LINKSTYLE",86:"CLASSDEF",87:"CLASS",88:"CLICK",89:"DOWN",90:"UP",93:"idString[vertex]",94:"idString[class]",95:"CALLBACKNAME",96:"CALLBACKARGS",97:"HREF",98:"LINK_TARGET",99:"STR[link]",100:"STR[tooltip]",102:"DEFAULT",104:"INTERPOLATE",105:"NUM",106:"COMMA",109:"NODE_STRING",110:"UNIT",111:"BRKT",112:"PCT",114:"MINUS",115:"MULT",116:"UNICODE_TEXT",117:"TEXT",118:"TAGSTART",119:"EDGE_TEXT",121:"direction_tb",122:"direction_bt",123:"direction_rl",124:"direction_lr",125:"direction_td"},productions_:[0,[3,2],[5,0],[5,2],[6,1],[6,1],[6,1],[6,1],[6,1],[4,2],[4,2],[4,2],[4,3],[16,2],[16,1],[17,1],[17,1],[17,1],[15,1],[15,1],[15,2],[19,2],[19,2],[19,1],[19,1],[18,2],[18,1],[7,2],[7,2],[7,2],[7,2],[7,2],[7,2],[7,9],[7,6],[7,4],[7,1],[7,2],[7,2],[7,1],[21,1],[21,1],[21,1],[39,2],[39,1],[20,4],[20,3],[20,4],[20,2],[20,2],[20,1],[42,1],[42,6],[42,5],[43,1],[43,3],[45,4],[45,4],[45,6],[45,4],[45,4],[45,4],[45,8],[45,4],[45,4],[45,4],[45,6],[45,4],[45,4],[45,4],[45,4],[45,4],[45,1],[41,2],[41,3],[41,3],[41,1],[41,3],[41,4],[76,1],[76,2],[76,1],[76,1],[72,1],[72,2],[73,3],[30,1],[30,2],[30,1],[30,1],[83,1],[83,1],[83,1],[83,1],[83,1],[83,1],[83,1],[83,1],[83,1],[83,1],[83,1],[28,1],[28,2],[28,1],[28,1],[24,5],[25,5],[26,2],[26,4],[26,3],[26,5],[26,3],[26,5],[26,5],[26,7],[26,2],[26,4],[26,2],[26,4],[26,4],[26,6],[22,5],[23,5],[23,5],[23,9],[23,9],[23,7],[23,7],[103,1],[103,3],[92,1],[92,3],[107,1],[107,2],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[113,1],[113,1],[113,1],[113,1],[113,1],[113,1],[113,1],[113,1],[113,1],[113,1],[113,1],[82,1],[82,1],[82,1],[82,1],[91,1],[91,1],[91,1],[91,1],[91,1],[91,1],[91,1],[91,1],[91,1],[91,1],[91,1],[79,1],[79,1],[120,1],[120,1],[120,1],[120,1],[120,1],[120,1],[120,1],[120,1],[120,1],[120,1],[120,1],[47,1],[47,2],[101,1],[101,2],[33,1],[33,1],[33,1],[33,1],[33,1]],performAction:s(function(fi,gt,Mt,kt,Qt,Pe,ii){var Ye=Pe.length-1;switch(Qt){case 2:this.$=[];break;case 3:(!Array.isArray(Pe[Ye])||Pe[Ye].length>0)&&Pe[Ye-1].push(Pe[Ye]),this.$=Pe[Ye-1];break;case 4:case 183:this.$=Pe[Ye];break;case 11:kt.setDirection("TB"),this.$="TB";break;case 12:kt.setDirection(Pe[Ye-1]),this.$=Pe[Ye-1];break;case 27:this.$=Pe[Ye-1].nodes;break;case 28:case 29:case 30:case 31:case 32:this.$=[];break;case 33:this.$=kt.addSubGraph(Pe[Ye-6],Pe[Ye-1],Pe[Ye-4]);break;case 34:this.$=kt.addSubGraph(Pe[Ye-3],Pe[Ye-1],Pe[Ye-3]);break;case 35:this.$=kt.addSubGraph(void 0,Pe[Ye-1],void 0);break;case 37:this.$=Pe[Ye].trim(),kt.setAccTitle(this.$);break;case 38:case 39:this.$=Pe[Ye].trim(),kt.setAccDescription(this.$);break;case 43:this.$=Pe[Ye-1]+Pe[Ye];break;case 44:this.$=Pe[Ye];break;case 45:kt.addVertex(Pe[Ye-1][Pe[Ye-1].length-1],void 0,void 0,void 0,void 0,void 0,void 0,Pe[Ye]),kt.addLink(Pe[Ye-3].stmt,Pe[Ye-1],Pe[Ye-2]),this.$={stmt:Pe[Ye-1],nodes:Pe[Ye-1].concat(Pe[Ye-3].nodes)};break;case 46:kt.addLink(Pe[Ye-2].stmt,Pe[Ye],Pe[Ye-1]),this.$={stmt:Pe[Ye],nodes:Pe[Ye].concat(Pe[Ye-2].nodes)};break;case 47:kt.addLink(Pe[Ye-3].stmt,Pe[Ye-1],Pe[Ye-2]),this.$={stmt:Pe[Ye-1],nodes:Pe[Ye-1].concat(Pe[Ye-3].nodes)};break;case 48:this.$={stmt:Pe[Ye-1],nodes:Pe[Ye-1]};break;case 49:kt.addVertex(Pe[Ye-1][Pe[Ye-1].length-1],void 0,void 0,void 0,void 0,void 0,void 0,Pe[Ye]),this.$={stmt:Pe[Ye-1],nodes:Pe[Ye-1],shapeData:Pe[Ye]};break;case 50:this.$={stmt:Pe[Ye],nodes:Pe[Ye]};break;case 51:this.$=[Pe[Ye]];break;case 52:kt.addVertex(Pe[Ye-5][Pe[Ye-5].length-1],void 0,void 0,void 0,void 0,void 0,void 0,Pe[Ye-4]),this.$=Pe[Ye-5].concat(Pe[Ye]);break;case 53:this.$=Pe[Ye-4].concat(Pe[Ye]);break;case 54:this.$=Pe[Ye];break;case 55:this.$=Pe[Ye-2],kt.setClass(Pe[Ye-2],Pe[Ye]);break;case 56:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"square");break;case 57:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"doublecircle");break;case 58:this.$=Pe[Ye-5],kt.addVertex(Pe[Ye-5],Pe[Ye-2],"circle");break;case 59:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"ellipse");break;case 60:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"stadium");break;case 61:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"subroutine");break;case 62:this.$=Pe[Ye-7],kt.addVertex(Pe[Ye-7],Pe[Ye-1],"rect",void 0,void 0,void 0,Object.fromEntries([[Pe[Ye-5],Pe[Ye-3]]]));break;case 63:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"cylinder");break;case 64:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"round");break;case 65:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"diamond");break;case 66:this.$=Pe[Ye-5],kt.addVertex(Pe[Ye-5],Pe[Ye-2],"hexagon");break;case 67:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"odd");break;case 68:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"trapezoid");break;case 69:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"inv_trapezoid");break;case 70:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"lean_right");break;case 71:this.$=Pe[Ye-3],kt.addVertex(Pe[Ye-3],Pe[Ye-1],"lean_left");break;case 72:this.$=Pe[Ye],kt.addVertex(Pe[Ye]);break;case 73:Pe[Ye-1].text=Pe[Ye],this.$=Pe[Ye-1];break;case 74:case 75:Pe[Ye-2].text=Pe[Ye-1],this.$=Pe[Ye-2];break;case 76:this.$=Pe[Ye];break;case 77:var ti=kt.destructLink(Pe[Ye],Pe[Ye-2]);this.$={type:ti.type,stroke:ti.stroke,length:ti.length,text:Pe[Ye-1]};break;case 78:var ti=kt.destructLink(Pe[Ye],Pe[Ye-2]);this.$={type:ti.type,stroke:ti.stroke,length:ti.length,text:Pe[Ye-1],id:Pe[Ye-3]};break;case 79:this.$={text:Pe[Ye],type:"text"};break;case 80:this.$={text:Pe[Ye-1].text+""+Pe[Ye],type:Pe[Ye-1].type};break;case 81:this.$={text:Pe[Ye],type:"string"};break;case 82:this.$={text:Pe[Ye],type:"markdown"};break;case 83:var ti=kt.destructLink(Pe[Ye]);this.$={type:ti.type,stroke:ti.stroke,length:ti.length};break;case 84:var ti=kt.destructLink(Pe[Ye]);this.$={type:ti.type,stroke:ti.stroke,length:ti.length,id:Pe[Ye-1]};break;case 85:this.$=Pe[Ye-1];break;case 86:this.$={text:Pe[Ye],type:"text"};break;case 87:this.$={text:Pe[Ye-1].text+""+Pe[Ye],type:Pe[Ye-1].type};break;case 88:this.$={text:Pe[Ye],type:"string"};break;case 89:case 104:this.$={text:Pe[Ye],type:"markdown"};break;case 101:this.$={text:Pe[Ye],type:"text"};break;case 102:this.$={text:Pe[Ye-1].text+""+Pe[Ye],type:Pe[Ye-1].type};break;case 103:this.$={text:Pe[Ye],type:"text"};break;case 105:this.$=Pe[Ye-4],kt.addClass(Pe[Ye-2],Pe[Ye]);break;case 106:this.$=Pe[Ye-4],kt.setClass(Pe[Ye-2],Pe[Ye]);break;case 107:case 115:this.$=Pe[Ye-1],kt.setClickEvent(Pe[Ye-1],Pe[Ye]);break;case 108:case 116:this.$=Pe[Ye-3],kt.setClickEvent(Pe[Ye-3],Pe[Ye-2]),kt.setTooltip(Pe[Ye-3],Pe[Ye]);break;case 109:this.$=Pe[Ye-2],kt.setClickEvent(Pe[Ye-2],Pe[Ye-1],Pe[Ye]);break;case 110:this.$=Pe[Ye-4],kt.setClickEvent(Pe[Ye-4],Pe[Ye-3],Pe[Ye-2]),kt.setTooltip(Pe[Ye-4],Pe[Ye]);break;case 111:this.$=Pe[Ye-2],kt.setLink(Pe[Ye-2],Pe[Ye]);break;case 112:this.$=Pe[Ye-4],kt.setLink(Pe[Ye-4],Pe[Ye-2]),kt.setTooltip(Pe[Ye-4],Pe[Ye]);break;case 113:this.$=Pe[Ye-4],kt.setLink(Pe[Ye-4],Pe[Ye-2],Pe[Ye]);break;case 114:this.$=Pe[Ye-6],kt.setLink(Pe[Ye-6],Pe[Ye-4],Pe[Ye]),kt.setTooltip(Pe[Ye-6],Pe[Ye-2]);break;case 117:this.$=Pe[Ye-1],kt.setLink(Pe[Ye-1],Pe[Ye]);break;case 118:this.$=Pe[Ye-3],kt.setLink(Pe[Ye-3],Pe[Ye-2]),kt.setTooltip(Pe[Ye-3],Pe[Ye]);break;case 119:this.$=Pe[Ye-3],kt.setLink(Pe[Ye-3],Pe[Ye-2],Pe[Ye]);break;case 120:this.$=Pe[Ye-5],kt.setLink(Pe[Ye-5],Pe[Ye-4],Pe[Ye]),kt.setTooltip(Pe[Ye-5],Pe[Ye-2]);break;case 121:this.$=Pe[Ye-4],kt.addVertex(Pe[Ye-2],void 0,void 0,Pe[Ye]);break;case 122:this.$=Pe[Ye-4],kt.updateLink([Pe[Ye-2]],Pe[Ye]);break;case 123:this.$=Pe[Ye-4],kt.updateLink(Pe[Ye-2],Pe[Ye]);break;case 124:this.$=Pe[Ye-8],kt.updateLinkInterpolate([Pe[Ye-6]],Pe[Ye-2]),kt.updateLink([Pe[Ye-6]],Pe[Ye]);break;case 125:this.$=Pe[Ye-8],kt.updateLinkInterpolate(Pe[Ye-6],Pe[Ye-2]),kt.updateLink(Pe[Ye-6],Pe[Ye]);break;case 126:this.$=Pe[Ye-6],kt.updateLinkInterpolate([Pe[Ye-4]],Pe[Ye]);break;case 127:this.$=Pe[Ye-6],kt.updateLinkInterpolate(Pe[Ye-4],Pe[Ye]);break;case 128:case 130:this.$=[Pe[Ye]];break;case 129:case 131:Pe[Ye-2].push(Pe[Ye]),this.$=Pe[Ye-2];break;case 133:this.$=Pe[Ye-1]+Pe[Ye];break;case 181:this.$=Pe[Ye];break;case 182:this.$=Pe[Ye-1]+""+Pe[Ye];break;case 184:this.$=Pe[Ye-1]+""+Pe[Ye];break;case 185:this.$={stmt:"dir",value:"TB"};break;case 186:this.$={stmt:"dir",value:"BT"};break;case 187:this.$={stmt:"dir",value:"RL"};break;case 188:this.$={stmt:"dir",value:"LR"};break;case 189:this.$={stmt:"dir",value:"TD"};break}},"anonymous"),table:[{3:1,4:2,9:t,10:r,12:i},{1:[3]},e(n,a,{5:6}),{4:7,9:t,10:r,12:i},{4:8,9:t,10:r,12:i},{13:[1,9],14:[1,10]},{1:[2,1],6:11,7:12,8:o,9:l,10:u,11:h,20:17,22:18,23:19,24:20,25:21,26:22,27:d,33:24,34:p,36:f,38:g,42:28,43:39,44:m,45:40,47:41,60:y,84:k,85:C,86:S,87:E,88:_,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j,121:I,122:z,123:F,124:O,125:B},e(n,[2,9]),e(n,[2,10]),e(n,[2,11]),{8:[1,55],9:[1,56],10:Y,15:54,18:57},e(U,[2,3]),e(U,[2,4]),e(U,[2,5]),e(U,[2,6]),e(U,[2,7]),e(U,[2,8]),{8:H,9:J,11:te,21:59,41:60,72:64,75:[1,65],77:[1,67],78:[1,66]},{8:H,9:J,11:te,21:68},{8:H,9:J,11:te,21:69},{8:H,9:J,11:te,21:70},{8:H,9:J,11:te,21:71},{8:H,9:J,11:te,21:72},{8:H,9:J,10:[1,73],11:te,21:74},e(U,[2,36]),{35:[1,75]},{37:[1,76]},e(U,[2,39]),e(ie,[2,50],{18:77,39:78,10:Y,40:ue}),{10:[1,80]},{10:[1,81]},{10:[1,82]},{10:[1,83]},{14:se,44:le,60:Te,80:[1,87],89:oe,95:[1,84],97:[1,85],101:86,105:fe,106:ge,109:ke,111:Ne,114:xe,115:$e,116:Me,120:88},e(U,[2,185]),e(U,[2,186]),e(U,[2,187]),e(U,[2,188]),e(U,[2,189]),e(He,[2,51]),e(He,[2,54],{46:[1,100]}),e(Ge,[2,72],{113:113,29:[1,101],44:m,48:[1,102],50:[1,103],52:[1,104],54:[1,105],56:[1,106],58:[1,107],60:y,63:[1,108],65:[1,109],67:[1,110],68:[1,111],70:[1,112],89:b,102:v,105:x,106:w,109:R,111:$,114:M,115:N,116:j}),e(ze,[2,181]),e(ze,[2,142]),e(ze,[2,143]),e(ze,[2,144]),e(ze,[2,145]),e(ze,[2,146]),e(ze,[2,147]),e(ze,[2,148]),e(ze,[2,149]),e(ze,[2,150]),e(ze,[2,151]),e(ze,[2,152]),e(n,[2,12]),e(n,[2,18]),e(n,[2,19]),{9:[1,114]},e(Ve,[2,26],{18:115,10:Y}),e(U,[2,27]),{42:116,43:39,44:m,45:40,47:41,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j},e(U,[2,40]),e(U,[2,41]),e(U,[2,42]),e(et,[2,76],{73:117,62:[1,119],74:[1,118]}),{76:120,79:121,80:he,81:ye,116:be,119:Ie},{75:[1,126],77:[1,127]},e(pe,[2,83]),e(U,[2,28]),e(U,[2,29]),e(U,[2,30]),e(U,[2,31]),e(U,[2,32]),{10:Qe,12:W,14:Ae,27:K,28:128,32:de,44:ee,60:ve,75:X,80:[1,130],81:[1,131],83:141,84:me,85:Ce,86:Be,87:Re,88:Ke,89:De,90:Ze,91:129,105:Fe,109:Je,111:Le,114:ct,115:qe,116:mt},e(St,a,{5:154}),e(U,[2,37]),e(U,[2,38]),e(ie,[2,48],{44:Lt}),e(ie,[2,49],{18:156,10:Y,40:Bt}),e(He,[2,44]),{44:m,47:158,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j},{102:[1,159],103:160,105:[1,161]},{44:m,47:162,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j},{44:m,47:163,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j},e(qt,[2,107],{10:[1,164],96:[1,165]}),{80:[1,166]},e(qt,[2,115],{120:168,10:[1,167],14:se,44:le,60:Te,89:oe,105:fe,106:ge,109:ke,111:Ne,114:xe,115:$e,116:Me}),e(qt,[2,117],{10:[1,169]}),e(tr,[2,183]),e(tr,[2,170]),e(tr,[2,171]),e(tr,[2,172]),e(tr,[2,173]),e(tr,[2,174]),e(tr,[2,175]),e(tr,[2,176]),e(tr,[2,177]),e(tr,[2,178]),e(tr,[2,179]),e(tr,[2,180]),{44:m,47:170,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j},{30:171,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{30:179,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{30:181,50:[1,180],67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{30:182,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{30:183,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{30:184,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{109:[1,185]},{30:186,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{30:187,65:[1,188],67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{30:189,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{30:190,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{30:191,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},e(ze,[2,182]),e(n,[2,20]),e(Ve,[2,25]),e(ie,[2,46],{39:192,18:193,10:Y,40:ue}),e(et,[2,73],{10:[1,194]}),{10:[1,195]},{30:196,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{77:[1,197],79:198,116:be,119:Ie},e(Br,[2,79]),e(Br,[2,81]),e(Br,[2,82]),e(Br,[2,168]),e(Br,[2,169]),{76:199,79:121,80:he,81:ye,116:be,119:Ie},e(pe,[2,84]),{8:H,9:J,10:Qe,11:te,12:W,14:Ae,21:201,27:K,29:[1,200],32:de,44:ee,60:ve,75:X,83:141,84:me,85:Ce,86:Be,87:Re,88:Ke,89:De,90:Ze,91:202,105:Fe,109:Je,111:Le,114:ct,115:qe,116:mt},e(At,[2,101]),e(At,[2,103]),e(At,[2,104]),e(At,[2,157]),e(At,[2,158]),e(At,[2,159]),e(At,[2,160]),e(At,[2,161]),e(At,[2,162]),e(At,[2,163]),e(At,[2,164]),e(At,[2,165]),e(At,[2,166]),e(At,[2,167]),e(At,[2,90]),e(At,[2,91]),e(At,[2,92]),e(At,[2,93]),e(At,[2,94]),e(At,[2,95]),e(At,[2,96]),e(At,[2,97]),e(At,[2,98]),e(At,[2,99]),e(At,[2,100]),{6:11,7:12,8:o,9:l,10:u,11:h,20:17,22:18,23:19,24:20,25:21,26:22,27:d,32:[1,203],33:24,34:p,36:f,38:g,42:28,43:39,44:m,45:40,47:41,60:y,84:k,85:C,86:S,87:E,88:_,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j,121:I,122:z,123:F,124:O,125:B},{10:Y,18:204},{44:[1,205]},e(He,[2,43]),{10:[1,206],44:m,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:113,114:M,115:N,116:j},{10:[1,207]},{10:[1,208],106:[1,209]},e(jr,[2,128]),{10:[1,210],44:m,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:113,114:M,115:N,116:j},{10:[1,211],44:m,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:113,114:M,115:N,116:j},{80:[1,212]},e(qt,[2,109],{10:[1,213]}),e(qt,[2,111],{10:[1,214]}),{80:[1,215]},e(tr,[2,184]),{80:[1,216],98:[1,217]},e(He,[2,55],{113:113,44:m,60:y,89:b,102:v,105:x,106:w,109:R,111:$,114:M,115:N,116:j}),{31:[1,218],67:Et,82:219,116:$t,117:Ot,118:Jt},e(br,[2,86]),e(br,[2,88]),e(br,[2,89]),e(br,[2,153]),e(br,[2,154]),e(br,[2,155]),e(br,[2,156]),{49:[1,220],67:Et,82:219,116:$t,117:Ot,118:Jt},{30:221,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{51:[1,222],67:Et,82:219,116:$t,117:Ot,118:Jt},{53:[1,223],67:Et,82:219,116:$t,117:Ot,118:Jt},{55:[1,224],67:Et,82:219,116:$t,117:Ot,118:Jt},{57:[1,225],67:Et,82:219,116:$t,117:Ot,118:Jt},{60:[1,226]},{64:[1,227],67:Et,82:219,116:$t,117:Ot,118:Jt},{66:[1,228],67:Et,82:219,116:$t,117:Ot,118:Jt},{30:229,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},{31:[1,230],67:Et,82:219,116:$t,117:Ot,118:Jt},{67:Et,69:[1,231],71:[1,232],82:219,116:$t,117:Ot,118:Jt},{67:Et,69:[1,234],71:[1,233],82:219,116:$t,117:Ot,118:Jt},e(ie,[2,45],{18:156,10:Y,40:Bt}),e(ie,[2,47],{44:Lt}),e(et,[2,75]),e(et,[2,74]),{62:[1,235],67:Et,82:219,116:$t,117:Ot,118:Jt},e(et,[2,77]),e(Br,[2,80]),{77:[1,236],79:198,116:be,119:Ie},{30:237,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},e(St,a,{5:238}),e(At,[2,102]),e(U,[2,35]),{43:239,44:m,45:40,47:41,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j},{10:Y,18:240},{10:Hr,60:Ei,84:Li,92:241,105:Cr,107:242,108:243,109:Ti,110:vt,111:wt,112:Wt},{10:Hr,60:Ei,84:Li,92:252,104:[1,253],105:Cr,107:242,108:243,109:Ti,110:vt,111:wt,112:Wt},{10:Hr,60:Ei,84:Li,92:254,104:[1,255],105:Cr,107:242,108:243,109:Ti,110:vt,111:wt,112:Wt},{105:[1,256]},{10:Hr,60:Ei,84:Li,92:257,105:Cr,107:242,108:243,109:Ti,110:vt,111:wt,112:Wt},{44:m,47:258,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j},e(qt,[2,108]),{80:[1,259]},{80:[1,260],98:[1,261]},e(qt,[2,116]),e(qt,[2,118],{10:[1,262]}),e(qt,[2,119]),e(Ge,[2,56]),e(br,[2,87]),e(Ge,[2,57]),{51:[1,263],67:Et,82:219,116:$t,117:Ot,118:Jt},e(Ge,[2,64]),e(Ge,[2,59]),e(Ge,[2,60]),e(Ge,[2,61]),{109:[1,264]},e(Ge,[2,63]),e(Ge,[2,65]),{66:[1,265],67:Et,82:219,116:$t,117:Ot,118:Jt},e(Ge,[2,67]),e(Ge,[2,68]),e(Ge,[2,70]),e(Ge,[2,69]),e(Ge,[2,71]),e([10,44,60,89,102,105,106,109,111,114,115,116],[2,85]),e(et,[2,78]),{31:[1,266],67:Et,82:219,116:$t,117:Ot,118:Jt},{6:11,7:12,8:o,9:l,10:u,11:h,20:17,22:18,23:19,24:20,25:21,26:22,27:d,32:[1,267],33:24,34:p,36:f,38:g,42:28,43:39,44:m,45:40,47:41,60:y,84:k,85:C,86:S,87:E,88:_,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j,121:I,122:z,123:F,124:O,125:B},e(He,[2,53]),{43:268,44:m,45:40,47:41,60:y,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j},e(qt,[2,121],{106:kr}),e(Ir,[2,130],{108:270,10:Hr,60:Ei,84:Li,105:Cr,109:Ti,110:vt,111:wt,112:Wt}),e(Lr,[2,132]),e(Lr,[2,134]),e(Lr,[2,135]),e(Lr,[2,136]),e(Lr,[2,137]),e(Lr,[2,138]),e(Lr,[2,139]),e(Lr,[2,140]),e(Lr,[2,141]),e(qt,[2,122],{106:kr}),{10:[1,271]},e(qt,[2,123],{106:kr}),{10:[1,272]},e(jr,[2,129]),e(qt,[2,105],{106:kr}),e(qt,[2,106],{113:113,44:m,60:y,89:b,102:v,105:x,106:w,109:R,111:$,114:M,115:N,116:j}),e(qt,[2,110]),e(qt,[2,112],{10:[1,273]}),e(qt,[2,113]),{98:[1,274]},{51:[1,275]},{62:[1,276]},{66:[1,277]},{8:H,9:J,11:te,21:278},e(U,[2,34]),e(He,[2,52]),{10:Hr,60:Ei,84:Li,105:Cr,107:279,108:243,109:Ti,110:vt,111:wt,112:Wt},e(Lr,[2,133]),{14:se,44:le,60:Te,89:oe,101:280,105:fe,106:ge,109:ke,111:Ne,114:xe,115:$e,116:Me,120:88},{14:se,44:le,60:Te,89:oe,101:281,105:fe,106:ge,109:ke,111:Ne,114:xe,115:$e,116:Me,120:88},{98:[1,282]},e(qt,[2,120]),e(Ge,[2,58]),{30:283,67:Et,80:_t,81:Kt,82:172,116:$t,117:Ot,118:Jt},e(Ge,[2,66]),e(St,a,{5:284}),e(Ir,[2,131],{108:270,10:Hr,60:Ei,84:Li,105:Cr,109:Ti,110:vt,111:wt,112:Wt}),e(qt,[2,126],{120:168,10:[1,285],14:se,44:le,60:Te,89:oe,105:fe,106:ge,109:ke,111:Ne,114:xe,115:$e,116:Me}),e(qt,[2,127],{120:168,10:[1,286],14:se,44:le,60:Te,89:oe,105:fe,106:ge,109:ke,111:Ne,114:xe,115:$e,116:Me}),e(qt,[2,114]),{31:[1,287],67:Et,82:219,116:$t,117:Ot,118:Jt},{6:11,7:12,8:o,9:l,10:u,11:h,20:17,22:18,23:19,24:20,25:21,26:22,27:d,32:[1,288],33:24,34:p,36:f,38:g,42:28,43:39,44:m,45:40,47:41,60:y,84:k,85:C,86:S,87:E,88:_,89:b,102:v,105:x,106:w,109:R,111:$,113:42,114:M,115:N,116:j,121:I,122:z,123:F,124:O,125:B},{10:Hr,60:Ei,84:Li,92:289,105:Cr,107:242,108:243,109:Ti,110:vt,111:wt,112:Wt},{10:Hr,60:Ei,84:Li,92:290,105:Cr,107:242,108:243,109:Ti,110:vt,111:wt,112:Wt},e(Ge,[2,62]),e(U,[2,33]),e(qt,[2,124],{106:kr}),e(qt,[2,125],{106:kr})],defaultActions:{},parseError:s(function(fi,gt){if(gt.recoverable)this.trace(fi);else{var Mt=new Error(fi);throw Mt.hash=gt,Mt}},"parseError"),parse:s(function(fi){var gt=this,Mt=[0],kt=[],Qt=[null],Pe=[],ii=this.table,Ye="",ti=0,Na=0,ka=0,Ao=2,Qo=1,Zo=Pe.slice.call(arguments,1),nn=Object.create(this.lexer),fs={yy:{}};for(var gs in this.yy)Object.prototype.hasOwnProperty.call(this.yy,gs)&&(fs.yy[gs]=this.yy[gs]);nn.setInput(fi,fs.yy),fs.yy.lexer=nn,fs.yy.parser=this,typeof nn.yylloc>"u"&&(nn.yylloc={});var ms=nn.yylloc;Pe.push(ms);var An=nn.options&&nn.options.ranges;typeof fs.yy.parseError=="function"?this.parseError=fs.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function We(ut){Mt.length=Mt.length-2*ut,Qt.length=Qt.length-ut,Pe.length=Pe.length-ut}s(We,"popStack");function q(){var ut;return ut=kt.pop()||nn.lex()||Qo,typeof ut!="number"&&(ut instanceof Array&&(kt=ut,ut=kt.pop()),ut=gt.symbols_[ut]||ut),ut}s(q,"lex");for(var T,A,D,P,V,Z,re={},Q,ae,we,Ue;;){if(D=Mt[Mt.length-1],this.defaultActions[D]?P=this.defaultActions[D]:((T===null||typeof T>"u")&&(T=q()),P=ii[D]&&ii[D][T]),typeof P>"u"||!P.length||!P[0]){var je="";Ue=[];for(Q in ii[D])this.terminals_[Q]&&Q>Ao&&Ue.push("'"+this.terminals_[Q]+"'");nn.showPosition?je="Parse error on line "+(ti+1)+`: +`+nn.showPosition()+` +Expecting `+Ue.join(", ")+", got '"+(this.terminals_[T]||T)+"'":je="Parse error on line "+(ti+1)+": Unexpected "+(T==Qo?"end of input":"'"+(this.terminals_[T]||T)+"'"),this.parseError(je,{text:nn.match,token:this.terminals_[T]||T,line:nn.yylineno,loc:ms,expected:Ue})}if(P[0]instanceof Array&&P.length>1)throw new Error("Parse Error: multiple actions possible at state: "+D+", token: "+T);switch(P[0]){case 1:Mt.push(T),Qt.push(nn.yytext),Pe.push(nn.yylloc),Mt.push(P[1]),T=null,A?(T=A,A=null):(Na=nn.yyleng,Ye=nn.yytext,ti=nn.yylineno,ms=nn.yylloc,ka>0&&ka--);break;case 2:if(ae=this.productions_[P[1]][1],re.$=Qt[Qt.length-ae],re._$={first_line:Pe[Pe.length-(ae||1)].first_line,last_line:Pe[Pe.length-1].last_line,first_column:Pe[Pe.length-(ae||1)].first_column,last_column:Pe[Pe.length-1].last_column},An&&(re._$.range=[Pe[Pe.length-(ae||1)].range[0],Pe[Pe.length-1].range[1]]),Z=this.performAction.apply(re,[Ye,Na,ti,fs.yy,P[1],Qt,Pe].concat(Zo)),typeof Z<"u")return Z;ae&&(Mt=Mt.slice(0,-1*ae*2),Qt=Qt.slice(0,-1*ae),Pe=Pe.slice(0,-1*ae)),Mt.push(this.productions_[P[1]][0]),Qt.push(re.$),Pe.push(re._$),we=ii[Mt[Mt.length-2]][Mt[Mt.length-1]],Mt.push(we);break;case 3:return!0}}return!0},"parse")},ki=(function(){var fi={EOF:1,parseError:s(function(gt,Mt){if(this.yy.parser)this.yy.parser.parseError(gt,Mt);else throw new Error(gt)},"parseError"),setInput:s(function(gt,Mt){return this.yy=Mt||this.yy||{},this._input=gt,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var gt=this._input[0];this.yytext+=gt,this.yyleng++,this.offset++,this.match+=gt,this.matched+=gt;var Mt=gt.match(/(?:\r\n?|\n).*/g);return Mt?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),gt},"input"),unput:s(function(gt){var Mt=gt.length,kt=gt.split(/(?:\r\n?|\n)/g);this._input=gt+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-Mt),this.offset-=Mt;var Qt=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),kt.length-1&&(this.yylineno-=kt.length-1);var Pe=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:kt?(kt.length===Qt.length?this.yylloc.first_column:0)+Qt[Qt.length-kt.length].length-kt[0].length:this.yylloc.first_column-Mt},this.options.ranges&&(this.yylloc.range=[Pe[0],Pe[0]+this.yyleng-Mt]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(gt){this.unput(this.match.slice(gt))},"less"),pastInput:s(function(){var gt=this.matched.substr(0,this.matched.length-this.match.length);return(gt.length>20?"...":"")+gt.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var gt=this.match;return gt.length<20&&(gt+=this._input.substr(0,20-gt.length)),(gt.substr(0,20)+(gt.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var gt=this.pastInput(),Mt=new Array(gt.length+1).join("-");return gt+this.upcomingInput()+` +`+Mt+"^"},"showPosition"),test_match:s(function(gt,Mt){var kt,Qt,Pe;if(this.options.backtrack_lexer&&(Pe={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(Pe.yylloc.range=this.yylloc.range.slice(0))),Qt=gt[0].match(/(?:\r\n?|\n).*/g),Qt&&(this.yylineno+=Qt.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:Qt?Qt[Qt.length-1].length-Qt[Qt.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+gt[0].length},this.yytext+=gt[0],this.match+=gt[0],this.matches=gt,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(gt[0].length),this.matched+=gt[0],kt=this.performAction.call(this,this.yy,this,Mt,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),kt)return kt;if(this._backtrack){for(var ii in Pe)this[ii]=Pe[ii];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var gt,Mt,kt,Qt;this._more||(this.yytext="",this.match="");for(var Pe=this._currentRules(),ii=0;iiMt[0].length)){if(Mt=kt,Qt=ii,this.options.backtrack_lexer){if(gt=this.test_match(kt,Pe[ii]),gt!==!1)return gt;if(this._backtrack){Mt=!1;continue}else return!1}else if(!this.options.flex)break}return Mt?(gt=this.test_match(Mt,Pe[Qt]),gt!==!1?gt:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var gt=this.next();return gt||this.lex()},"lex"),begin:s(function(gt){this.conditionStack.push(gt)},"begin"),popState:s(function(){var gt=this.conditionStack.length-1;return gt>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(gt){return gt=this.conditionStack.length-1-Math.abs(gt||0),gt>=0?this.conditionStack[gt]:"INITIAL"},"topState"),pushState:s(function(gt){this.begin(gt)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:s(function(gt,Mt,kt,Qt){var Pe=Qt;switch(kt){case 0:return this.begin("acc_title"),34;case 1:return this.popState(),"acc_title_value";case 2:return this.begin("acc_descr"),36;case 3:return this.popState(),"acc_descr_value";case 4:this.begin("acc_descr_multiline");break;case 5:this.popState();break;case 6:return"acc_descr_multiline_value";case 7:return this.pushState("shapeData"),Mt.yytext="",40;case 8:return this.pushState("shapeDataStr"),40;case 9:return this.popState(),40;case 10:let ii=/\n\s*/g;return Mt.yytext=Mt.yytext.replace(ii,"
    "),40;case 11:return 40;case 12:this.popState();break;case 13:this.begin("callbackname");break;case 14:this.popState();break;case 15:this.popState(),this.begin("callbackargs");break;case 16:return 95;case 17:this.popState();break;case 18:return 96;case 19:return"MD_STR";case 20:this.popState();break;case 21:this.begin("md_string");break;case 22:return"STR";case 23:this.popState();break;case 24:this.pushState("string");break;case 25:return 84;case 26:return 102;case 27:return 85;case 28:return 104;case 29:return 86;case 30:return 87;case 31:return 97;case 32:this.begin("click");break;case 33:this.popState();break;case 34:return 88;case 35:return gt.lex.firstGraph()&&this.begin("dir"),12;case 36:return gt.lex.firstGraph()&&this.begin("dir"),12;case 37:return gt.lex.firstGraph()&&this.begin("dir"),12;case 38:return gt.lex.firstGraph()&&this.begin("dir"),12;case 39:return 27;case 40:return 32;case 41:return 98;case 42:return 98;case 43:return 98;case 44:return 98;case 45:return this.popState(),13;case 46:return this.popState(),14;case 47:return this.popState(),14;case 48:return this.popState(),14;case 49:return this.popState(),14;case 50:return this.popState(),14;case 51:return this.popState(),14;case 52:return this.popState(),14;case 53:return this.popState(),14;case 54:return this.popState(),14;case 55:return this.popState(),14;case 56:return 121;case 57:return 122;case 58:return 123;case 59:return 124;case 60:return 125;case 61:return 78;case 62:return 105;case 63:return 111;case 64:return 46;case 65:return 60;case 66:return 44;case 67:return 8;case 68:return 106;case 69:return 115;case 70:return this.popState(),77;case 71:return this.pushState("edgeText"),75;case 72:return 119;case 73:return this.popState(),77;case 74:return this.pushState("thickEdgeText"),75;case 75:return 119;case 76:return this.popState(),77;case 77:return this.pushState("dottedEdgeText"),75;case 78:return 119;case 79:return 77;case 80:return this.popState(),53;case 81:return"TEXT";case 82:return this.pushState("ellipseText"),52;case 83:return this.popState(),55;case 84:return this.pushState("text"),54;case 85:return this.popState(),57;case 86:return this.pushState("text"),56;case 87:return 58;case 88:return this.pushState("text"),67;case 89:return this.popState(),64;case 90:return this.pushState("text"),63;case 91:return this.popState(),49;case 92:return this.pushState("text"),48;case 93:return this.popState(),69;case 94:return this.popState(),71;case 95:return 117;case 96:return this.pushState("trapText"),68;case 97:return this.pushState("trapText"),70;case 98:return 118;case 99:return 67;case 100:return 90;case 101:return"SEP";case 102:return 89;case 103:return 115;case 104:return 111;case 105:return 44;case 106:return 109;case 107:return 114;case 108:return 116;case 109:return this.popState(),62;case 110:return this.pushState("text"),62;case 111:return this.popState(),51;case 112:return this.pushState("text"),50;case 113:return this.popState(),31;case 114:return this.pushState("text"),29;case 115:return this.popState(),66;case 116:return this.pushState("text"),65;case 117:return"TEXT";case 118:return"QUOTE";case 119:return 9;case 120:return 10;case 121:return 11}},"anonymous"),rules:[/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:@\{)/,/^(?:["])/,/^(?:["])/,/^(?:[^\"]+)/,/^(?:[^}^"]+)/,/^(?:\})/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:[^`"]+)/,/^(?:[`]["])/,/^(?:["][`])/,/^(?:[^"]+)/,/^(?:["])/,/^(?:["])/,/^(?:style\b)/,/^(?:default\b)/,/^(?:linkStyle\b)/,/^(?:interpolate\b)/,/^(?:classDef\b)/,/^(?:class\b)/,/^(?:href[\s])/,/^(?:click[\s]+)/,/^(?:[\s\n])/,/^(?:[^\s\n]*)/,/^(?:flowchart-elk\b)/,/^(?:swimlane-beta\b)/,/^(?:graph\b)/,/^(?:flowchart\b)/,/^(?:subgraph\b)/,/^(?:end\b\s*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:(\r?\n)*\s*\n)/,/^(?:\s*LR\b)/,/^(?:\s*RL\b)/,/^(?:\s*TB\b)/,/^(?:\s*BT\b)/,/^(?:\s*TD\b)/,/^(?:\s*BR\b)/,/^(?:\s*<)/,/^(?:\s*>)/,/^(?:\s*\^)/,/^(?:\s*v\b)/,/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:.*direction\s+TD[^\n]*)/,/^(?:[^\s\"]+@(?=[^\{\"]))/,/^(?:[0-9]+)/,/^(?:#)/,/^(?::::)/,/^(?::)/,/^(?:&)/,/^(?:;)/,/^(?:,)/,/^(?:\*)/,/^(?:\s*[xo<]?--+[-xo>]\s*)/,/^(?:\s*[xo<]?--\s*)/,/^(?:[^-]|-(?!-)+)/,/^(?:\s*[xo<]?==+[=xo>]\s*)/,/^(?:\s*[xo<]?==\s*)/,/^(?:[^=]|=(?!))/,/^(?:\s*[xo<]?-?\.+-[xo>]?\s*)/,/^(?:\s*[xo<]?-\.\s*)/,/^(?:[^\.]|\.(?!))/,/^(?:\s*~~[\~]+\s*)/,/^(?:[-/\)][\)])/,/^(?:[^\(\)\[\]\{\}]|!\)+)/,/^(?:\(-)/,/^(?:\]\))/,/^(?:\(\[)/,/^(?:\]\])/,/^(?:\[\[)/,/^(?:\[\|)/,/^(?:>)/,/^(?:\)\])/,/^(?:\[\()/,/^(?:\)\)\))/,/^(?:\(\(\()/,/^(?:[\\(?=\])][\]])/,/^(?:\/(?=\])\])/,/^(?:\/(?!\])|\\(?!\])|[^\\\[\]\(\)\{\}\/]+)/,/^(?:\[\/)/,/^(?:\[\\)/,/^(?:<)/,/^(?:>)/,/^(?:\^)/,/^(?:\\\|)/,/^(?:v\b)/,/^(?:\*)/,/^(?:#)/,/^(?:&)/,/^(?:([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|-(?=[^\>\-\.])|(?!))+)/,/^(?:-)/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\|)/,/^(?:\|)/,/^(?:\))/,/^(?:\()/,/^(?:\])/,/^(?:\[)/,/^(?:(\}))/,/^(?:\{)/,/^(?:[^\[\]\(\)\{\}\|\"]+)/,/^(?:")/,/^(?:(\r?\n)+)/,/^(?:\s)/,/^(?:$)/],conditions:{shapeDataEndBracket:{rules:[21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},shapeDataStr:{rules:[9,10,21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},shapeData:{rules:[8,11,12,21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},callbackargs:{rules:[17,18,21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},callbackname:{rules:[14,15,16,21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},href:{rules:[21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},click:{rules:[21,24,33,34,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},dottedEdgeText:{rules:[21,24,76,78,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},thickEdgeText:{rules:[21,24,73,75,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},edgeText:{rules:[21,24,70,72,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},trapText:{rules:[21,24,79,82,84,86,90,92,93,94,95,96,97,110,112,114,116],inclusive:!1},ellipseText:{rules:[21,24,79,80,81,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},text:{rules:[21,24,79,82,83,84,85,86,89,90,91,92,96,97,109,110,111,112,113,114,115,116,117],inclusive:!1},vertex:{rules:[21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},dir:{rules:[21,24,45,46,47,48,49,50,51,52,53,54,55,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},acc_descr_multiline:{rules:[5,6,21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},acc_descr:{rules:[3,21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},acc_title:{rules:[1,21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},md_string:{rules:[19,20,21,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},string:{rules:[21,22,23,24,79,82,84,86,90,92,96,97,110,112,114,116],inclusive:!1},INITIAL:{rules:[0,2,4,7,13,21,24,25,26,27,28,29,30,31,32,35,36,37,38,39,40,41,42,43,44,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,73,74,76,77,79,82,84,86,87,88,90,92,96,97,98,99,100,101,102,103,104,105,106,107,108,110,112,114,116,118,119,120,121],inclusive:!0}}};return fi})();di.lexer=ki;function qr(){this.yy={}}return s(qr,"Parser"),qr.prototype=di,di.Parser=qr,new qr})(),I8.parser=I8,lK=I8}),cK,eGe,o8t=G(()=>{"use strict";s8t(),cK=Object.assign({},lK),cK.parse=e=>{let t=e.replace(/}\s*\n/g,`} +`);return lK.parse(t)},eGe=cK}),yv,Lk=G(()=>{"use strict";yv=s(()=>` + /* Font Awesome icon styling - consolidated */ + .label-icon { + display: inline-block; + height: 1em; + overflow: visible; + vertical-align: -0.125em; + } + + .node .label-icon path { + fill: currentColor; + stroke: revert; + stroke-width: revert; + } +`,"getIconStyles")}),tGe,rGe,uK,iGe=G(()=>{"use strict";Qa(),Lk(),tGe=s((e,t)=>{let r=mE,i=r(e,"r"),n=r(e,"g"),a=r(e,"b");return Yl(i,n,a,t)},"fade"),rGe=s(e=>`.label { + font-family: ${e.fontFamily}; + color: ${e.nodeTextColor||e.textColor}; + } + .cluster-label text { + fill: ${e.titleColor}; + } + .cluster-label span { + color: ${e.titleColor}; + } + .cluster-label span p { + background-color: transparent; + } + + .label text,span { + fill: ${e.nodeTextColor||e.textColor}; + color: ${e.nodeTextColor||e.textColor}; + } + + .node rect, + .node circle, + .node ellipse, + .node polygon, + .node path { + fill: ${e.mainBkg}; + stroke: ${e.nodeBorder}; + stroke-width: ${e.strokeWidth??1}px; + } + .rough-node .label text , .node .label text, .image-shape .label, .icon-shape .label { + text-anchor: middle; + } + + .node .katex path { + fill: #000; + stroke: #000; + stroke-width: 1px; + } + + .rough-node .label,.node .label, .image-shape .label, .icon-shape .label { + text-align: center; + } + .node.clickable { + cursor: pointer; + } + + + .root .anchor path { + fill: ${e.lineColor} !important; + stroke-width: 0; + stroke: ${e.lineColor}; + } + + .arrowheadPath { + fill: ${e.arrowheadColor}; + } + + .edgePaths .path { + stroke: ${e.lineColor}; + stroke-width: ${e.strokeWidth??2}px; + } + + .flowchart-link { + stroke: ${e.lineColor}; + fill: none; + } + + .edgeLabel { + background-color: ${e.edgeLabelBackground}; + p { + background-color: ${e.edgeLabelBackground}; + } + rect { + opacity: 0.5; + background-color: ${e.edgeLabelBackground}; + fill: ${e.edgeLabelBackground}; + } + text-align: center; + } + + /* For html labels only */ + .labelBkg { + background-color: ${tGe(e.edgeLabelBackground,.5)}; + // background-color: + } + + .cluster rect { + fill: ${e.clusterBkg}; + stroke: ${e.clusterBorder}; + stroke-width: 1px; + } + + .cluster text { + fill: ${e.titleColor}; + } + + .cluster span { + color: ${e.titleColor}; + } + /* .cluster div { + color: ${e.titleColor}; + } */ + + /* Collapsed subgraph node (@{ view: collapsed }) */ + .node .collapsed-indicator { + fill: ${e.clusterBorder}; + stroke: none; + opacity: 0.6; + } + + .node .collapsed-separator { + stroke: ${e.clusterBorder}; + stroke-width: 0.75px; + } + + div.mermaidTooltip { + position: absolute; + text-align: center; + max-width: 200px; + padding: 2px; + font-family: ${e.fontFamily}; + font-size: 12px; + background: ${e.tertiaryColor}; + border: 1px solid ${e.border2}; + border-radius: 2px; + pointer-events: none; + z-index: 100; + } + + .flowchartTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${e.textColor}; + } + + rect.text { + fill: none; + stroke-width: 0; + } + + .icon-shape, .image-shape { + background-color: ${e.edgeLabelBackground}; + p { + background-color: ${e.edgeLabelBackground}; + padding: 2px; + } + .label rect { + opacity: 0.5; + background-color: ${e.edgeLabelBackground}; + fill: ${e.edgeLabelBackground}; + } + text-align: center; + } + ${yv()} +`,"getStyles"),uK=rGe}),M8={};Mr(M8,{createFlowDiagram:()=>D8,diagram:()=>nGe});var D8,nGe,N8=G(()=>{"use strict";Gr(),Er(),JSt(),a8t(),o8t(),iGe(),D8=s(({defaultLayout:e,styles:t=uK}={})=>({parser:eGe,get db(){return new t6e},renderer:JHe,styles:t,init:s(r=>{r.flowchart||(r.flowchart={});let i=xD().layout??e??r.layout;i&&MS({layout:i}),r.flowchart.arrowMarkerAbsolute=r.arrowMarkerAbsolute,MS({flowchart:{arrowMarkerAbsolute:r.arrowMarkerAbsolute}})},"init")}),"createFlowDiagram"),nGe=D8()}),aGe,sGe,l8t=G(()=>{"use strict";iGe(),aGe=s(e=>`${uK(e)} + .swimlane.cluster rect { + stroke: ${e.clusterBorder} !important; + } + [data-look="neo"].cluster rect { + filter: none; + } +`,"getStyles"),sGe=aGe}),oGe={};Mr(oGe,{diagram:()=>lGe});var lGe,c8t=G(()=>{"use strict";N8(),l8t(),lGe=D8({defaultLayout:"swimlane",styles:sGe})}),O8,cGe,u8t=G(()=>{"use strict";O8=(function(){var e=s(function(et,he,ye,be){for(ye=ye||{},be=et.length;be--;ye[et[be]]=he);return ye},"o"),t=[6,9,21,23,25,27,34,37,38,39,40,41,43,46,47,51,53,54,55],r=[2,2],i=[1,7],n=[1,9],a=[1,10],o=[1,11],l=[1,12],u=[1,30],h=[1,23],d=[1,24],p=[1,25],f=[1,26],g=[1,27],m=[1,19],y=[1,28],k=[1,29],C=[1,20],S=[1,18],E=[1,21],_=[1,22],b=[2,6],v=[6,9,21,23,25,27,33,34,37,38,39,40,41,43,46,47,51,53,54,55],x=[1,36],w=[1,37],R=[1,38],$=[1,39],M=[1,40],N=[6,9,12,14,16,19,20,21,23,25,27,33,34,37,38,39,40,41,43,46,47,50,51,53,54,55,69,70,71,72,73],j=[1,46],I=[1,47],z=[1,57],F=[43,51,53,54,55,74,75],O=[1,70],B=[1,68],Y=[1,65],U=[1,69],H=[1,71],J=[6,9,12,16,21,23,25,27,33,34,37,38,39,40,41,43,44,45,46,47,51,52,53,54,55,69,70,71,72,73],te=[1,78],ie=[1,77],ue=[1,76],se=[69,70,71,72,73],le=[1,91],Te=[6,9,45,50],oe=[6,9,12,44,45,50,51,52],fe=[1,101],ge=[1,100],ke=[1,99],Ne=[18,61],xe=[1,110],$e=[1,109],Me=[20,43,51,53,54,55],He=[18,61,64,66],Ge={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,ER_DIAGRAM:4,document:5,EOF:6,line:7,statement:8,NEWLINE:9,entityName:10,relSpec:11,COLON:12,role:13,STYLE_SEPARATOR:14,idList:15,BLOCK_START:16,attributes:17,BLOCK_STOP:18,SQS:19,SQE:20,title:21,title_value:22,acc_title:23,acc_title_value:24,acc_descr:25,acc_descr_value:26,acc_descr_multiline_value:27,direction:28,classDefStatement:29,classStatement:30,styleStatement:31,subgraphHeader:32,END:33,SUBGRAPH:34,separator:35,subgraphTitle:36,direction_tb:37,direction_bt:38,direction_rl:39,direction_lr:40,CLASSDEF:41,stylesOpt:42,UNICODE_TEXT:43,STYLE_TEXT:44,COMMA:45,CLASS:46,STYLE:47,style:48,styleComponent:49,SEMI:50,NUM:51,BRKT:52,ENTITY_NAME:53,DECIMAL_NUM:54,ENTITY_ONE:55,attribute:56,attributeType:57,attributeName:58,attributeKeyTypeList:59,attributeComment:60,ATTRIBUTE_WORD:61,"?":62,attributeKeyType:63,",":64,ATTRIBUTE_KEY:65,COMMENT:66,cardinality:67,relType:68,ZERO_OR_ONE:69,ZERO_OR_MORE:70,ONE_OR_MORE:71,ONLY_ONE:72,MD_PARENT:73,NON_IDENTIFYING:74,IDENTIFYING:75,WORD:76,$accept:0,$end:1},terminals_:{2:"error",4:"ER_DIAGRAM",6:"EOF",9:"NEWLINE",12:"COLON",14:"STYLE_SEPARATOR",16:"BLOCK_START",18:"BLOCK_STOP",19:"SQS",20:"SQE",21:"title",22:"title_value",23:"acc_title",24:"acc_title_value",25:"acc_descr",26:"acc_descr_value",27:"acc_descr_multiline_value",33:"END",34:"SUBGRAPH",37:"direction_tb",38:"direction_bt",39:"direction_rl",40:"direction_lr",41:"CLASSDEF",43:"UNICODE_TEXT",44:"STYLE_TEXT",45:"COMMA",46:"CLASS",47:"STYLE",50:"SEMI",51:"NUM",52:"BRKT",53:"ENTITY_NAME",54:"DECIMAL_NUM",55:"ENTITY_ONE",61:"ATTRIBUTE_WORD",62:"?",64:",",65:"ATTRIBUTE_KEY",66:"COMMENT",69:"ZERO_OR_ONE",70:"ZERO_OR_MORE",71:"ONE_OR_MORE",72:"ONLY_ONE",73:"MD_PARENT",74:"NON_IDENTIFYING",75:"IDENTIFYING",76:"WORD"},productions_:[0,[3,3],[5,0],[5,2],[7,1],[7,1],[7,1],[8,5],[8,9],[8,7],[8,7],[8,4],[8,6],[8,3],[8,5],[8,1],[8,3],[8,7],[8,9],[8,6],[8,8],[8,4],[8,6],[8,2],[8,2],[8,2],[8,1],[8,1],[8,1],[8,1],[8,1],[8,3],[32,3],[32,6],[36,1],[36,2],[28,1],[28,1],[28,1],[28,1],[29,4],[15,1],[15,1],[15,3],[15,3],[30,3],[31,4],[42,1],[42,3],[48,1],[48,2],[35,1],[35,1],[35,1],[49,1],[49,1],[49,1],[49,1],[10,1],[10,1],[10,1],[10,1],[10,1],[17,1],[17,2],[56,2],[56,3],[56,3],[56,4],[57,1],[57,2],[58,1],[59,1],[59,3],[63,1],[60,1],[11,3],[67,1],[67,1],[67,1],[67,1],[67,1],[68,1],[68,1],[13,1],[13,1],[13,1]],performAction:s(function(et,he,ye,be,Ie,pe,Qe){var W=pe.length-1;switch(Ie){case 1:break;case 2:this.$=[];break;case 3:this.$=pe[W-1].concat(pe[W]);break;case 4:this.$=pe[W];break;case 5:case 6:this.$=[];break;case 7:be.addEntity(pe[W-4]),be.addEntity(pe[W-2]),be.addRelationship(pe[W-4],pe[W],pe[W-2],pe[W-3]),this.$=[pe[W-4],pe[W-2]];break;case 8:be.addEntity(pe[W-8]),be.addEntity(pe[W-4]),be.addRelationship(pe[W-8],pe[W],pe[W-4],pe[W-5]),be.setClass([pe[W-8]],pe[W-6]),be.setClass([pe[W-4]],pe[W-2]),this.$=[pe[W-8],pe[W-4]];break;case 9:be.addEntity(pe[W-6]),be.addEntity(pe[W-2]),be.addRelationship(pe[W-6],pe[W],pe[W-2],pe[W-3]),be.setClass([pe[W-6]],pe[W-4]),this.$=[pe[W-6],pe[W-2]];break;case 10:be.addEntity(pe[W-6]),be.addEntity(pe[W-4]),be.addRelationship(pe[W-6],pe[W],pe[W-4],pe[W-5]),be.setClass([pe[W-4]],pe[W-2]),this.$=[pe[W-6],pe[W-4]];break;case 11:be.addEntity(pe[W-3]),be.addAttributes(pe[W-3],pe[W-1]),this.$=[pe[W-3]];break;case 12:be.addEntity(pe[W-5]),be.addAttributes(pe[W-5],pe[W-1]),be.setClass([pe[W-5]],pe[W-3]),this.$=[pe[W-5]];break;case 13:be.addEntity(pe[W-2]),this.$=[pe[W-2]];break;case 14:be.addEntity(pe[W-4]),be.setClass([pe[W-4]],pe[W-2]),this.$=[pe[W-4]];break;case 15:be.addEntity(pe[W]),this.$=[pe[W]];break;case 16:be.addEntity(pe[W-2]),be.setClass([pe[W-2]],pe[W]),this.$=[pe[W-2]];break;case 17:be.addEntity(pe[W-6],pe[W-4]),be.addAttributes(pe[W-6],pe[W-1]),this.$=[pe[W-6]];break;case 18:be.addEntity(pe[W-8],pe[W-6]),be.addAttributes(pe[W-8],pe[W-1]),be.setClass([pe[W-8]],pe[W-3]),this.$=[pe[W-8]];break;case 19:be.addEntity(pe[W-5],pe[W-3]),this.$=[pe[W-5]];break;case 20:be.addEntity(pe[W-7],pe[W-5]),be.setClass([pe[W-7]],pe[W-2]),this.$=[pe[W-7]];break;case 21:be.addEntity(pe[W-3],pe[W-1]);break;case 22:be.addEntity(pe[W-5],pe[W-3]),be.setClass([pe[W-5]],pe[W]);break;case 23:case 24:this.$=pe[W].trim(),be.setAccTitle(this.$);break;case 25:case 26:this.$=pe[W].trim(),be.setAccDescription(this.$);break;case 27:be.subgraphDepth?this.$=pe[W]:(be.setDirection(pe[W].value),this.$=[]);break;case 31:be.subgraphDepth=(be.subgraphDepth||1)-1,this.$=be.addSubGraph({text:pe[W-2].id},pe[W-1],{text:pe[W-2].text});break;case 32:be.subgraphDepth=(be.subgraphDepth||0)+1,this.$={id:pe[W-1],text:pe[W-1]};break;case 33:be.subgraphDepth=(be.subgraphDepth||0)+1,this.$={id:pe[W-4],text:pe[W-2]};break;case 34:case 59:case 60:case 61:case 62:case 86:this.$=pe[W];break;case 35:this.$=pe[W-1]+" "+pe[W];break;case 36:this.$={stmt:"dir",value:"TB"};break;case 37:this.$={stmt:"dir",value:"BT"};break;case 38:this.$={stmt:"dir",value:"RL"};break;case 39:this.$={stmt:"dir",value:"LR"};break;case 40:this.$=pe[W-3],be.addClass(pe[W-2],pe[W-1]);break;case 41:case 42:case 63:case 72:this.$=[pe[W]];break;case 43:case 44:this.$=pe[W-2].concat([pe[W]]);break;case 45:this.$=pe[W-2],be.setClass(pe[W-1],pe[W]);break;case 46:this.$=pe[W-3],be.addCssStyles(pe[W-2],pe[W-1]);break;case 47:this.$=[pe[W]];break;case 48:pe[W-2].push(pe[W]),this.$=pe[W-2];break;case 50:this.$=pe[W-1]+pe[W];break;case 58:case 84:case 85:this.$=pe[W].replace(/"/g,"");break;case 64:pe[W].push(pe[W-1]),this.$=pe[W];break;case 65:this.$={type:pe[W-1],name:pe[W]};break;case 66:this.$={type:pe[W-2],name:pe[W-1],keys:pe[W]};break;case 67:this.$={type:pe[W-2],name:pe[W-1],comment:pe[W]};break;case 68:this.$={type:pe[W-3],name:pe[W-2],keys:pe[W-1],comment:pe[W]};break;case 69:case 71:case 74:this.$=pe[W];break;case 70:this.$=pe[W-1]+pe[W];break;case 73:pe[W-2].push(pe[W]),this.$=pe[W-2];break;case 75:this.$=pe[W].replace(/"/g,"");break;case 76:this.$={cardA:pe[W],relType:pe[W-1],cardB:pe[W-2]};break;case 77:this.$=be.Cardinality.ZERO_OR_ONE;break;case 78:this.$=be.Cardinality.ZERO_OR_MORE;break;case 79:this.$=be.Cardinality.ONE_OR_MORE;break;case 80:this.$=be.Cardinality.ONLY_ONE;break;case 81:this.$=be.Cardinality.MD_PARENT;break;case 82:this.$=be.Identification.NON_IDENTIFYING;break;case 83:this.$=be.Identification.IDENTIFYING;break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},e(t,r,{5:3}),{6:[1,4],7:5,8:6,9:i,10:8,21:n,23:a,25:o,27:l,28:13,29:14,30:15,31:16,32:17,34:u,37:h,38:d,39:p,40:f,41:g,43:m,46:y,47:k,51:C,53:S,54:E,55:_},e(t,b,{1:[2,1]}),e(v,[2,3]),e(v,[2,4]),e(v,[2,5]),e(v,[2,15],{11:31,67:35,14:[1,32],16:[1,33],19:[1,34],69:x,70:w,71:R,72:$,73:M}),{22:[1,41]},{24:[1,42]},{26:[1,43]},e(v,[2,26]),e(v,[2,27]),e(v,[2,28]),e(v,[2,29]),e(v,[2,30]),e(v,r,{5:44}),e(N,[2,58]),e(N,[2,59]),e(N,[2,60]),e(N,[2,61]),e(N,[2,62]),e(v,[2,36]),e(v,[2,37]),e(v,[2,38]),e(v,[2,39]),{15:45,43:j,44:I},{15:48,43:j,44:I},{15:49,43:j,44:I},{10:50,43:m,51:C,53:S,54:E,55:_},{10:51,43:m,51:C,53:S,54:E,55:_},{15:52,43:j,44:I},{17:53,18:[1,54],56:55,57:56,61:z},{10:58,43:m,51:C,53:S,54:E,55:_},{68:59,74:[1,60],75:[1,61]},e(F,[2,77]),e(F,[2,78]),e(F,[2,79]),e(F,[2,80]),e(F,[2,81]),e(v,[2,23]),e(v,[2,24]),e(v,[2,25]),{6:[1,63],7:5,8:6,9:i,10:8,21:n,23:a,25:o,27:l,28:13,29:14,30:15,31:16,32:17,33:[1,62],34:u,37:h,38:d,39:p,40:f,41:g,43:m,46:y,47:k,51:C,53:S,54:E,55:_},{12:O,42:64,44:B,45:Y,48:66,49:67,51:U,52:H},e(J,[2,41]),e(J,[2,42]),{15:72,43:j,44:I,45:Y},{12:O,42:73,44:B,45:Y,48:66,49:67,51:U,52:H},{6:te,9:ie,19:[1,75],35:74,50:ue},{12:[1,79],14:[1,80]},e(v,[2,16],{67:35,11:81,16:[1,82],45:Y,69:x,70:w,71:R,72:$,73:M}),{18:[1,83]},e(v,[2,13]),{17:84,18:[2,63],56:55,57:56,61:z},{58:85,61:[1,86]},{61:[2,69],62:[1,87]},{20:[1,88]},{67:89,69:x,70:w,71:R,72:$,73:M},e(se,[2,82]),e(se,[2,83]),e(v,[2,31]),e(v,b),{6:te,9:ie,35:90,45:le,50:ue},{43:[1,92],44:[1,93]},e(Te,[2,47],{49:94,12:O,44:B,51:U,52:H}),e(oe,[2,49]),e(oe,[2,54]),e(oe,[2,55]),e(oe,[2,56]),e(oe,[2,57]),e(v,[2,45],{45:Y}),{6:te,9:ie,35:95,45:le,50:ue},e(v,[2,32]),{10:97,36:96,43:m,51:C,53:S,54:E,55:_},e(v,[2,51]),e(v,[2,52]),e(v,[2,53]),{13:98,43:fe,53:ge,76:ke},{15:102,43:j,44:I},{10:103,43:m,51:C,53:S,54:E,55:_},{17:104,18:[1,105],56:55,57:56,61:z},e(v,[2,11]),{18:[2,64]},e(Ne,[2,65],{59:106,60:107,63:108,65:xe,66:$e}),e([18,61,65,66],[2,71]),{61:[2,70]},e(v,[2,21],{14:[1,112],16:[1,111]}),e([43,51,53,54,55],[2,76]),e(v,[2,40]),{12:O,44:B,48:113,49:67,51:U,52:H},e(J,[2,43]),e(J,[2,44]),e(oe,[2,50]),e(v,[2,46]),{10:115,20:[1,114],43:m,51:C,53:S,54:E,55:_},e(Me,[2,34]),e(v,[2,7]),e(v,[2,84]),e(v,[2,85]),e(v,[2,86]),{12:[1,116],45:Y},{12:[1,118],14:[1,117]},{18:[1,119]},e(v,[2,14]),e(Ne,[2,66],{60:120,64:[1,121],66:$e}),e(Ne,[2,67]),e(He,[2,72]),e(Ne,[2,75]),e(He,[2,74]),{17:122,18:[1,123],56:55,57:56,61:z},{15:124,43:j,44:I},e(Te,[2,48],{49:94,12:O,44:B,51:U,52:H}),{6:te,9:ie,35:125,50:ue},e(Me,[2,35]),{13:126,43:fe,53:ge,76:ke},{15:127,43:j,44:I},{13:128,43:fe,53:ge,76:ke},e(v,[2,12]),e(Ne,[2,68]),{63:129,65:xe},{18:[1,130]},e(v,[2,19]),e(v,[2,22],{16:[1,131],45:Y}),e(v,[2,33]),e(v,[2,10]),{12:[1,132],45:Y},e(v,[2,9]),e(He,[2,73]),e(v,[2,17]),{17:133,18:[1,134],56:55,57:56,61:z},{13:135,43:fe,53:ge,76:ke},{18:[1,136]},e(v,[2,20]),e(v,[2,8]),e(v,[2,18])],defaultActions:{84:[2,64],87:[2,70]},parseError:s(function(et,he){if(he.recoverable)this.trace(et);else{var ye=new Error(et);throw ye.hash=he,ye}},"parseError"),parse:s(function(et){var he=this,ye=[0],be=[],Ie=[null],pe=[],Qe=this.table,W="",Ae=0,K=0,de=0,ee=2,ve=1,X=pe.slice.call(arguments,1),me=Object.create(this.lexer),Ce={yy:{}};for(var Be in this.yy)Object.prototype.hasOwnProperty.call(this.yy,Be)&&(Ce.yy[Be]=this.yy[Be]);me.setInput(et,Ce.yy),Ce.yy.lexer=me,Ce.yy.parser=this,typeof me.yylloc>"u"&&(me.yylloc={});var Re=me.yylloc;pe.push(Re);var Ke=me.options&&me.options.ranges;typeof Ce.yy.parseError=="function"?this.parseError=Ce.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function De(_t){ye.length=ye.length-2*_t,Ie.length=Ie.length-_t,pe.length=pe.length-_t}s(De,"popStack");function Ze(){var _t;return _t=be.pop()||me.lex()||ve,typeof _t!="number"&&(_t instanceof Array&&(be=_t,_t=be.pop()),_t=he.symbols_[_t]||_t),_t}s(Ze,"lex");for(var Fe,Je,Le,ct,qe,mt,St={},Lt,Bt,qt,tr;;){if(Le=ye[ye.length-1],this.defaultActions[Le]?ct=this.defaultActions[Le]:((Fe===null||typeof Fe>"u")&&(Fe=Ze()),ct=Qe[Le]&&Qe[Le][Fe]),typeof ct>"u"||!ct.length||!ct[0]){var Et="";tr=[];for(Lt in Qe[Le])this.terminals_[Lt]&&Lt>ee&&tr.push("'"+this.terminals_[Lt]+"'");me.showPosition?Et="Parse error on line "+(Ae+1)+`: +`+me.showPosition()+` +Expecting `+tr.join(", ")+", got '"+(this.terminals_[Fe]||Fe)+"'":Et="Parse error on line "+(Ae+1)+": Unexpected "+(Fe==ve?"end of input":"'"+(this.terminals_[Fe]||Fe)+"'"),this.parseError(Et,{text:me.match,token:this.terminals_[Fe]||Fe,line:me.yylineno,loc:Re,expected:tr})}if(ct[0]instanceof Array&&ct.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Le+", token: "+Fe);switch(ct[0]){case 1:ye.push(Fe),Ie.push(me.yytext),pe.push(me.yylloc),ye.push(ct[1]),Fe=null,Je?(Fe=Je,Je=null):(K=me.yyleng,W=me.yytext,Ae=me.yylineno,Re=me.yylloc,de>0&&de--);break;case 2:if(Bt=this.productions_[ct[1]][1],St.$=Ie[Ie.length-Bt],St._$={first_line:pe[pe.length-(Bt||1)].first_line,last_line:pe[pe.length-1].last_line,first_column:pe[pe.length-(Bt||1)].first_column,last_column:pe[pe.length-1].last_column},Ke&&(St._$.range=[pe[pe.length-(Bt||1)].range[0],pe[pe.length-1].range[1]]),mt=this.performAction.apply(St,[W,K,Ae,Ce.yy,ct[1],Ie,pe].concat(X)),typeof mt<"u")return mt;Bt&&(ye=ye.slice(0,-1*Bt*2),Ie=Ie.slice(0,-1*Bt),pe=pe.slice(0,-1*Bt)),ye.push(this.productions_[ct[1]][0]),Ie.push(St.$),pe.push(St._$),qt=Qe[ye[ye.length-2]][ye[ye.length-1]],ye.push(qt);break;case 3:return!0}}return!0},"parse")},ze=(function(){var et={EOF:1,parseError:s(function(he,ye){if(this.yy.parser)this.yy.parser.parseError(he,ye);else throw new Error(he)},"parseError"),setInput:s(function(he,ye){return this.yy=ye||this.yy||{},this._input=he,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var he=this._input[0];this.yytext+=he,this.yyleng++,this.offset++,this.match+=he,this.matched+=he;var ye=he.match(/(?:\r\n?|\n).*/g);return ye?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),he},"input"),unput:s(function(he){var ye=he.length,be=he.split(/(?:\r\n?|\n)/g);this._input=he+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-ye),this.offset-=ye;var Ie=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),be.length-1&&(this.yylineno-=be.length-1);var pe=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:be?(be.length===Ie.length?this.yylloc.first_column:0)+Ie[Ie.length-be.length].length-be[0].length:this.yylloc.first_column-ye},this.options.ranges&&(this.yylloc.range=[pe[0],pe[0]+this.yyleng-ye]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(he){this.unput(this.match.slice(he))},"less"),pastInput:s(function(){var he=this.matched.substr(0,this.matched.length-this.match.length);return(he.length>20?"...":"")+he.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var he=this.match;return he.length<20&&(he+=this._input.substr(0,20-he.length)),(he.substr(0,20)+(he.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var he=this.pastInput(),ye=new Array(he.length+1).join("-");return he+this.upcomingInput()+` +`+ye+"^"},"showPosition"),test_match:s(function(he,ye){var be,Ie,pe;if(this.options.backtrack_lexer&&(pe={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(pe.yylloc.range=this.yylloc.range.slice(0))),Ie=he[0].match(/(?:\r\n?|\n).*/g),Ie&&(this.yylineno+=Ie.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:Ie?Ie[Ie.length-1].length-Ie[Ie.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+he[0].length},this.yytext+=he[0],this.match+=he[0],this.matches=he,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(he[0].length),this.matched+=he[0],be=this.performAction.call(this,this.yy,this,ye,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),be)return be;if(this._backtrack){for(var Qe in pe)this[Qe]=pe[Qe];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var he,ye,be,Ie;this._more||(this.yytext="",this.match="");for(var pe=this._currentRules(),Qe=0;Qeye[0].length)){if(ye=be,Ie=Qe,this.options.backtrack_lexer){if(he=this.test_match(be,pe[Qe]),he!==!1)return he;if(this._backtrack){ye=!1;continue}else return!1}else if(!this.options.flex)break}return ye?(he=this.test_match(ye,pe[Ie]),he!==!1?he:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var he=this.next();return he||this.lex()},"lex"),begin:s(function(he){this.conditionStack.push(he)},"begin"),popState:s(function(){var he=this.conditionStack.length-1;return he>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(he){return he=this.conditionStack.length-1-Math.abs(he||0),he>=0?this.conditionStack[he]:"INITIAL"},"topState"),pushState:s(function(he){this.begin(he)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(he,ye,be,Ie){var pe=Ie;switch(be){case 0:return this.begin("acc_title"),23;case 1:return this.popState(),"acc_title_value";case 2:return this.begin("acc_descr"),25;case 3:return this.popState(),"acc_descr_value";case 4:this.begin("acc_descr_multiline");break;case 5:this.popState();break;case 6:return"acc_descr_multiline_value";case 7:return 37;case 8:return 38;case 9:return 39;case 10:return 40;case 11:break;case 12:return 9;case 13:return 53;case 14:return 76;case 15:return 4;case 16:return this.begin("block"),16;case 17:return 52;case 18:return 52;case 19:return 45;case 20:return 14;case 21:return 12;case 22:break;case 23:return 65;case 24:return 61;case 25:return 61;case 26:this.begin("block_bq");break;case 27:return 61;case 28:this.popState();break;case 29:return 66;case 30:break;case 31:return this.popState(),18;case 32:return ye.yytext[0];case 33:return 19;case 34:return 20;case 35:return this.begin("style"),47;case 36:return this.popState(),9;case 37:break;case 38:return 12;case 39:return 45;case 40:return 52;case 41:return this.begin("style"),41;case 42:return 46;case 43:return 34;case 44:return 33;case 45:return 69;case 46:return 71;case 47:return 71;case 48:return 71;case 49:return 69;case 50:return 69;case 51:return 70;case 52:return 70;case 53:return 70;case 54:return 70;case 55:return 70;case 56:return 71;case 57:return 70;case 58:return 71;case 59:return 72;case 60:return 72;case 61:return 54;case 62:return 72;case 63:return 72;case 64:return 72;case 65:return 55;case 66:return 51;case 67:return 72;case 68:return 69;case 69:return 70;case 70:return 71;case 71:return 73;case 72:return 74;case 73:return 75;case 74:return 75;case 75:return 74;case 76:return 74;case 77:return 74;case 78:return 44;case 79:return 50;case 80:return 43;case 81:return ye.yytext[0];case 82:return 6}},"anonymous"),rules:[/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:[ \t\r]+)/i,/^(?:[\n]+)/i,/^(?:"[^"%\r\n\v\b\\]+")/i,/^(?:"[^"]*")/i,/^(?:erDiagram\b)/i,/^(?:\{)/i,/^(?:#)/i,/^(?:#)/i,/^(?:,)/i,/^(?::::)/i,/^(?::)/i,/^(?:\s+)/i,/^(?:\b((?:PK)|(?:FK)|(?:UK))\b)/i,/^(?:([^\s]*)[~].*[~]([^\s]*))/i,/^(?:([\*A-Za-z_\u00C0-\uFFFF][A-Za-z0-9\-\_\[\]\(\)\.,\u00C0-\uFFFF\*]*))/i,/^(?:[`])/i,/^(?:[^`]+)/i,/^(?:[`])/i,/^(?:"[^"]*")/i,/^(?:[\n]+)/i,/^(?:\})/i,/^(?:.)/i,/^(?:\[)/i,/^(?:\])/i,/^(?:style\b)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?::)/i,/^(?:,)/i,/^(?:#)/i,/^(?:classDef\b)/i,/^(?:class\b)/i,/^(?:subgraph\b)/i,/^(?:end\b\s*)/i,/^(?:one or zero\b)/i,/^(?:one or more\b)/i,/^(?:one or many\b)/i,/^(?:1\+)/i,/^(?:\|o\b)/i,/^(?:zero or one\b)/i,/^(?:zero or more\b)/i,/^(?:zero or many\b)/i,/^(?:0\+)/i,/^(?:\}o\b)/i,/^(?:many\(0\))/i,/^(?:many\(1\))/i,/^(?:many\b)/i,/^(?:\}\|)/i,/^(?:one\b)/i,/^(?:only one\b)/i,/^(?:[0-9]+\.[0-9]+)/i,/^(?:1(?=\s+[A-Za-z_"']))/i,/^(?:1(?=\s+[0-9]))/i,/^(?:1(?=(--|\.\.|\.-|-\.)))/i,/^(?:1\b)/i,/^(?:[0-9]+)/i,/^(?:\|\|)/i,/^(?:o\|)/i,/^(?:o\{)/i,/^(?:\|\{)/i,/^(?:u(?=[\.\-\|]))/i,/^(?:\.\.)/i,/^(?:--)/i,/^(?:to\b)/i,/^(?:optionally to\b)/i,/^(?:\.-)/i,/^(?:-\.)/i,/^(?:([^\x00-\x7F]|\w|-|\*)+)/i,/^(?:;)/i,/^(?:([^\x00-\x7F]|\w|-|\*|\.)+)/i,/^(?:.)/i,/^(?:$)/i],conditions:{style:{rules:[36,37,38,39,40,78,79],inclusive:!1},acc_descr_multiline:{rules:[5,6],inclusive:!1},acc_descr:{rules:[3],inclusive:!1},acc_title:{rules:[1],inclusive:!1},block_bq:{rules:[27,28],inclusive:!1},block:{rules:[22,23,24,25,26,29,30,31,32],inclusive:!1},INITIAL:{rules:[0,2,4,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,33,34,35,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,80,81,82],inclusive:!0}}};return et})();Ge.lexer=ze;function Ve(){this.yy={}}return s(Ve,"Parser"),Ve.prototype=Ge,Ge.Parser=Ve,new Ve})(),O8.parser=O8,cGe=O8}),uGe,h8t=G(()=>{"use strict";var e;Nt(),Er(),mi(),fn(),Ar(),uGe=(e=class{constructor(){this.entities=new Map,this.relationships=[],this.classes=new Map,this.subgraphDepth=0,this.subGraphs=[],this.subGraphLookup=new Map,this.subCount=0,this.direction="TB",this.Cardinality={ZERO_OR_ONE:"ZERO_OR_ONE",ZERO_OR_MORE:"ZERO_OR_MORE",ONE_OR_MORE:"ONE_OR_MORE",ONLY_ONE:"ONLY_ONE",MD_PARENT:"MD_PARENT"},this.Identification={NON_IDENTIFYING:"NON_IDENTIFYING",IDENTIFYING:"IDENTIFYING"},this.setAccTitle=bn,this.getAccTitle=Ln,this.setAccDescription=In,this.getAccDescription=Mn,this.setDiagramTitle=Dn,this.getDiagramTitle=xn,this.getConfig=s(()=>it().er,"getConfig"),this.clear(),this.addEntity=this.addEntity.bind(this),this.addAttributes=this.addAttributes.bind(this),this.addRelationship=this.addRelationship.bind(this),this.setDirection=this.setDirection.bind(this),this.addCssStyles=this.addCssStyles.bind(this),this.addClass=this.addClass.bind(this),this.setClass=this.setClass.bind(this),this.setAccTitle=this.setAccTitle.bind(this),this.setAccDescription=this.setAccDescription.bind(this),this.addSubGraph=this.addSubGraph.bind(this)}addEntity(r,i=""){return this.entities.has(r)?!this.entities.get(r)?.alias&&i&&(this.entities.get(r).alias=i,Ee.info(`Add alias '${i}' to entity '${r}'`)):(this.entities.set(r,{id:`entity-${r}-${this.entities.size}`,label:r,attributes:[],alias:i,shape:"erBox",look:it().look??"default",cssClasses:"default",cssStyles:[],labelType:"markdown"}),Ee.info("Added new entity :",r)),this.entities.get(r)}getEntity(r){return this.entities.get(r)}getEntities(){return this.entities}getClasses(){return this.classes}addAttributes(r,i){let n=this.addEntity(r),a;for(a=i.length-1;a>=0;a--)i[a].keys||(i[a].keys=[]),i[a].comment||(i[a].comment=""),n.attributes.push(i[a]),Ee.debug("Added attribute ",i[a].name)}addRelationship(r,i,n,a){let o;if(this.subGraphLookup.has(r))o=r;else{let h=this.addEntity(r);if(!h)return;o=h.id}let l;if(this.subGraphLookup.has(n))l=n;else{let h=this.addEntity(n);if(!h)return;l=h.id}let u={entityA:o,roleA:i,entityB:l,relSpec:a};this.relationships.push(u),Ee.debug("Added new relationship :",u)}getRelationships(){return this.relationships}getDirection(){return this.direction}setDirection(r){this.direction=r}getCompiledStyles(r){let i=[];for(let n of r){let a=this.classes.get(n);a?.styles&&(i=[...i,...a.styles??[]].map(o=>o.trim())),a?.textStyles&&(i=[...i,...a.textStyles??[]].map(o=>o.trim()))}return i}addCssStyles(r,i){for(let n of r){let a=this.entities.get(n),o=this.subGraphLookup.get(n);if(i){if(a)for(let l of i)a.cssStyles.push(l);if(o){o.cssStyles||(o.cssStyles=[]);for(let l of i)o.cssStyles.push(l)}}}}addClass(r,i){r.forEach(n=>{let a=this.classes.get(n);a===void 0&&(a={id:n,styles:[],textStyles:[]},this.classes.set(n,a)),i&&i.forEach(function(o){if(/color/.exec(o)){let l=o.replace("fill","bgFill");a.textStyles.push(l)}a.styles.push(o)})})}addSubGraph(r,i,n){let a=r.text.trim(),o=n.text,l=s(p=>{let f=new Set,g;return{nodeList:p.filter(m=>{if(m?.stmt)return m.stmt==="dir"&&(g=m.value),!1;if(typeof m!="string")return!1;let y=m.trim();return!y||f.has(y)?!1:(f.add(y),!0)}),dir:g}},"uniq")(i.flat()),u=l.nodeList,h=l.dir;a=a??"subGraph"+this.subCount,o=o||"",o=this.sanitizeText(o),this.subCount=this.subCount+1;let d={id:a,nodes:u,title:o.trim(),classes:[],cssStyles:[],dir:h,labelType:this.sanitizeNodeLabelType(n?.type)};return Ee.info("Adding",d.id,d.nodes,d.dir),d.nodes=this.makeUniq(d,this.subGraphs).nodes,this.subGraphs.push(d),this.subGraphLookup.set(a,d),a}getSubGraphs(){return this.subGraphs}setClass(r,i){for(let n of r){let a=this.entities.get(n);if(a)for(let l of i)a.cssClasses+=" "+l;let o=this.subGraphLookup.get(n);if(o)for(let l of i)o.classes.push(l)}}subgraphNodeCache(r){let i=new Set;for(let n of r)for(let a of n.nodes)i.add(a);return i}makeUniq(r,i){let n=this.subgraphNodeCache(i),a=[];return r.nodes.forEach((o,l)=>{n.has(o)?Ee.warn(`Entity '${o}' already belongs to another subgraph and will be ignored`):a.push(r.nodes[l])}),{nodes:a}}sanitizeText(r){return Vt.sanitizeText(r,it())}sanitizeNodeLabelType(r){switch(r){case"markdown":case"string":case"text":return r;default:return"markdown"}}clear(){this.entities=new Map,this.classes=new Map,this.relationships=[],this.subGraphs=[],this.subGraphLookup=new Map,this.subCount=0,this.subgraphDepth=0,pn()}getData(){let r=[],i=[],n=it(),a=this.getSubGraphs(),o=new Map,l=new Map;for(let p=a.length-1;p>=0;p--){let f=a[p];f.nodes.length>0&&l.set(f.id,!0);for(let g of f.nodes)o.set(g,f.id)}for(let p=a.length-1;p>=0;p--){let f=a[p];r.push({id:f.id,label:f.title,labelStyle:"",labelType:f.labelType,parentId:o.get(f.id),padding:8,cssCompiledStyles:this.getCompiledStyles(f.classes),cssStyles:f.cssStyles,cssClasses:f.classes.join(" "),shape:"rect",dir:f.dir,isGroup:!0,look:n.look})}let u=new Set(a.map(p=>p.id)),h=0;for(let p of this.entities.keys()){if(u.has(p))continue;let f=this.entities.get(p);f&&(f.cssCompiledStyles=this.getCompiledStyles(f.cssClasses.split(" ")),f.colorIndex=h++,r.push({...f,parentId:o.get(p),isGroup:!1}))}let d=0;for(let p of this.relationships){let f={id:gg(p.entityA,p.entityB,{prefix:"id",counter:d++}),type:"normal",curve:"basis",start:p.entityA,end:p.entityB,label:p.roleA,labelpos:"c",thickness:"normal",classes:"relationshipLine",arrowTypeStart:p.relSpec.cardB.toLowerCase(),arrowTypeEnd:p.relSpec.cardA.toLowerCase(),pattern:p.relSpec.relType=="IDENTIFYING"?"solid":"dashed",look:n.look,labelType:"markdown"};i.push(f)}return{nodes:r,edges:i,other:{},config:n,direction:this.direction}}},s(e,"ErDB"),e)}),hGe={};Mr(hGe,{draw:()=>dGe});var dGe,d8t=G(()=>{"use strict";Er(),Nt(),l2(),xm(),Tm(),Ar(),pi(),dGe=s(async function(e,t,r,i){Ee.info("REF0:"),Ee.info("Drawing er diagram (unified)",t);let{securityLevel:n,er:a,layout:o}=it(),l=i.db.getData(),u=bg(t,n);l.type=i.type,l.layoutAlgorithm=mv(o),l.config.flowchart.nodeSpacing=a?.nodeSpacing||140,l.config.flowchart.rankSpacing=a?.rankSpacing||80,l.direction=i.db.getDirection();let{config:h}=l,{look:d}=h;d==="neo"?l.markers=["only_one_neo","zero_or_one_neo","one_or_more_neo","zero_or_more_neo"]:l.markers=["only_one","zero_or_one","one_or_more","zero_or_more"],l.diagramId=t,await bm(l,u),l.layoutAlgorithm==="elk"&&u.select(".edges").lower();let p=u.selectAll('[id*="-background"]');Array.from(p).length>0&&p.each(function(){let g=zt(this),m=g.attr("id").replace("-background",""),y=u.select(`#${CSS.escape(m)}`);if(!y.empty()){let k=y.attr("transform");g.attr("transform",k)}}),ai.insertTitle(u,"erDiagramTitleText",a?.titleTopMargin??25,i.db.getDiagramTitle()),kf(u,8,"erDiagram",a?.useMaxWidth??!0)},"draw")}),hK,Ik,pGe,fGe,gGe,p8t=G(()=>{"use strict";Qa(),hK=s((e,t)=>{let r=mE,i=r(e,"r"),n=r(e,"g"),a=r(e,"b");return Yl(i,n,a,t)},"fade"),Ik=new Set(["redux-color","redux-dark-color"]),pGe=s(e=>{let{theme:t,look:r,bkgColorArray:i,borderColorArray:n}=e;if(!Ik.has(t))return"";let a=i?.length>0,o="";for(let l=0;l{let{look:t,theme:r,erEdgeLabelBackground:i,strokeWidth:n}=e;return` + ${pGe(e)} + .entityBox { + fill: ${e.mainBkg}; + stroke: ${e.nodeBorder}; + } + + .relationshipLabelBox { + fill: ${e.tertiaryColor}; + opacity: 0.7; + background-color: ${e.tertiaryColor}; + rect { + opacity: 0.5; + } + } + + .labelBkg { + background-color: ${Ik.has(r)&&i?i:hK(e.tertiaryColor,.5)}; + } + + .edgeLabel { + background-color: ${Ik.has(r)&&i?i:e.edgeLabelBackground}; + } + .edgeLabel .label rect { + fill: ${Ik.has(r)&&i?i:e.edgeLabelBackground}; + } + .edgeLabel .label text { + fill: ${e.textColor}; + } + + .edgeLabel .label { + fill: ${e.nodeBorder}; + font-size: 14px; + } + + .label { + font-family: ${e.fontFamily}; + color: ${e.nodeTextColor||e.textColor}; + } + + .edge-pattern-dashed { + stroke-dasharray: 8,8; + } + + .node rect, + .node circle, + .node ellipse, + .node polygon + { + fill: ${e.mainBkg}; + stroke: ${e.nodeBorder}; + stroke-width: ${t==="neo"?n:"1px"}; + } + + .relationshipLine { + stroke: ${e.lineColor}; + stroke-width: ${t==="neo"?n:"1px"}; + fill: none; + } + + .marker { + fill: none !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; + } + [data-look=neo].labelBkg { + background-color: ${hK(e.tertiaryColor,.5)}; + } + + .cluster rect { + fill: ${e.clusterBkg??e.mainBkg}; + stroke: ${e.clusterBorder??e.nodeBorder}; + stroke-width: 1px; + } + + .cluster text { + fill: ${e.titleColor??e.textColor}; + } + + .cluster-label text { + fill: ${e.titleColor??e.textColor}; + } +`},"getStyles"),gGe=fGe}),mGe={};Mr(mGe,{diagram:()=>yGe});var yGe,f8t=G(()=>{"use strict";u8t(),h8t(),d8t(),p8t(),yGe={parser:cGe,get db(){return new uGe},renderer:hGe,styles:gGe}});function Da(e){return typeof e=="object"&&e!==null&&typeof e.$type=="string"}function fo(e){return typeof e=="object"&&e!==null&&typeof e.$refText=="string"&&"ref"in e}function Zc(e){return typeof e=="object"&&e!==null&&typeof e.$refText=="string"&&"items"in e}function B8(e){return typeof e=="object"&&e!==null&&typeof e.name=="string"&&typeof e.type=="string"&&typeof e.path=="string"}function Cf(e){return typeof e=="object"&&e!==null&&typeof e.info=="object"&&typeof e.message=="string"}function sh(e){return typeof e=="object"&&e!==null&&Array.isArray(e.content)}function wf(e){return typeof e=="object"&&e!==null&&typeof e.tokenType=="object"}function Mk(e){return sh(e)&&typeof e.fullText=="string"}function dK(e){return typeof e=="string"?e:typeof e>"u"?"undefined":typeof e.toString=="function"?e.toString():Object.prototype.toString.call(e)}function vv(e){return!!e&&typeof e[Symbol.iterator]=="function"}function an(...e){if(e.length===1){let t=e[0];if(t instanceof eu)return t;if(vv(t))return new eu(()=>t[Symbol.iterator](),r=>r.next());if(typeof t.length=="number")return new eu(()=>({index:0}),r=>r.index1?new eu(()=>({collIndex:0,arrIndex:0}),t=>{do{if(t.iterator){let r=t.iterator.next();if(!r.done)return r;t.iterator=void 0}if(t.array){if(t.arrIndex{Da(n)&&(n.$container=e,n.$containerProperty=r,n.$containerIndex=a,t.deep&&km(n,t))}):Da(i)&&(i.$container=e,i.$containerProperty=r,t.deep&&km(i,t)))}function Ef(e,t){let r=e;for(;r;){if(t(r))return r;r=r.$container}}function pK(e,t){let r=e;for(;r;){if(t(r))return!0;r=r.$container}return!1}function lc(e){let t=Cm(e).$document;if(!t)throw new Error("AST node has no document.");return t}function Cm(e){for(;e.$container;)e=e.$container;return e}function Dk(e){return fo(e)?e.ref?[e.ref]:[]:Zc(e)?e.items.map(t=>t.ref):[]}function bv(e,t){if(!e)throw new Error("Node must be an AstNode.");let r=t?.range;return new eu(()=>({keys:Object.keys(e),keyIndex:0,arrayIndex:0}),i=>{for(;i.keyIndexbv(r,t))}function cc(e,t){if(e){if(t?.range&&!Nk(e,t.range))return new qm(e,()=>[])}else throw new Error("Root node must be an AstNode.");return new qm(e,r=>bv(r,t),{includeRoot:!0})}function Nk(e,t){if(!t)return!0;let r=e.$cstNode?.range;return r?sR(r,t):!1}function wm(e){return new eu(()=>({keys:Object.keys(e),keyIndex:0,arrayIndex:0}),t=>{for(;t.keyIndexsh(t)?t.content:[],{includeRoot:!0})}function LK(e){return Sm(e).filter(wf)}function nR(e,t){for(;e.container;)if(e=e.container,e===t)return!0;return!1}function Tv(e){return{start:{character:e.startColumn-1,line:e.startLine-1},end:{character:e.endColumn,line:e.endLine-1}}}function _m(e){if(!e)return;let{offset:t,end:r,range:i}=e;return{range:i,offset:t,end:r,length:r-t}}function aR(e,t){if(e.end.linet.end.line||e.start.line===t.end.line&&e.start.character>=t.end.character)return mh.After;let r=e.start.line>t.start.line||e.start.line===t.start.line&&e.start.character>=t.start.character,i=e.end.linemh.After}function IK(e,t,r=iL){if(e){if(t>0){let i=t-e.offset,n=e.text.charAt(i);r.test(n)||t--}return Vk(e,t)}}function oR(e,t){if(e){let r=uR(e,!0);if(r&&jk(r,t))return r;if(Mk(e)){let i=e.content.findIndex(n=>!n.hidden);for(let n=i-1;n>=0;n--){let a=e.content[n];if(jk(a,t))return a}}}}function jk(e,t){return wf(e)&&t.includes(e.tokenType.name)}function Vk(e,t){if(wf(e))return e;if(sh(e)){let r=cR(e,t,!1);if(r)return Vk(r,t)}}function lR(e,t){if(wf(e))return e;if(sh(e)){let r=cR(e,t,!0);if(r)return lR(r,t)}}function cR(e,t,r){let i=0,n=e.content.length-1,a;for(;i<=n;){let o=Math.floor((i+n)/2),l=e.content[o];if(l.offset<=t&&l.end>t)return l;l.end<=t?(a=r?l:void 0,i=o+1):n=o-1}return a}function uR(e,t=!0){for(;e.container;){let r=e.container,i=r.content.indexOf(e);for(;i>0;){i--;let n=r.content[i];if(t||!n.hidden)return n}e=r}}function MK(e,t=!0){for(;e.container;){let r=e.container,i=r.content.indexOf(e),n=r.content.length-1;for(;it.test(r))}function Rm(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function gR(e,t){let r=mR(e),i=t.match(r);return!!i&&i[0].length>0}function mR(e){typeof e=="string"&&(e=new RegExp(e));let t=e,r=e.source,i=0;function n(){let a="",o;function l(h){a+=r.substr(i,h),i+=h}s(l,"appendRaw"),L(l,"appendRaw");function u(h){a+="(?:"+r.substr(i,h)+"|$)",i+=h}for(s(u,"appendOptional"),L(u,"appendOptional");i",i)-i+1);break;default:u(2);break}break;case"[":o=/\[(?:\\.|.)*?\]/g,o.lastIndex=i,o=o.exec(r)||[],u(o[0].length);break;case"|":case"^":case"$":case"*":case"+":case"?":l(1);break;case"{":o=/\{\d+,?\d*\}/g,o.lastIndex=i,o=o.exec(r),o?l(o[0].length):u(1);break;case"(":if(r[i+1]==="?")switch(r[i+2]){case":":a+="(?:",i+=3,a+=n()+"|$)";break;case"=":a+="(?=",i+=3,a+=n()+")";break;case"!":o=i,i+=3,n(),a+=r.substr(o,i-o);break;case"<":switch(r[i+3]){case"=":case"!":o=i,i+=4,n(),a+=r.substr(o,i-o);break;default:l(r.indexOf(">",i)-i+1),a+=n()+"|$)";break}break}else l(1),a+=n()+"|$)";break;case")":return++i,a;default:u(1);break}return a}return s(n,"process2"),L(n,"process"),new RegExp(n(),e.flags)}function yR(e){return e.rules.find(t=>go(t)&&t.entry)}function vR(e){return e.rules.filter(t=>Ml(t)&&t.hidden)}function Hk(e,t){let r=new Set,i=yR(e);if(!i)return new Set(e.rules);let n=[i].concat(vR(e));for(let o of n)bR(o,r,t);let a=new Set;for(let o of e.rules)(r.has(o.name)||Ml(o)&&o.hidden)&&a.add(o);return a}function bR(e,t,r){t.add(e.name),oh(e).forEach(i=>{if(uh(i)||r&&zk(i)){let n=i.rule.ref;n&&!t.has(n.name)&&bR(n,t,r)}})}function PK(e){let t=new Set;return oh(e).forEach(r=>{_f(r)&&(go(r.type.ref)&&t.add(r.type.ref),xv(r.type.ref)&&go(r.type.ref.$container)&&t.add(r.type.ref.$container))}),t}function xR(e){if(e.terminal)return e.terminal;if(e.type.ref)return Qk(e.type.ref)?.terminal}function TR(e){return e.hidden&&!Yk(Ev(e))}function kR(e,t){return!e||!t?[]:Xk(e,t,e.astNode,!0)}function Gk(e,t,r){if(!e||!t)return;let i=Xk(e,t,e.astNode,!0);if(i.length!==0)return r!==void 0?r=Math.max(0,Math.min(r,i.length-1)):r=0,i[r]}function Xk(e,t,r,i){if(!i){let n=Ef(e.grammarSource,lh);if(n&&n.feature===t)return[e]}return sh(e)&&e.astNode===r?e.content.flatMap(n=>Xk(n,t,r,!1)):[]}function FK(e,t){return e?Kk(e,t,e?.astNode):[]}function CR(e,t,r){if(!e)return;let i=Kk(e,t,e?.astNode);if(i.length!==0)return r!==void 0?r=Math.max(0,Math.min(r,i.length-1)):r=0,i[r]}function Kk(e,t,r){if(e.astNode!==r)return[];if(ch(e.grammarSource)&&e.grammarSource.value===t)return[e];let i=Sm(e).iterator(),n,a=[];do if(n=i.next(),!n.done){let o=n.value;o.astNode===r?ch(o.grammarSource)&&o.grammarSource.value===t&&a.push(o):i.prune()}while(!n.done);return a}function wR(e){let t=e.astNode;for(;t===e.container?.astNode;){let r=Ef(e.grammarSource,lh);if(r)return r;e=e.container}}function Qk(e){let t=e;return xv(t)&&(zd(t.$container)?t=t.$container.$container:Sf(t.$container)?t=t.$container:qd(t.$container)),ER(e,t,new Map)}function ER(e,t,r){function i(n,a){let o;return Ef(n,lh)||(o=ER(a,a,r)),r.set(e,o),o}if(s(i,"go"),L(i,"go"),r.has(e))return r.get(e);r.set(e,void 0);for(let n of oh(t)){if(lh(n)&&n.feature.toLowerCase()==="name")return r.set(e,n),n;if(uh(n)&&go(n.rule.ref))return i(n,n.rule.ref);if(Fk(n)&&n.typeRef?.ref)return i(n,n.typeRef.ref)}}function SR(e){let t=e.$container;if(Af(t)){let r=t.elements,i=r.indexOf(e);for(let n=i-1;n>=0;n--){let a=r[n];if(zd(a))return a;{let o=oh(r[n]).find(zd);if(o)return o}}}if(Bk(t))return SR(t)}function zK(e,t){return e==="?"||e==="*"||Af(t)&&!!t.guardCondition}function qK(e){return e==="*"||e==="+"}function WK(e){return e==="+="}function Cv(e){return _R(e,new Set)}function _R(e,t){if(t.has(e))return!0;t.add(e);for(let r of oh(e))if(uh(r)){if(!r.rule.ref||go(r.rule.ref)&&!_R(r.rule.ref,t)||Em(r.rule.ref))return!1}else if(lh(r)||zd(r))return!1;return!!e.definition}function jK(e){return Zk(e.type,new Set)}function Zk(e,t){if(t.has(e))return!0;if(t.add(e),z8(e)||K8(e))return!1;if(tR(e))return e.types.every(r=>Zk(r,t));if(Fk(e)){if(e.primitiveType!==void 0||e.stringType!==void 0)return!0;if(e.typeRef!==void 0){let r=e.typeRef.ref;return qk(r)?Zk(r.type,t):!1}else return!1}else return!1}function wv(e){if(!Ml(e)){if(e.inferredType)return e.inferredType.name;if(e.dataType)return e.dataType;if(e.returnType){let t=e.returnType.ref;if(t)return t.name}}}function $f(e){if(Sf(e))return go(e)&&Cv(e)?e.name:wv(e)??e.name;if(Y8(e)||qk(e)||Z8(e))return e.name;if(zd(e)){let t=AR(e);if(t)return t}else if(xv(e))return e.name;throw new Error("Cannot get name of Unknown Type")}function AR(e){if(e.inferredType)return e.inferredType.name;if(e.type?.ref)return $f(e.type.ref)}function VK(e){return Ml(e)?e.type?.name??"string":go(e)&&Cv(e)?e.name:wv(e)??e.name}function RR(e){return Ml(e)?e.type?.name??"string":wv(e)??e.name}function Ev(e){let t={s:!1,i:!1,u:!1},r=Lf(e.definition,t),i=Object.entries(t).filter(([,n])=>n).map(([n])=>n).join("");return new RegExp(r,i)}function Lf(e,t){if(J8(e))return UK(e);if(eR(e))return YK(e);if(W8(e))return XK(e);if(zk(e)){let r=e.rule.ref;if(!r)throw new Error("Missing rule reference.");return Jc(Lf(r.definition),{cardinality:e.cardinality,lookahead:e.lookahead,parenthesized:e.parenthesized})}else{if(H8(e))return GK(e);if(rR(e))return HK(e);if(Q8(e)){let r=e.regex.lastIndexOf("/"),i=e.regex.substring(1,r),n=e.regex.substring(r+1);return t&&(t.i=n.includes("i"),t.s=n.includes("s"),t.u=n.includes("u")),Jc(i,{cardinality:e.cardinality,lookahead:e.lookahead,parenthesized:e.parenthesized,wrap:!1})}else{if(iR(e))return Jc(sL,{cardinality:e.cardinality,lookahead:e.lookahead,parenthesized:e.parenthesized});throw new Error(`Invalid terminal element: ${e?.$type}, ${e?.$cstNode?.text}`)}}}function UK(e){return Jc(e.elements.map(t=>Lf(t)).join("|"),{cardinality:e.cardinality,lookahead:e.lookahead,parenthesized:e.parenthesized,wrap:!1})}function YK(e){return Jc(e.elements.map(t=>Lf(t)).join(""),{cardinality:e.cardinality,lookahead:e.lookahead,parenthesized:e.parenthesized,wrap:!1})}function HK(e){return Jc(`${sL}*?${Lf(e.terminal)}`,{cardinality:e.cardinality,lookahead:e.lookahead,parenthesized:e.parenthesized})}function GK(e){return Jc(`(?!${Lf(e.terminal)})${sL}*?`,{cardinality:e.cardinality,lookahead:e.lookahead,parenthesized:e.parenthesized})}function XK(e){return e.right?Jc(`[${Jk(e.left)}-${Jk(e.right)}]`,{cardinality:e.cardinality,lookahead:e.lookahead,parenthesized:e.parenthesized,wrap:!1}):Jc(Jk(e.left),{cardinality:e.cardinality,lookahead:e.lookahead,parenthesized:e.parenthesized,wrap:!1})}function Jk(e){return Rm(e.value)}function Jc(e,t){return(t.parenthesized||t.lookahead||t.wrap!==!1)&&(e=`(${t.lookahead??(t.parenthesized?"":"?:")}${e})`),t.cardinality?`${e}${t.cardinality}`:e}function $R(e){let t=[],r=e.Grammar;for(let i of r.rules)Ml(i)&&TR(i)&&fR(Ev(i))&&t.push(i.name);return{multilineCommentRules:t,nameRegexp:iL}}function KK(e){var t=MXe.call(e,nb),r=e[nb];try{e[nb]=void 0;var i=!0}catch{}var n=DXe.call(e);return i&&(t?e[nb]=r:delete e[nb]),n}function QK(e){return BXe.call(e)}function ZK(e){return e==null?e===void 0?zXe:FXe:sse&&sse in Object(e)?NXe(e):PXe(e)}function JK(e){return e!=null&&typeof e=="object"}function eQ(e){return typeof e=="symbol"||pc(e)&&Qd(e)==qXe}function tQ(e,t){for(var r=-1,i=e==null?0:e.length,n=Array(i);++r0){if(++t>=wKe)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}function bQ(e){return function(){return e}}function xQ(e,t){for(var r=-1,i=e==null?0:e.length;++r-1}function SQ(e,t){var r=typeof e;return t=t??OKe,!!t&&(r=="number"||r!="symbol"&&BKe.test(e))&&e>-1&&e%1==0&&e-1&&e%1==0&&e<=qKe}function DQ(e){return e!=null&&dL(e.length)&&!yh(e)}function NQ(e,t,r){if(!Nl(r))return!1;var i=typeof t;return(i=="number"?iu(r)&&ow(t,r.length):i=="string"&&t in r)?lb(r[t],e):!1}function OQ(e){return hL(function(t,r){var i=-1,n=r.length,a=n>1?r[n-1]:void 0,o=n>2?r[2]:void 0;for(a=e.length>3&&typeof a=="function"?(n--,a):void 0,o&&cw(r[0],r[1],o)&&(a=n<3?void 0:a,n=1),t=Object(t);++i-1}function oZ(e,t){var r=this.__data__,i=dw(r,e);return i<0?(++this.size,r.push([e,t])):r[i][1]=t,this}function Mf(e){var t=-1,r=e==null?0:e.length;for(this.clear();++t0&&r(l)?t>1?IR(l,t-1,r,i,n):yL(n,l):i||(n[n.length]=l)}return n}function CZ(e){var t=e==null?0:e.length;return t?vL(e,1):[]}function wZ(e,t,r){var i=-1,n=e.length;t<0&&(t=-t>n?0:n+t),r=r>n?n:r,r<0&&(r+=n),n=t>r?0:r-t>>>0,t>>>=0;for(var a=Array(n);++il))return!1;var h=a.get(e),d=a.get(t);if(h&&d)return h==t&&d==e;var p=-1,f=!0,g=r&yet?new EL:void 0;for(a.set(e,t),a.set(t,e);++p=htt&&(a=SL,o=!1,t=new EL(t));e:for(;++n-1?n[a?t[o]:o]:void 0}}function WJ(e,t,r){var i=e==null?0:e.length;if(!i)return-1;var n=r==null?0:sb(r);return n<0&&(n=vtt(i+n,0)),fse(e,nu(t,3),n)}function jJ(e){return e&&e.length?e[0]:void 0}function VJ(e,t){var r=-1,i=iu(e)?Array(e.length):[];return o0(e,function(n,a,o){i[++r]=t(n,a,o)}),i}function UJ(e,t){var r=qi(e)?ab:Ttt;return r(e,nu(t,3))}function YJ(e,t){return vL(Fr(e,t),1)}function HJ(e,t){return e!=null&&_tt.call(e,t)}function GJ(e,t){return e!=null&&ooe(e,t,Att)}function XJ(e){return typeof e=="string"||!qi(e)&&pc(e)&&Qd(e)==Rtt}function KJ(e,t){return ab(t,function(r){return e[r]})}function QJ(e){return e==null?[]:$tt(e,dl(e))}function ZJ(e,t,r,i){e=iu(e)?e:Ha(e),r=r&&!i?sb(r):0;var n=e.length;return r<0&&(r=Ltt(n+r,0)),zo(e)?r<=n&&e.indexOf(t,r)>-1:!!n&&cL(e,t,r)>-1}function JJ(e,t,r){var i=e==null?0:e.length;if(!i)return-1;var n=r==null?0:sb(r);return n<0&&(n=Itt(i+n,0)),cL(e,t,n)}function eee(e){if(e==null)return!0;if(iu(e)&&(qi(e)||typeof e=="string"||typeof e.splice=="function"||hb(e)||fL(e)||uw(e)))return!e.length;var t=Km(e);if(t==Mtt||t==Dtt)return!e.size;if(ub(e))return!Sse(e).length;for(var r in e)if(Ott.call(e,r))return!1;return!0}function tee(e){return pc(e)&&Qd(e)==Btt}function ree(e){return e===void 0}function iee(e){if(typeof e!="function")throw new TypeError(ztt);return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}function nee(e,t,r,i){if(!Nl(e))return e;t=mw(t,e);for(var n=-1,a=t.length,o=a-1,l=e;l!=null&&++n=Xtt){var h=t?null:Gtt(e);if(h)return _L(h);o=!1,n=SL,u=new EL}else u=t?[]:l;e:for(;++iRv(r,t)):e instanceof vo&&yo(t,e)?!1:e instanceof au?(e instanceof vo&&t.push(e),gc(e.definition,r=>Rv(r,t))):!1}function vee(e){return e instanceof jo}function uc(e){if(e instanceof vo)return"SUBRULE";if(e instanceof Rs)return"OPTION";if(e instanceof jo)return"OR";if(e instanceof gl)return"AT_LEAST_ONE";if(e instanceof ml)return"AT_LEAST_ONE_SEP";if(e instanceof Wo)return"MANY_SEP";if(e instanceof la)return"MANY";if(e instanceof Pn)return"CONSUME";throw Error("non exhaustive match")}function BR(e,t,r){return[new Rs({definition:[new Pn({terminalType:e.separator})].concat(e.definition)})].concat(t,r)}function $m(e){if(e instanceof vo)return $m(e.referencedRule);if(e instanceof Pn)return Tee(e);if(yee(e))return bee(e);if(vee(e))return xee(e);throw Error("non exhaustive match")}function bee(e){let t=[],r=e.definition,i=0,n=r.length>i,a,o=!0;for(;n&&o;)a=r[i],o=Rv(a),t=t.concat($m(a)),i=i+1,n=r.length>i;return $L(t)}function xee(e){let t=Fr(e.definition,r=>$m(r));return $L(fc(t))}function Tee(e){return[e.terminalType]}function kee(e){let t={};return ei(e,r=>{let i=new Qtt(r).startWalking();pl(t,i)}),t}function Cee(e,t){return e.name+t+goe}function $v(e){let t=e.toString();if(kw.hasOwnProperty(t))return kw[t];{let r=Ztt.pattern(t);return kw[t]=r,r}}function wee(){kw={}}function Eee(e,t=!1){try{let r=$v(e);return rC(r.value,{},r.flags.ignoreCase)}catch(r){if(r.message===moe)t&&DR(`${Cw} Unable to optimize: < ${e.toString()} > + Complement Sets cannot be automatically optimized. + This will disable the lexer's first char optimizations. + See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#COMPLEMENT for details.`);else{let i="";t&&(i=` + This will disable the lexer's first char optimizations. + See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#REGEXP_PARSING for details.`),tC(`${Cw} + Failed parsing: < ${e.toString()} > + Using the @chevrotain/regexp-to-ast library + Please open an issue at: https://github.com/chevrotain/chevrotain/issues`+i)}}return[]}function rC(e,t,r){switch(e.type){case"Disjunction":for(let n=0;n{if(typeof u=="number")Lv(u,t,r);else{let h=u;if(r===!0)for(let d=h.from;d<=h.to;d++)Lv(d,t,r);else{for(let d=h.from;d<=h.to&&d=Tb){let d=h.from>=Tb?h.from:Tb,p=h.to,f=hh(d),g=hh(p);for(let m=f;m<=g;m++)t[m]=m}}}});break;case"Group":rC(o.value,t,r);break;default:throw Error("Non Exhaustive Match")}let l=o.quantifier!==void 0&&o.quantifier.atLeast===0;if(o.type==="Group"&&iC(o)===!1||o.type!=="Group"&&l===!1)break}break;default:throw Error("non exhaustive match!")}return Ha(t)}function Lv(e,t,r){let i=hh(e);t[i]=i,r===!0&&See(e,t)}function See(e,t){let r=String.fromCharCode(e),i=r.toUpperCase();if(i!==r){let n=hh(i.charCodeAt(0));t[n]=n}else{let n=r.toLowerCase();if(n!==r){let a=hh(n.charCodeAt(0));t[a]=a}}}function PR(e,t){return Qm(e.value,r=>{if(typeof r=="number")return yo(t,r);{let i=r;return Qm(t,n=>i.from<=n&&n<=i.to)!==void 0}})}function iC(e){let t=e.quantifier;return t&&t.atLeast===0?!0:e.value?qi(e.value)?gc(e.value,iC):iC(e.value):!1}function nC(e,t){if(t instanceof RegExp){let r=$v(t),i=new Jtt(e);return i.visit(r),i.found}else return Qm(t,r=>yo(e,r.charCodeAt(0)))!==void 0}function _ee(e,t){t=RL(t,{debug:!1,safeMode:!1,positionTracking:"full",lineTerminatorCharacters:["\r",` +`],tracer:L((S,E)=>E(),"tracer")});let r=t.tracer;r("initCharCodeToOptimizedIndexMap",()=>{Gee()});let i;r("Reject Lexer.NA",()=>{i=xw(e,S=>S[c0]===bo.NA)});let n=!1,a;r("Transform Patterns",()=>{n=!1,a=Fr(i,S=>{let E=S[c0];if(vh(E)){let _=E.source;return _.length===1&&_!=="^"&&_!=="$"&&_!=="."&&!E.ignoreCase?_:_.length===2&&_[0]==="\\"&&!yo(["d","D","s","S","t","r","n","t","0","c","b","B","f","v","w","W"],_[1])?_[1]:FR(E)}else{if(yh(E))return n=!0,{exec:E};if(typeof E=="object")return n=!0,E;if(typeof E=="string"){if(E.length===1)return E;{let _=E.replace(/[\\^$.*+?()[\]{}|]/g,"\\$&"),b=new RegExp(_);return FR(b)}}else throw Error("non exhaustive match")}})});let o,l,u,h,d;r("misc mapping",()=>{o=Fr(i,S=>S.tokenTypeIdx),l=Fr(i,S=>{let E=S.GROUP;if(E!==bo.SKIPPED){if(zo(E))return E;if(bh(E))return!1;throw Error("non exhaustive match")}}),u=Fr(i,S=>{let E=S.LONGER_ALT;if(E)return qi(E)?Fr(E,_=>doe(i,_)):[doe(i,E)]}),h=Fr(i,S=>S.PUSH_MODE),d=Fr(i,S=>Kr(S,"POP_MODE"))});let p;r("Line Terminator Handling",()=>{let S=WR(t.lineTerminatorCharacters);p=Fr(i,E=>!1),t.positionTracking!=="onlyOffset"&&(p=Fr(i,E=>Kr(E,"LINE_BREAKS")?!!E.LINE_BREAKS:qR(E,S)===!1&&nC(S,E.PATTERN)))});let f,g,m,y;r("Misc Mapping #2",()=>{f=Fr(i,zR),g=Fr(a,Yee),m=fl(i,(S,E)=>{let _=E.GROUP;return zo(_)&&_!==bo.SKIPPED&&(S[_]=[]),S},{}),y=Fr(a,(S,E)=>({pattern:a[E],longerAlt:u[E],canLineTerminator:p[E],isCustom:f[E],short:g[E],group:l[E],push:h[E],pop:d[E],tokenTypeIdx:o[E],tokenType:i[E]}))});let k=!0,C=[];return t.safeMode||r("First Char Optimization",()=>{C=fl(i,(S,E,_)=>{if(typeof E.PATTERN=="string"){let b=E.PATTERN.charCodeAt(0),v=hh(b);aC(S,v,y[_])}else if(qi(E.START_CHARS_HINT)){let b;ei(E.START_CHARS_HINT,v=>{let x=typeof v=="string"?v.charCodeAt(0):v,w=hh(x);b!==w&&(b=w,aC(S,w,y[_]))})}else if(vh(E.PATTERN))if(E.PATTERN.unicode)k=!1,t.ensureOptimizations&&tC(`${Cw} Unable to analyze < ${E.PATTERN.toString()} > pattern. + The regexp unicode flag is not currently supported by the regexp-to-ast library. + This will disable the lexer's first char optimizations. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNICODE_OPTIMIZE`);else{let b=Eee(E.PATTERN,t.ensureOptimizations);En(b)&&(k=!1),ei(b,v=>{aC(S,v,y[_])})}else t.ensureOptimizations&&tC(`${Cw} TokenType: <${E.name}> is using a custom token pattern without providing parameter. + This will disable the lexer's first char optimizations. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_OPTIMIZE`),k=!1;return S},[])}),{emptyGroups:m,patternIdxToConfig:y,charCodeToPatternIdxToConfig:C,hasCustom:n,canBeOptimized:k}}function Aee(e,t){let r=[],i=$ee(e);r=r.concat(i.errors);let n=Lee(i.valid),a=n.valid;return r=r.concat(n.errors),r=r.concat(Ree(a)),r=r.concat(Bee(a)),r=r.concat(Pee(a,t)),r=r.concat(Fee(a)),r}function Ree(e){let t=[],r=Ol(e,i=>vh(i[c0]));return t=t.concat(Iee(r)),t=t.concat(Dee(r)),t=t.concat(Nee(r)),t=t.concat(Oee(r)),t=t.concat(Mee(r)),t}function $ee(e){let t=Ol(e,n=>!Kr(n,c0)),r=Fr(t,n=>({message:"Token Type: ->"+n.name+"<- missing static 'PATTERN' property",type:xa.MISSING_PATTERN,tokenTypes:[n]})),i=bw(e,t);return{errors:r,valid:i}}function Lee(e){let t=Ol(e,n=>{let a=n[c0];return!vh(a)&&!yh(a)&&!Kr(a,"exec")&&!zo(a)}),r=Fr(t,n=>({message:"Token Type: ->"+n.name+"<- static 'PATTERN' can only be a RegExp, a Function matching the {CustomPatternMatcherFunc} type or an Object matching the {ICustomPattern} interface.",type:xa.INVALID_PATTERN,tokenTypes:[n]})),i=bw(e,t);return{errors:r,valid:i}}function Iee(e){const i=class i extends nw{constructor(){super(...arguments),this.found=!1}visitEndAnchor(a){this.found=!0}};s(i,"EndAnchorFinder"),L(i,"EndAnchorFinder");let t=i,r=Ol(e,n=>{let a=n.PATTERN;try{let o=$v(a),l=new t;return l.visit(o),l.found}catch{return ert.test(a.source)}});return Fr(r,n=>({message:`Unexpected RegExp Anchor Error: + Token Type: ->`+n.name+`<- static 'PATTERN' cannot contain end of input anchor '$' + See chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS for details.`,type:xa.EOI_ANCHOR_FOUND,tokenTypes:[n]}))}function Mee(e){let t=Ol(e,r=>r.PATTERN.test(""));return Fr(t,r=>({message:"Token Type: ->"+r.name+"<- static 'PATTERN' must not match an empty string",type:xa.EMPTY_MATCH_PATTERN,tokenTypes:[r]}))}function Dee(e){const i=class i extends nw{constructor(){super(...arguments),this.found=!1}visitStartAnchor(a){this.found=!0}};s(i,"StartAnchorFinder"),L(i,"StartAnchorFinder");let t=i,r=Ol(e,n=>{let a=n.PATTERN;try{let o=$v(a),l=new t;return l.visit(o),l.found}catch{return trt.test(a.source)}});return Fr(r,n=>({message:`Unexpected RegExp Anchor Error: + Token Type: ->`+n.name+`<- static 'PATTERN' cannot contain start of input anchor '^' + See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS for details.`,type:xa.SOI_ANCHOR_FOUND,tokenTypes:[n]}))}function Nee(e){let t=Ol(e,r=>{let i=r[c0];return i instanceof RegExp&&(i.multiline||i.global)});return Fr(t,r=>({message:"Token Type: ->"+r.name+"<- static 'PATTERN' may NOT contain global('g') or multiline('m')",type:xa.UNSUPPORTED_FLAGS_FOUND,tokenTypes:[r]}))}function Oee(e){let t=[],r=Fr(e,n=>fl(e,(a,o)=>(n.PATTERN.source===o.PATTERN.source&&!yo(t,o)&&o.PATTERN!==bo.NA&&(t.push(o),a.push(o)),a),[]));r=vb(r);let i=Ol(r,n=>n.length>1);return Fr(i,n=>{let a=Fr(n,o=>o.name);return{message:`The same RegExp pattern ->${mc(n).PATTERN}<-has been used in all of the following Token Types: ${a.join(", ")} <-`,type:xa.DUPLICATE_PATTERNS_FOUND,tokenTypes:n}})}function Bee(e){let t=Ol(e,r=>{if(!Kr(r,"GROUP"))return!1;let i=r.GROUP;return i!==bo.SKIPPED&&i!==bo.NA&&!zo(i)});return Fr(t,r=>({message:"Token Type: ->"+r.name+"<- static 'GROUP' can only be Lexer.SKIPPED/Lexer.NA/A String",type:xa.INVALID_GROUP_TYPE_FOUND,tokenTypes:[r]}))}function Pee(e,t){let r=Ol(e,i=>i.PUSH_MODE!==void 0&&!yo(t,i.PUSH_MODE));return Fr(r,i=>({message:`Token Type: ->${i.name}<- static 'PUSH_MODE' value cannot refer to a Lexer Mode ->${i.PUSH_MODE}<-which does not exist`,type:xa.PUSH_MODE_DOES_NOT_EXIST,tokenTypes:[i]}))}function Fee(e){let t=[],r=fl(e,(i,n,a)=>{let o=n.PATTERN;return o===bo.NA||(zo(o)?i.push({str:o,idx:a,tokenType:n}):vh(o)&&qee(o)&&i.push({str:o.source,idx:a,tokenType:n})),i},[]);return ei(e,(i,n)=>{ei(r,({str:a,idx:o,tokenType:l})=>{if(n${l.name}<- can never be matched. +Because it appears AFTER the Token Type ->${i.name}<-in the lexer's definition. +See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNREACHABLE`;t.push({message:u,type:xa.UNREACHABLE_PATTERN,tokenTypes:[i,l]})}})}),t}function zee(e,t){if(vh(t)){if(Wee(t))return!1;let r=t.exec(e);return r!==null&&r.index===0}else{if(yh(t))return t(e,0,[],{});if(Kr(t,"exec"))return t.exec(e,0,[],{});if(typeof t=="string")return t===e;throw Error("non exhaustive match")}}function qee(e){return Qm([".","\\","[","]","|","^","$","(",")","?","*","+","{"],t=>e.source.indexOf(t)!==-1)===void 0}function Wee(e){return/(\(\?=)|(\(\?!)|(\(\?<=)|(\(\? property in its definition +`,type:xa.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE}),Kr(e,ww)||i.push({message:"A MultiMode Lexer cannot be initialized without a <"+ww+`> property in its definition +`,type:xa.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY}),Kr(e,ww)&&Kr(e,xb)&&!Kr(e.modes,e.defaultMode)&&i.push({message:`A MultiMode Lexer cannot be initialized with a ${xb}: <${e.defaultMode}>which does not exist +`,type:xa.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST}),Kr(e,ww)&&ei(e.modes,(n,a)=>{ei(n,(o,l)=>{if(bh(o))i.push({message:`A Lexer cannot be initialized using an undefined Token Type. Mode:<${a}> at index: <${l}> +`,type:xa.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED});else if(Kr(o,"LONGER_ALT")){let u=qi(o.LONGER_ALT)?o.LONGER_ALT:[o.LONGER_ALT];ei(u,h=>{!bh(h)&&!yo(n,h)&&i.push({message:`A MultiMode Lexer cannot be initialized with a longer_alt <${h.name}> on token <${o.name}> outside of mode <${a}> +`,type:xa.MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE})})}})}),i}function Vee(e,t,r){let i=[],n=!1,a=vb(fc(Ha(e.modes))),o=xw(a,u=>u[c0]===bo.NA),l=WR(r);return t&&ei(o,u=>{let h=qR(u,l);if(h!==!1){let d={message:Hee(u,h),type:h.issue,tokenType:u};i.push(d)}else Kr(u,"LINE_BREAKS")?u.LINE_BREAKS===!0&&(n=!0):nC(l,u.PATTERN)&&(n=!0)}),t&&!n&&i.push({message:`Warning: No LINE_BREAKS Found. + This Lexer has been defined to track line and column information, + But none of the Token Types can be identified as matching a line terminator. + See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#LINE_BREAKS + for details.`,type:xa.NO_LINE_BREAKS_FLAGS}),i}function Uee(e){let t={},r=dl(e);return ei(r,i=>{let n=e[i];if(qi(n))t[i]=[];else throw Error("non exhaustive match")}),t}function zR(e){let t=e.PATTERN;if(vh(t))return!1;if(yh(t)||Kr(t,"exec"))return!0;if(zo(t))return!1;throw Error("non exhaustive match")}function Yee(e){return zo(e)&&e.length===1?e.charCodeAt(0):!1}function qR(e,t){if(Kr(e,"LINE_BREAKS"))return!1;if(vh(e.PATTERN)){try{nC(t,e.PATTERN)}catch(r){return{issue:xa.IDENTIFY_TERMINATOR,errMsg:r.message}}return!1}else{if(zo(e.PATTERN))return!1;if(zR(e))return{issue:xa.CUSTOM_LINE_BREAK};throw Error("non exhaustive match")}}function Hee(e,t){if(t.issue===xa.IDENTIFY_TERMINATOR)return`Warning: unable to identify line terminator usage in pattern. + The problem is in the <${e.name}> Token Type + Root cause: ${t.errMsg}. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#IDENTIFY_TERMINATOR`;if(t.issue===xa.CUSTOM_LINE_BREAK)return`Warning: A Custom Token Pattern should specify the option. + The problem is in the <${e.name}> Token Type + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_LINE_BREAK`;throw Error("non exhaustive match")}function WR(e){return Fr(e,t=>zo(t)?t.charCodeAt(0):t)}function aC(e,t,r){e[t]===void 0?e[t]=[r]:e[t].push(r)}function hh(e){return e255?255+~~(e/255):e}}function Lm(e,t){let r=e.tokenTypeIdx;return r===t.tokenTypeIdx?!0:t.isParent===!0&&t.categoryMatchesMap[r]===!0}function Iv(e,t){return e.tokenTypeIdx===t.tokenTypeIdx}function Im(e){let t=Xee(e);Kee(t),Zee(t),Qee(t),ei(t,r=>{r.isParent=r.categoryMatches.length>0})}function Xee(e){let t=_s(e),r=e,i=!0;for(;i;){r=vb(fc(Fr(r,a=>a.CATEGORIES)));let n=bw(r,t);t=t.concat(n),En(n)?i=!1:r=n}return t}function Kee(e){ei(e,t=>{VR(t)||(voe[yoe]=t,t.tokenTypeIdx=yoe++),UR(t)&&!qi(t.CATEGORIES)&&(t.CATEGORIES=[t.CATEGORIES]),UR(t)||(t.CATEGORIES=[]),Jee(t)||(t.categoryMatches=[]),ete(t)||(t.categoryMatchesMap={})})}function Qee(e){ei(e,t=>{t.categoryMatches=[],ei(t.categoryMatchesMap,(r,i)=>{t.categoryMatches.push(voe[i].tokenTypeIdx)})})}function Zee(e){ei(e,t=>{jR([],t)})}function jR(e,t){ei(e,r=>{t.categoryMatchesMap[r.tokenTypeIdx]=!0}),ei(t.CATEGORIES,r=>{let i=e.concat(t);yo(i,r)||jR(i,r)})}function VR(e){return Kr(e,"tokenTypeIdx")}function UR(e){return Kr(e,"CATEGORIES")}function Jee(e){return Kr(e,"categoryMatches")}function ete(e){return Kr(e,"categoryMatchesMap")}function tte(e){return Kr(e,"tokenTypeIdx")}function Of(e){return YR(e)?e.LABEL:e.name}function YR(e){return zo(e.LABEL)&&e.LABEL!==""}function Mm(e){return rte(e)}function rte(e){let t=e.pattern,r={};if(r.name=e.name,bh(t)||(r.PATTERN=t),Kr(e,irt))throw`The parent property is no longer supported. +See: https://github.com/chevrotain/chevrotain/issues/564#issuecomment-349062346 for details.`;return Kr(e,boe)&&(r.CATEGORIES=e[boe]),Im([r]),Kr(e,xoe)&&(r.LABEL=e[xoe]),Kr(e,Toe)&&(r.GROUP=e[Toe]),Kr(e,Coe)&&(r.POP_MODE=e[Coe]),Kr(e,koe)&&(r.PUSH_MODE=e[koe]),Kr(e,woe)&&(r.LONGER_ALT=e[woe]),Kr(e,Eoe)&&(r.LINE_BREAKS=e[Eoe]),Kr(e,Soe)&&(r.START_CHARS_HINT=e[Soe]),r}function Mv(e,t,r,i,n,a,o,l){return{image:t,startOffset:r,endOffset:i,startLine:n,endLine:a,startColumn:o,endColumn:l,tokenTypeIdx:e.tokenTypeIdx,tokenType:e}}function HR(e,t){return Lm(e,t)}function ite(e,t){let r=new art(e,t);return r.resolveRefs(),r.errors}function sC(e,t,r=[]){r=_s(r);let i=[],n=0;function a(l){return l.concat(As(e,n+1))}s(a,"remainingPathWith"),L(a,"remainingPathWith");function o(l){let u=sC(a(l),t,r);return i.concat(u)}for(s(o,"getAlternativesForProd"),L(o,"getAlternativesForProd");r.length{En(u.definition)===!1&&(i=o(u.definition))}),i;if(l instanceof Pn)r.push(l.terminalType);else throw Error("non exhaustive match")}n++}return i.push({partialPath:r,suffixDef:As(e,n)}),i}function GR(e,t,r,i){let n="EXIT_NONE_TERMINAL",a=[n],o="EXIT_ALTERNATIVE",l=!1,u=t.length,h=u-i-1,d=[],p=[];for(p.push({idx:-1,def:e,ruleStack:[],occurrenceStack:[]});!En(p);){let f=p.pop();if(f===o){l&&l0(p).idx<=h&&p.pop();continue}let g=f.def,m=f.idx,y=f.ruleStack,k=f.occurrenceStack;if(En(g))continue;let C=g[0];if(C===n){let S={idx:m,def:As(g),ruleStack:bb(y),occurrenceStack:bb(k)};p.push(S)}else if(C instanceof Pn)if(m=0;S--){let E=C.definition[S],_={idx:m,def:E.definition.concat(As(g)),ruleStack:y,occurrenceStack:k};p.push(_),p.push(o)}else if(C instanceof qo)p.push({idx:m,def:C.definition.concat(As(g)),ruleStack:y,occurrenceStack:k});else if(C instanceof Zm)p.push(nte(C,m,y,k));else throw Error("non exhaustive match")}return d}function nte(e,t,r,i){let n=_s(r);n.push(e.name);let a=_s(i);return a.push(1),{idx:t,def:e.definition,ruleStack:n,occurrenceStack:a}}function oC(e){if(e instanceof Rs||e==="Option")return ca.OPTION;if(e instanceof la||e==="Repetition")return ca.REPETITION;if(e instanceof gl||e==="RepetitionMandatory")return ca.REPETITION_MANDATORY;if(e instanceof ml||e==="RepetitionMandatoryWithSeparator")return ca.REPETITION_MANDATORY_WITH_SEPARATOR;if(e instanceof Wo||e==="RepetitionWithSeparator")return ca.REPETITION_WITH_SEPARATOR;if(e instanceof jo||e==="Alternation")return ca.ALTERNATION;throw Error("non exhaustive match")}function XR(e){let{occurrence:t,rule:r,prodType:i,maxLookahead:n}=e,a=oC(i);return a===ca.ALTERNATION?Dv(t,r,n):Nv(t,r,a,n)}function ate(e,t,r,i,n,a){let o=Dv(e,t,r),l=ZR(o)?Iv:Lm;return a(o,i,l,n)}function ste(e,t,r,i,n,a){let o=Nv(e,t,n,r),l=ZR(o)?Iv:Lm;return a(o[0],l,i)}function ote(e,t,r,i){let n=e.length,a=gc(e,o=>gc(o,l=>l.length===1));if(t)return function(o){let l=Fr(o,u=>u.GATE);for(let u=0;ufc(u)),l=fl(o,(u,h,d)=>(ei(h,p=>{Kr(u,p.tokenTypeIdx)||(u[p.tokenTypeIdx]=d),ei(p.categoryMatches,f=>{Kr(u,f)||(u[f]=d)})}),u),{});return function(){let u=this.LA(1);return l[u.tokenTypeIdx]}}else return function(){for(let o=0;oa.length===1),n=e.length;if(i&&!r){let a=fc(e);if(a.length===1&&En(a[0].categoryMatches)){let o=a[0].tokenTypeIdx;return function(){return this.LA(1).tokenTypeIdx===o}}else{let o=fl(a,(l,u,h)=>(l[u.tokenTypeIdx]=!0,ei(u.categoryMatches,d=>{l[d]=!0}),l),[]);return function(){let l=this.LA(1);return o[l.tokenTypeIdx]===!0}}}else return function(){e:for(let a=0;asC([o],1)),i=KR(r.length),n=Fr(r,o=>{let l={};return ei(o,u=>{let h=lC(u.partialPath);ei(h,d=>{l[d]=!0})}),l}),a=r;for(let o=1;o<=t;o++){let l=a;a=KR(l.length);for(let u=0;u{let k=lC(y.partialPath);ei(k,C=>{n[u][C]=!0})})}}}}return i}function Dv(e,t,r,i){let n=new Roe(e,ca.ALTERNATION,i);return t.accept(n),QR(n.result,r)}function Nv(e,t,r,i){let n=new Roe(e,r);t.accept(n);let a=n.result,o=new urt(t,e,r).startWalking(),l=new qo({definition:a}),u=new qo({definition:o});return QR([l,u],i)}function cC(e,t){e:for(let r=0;r{let n=t[i];return r===n||n.categoryMatchesMap[r.tokenTypeIdx]})}function ZR(e){return gc(e,t=>gc(t,r=>gc(r,i=>En(i.categoryMatches))))}function hte(e){let t=e.lookaheadStrategy.validate({rules:e.rules,tokenTypes:e.tokenTypes,grammarName:e.grammarName});return Fr(t,r=>Object.assign({type:Vo.CUSTOM_LOOKAHEAD_VALIDATION},r))}function dte(e,t,r,i){let n=Bl(e,u=>pte(u,r)),a=Cte(e,t,r),o=Bl(e,u=>bte(u,r)),l=Bl(e,u=>gte(u,e,i,r));return n.concat(a,o,l)}function pte(e,t){let r=new hrt;e.accept(r);let i=r.allProductions,n=Ett(i,fte),a=yc(n,o=>o.length>1);return Fr(Ha(a),o=>{let l=mc(o),u=t.buildDuplicateFoundError(e,o),h=uc(l),d={message:u,type:Vo.DUPLICATE_PRODUCTIONS,ruleName:e.name,dslName:h,occurrence:l.idx},p=JR(l);return p&&(d.parameter=p),d})}function fte(e){return`${uc(e)}_#_${e.idx}_#_${JR(e)}`}function JR(e){return e instanceof Pn?e.terminalType.name:e instanceof vo?e.nonTerminalName:""}function gte(e,t,r,i){let n=[];if(fl(t,(a,o)=>o.name===e.name?a+1:a,0)>1){let a=i.buildDuplicateRuleNameError({topLevelRule:e,grammarName:r});n.push({message:a,type:Vo.DUPLICATE_RULE_NAME,ruleName:e.name})}return n}function mte(e,t,r){let i=[],n;return yo(t,e)||(n=`Invalid rule override, rule: ->${e}<- cannot be overridden in the grammar: ->${r}<-as it is not defined in any of the super grammars `,i.push({message:n,type:Vo.INVALID_RULE_OVERRIDE,ruleName:e})),i}function e$(e,t,r,i=[]){let n=[],a=Ov(t.definition);if(En(a))return[];{let o=e.name;yo(a,e)&&n.push({message:r.buildLeftRecursionError({topLevelRule:e,leftRecursionPath:i}),type:Vo.LEFT_RECURSION,ruleName:o});let l=bw(a,i.concat([e])),u=Bl(l,h=>{let d=_s(i);return d.push(h),e$(e,h,r,d)});return n.concat(u)}}function Ov(e){let t=[];if(En(e))return t;let r=mc(e);if(r instanceof vo)t.push(r.referencedRule);else if(r instanceof qo||r instanceof Rs||r instanceof gl||r instanceof ml||r instanceof Wo||r instanceof la)t=t.concat(Ov(r.definition));else if(r instanceof jo)t=fc(Fr(r.definition,a=>Ov(a.definition)));else if(!(r instanceof Pn))throw Error("non exhaustive match");let i=Rv(r),n=e.length>1;if(i&&n){let a=As(e);return t.concat(Ov(a))}else return t}function yte(e,t){let r=new IL;e.accept(r);let i=r.alternations;return Bl(i,n=>{let a=bb(n.definition);return Bl(a,(o,l)=>{let u=GR([o],[],Lm,1);return En(u)?[{message:t.buildEmptyAlternationError({topLevelRule:e,alternation:n,emptyChoiceIdx:l}),type:Vo.NONE_LAST_EMPTY_ALT,ruleName:e.name,occurrence:n.idx,alternative:l+1}]:[]})})}function vte(e,t,r){let i=new IL;e.accept(i);let n=i.alternations;return n=xw(n,a=>a.ignoreAmbiguities===!0),Bl(n,a=>{let o=a.idx,l=a.maxLookahead||t,u=Dv(o,e,l,a),h=Tte(u,a,e,r),d=kte(u,a,e,r);return h.concat(d)})}function bte(e,t){let r=new IL;e.accept(r);let i=r.alternations;return Bl(i,n=>n.definition.length>255?[{message:t.buildTooManyAlternativesError({topLevelRule:e,alternation:n}),type:Vo.TOO_MANY_ALTS,ruleName:e.name,occurrence:n.idx}]:[])}function xte(e,t,r){let i=[];return ei(e,n=>{let a=new drt;n.accept(a);let o=a.allProductions;ei(o,l=>{let u=oC(l),h=l.maxLookahead||t,d=l.idx,p=Nv(d,n,u,h)[0];if(En(fc(p))){let f=r.buildEmptyRepetitionError({topLevelRule:n,repetition:l});i.push({message:f,type:Vo.NO_NON_EMPTY_LOOKAHEAD,ruleName:n.name})}})}),i}function Tte(e,t,r,i){let n=[],a=fl(e,(o,l,u)=>(t.definition[u].ignoreAmbiguities===!0||ei(l,h=>{let d=[u];ei(e,(p,f)=>{u!==f&&cC(p,h)&&t.definition[f].ignoreAmbiguities!==!0&&d.push(f)}),d.length>1&&!cC(n,h)&&(n.push(h),o.push({alts:d,path:h}))}),o),[]);return Fr(a,o=>{let l=Fr(o.alts,u=>u+1);return{message:i.buildAlternationAmbiguityError({topLevelRule:r,alternation:t,ambiguityIndices:l,prefixPath:o.path}),type:Vo.AMBIGUOUS_ALTS,ruleName:r.name,occurrence:t.idx,alternatives:o.alts}})}function kte(e,t,r,i){let n=fl(e,(a,o,l)=>{let u=Fr(o,h=>({idx:l,path:h}));return a.concat(u)},[]);return vb(Bl(n,a=>{if(t.definition[a.idx].ignoreAmbiguities===!0)return[];let o=a.idx,l=a.path,u=Ol(n,h=>t.definition[h.idx].ignoreAmbiguities!==!0&&h.idx{let d=[h.idx+1,o+1],p=t.idx===0?"":t.idx;return{message:i.buildAlternationPrefixAmbiguityError({topLevelRule:r,alternation:t,ambiguityIndices:d,prefixPath:h.path}),type:Vo.AMBIGUOUS_PREFIX_ALTS,ruleName:r.name,occurrence:p,alternatives:d}})}))}function Cte(e,t,r){let i=[],n=Fr(t,a=>a.name);return ei(e,a=>{let o=a.name;if(yo(n,o)){let l=r.buildNamespaceConflictError(a);i.push({message:l,type:Vo.CONFLICT_TOKENS_RULES_NAMESPACE,ruleName:o})}}),i}function wte(e){let t=RL(e,{errMsgProvider:nrt}),r={};return ei(e.rules,i=>{r[i.name]=i}),ite(r,t.errMsgProvider)}function Ete(e){return e=RL(e,{errMsgProvider:u0}),dte(e.rules,e.tokenTypes,e.errMsgProvider,e.grammarName)}function Bv(e){return yo(Doe,e.name)}function Ste(e,t,r,i,n,a,o){let l=this.getKeyForAutomaticLookahead(i,n),u=this.firstAfterRepMap[l];if(u===void 0){let f=this.getCurrRuleFullName(),g=this.getGAstProductions()[f];u=new a(g,n).startWalking(),this.firstAfterRepMap[l]=u}let h=u.token,d=u.occurrence,p=u.isEndOfRule;this.RULE_STACK.length===1&&p&&h===void 0&&(h=Jd,d=1),!(h===void 0||d===void 0)&&this.shouldInRepetitionRecoveryBeTried(h,d,o)&&this.tryInRepetitionRecovery(e,t,r,h)}function uC(e,t,r){return r|t|e}function _te(e){Rw.reset(),e.accept(Rw);let t=Rw.dslMethods;return Rw.reset(),t}function t$(e,t){isNaN(e.startOffset)===!0?(e.startOffset=t.startOffset,e.endOffset=t.endOffset):e.endOffseto.msg);throw Error(`Errors Detected in CST Visitor <${this.constructor.name}>: + ${a.join(` + +`).replace(/\n/g,` + `)}`)}},"validateVisitor")};return r.prototype=i,r.prototype.constructor=r,r._RULE_NAMES=t,r}function Ite(e,t,r){let i=L(function(){},"derivedConstructor");i$(i,e+"BaseSemanticsWithDefaults");let n=Object.create(r.prototype);return ei(t,a=>{n[a]=$te}),i.prototype=n,i.prototype.constructor=i,i}function Mte(e,t){return Dte(e,t)}function Dte(e,t){let r=Ol(t,n=>yh(e[n])===!1),i=Fr(r,n=>({msg:`Missing visitor method: <${n}> on ${e.constructor.name} CST Visitor.`,type:Foe.MISSING_METHOD,methodName:n}));return vb(i)}function Dm(e,t,r,i=!1){Pv(r);let n=l0(this.recordingProdStack),a=yh(t)?t:t.DEF,o=new e({definition:[],idx:r});return i&&(o.separator=t.SEP),Kr(t,"MAX_LOOKAHEAD")&&(o.maxLookahead=t.MAX_LOOKAHEAD),this.recordingProdStack.push(o),a.call(this),n.definition.push(o),this.recordingProdStack.pop(),$w}function Nte(e,t){Pv(t);let r=l0(this.recordingProdStack),i=qi(e)===!1,n=i===!1?e:e.DEF,a=new jo({definition:[],idx:t,ignoreAmbiguities:i&&e.IGNORE_AMBIGUITIES===!0});Kr(e,"MAX_LOOKAHEAD")&&(a.maxLookahead=e.MAX_LOOKAHEAD);let o=foe(n,l=>yh(l.GATE));return a.hasPredicates=o,r.definition.push(a),ei(n,l=>{let u=new qo({definition:[]});a.definition.push(u),Kr(l,"IGNORE_AMBIGUITIES")?u.ignoreAmbiguities=l.IGNORE_AMBIGUITIES:Kr(l,"GATE")&&(u.ignoreAmbiguities=!0),this.recordingProdStack.push(u),l.ALT.call(this),this.recordingProdStack.pop()}),$w}function n$(e){return e===0?"":`${e}`}function Pv(e){if(e<0||e>qoe){let t=new Error(`Invalid DSL Method idx value: <${e}> + Idx value must be a none negative value smaller than ${qoe+1}`);throw t.KNOWN_RECORDER_ERROR=!0,t}}function Ote(e,t){t.forEach(r=>{let i=r.prototype;Object.getOwnPropertyNames(i).forEach(n=>{if(n==="constructor")return;let a=Object.getOwnPropertyDescriptor(i,n);a&&(a.get||a.set)?Object.defineProperty(e.prototype,n,a):e.prototype[n]=r.prototype[n]})})}function a$(e=void 0){return function(){return e}}function Bte(e,t){for(var r=-1,i=e==null?0:e.length,n=Array(i);++r-1}function Vte(e,t){var r=this.__data__,i=Mw(r,e);return i<0?(++this.size,r.push([e,t])):r[i][1]=t,this}function Bf(e){var t=-1,r=e==null?0:e.length;for(this.clear();++tl))return!1;var h=a.get(e),d=a.get(t);if(h&&d)return h==t&&d==e;var p=-1,f=!0,g=r&Uit?new Zoe:void 0;for(a.set(e,t),a.set(t,e);++p-1&&e%1==0&&e-1&&e%1==0&&e<=Snt}function Ore(e){return i1(e)&&jL(e.length)&&!!Fn[t1(e)]}function Bre(e){return function(t){return e(t)}}function Pre(e,t){var r=xo(e),i=!r&&Pw(e),n=!r&&!i&&Fw(e),a=!r&&!i&&!n&&UL(e),o=r||i||n||a,l=o?gnt(e.length,String):[],u=l.length;for(var h in e)(t||iat.call(e,h))&&!(o&&(h=="length"||n&&(h=="offset"||h=="parent")||a&&(h=="buffer"||h=="byteLength"||h=="byteOffset")||hle(h,u)))&&l.push(h);return l}function Fre(e){var t=e&&e.constructor,r=typeof t=="function"&&t.prototype||aat;return e===r}function zre(e,t){return function(r){return e(t(r))}}function qre(e){if(!gle(e))return lat(e);var t=[];for(var r in Object(e))uat.call(e,r)&&r!="constructor"&&t.push(r);return t}function Wre(e){return e!=null&&jL(e.length)&&!Xoe(e)}function jre(e){return zw(e)?nat(e):mle(e)}function Vre(e){return cnt(e,YL,fnt)}function Ure(e,t,r,i,n,a){var o=r&hat,l=yle(e),u=l.length,h=yle(t),d=h.length;if(u!=d&&!o)return!1;for(var p=u;p--;){var f=l[p];if(!(o?f in t:pat.call(t,f)))return!1}var g=a.get(e),m=a.get(t);if(g&&m)return g==t&&m==e;var y=!0;a.set(e,t),a.set(t,e);for(var k=o;++pl$(e,t,a));return Wf(e,t,i,r,...n)}function Rie(e,t,r){let i=Ya(e,t,r,{type:tp});dh(e,i);let n=Wf(e,t,i,r,Wd(e,t,r));return $ie(e,t,r,n)}function Wd(e,t,r){let i=dst(kh(r.definition,n=>l$(e,t,n)),n=>n!==void 0);return i.length===1?i[0]:i.length===0?void 0:Iie(e,i)}function c$(e,t,r,i,n){let a=i.left,o=i.right,l=Ya(e,t,r,{type:yst});dh(e,l);let u=Ya(e,t,r,{type:Nle});return a.loopback=l,u.loopback=l,e.decisionMap[qf(t,n?"RepetitionMandatoryWithSeparator":"RepetitionMandatory",r.idx)]=l,ba(o,l),n===void 0?(ba(l,a),ba(l,u)):(ba(l,u),ba(l,n.left),ba(n.right,a)),{left:a,right:u}}function u$(e,t,r,i,n){let a=i.left,o=i.right,l=Ya(e,t,r,{type:mst});dh(e,l);let u=Ya(e,t,r,{type:Nle}),h=Ya(e,t,r,{type:gst});return l.loopback=h,u.loopback=h,ba(l,a),ba(l,u),ba(o,h),n!==void 0?(ba(h,u),ba(h,n.left),ba(n.right,a)):ba(h,l),e.decisionMap[qf(t,n?"RepetitionWithSeparator":"Repetition",r.idx)]=l,{left:l,right:u}}function $ie(e,t,r,i){let n=i.left,a=i.right;return ba(n,a),e.decisionMap[qf(t,"Option",r.idx)]=n,i}function dh(e,t){return e.decisionStates.push(t),t.decision=e.decisionStates.length-1,t.decision}function Wf(e,t,r,i,...n){let a=Ya(e,t,i,{type:fst,start:r});r.end=a;for(let l of n)l!==void 0?(ba(r,l.left),ba(l.right,a)):ba(r,a);let o={left:r,right:a};return e.decisionMap[qf(t,Lie(i),i.idx)]=r,o}function Lie(e){if(e instanceof jo)return"Alternation";if(e instanceof Rs)return"Option";if(e instanceof la)return"Repetition";if(e instanceof Wo)return"RepetitionWithSeparator";if(e instanceof gl)return"RepetitionMandatory";if(e instanceof ml)return"RepetitionMandatoryWithSeparator";throw new Error("Invalid production type encountered")}function Iie(e,t){let r=t.length;for(let a=0;ar.stateNumber.toString()).join("_")}`}function Oie(e,t,r){for(var i=-1,n=e.length;++i0&&r(l)?t>1?d$(l,t-1,r,i,n):ile(n,l):i||(n[n.length]=l)}return n}function zie(e,t){return Ple(kh(e,t),1)}function qie(e,t,r,i){for(var n=e.length,a=r+(i?1:-1);i?a--:++a-1}function Yie(e,t,r){for(var i=-1,n=e==null?0:e.length;++i=Mst){var h=t?null:Ist(e);if(h)return qL(h);o=!1,n=Joe,u=new Zoe}else u=t?[]:l;e:for(;++i{let n=i.toString(),a=r[n];return a!==void 0||(a={atnStartState:e,decision:t,states:{}},r[n]=a),a}}function p$(e,t=!0){let r=new Set;for(let i of e){let n=new Set;for(let a of i){if(a===void 0){if(t)break;return!1}let o=[a.tokenTypeIdx].concat(a.categoryMatches);for(let l of o)if(r.has(l)){if(!n.has(l))return!1}else r.add(l),n.add(l)}}return!0}function ane(e){let t=e.decisionStates.length,r=Array(t);for(let i=0;iOf(n)).join(", "),r=e.production.idx===0?"":e.production.idx,i=`Ambiguous Alternatives Detected: <${e.ambiguityIndices.join(", ")}> in <${une(e.production)}${r}> inside <${e.topLevelRule.name}> Rule, +<${t}> may appears as a prefix path in all these alternatives. +`;return i=i+`See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES +For Further details.`,i}function une(e){if(e instanceof vo)return"SUBRULE";if(e instanceof Rs)return"OPTION";if(e instanceof jo)return"OR";if(e instanceof gl)return"AT_LEAST_ONE";if(e instanceof ml)return"AT_LEAST_ONE_SEP";if(e instanceof Wo)return"MANY_SEP";if(e instanceof la)return"MANY";if(e instanceof Pn)return"CONSUME";throw Error("non exhaustive match")}function hne(e,t,r){let i=kst(t.configs.elements,a=>a.state.transitions),n=Nst(i.filter(a=>a instanceof e9).map(a=>a.tokenType),a=>a.tokenTypeIdx);return{actualToken:r,possibleTokenTypes:n,tokenPath:e}}function dne(e,t){return e.edges[t.tokenTypeIdx]}function pne(e,t,r){let i=new r9,n=[];for(let o of e.elements){if(r.is(o.alt)===!1)continue;if(o.state.type===_b){n.push(o);continue}let l=o.state.transitions.length;for(let u=0;u0&&!vne(a))for(let o of n)a.add(o);return a}function fne(e,t){if(e instanceof e9&&HR(t,e.tokenType))return e.target}function gne(e,t){let r;for(let i of e.elements)if(t.is(i.alt)===!0){if(r===void 0)r=i.alt;else if(r!==i.alt)return}return r}function f$(e){return{configs:e,edges:{},isAcceptState:!1,prediction:-1}}function g$(e,t,r,i){return i=m$(e,i),t.edges[r.tokenTypeIdx]=i,i}function m$(e,t){if(t===Yw)return t;let r=t.configs.key,i=e.states[r];return i!==void 0?i:(t.configs.finalize(),e.states[r]=t,t)}function mne(e){let t=new r9,r=e.transitions.length;for(let i=0;i0){let n=[...e.stack],a={state:n.pop(),alt:e.alt,stack:n};zv(a,t)}else t.add(e);return}r.epsilonOnlyTransitions||t.add(e);let i=r.transitions.length;for(let n=0;n1)return!0;return!1}function Cne(e){for(let t of Array.from(e.values()))if(Object.keys(t).length===1)return!0;return!1}function gC(e){return e.$type===Xw}function mC(e,t,r){return wne({parser:t,tokens:r,ruleNames:new Map},e),t}function wne(e,t){let r=Hk(t,!1),i=an(t.rules).filter(go).filter(a=>r.has(a));for(let a of i){let o={...e,consume:1,optional:1,subrule:1,many:1,or:1};e.parser.rule(a,jd(o,a.definition))}let n=an(t.rules).filter(Em).filter(a=>r.has(a));for(let a of n)e.parser.rule(a,Ene(e,a))}function Ene(e,t){let r=t.call.rule.ref;if(!r)throw new Error("Could not resolve reference to infix operator rule: "+t.call.rule.$refText);if(Ml(r))throw new Error("Cannot use terminal rule in infix expression");let i=t.operators.precedences.flatMap(p=>p.operators),n={$type:"Group",elements:[]},a={$container:n,$type:"Assignment",feature:"parts",operator:"+=",terminal:t.call},o={$container:n,$type:"Group",elements:[],cardinality:"*"};n.elements.push(a,o);let l={$container:o,$type:"Assignment",feature:"operators",operator:"+=",terminal:{$type:"Alternatives",elements:i}},u={...a,$container:o};o.elements.push(l,u);let h=i.map(p=>e.tokens[p.value]).map((p,f)=>({ALT:L(()=>e.parser.consume(f,p,l),"ALT")})),d;return p=>{d??(d=yC(e,r)),e.parser.subrule(0,d,!1,a,p),e.parser.many(0,{DEF:L(()=>{e.parser.alternatives(0,h),e.parser.subrule(1,d,!1,u,p)},"DEF")})}}function jd(e,t,r=!1){let i;if(ch(t))i=Ine(e,t);else if(zd(t))i=Sne(e,t);else if(lh(t))i=jd(e,t.terminal);else if(_f(t))i=y$(e,t);else if(uh(t))i=_ne(e,t);else if(Pk(t))i=Rne(e,t);else if(Wk(t))i=$ne(e,t);else if(Af(t))i=Lne(e,t);else if(U8(t)){let n=e.consume++;i=L(()=>e.parser.consume(n,Jd,t),"method")}else throw new ew(t.$cstNode,`Unexpected element type: ${t.$type}`);return v$(e,r?void 0:qv(t),i,t.cardinality)}function Sne(e,t){let r=$f(t);return()=>e.parser.action(r,t)}function _ne(e,t){let r=t.rule.ref;if(Sf(r)){let i=e.subrule++,n=go(r)&&r.fragment,a=t.arguments.length>0?Ane(r,t.arguments):()=>({}),o;return l=>{o??(o=yC(e,r)),e.parser.subrule(i,o,n,t,a(l))}}else if(Ml(r)){let i=e.consume++,n=vC(e,r.name);return()=>e.parser.consume(i,n,t)}else if(r)qd(r);else throw new ew(t.$cstNode,`Undefined rule: ${t.rule.$refText}`)}function Ane(e,t){if(t.some(r=>r.calledByName)){let r=t.map(i=>({parameterName:i.parameter?.ref?.name,predicate:hc(i.value)}));return i=>{let n={};for(let{parameterName:a,predicate:o}of r)a&&(n[a]=o(i));return n}}else{let r=t.map(i=>hc(i.value));return i=>{let n={};for(let a=0;at(i)||r(i)}else if(j8(e)){let t=hc(e.left),r=hc(e.right);return i=>t(i)&&r(i)}else if(G8(e)){let t=hc(e.value);return r=>!t(r)}else if(X8(e)){let t=e.parameter.ref.name;return r=>r!==void 0&&r[t]===!0}else if(q8(e)){let t=!!e.true;return()=>t}qd(e)}function Rne(e,t){if(t.elements.length===1)return jd(e,t.elements[0]);{let r=[];for(let n of t.elements){let a={ALT:jd(e,n,!0)},o=qv(n);o&&(a.GATE=hc(o)),r.push(a)}let i=e.or++;return n=>e.parser.alternatives(i,r.map(a=>{let o={ALT:L(()=>a.ALT(n),"ALT")},l=a.GATE;return l&&(o.GATE=()=>l(n)),o}))}}function $ne(e,t){if(t.elements.length===1)return jd(e,t.elements[0]);let r=[];for(let l of t.elements){let u={ALT:jd(e,l,!0)},h=qv(l);h&&(u.GATE=hc(h)),r.push(u)}let i=e.or++,n=L((l,u)=>{let h=u.getRuleStack().join("-");return`uGroup_${l}_${h}`},"idFunc"),a=L(l=>e.parser.alternatives(i,r.map((u,h)=>{let d={ALT:L(()=>!0,"ALT")},p=e.parser;d.ALT=()=>{if(u.ALT(l),!p.isRecording()){let g=n(i,p);p.unorderedGroups.get(g)||p.unorderedGroups.set(g,[]);let m=p.unorderedGroups.get(g);typeof m?.[h]>"u"&&(m[h]=!0)}};let f=u.GATE;return f?d.GATE=()=>f(l):d.GATE=()=>!p.unorderedGroups.get(n(i,p))?.[h],d})),"alternatives"),o=v$(e,qv(t),a,"*");return l=>{o(l),e.parser.isRecording()||e.parser.unorderedGroups.delete(n(i,e.parser))}}function Lne(e,t){let r=t.elements.map(i=>jd(e,i));return i=>r.forEach(n=>n(i))}function qv(e){if(Af(e))return e.guardCondition}function y$(e,t,r=t.terminal){if(r)if(uh(r)&&go(r.rule.ref)){let i=r.rule.ref,n=e.subrule++,a;return o=>{a??(a=yC(e,i)),e.parser.subrule(n,a,!1,t,o)}}else if(uh(r)&&Ml(r.rule.ref)){let i=e.consume++,n=vC(e,r.rule.ref.name);return()=>e.parser.consume(i,n,t)}else if(ch(r)){let i=e.consume++,n=vC(e,r.value);return()=>e.parser.consume(i,n,t)}else throw new Error("Could not build cross reference parser");else{if(!t.type.ref)throw new Error("Could not resolve reference to type: "+t.type.$refText);let i=Qk(t.type.ref)?.terminal;if(!i)throw new Error("Could not find name assignment for type: "+$f(t.type.ref));return y$(e,t,i)}}function Ine(e,t){let r=e.consume++,i=e.tokens[t.value];if(!i)throw new Error("Could not find token for keyword: "+t.value);return()=>e.parser.consume(r,i,t)}function v$(e,t,r,i){let n=t&&hc(t);if(!i)if(n){let a=e.or++;return o=>e.parser.alternatives(a,[{ALT:L(()=>r(o),"ALT"),GATE:L(()=>n(o),"GATE")},{ALT:a$(),GATE:L(()=>!n(o),"GATE")}])}else return r;if(i==="*"){let a=e.many++;return o=>e.parser.many(a,{DEF:L(()=>r(o),"DEF"),GATE:n?()=>n(o):void 0})}else if(i==="+"){let a=e.many++;if(n){let o=e.or++;return l=>e.parser.alternatives(o,[{ALT:L(()=>e.parser.atLeastOne(a,{DEF:L(()=>r(l),"DEF")}),"ALT"),GATE:L(()=>n(l),"GATE")},{ALT:a$(),GATE:L(()=>!n(l),"GATE")}])}else return o=>e.parser.atLeastOne(a,{DEF:L(()=>r(o),"DEF")})}else if(i==="?"){let a=e.optional++;return o=>e.parser.optional(a,{DEF:L(()=>r(o),"DEF"),GATE:n?()=>n(o):void 0})}else qd(i)}function yC(e,t){let r=Mne(e,t),i=e.parser.getRule(r);if(!i)throw new Error(`Rule "${r}" not found."`);return i}function Mne(e,t){if(Sf(t))return t.name;if(e.ruleNames.has(t))return e.ruleNames.get(t);{let r=t,i=r.$container,n=t.$type;for(;!go(i);)(Af(i)||Pk(i)||Wk(i))&&(n=i.elements.indexOf(r).toString()+":"+n),r=i,i=i.$container;return n=i.name+":"+n,e.ruleNames.set(t,n),n}}function vC(e,t){let r=e.tokens[t];if(!r)throw new Error(`Token "${t}" not found."`);return r}function b$(e){let t=e.Grammar,r=e.parser.Lexer,i=new Hle(e);return mC(t,i,r.definition),i.finalize(),i}function x$(e){let t=T$(e);return t.finalize(),t}function T$(e){let t=e.Grammar,r=e.parser.Lexer,i=new Ule(e);return mC(t,i,r.definition)}function bC(){return new Promise(e=>{typeof setImmediate>"u"?setTimeout(e,0):setImmediate(e)})}function xC(){return Qw=performance.now(),new Sn.CancellationTokenSource}function k$(e){Xle=e}function jf(e){return e===ou}async function cs(e){if(e===Sn.CancellationToken.None)return;let t=performance.now();if(t-Qw>=Xle&&(Qw=t,await bC(),Qw=performance.now()),e.isCancellationRequested)throw ou}function TC(e,t){if(e.length<=1)return e;let r=e.length/2|0,i=e.slice(0,r),n=e.slice(r);TC(i,t),TC(n,t);let a=0,o=0,l=0;for(;ar.line||t.line===r.line&&t.character>r.character?{start:r,end:t}:e}function Dne(e){let t=E$(e.range);return t!==e.range?{newText:e.newText,range:t}:e}function S$(e){return typeof e.name=="string"}function _$(e){return typeof e.$comment=="string"}function A$(e){return typeof e=="object"&&!!e&&("$ref"in e||"$error"in e)}function Vf(e){return{code:e}}function R$(e){if(e.range)return e.range;let t;return typeof e.property=="string"?t=Gk(e.node.$cstNode,e.property,e.index):typeof e.keyword=="string"&&(t=CR(e.node.$cstNode,e.keyword,e.index)),t??(t=e.node.$cstNode),t?t.range:{start:{line:0,character:0},end:{line:0,character:0}}}function Wv(e){switch(e){case"error":return 1;case"warning":return 2;case"info":return 3;case"hint":return 4;default:throw new Error("Invalid diagnostic severity: "+e)}}function $$(e){switch(e){case"error":return Vf(vc.LexingError);case"warning":return Vf(vc.LexingWarning);case"info":return Vf(vc.LexingInfo);case"hint":return Vf(vc.LexingHint);default:throw new Error("Invalid diagnostic severity: "+e)}}function kC(e){return Array.isArray(e)&&(e.length===0||"name"in e[0])}function CC(e){return e&&"modes"in e&&"defaultMode"in e}function wC(e){return!kC(e)&&!CC(e)}function L$(e,t,r){let i,n;typeof e=="string"?(n=t,i=r):(n=e.range.start,i=t),n||(n=tn.create(0,0));let a=M$(e),o=SC(i),l=Nne({lines:a,position:n,options:o});return Pne({index:0,tokens:l,position:n})}function I$(e,t){let r=SC(t),i=M$(e);if(i.length===0)return!1;let n=i[0],a=i[i.length-1],o=r.start,l=r.end;return!!o?.exec(n)&&!!l?.exec(a)}function M$(e){let t="";return typeof e=="string"?t=e:t=e.text,t.split(tse)}function Nne(e){let t=[],r=e.position.line,i=e.position.character;for(let n=0;n=l.length){if(t.length>0){let h=tn.create(r,i);t.push({type:"break",content:"",range:Pi.create(h,h)})}}else{bce.lastIndex=u;let h=bce.exec(l);if(h){let d=h[0],p=h[1],f=tn.create(r,i+u),g=tn.create(r,i+u+d.length);t.push({type:"tag",content:p,range:Pi.create(f,g)}),u+=d.length,u=EC(l,u)}if(u0&&t[t.length-1].type==="break"?t.slice(0,-1):t}function One(e,t,r,i){let n=[];if(e.length===0){let a=tn.create(r,i),o=tn.create(r,i+t.length);n.push({type:"text",content:t,range:Pi.create(a,o)})}else{let a=0;for(let l of e){let u=l.index,h=t.substring(a,u);h.length>0&&n.push({type:"text",content:t.substring(a,u),range:Pi.create(tn.create(r,a+i),tn.create(r,u+i))});let d=h.length+1,p=l[1];if(n.push({type:"inline-tag",content:p,range:Pi.create(tn.create(r,a+d+i),tn.create(r,a+d+p.length+i))}),d+=p.length,l.length===4){d+=l[2].length;let f=l[3];n.push({type:"text",content:f,range:Pi.create(tn.create(r,a+d+i),tn.create(r,a+d+f.length+i))})}else n.push({type:"text",content:"",range:Pi.create(tn.create(r,a+d+i),tn.create(r,a+d+i))});a=u+l[0].length}let o=t.substring(a);o.length>0&&n.push({type:"text",content:o,range:Pi.create(tn.create(r,a+i),tn.create(r,a+i+o.length))})}return n}function EC(e,t){let r=e.substring(t).match(eot);return r?t+r.index:e.length}function Bne(e){let t=e.match(tot);if(t&&typeof t.index=="number")return t.index}function Pne(e){let t=tn.create(e.position.line,e.position.character);if(e.tokens.length===0)return new xce([],Pi.create(t,t));let r=[];for(;e.index0){let a=EC(t,i);n=t.substring(a),t=t.substring(0,i)}return(e==="linkcode"||e==="link"&&r.link==="code")&&(n=`\`${n}\``),r.renderLink?.(t,n)??jne(t,n)}}function jne(e,t){try{return yl.parse(e,!0),`[${t}](${e})`}catch{return e}}function B$(e){return e.endsWith(` +`)?` +`:` + +`}function us(e){return{documentation:{CommentProvider:L(t=>new Cce(t),"CommentProvider"),DocumentationProvider:L(t=>new kce(t),"DocumentationProvider")},parser:{AsyncParser:L(t=>new wce(t),"AsyncParser"),GrammarConfig:L(t=>$R(t),"GrammarConfig"),LangiumParser:L(t=>x$(t),"LangiumParser"),CompletionParser:L(t=>b$(t),"CompletionParser"),ValueConverter:L(()=>new l9,"ValueConverter"),TokenBuilder:L(()=>new Kw,"TokenBuilder"),Lexer:L(t=>new m9(t),"Lexer"),ParserErrorMessageProvider:L(()=>new o9,"ParserErrorMessageProvider"),LexerErrorMessageProvider:L(()=>new vce,"LexerErrorMessageProvider")},workspace:{AstNodeLocator:L(()=>new pce,"AstNodeLocator"),AstNodeDescriptionProvider:L(t=>new hce(t),"AstNodeDescriptionProvider"),ReferenceDescriptionProvider:L(t=>new dce(t),"ReferenceDescriptionProvider")},references:{Linker:L(t=>new Jle(t),"Linker"),NameProvider:L(()=>new ece,"NameProvider"),ScopeProvider:L(t=>new ace(t),"ScopeProvider"),ScopeComputation:L(t=>new rce(t),"ScopeComputation"),References:L(t=>new tce(t),"References")},serializer:{Hydrator:L(t=>new Sce(t),"Hydrator"),JsonSerializer:L(t=>new sce(t),"JsonSerializer")},validation:{DocumentValidator:L(t=>new uce(t),"DocumentValidator"),ValidationRegistry:L(t=>new lce(t),"ValidationRegistry")},shared:L(()=>e.shared,"shared")}}function hs(e){return{ServiceRegistry:L(t=>new oce(t),"ServiceRegistry"),workspace:{LangiumDocuments:L(t=>new Zle(t),"LangiumDocuments"),LangiumDocumentFactory:L(t=>new Qle(t),"LangiumDocumentFactory"),DocumentBuilder:L(t=>new gce(t),"DocumentBuilder"),IndexManager:L(t=>new mce(t),"IndexManager"),WorkspaceManager:L(t=>new yce(t),"WorkspaceManager"),FileSystemProvider:L(t=>e.fileSystemProvider(t),"FileSystemProvider"),WorkspaceLock:L(()=>new Ece,"WorkspaceLock"),ConfigurationProvider:L(t=>new fce(t),"ConfigurationProvider")},profilers:{}}}function Bi(e,t,r,i,n,a,o,l,u){let h=[e,t,r,i,n,a,o,l,u].reduce(Nm,{});return F$(h)}function P$(e){if(e&&e[Ace])for(let t of Object.values(e))P$(t);return e}function F$(e,t){let r=new Proxy({},{deleteProperty:L(()=>!1,"deleteProperty"),set:L(()=>{throw new Error("Cannot set property on injected service container")},"set"),get:L((i,n)=>n===Ace?!0:z$(i,n,e,t||r),"get"),getOwnPropertyDescriptor:L((i,n)=>(z$(i,n,e,t||r),Object.getOwnPropertyDescriptor(i,n)),"getOwnPropertyDescriptor"),has:L((i,n)=>n in e,"has"),ownKeys:L(()=>[...Object.getOwnPropertyNames(e)],"ownKeys")});return r}function z$(e,t,r,i){if(t in e){if(e[t]instanceof Error)throw new Error("Construction failure. Please make sure that your dependencies are constructable. Cause: "+e[t]);if(e[t]===Rce)throw new Error('Cycle detected. Please make "'+String(t)+'" lazy. Visit https://langium.org/docs/reference/configuration-services/#resolving-cyclic-dependencies');return e[t]}else if(t in r){let n=r[t];e[t]=Rce;try{e[t]=typeof n=="function"?n(i):F$(n,i)}catch(a){throw e[t]=a instanceof Error?a:void 0,a}return e[t]}else return}function Nm(e,t){if(t){for(let[r,i]of Object.entries(t))if(i!=null)if(typeof i=="object"){let n=e[r];typeof n=="object"&&n!==null?e[r]=Nm(n,i):e[r]=Nm({},i)}else e[r]=i}return e}function Vne(){let e=Bi(hs($s),sot),t=Bi(us({shared:e}),aot);return e.ServiceRegistry.register(t),t}function ds(e){let t=Vne(),r=t.serializer.JsonSerializer.deserialize(e);return t.shared.workspace.LangiumDocumentFactory.fromModel(r,yl.parse(`memory:/${r.name??"grammar"}.langium`)),r}function vGe(e){return Ta.isInstance(e,lu.$type)}function bGe(e){return Ta.isInstance(e,s5.$type)}function xGe(e){return Ta.isInstance(e,l1.$type)}function TGe(e){return Ta.isInstance(e,g0.$type)}function kGe(e){return Ta.isInstance(e,h5.$type)}function CGe(e){return Ta.isInstance(e,S9.$type)}function wGe(e){return e==="rmo"||e==="readmodel"||e==="ui"||e==="cmd"||e==="command"||e==="evt"||e==="event"||e==="pcr"||e==="processor"}function Une(e){return Ta.isInstance(e,Sh.$type)}function EGe(e){return Ta.isInstance(e,v0.$type)}function SGe(e){return Ta.isInstance(e,Mb.$type)}function _Ge(e){return Ta.isInstance(e,h1.$type)}function AGe(e){return Ta.isInstance(e,d1.$type)}function RGe(e){return Ta.isInstance(e,p1.$type)}function $Ge(e){return Ta.isInstance(e,x0.$type)}function LGe(e){return Ta.isInstance(e,m5.$type)}function IGe(e){return Ta.isInstance(e,f1.$type)}function MGe(e){return Ta.isInstance(e,g1.$type)}function DGe(e){return Ta.isInstance(e,m1.$type)}function NGe(e){return Ta.isInstance(e,y1.$type)}function OGe(e){return Ta.isInstance(e,Bb.$type)}function BGe(e){return Ta.isInstance(e,x1.$type)}function PGe(e){return Ta.isInstance(e,Ls.$type)}var FGe,jv,zGe,q$,qGe,WGe,L,jGe,ci,Vd,AC,RC,W$,j$,$C,Yne,V$,Hne,LC,tn,Pi,IC,Gne,U$,Xne,Kne,Qne,Zne,Y$,Jne,eae,tae,MC,Om,ph,Bm,Xs,Ud,DC,Vv,Uv,Yv,H$,NC,rae,VGe,iae,nae,OC,aae,G$,Hv,sae,oae,lae,cae,uae,hae,dae,pae,BC,fae,gae,mae,yae,vae,bae,xae,Tae,kae,Cae,wae,PC,Eae,Sae,_ae,Aae,Rae,$ae,Lae,Iae,Mae,Dae,Nae,Oae,Bae,X$,K$,Pae,Fae,zae,qae,Wae,jae,Vae,Uae,UGe,Yae,YGe,ft,Gv,Uf,Xv,Pm,FC,Hae,Gae,HGe,GGe,Xae,XGe,KGe,QGe,ZGe,Q$,JGe,Fm,Kae,ra,Z$,eXe,tXe,rXe,iXe,nXe,aXe,sXe,oXe,lXe,cXe,uXe,hXe,dXe,pXe,fXe,gXe,mXe,yXe,vXe,bXe,xXe,TXe,kXe,CXe,wXe,Qae,J$,eL,eu,zm,mo,qm,zC,tL,Zae,EXe,Po,Kv,Wm,hl,Yd,Qv,qC,WC,Hd,jC,Gd,Xd,Zv,Kd,Jv,VC,fh,UC,Yf,YC,tu,eb,HC,jm,Vm,Um,Hf,GC,XC,Ym,KC,dc,tb,Gf,QC,Xf,Hm,ZC,Kf,Fo,Qf,gh,Zf,rb,Jf,e0,JC,ib,t0,r0,Gm,rL,Yr,mh,iL,nL,ew,aL,tw,rw,Jae,SXe,iw,_Xe,ese,nw,tse,rse,AXe,i0,ise,sL,RXe,nse,$Xe,LXe,ru,IXe,Dl,ase,MXe,DXe,nb,NXe,OXe,BXe,PXe,FXe,zXe,sse,Qd,pc,qXe,aw,ab,WXe,qi,jXe,ose,lse,VXe,UXe,YXe,HXe,GXe,Nl,cse,XXe,KXe,QXe,ZXe,JXe,use,eKe,tKe,sb,ob,rKe,iKe,nKe,aKe,yh,sKe,oL,hse,oKe,lKe,cKe,n0,uKe,hKe,dKe,pKe,fKe,gKe,mKe,yKe,vKe,a0,bKe,lL,dse,xKe,TKe,kKe,ps,CKe,wKe,EKe,SKe,_Ke,AKe,RKe,sw,$Ke,LKe,IKe,MKe,pse,fse,DKe,NKe,cL,gse,OKe,BKe,ow,uL,lb,PKe,FKe,lw,cb,mse,zKe,hL,qKe,dL,iu,cw,WKe,jKe,ub,VKe,UKe,yse,vse,YKe,HKe,GKe,uw,XKe,bse,xse,KKe,Tse,QKe,ZKe,hb,JKe,eQe,tQe,rQe,iQe,nQe,aQe,sQe,oQe,lQe,cQe,uQe,hQe,dQe,pQe,fQe,gQe,mQe,yQe,vQe,bQe,xQe,TQe,kQe,Bn,CQe,db,kse,pb,wQe,pL,EQe,Zd,Cse,SQe,fL,_Qe,AQe,wse,Ese,RQe,$Qe,LQe,IQe,Sse,dl,MQe,DQe,NQe,pl,OQe,BQe,PQe,FQe,hw,zQe,qQe,gL,WQe,fb,jQe,VQe,UQe,YQe,HQe,GQe,XQe,KQe,QQe,ZQe,JQe,_se,eZe,dw,tZe,rZe,iZe,nZe,aZe,sZe,pw,oZe,gb,lZe,cZe,fw,uZe,hZe,dZe,pZe,gw,fZe,gZe,mZe,yZe,vZe,bZe,xZe,TZe,kZe,mw,CZe,mb,mL,wZe,yL,Ase,EZe,vL,fc,SZe,Rse,$se,_Ze,AZe,RZe,$Ze,LZe,IZe,MZe,yb,DZe,NZe,Lse,Ise,OZe,Mse,Dse,BZe,bL,Nse,PZe,FZe,Ose,zZe,xL,qZe,WZe,jZe,Bse,VZe,Pse,TL,Fse,UZe,kL,YZe,CL,HZe,Xm,zse,GZe,qse,Wse,jse,Vse,XZe,KZe,QZe,ZZe,JZe,s0,Km,eJe,tJe,rJe,iJe,yw,wL,nJe,aJe,sJe,Use,Yse,oJe,lJe,cJe,uJe,hJe,dJe,pJe,fJe,gJe,mJe,yJe,vJe,bJe,xJe,TJe,kJe,CJe,wJe,EJe,SJe,_Je,AJe,RJe,$Je,LJe,Hse,IJe,MJe,DJe,NJe,Gse,OJe,BJe,PJe,FJe,zJe,Xse,qJe,WJe,jJe,VJe,Kse,UJe,YJe,HJe,Qse,GJe,XJe,KJe,QJe,ZJe,JJe,eet,tet,ret,iet,net,aet,set,oet,cet,uet,wn,het,det,_s,vb,pet,fet,get,EL,Zse,SL,met,yet,Jse,vet,_L,bet,xet,Tet,ket,Cet,wet,Eet,_et,Aet,Ret,$et,Let,Iet,eoe,AL,Met,Det,Net,Oet,Bet,Pet,toe,roe,vw,Fet,ioe,zet,noe,qet,Wet,jet,aoe,Vet,soe,Uet,Yet,ooe,Het,Get,Xet,Ket,Qet,Zet,Jet,nu,ett,ttt,rtt,itt,ntt,att,stt,o0,ott,ltt,loe,ctt,utt,RL,coe,uoe,htt,dtt,ptt,bw,l0,As,bb,ftt,ei,gtt,mtt,gc,hoe,Ol,ytt,vtt,btt,xtt,Qm,mc,Ttt,Fr,Bl,ktt,Ctt,wtt,Ett,Stt,_tt,Att,Kr,Rtt,zo,$tt,Ha,Ltt,yo,Itt,doe,Mtt,Dtt,Ntt,Ott,En,Btt,Ptt,poe,Ftt,vh,bh,ztt,qtt,Wtt,jtt,yc,Vtt,fl,xw,Utt,foe,Ytt,Htt,Gtt,Xtt,Ktt,$L,au,vo,Zm,qo,Rs,gl,ml,la,Wo,jo,Pn,Jm,Tw,goe,Qtt,kw,Ztt,moe,Cw,Jtt,c0,xb,ww,ert,trt,rrt,Tb,Ew,yoe,voe,LL,xa,kb,bo,irt,boe,xoe,Toe,koe,Coe,woe,Eoe,Soe,Jd,e1,nrt,u0,art,srt,ort,Sw,lrt,_oe,crt,Aoe,ca,urt,Roe,hrt,IL,drt,$oe,Loe,Ioe,Moe,Doe,_w,Noe,prt,frt,grt,ML,Ooe,mrt,yrt,vrt,ep,brt,Boe,Poe,DL,NL,OL,Aw,g8t,BL,xrt,Trt,Rw,krt,Foe,Crt,wrt,Ert,Srt,_rt,Art,$w,zoe,qoe,Woe,joe,Rrt,$rt,Lrt,Lw,xh,Iw,Vo,PL,Irt,Voe,Mrt,Uoe,Mw,Drt,Nrt,Ort,Brt,Prt,Frt,Dw,zrt,qrt,Wrt,jrt,Vrt,Yoe,Urt,Yrt,Th,Hrt,su,Hoe,Grt,Xrt,Cb,Krt,Qrt,Zrt,Jrt,eit,tit,Goe,t1,FL,rit,iit,nit,ait,Xoe,sit,zL,Koe,oit,lit,cit,h0,uit,hit,dit,pit,fit,git,mit,yit,vit,r1,bit,wb,xit,Eb,Tit,kit,Cit,wit,Eit,Sit,_it,Ait,Rit,$it,Lit,Qoe,Iit,Mit,Nw,Dit,Nit,Oit,Bit,Ow,Pit,Fit,Bw,zit,qit,Wit,Zoe,jit,Joe,Vit,Uit,ele,Yit,tle,Hit,qL,Git,Xit,Kit,Qit,Zit,Jit,ent,tnt,rnt,int,nnt,ant,snt,rle,WL,ont,ile,lnt,xo,cnt,nle,unt,hnt,dnt,ale,pnt,fnt,gnt,i1,mnt,sle,ole,ynt,vnt,bnt,Pw,xnt,lle,cle,Tnt,ule,knt,Cnt,Fw,wnt,Ent,hle,Snt,jL,_nt,Ant,Rnt,$nt,Lnt,Int,Mnt,Dnt,Nnt,Ont,Bnt,Pnt,Fnt,znt,qnt,Wnt,jnt,Vnt,Unt,Ynt,Hnt,Gnt,Xnt,Knt,Fn,Qnt,Znt,dle,Sb,Jnt,VL,eat,ple,fle,tat,UL,rat,iat,nat,aat,gle,sat,oat,lat,cat,uat,mle,zw,YL,yle,hat,dat,pat,fat,gat,HL,mat,GL,yat,n1,vat,XL,vle,bat,ble,xle,Tle,kle,xat,Tat,kat,Cat,wat,d0,KL,Eat,Cle,wle,qw,Sat,Ele,_at,Sle,Aat,Rat,$at,_le,Lat,Ale,Iat,Mat,Ww,Dat,Nat,QL,Oat,Bat,Pat,Fat,zat,qat,Wat,jat,Vat,Rle,$le,Uat,Yat,Lle,Hat,jw,Ile,Gat,Xat,Kat,Qat,Zat,Jat,est,ZL,tst,rst,ist,Vw,nst,ast,sst,ost,lst,cst,Uw,ust,kh,hst,dst,tp,pst,Mle,Dle,_b,fst,gst,mst,yst,Nle,JL,e9,Ole,t9,Yw,r9,vst,bst,xst,Ble,Tst,Ple,kst,Cst,wst,Est,Sst,_st,Ast,Rst,$st,Lst,Ist,Mst,Dst,Nst,Ost,Bst,Pst,i9,Fst,zst,qst,Wst,jst,Vst,Ust,Fle,zle,qle,Yst,Wle,n9,Hw,Gw,Hst,a9,Xw,jle,Vle,s9,Ule,Yle,o9,Hle,Gst,Gle,Xst,Kw,l9,Ch,Sn,Qw,Xle,ou,wh,Kle,c9,Kst,yl,Ab,Uo,u9,_i,Qle,Zle,p0,Jle,ece,tce,Eh,Zw,rce,h9,Qst,ice,Zst,Jw,d9,e5,nce,p9,ace,sce,oce,f9,lce,cce,uce,vc,hce,dce,pce,t5,fce,r5,a1,gce,mce,yce,vce,g9,m9,bce,Jst,eot,tot,xce,y9,v9,Tce,kce,Cce,wce,rot,iot,Ece,Sce,_ce,Ace,Rce,b9,s1,$ce,not,x9,Lce,$s,aot,sot,oot,Ice,Mce,Dce,Nce,Oce,Bce,Pce,Fce,zce,qce,Wce,jce,Vce,Uce,Yce,Hce,m8t,Gce,Xce,T9,Kce,Qce,Zce,o1,k9,Jce,eue,i5,C9,n5,Rb,w9,lu,a5,s5,tue,o5,E9,l1,l5,f0,c5,g0,u5,rue,c1,h5,S9,iue,nue,aue,sue,oue,lue,cue,$b,m0,uue,_9,hue,due,A9,pue,fue,cu,u1,y0,Lb,gue,R9,d5,Sh,rp,$9,_h,mue,p5,L9,v0,Ib,Mb,Db,I9,Nb,f5,b0,h1,g5,M9,d1,p1,yue,vue,bue,xue,Tue,D9,Ob,N9,kue,O9,x0,m5,B9,y5,T0,f1,g1,Cue,m1,Ah,wue,Eue,Sue,y1,P9,_ue,Aue,Rue,$ue,F9,v1,z9,b1,Bb,x1,q9,T1,Pb,Ls,Lue,Ta,lot,cot,uot,hot,dot,pot,fot,got,mot,yot,vot,bot,xot,Tot,kot,Cot,wot,Eot,Sot,_ot,Aot,Rot,$ot,Lot,Iot,Mot,Dot,Not,Oot,Bot,Pot,Fot,zot,qot,Wot,jot,Vot,Uot,Yot,Hot,Got,Xot,Kot,Qot,Zot,To,Jot,elt,tlt,rlt,ilt,nlt,alt,slt,olt,llt,clt,ult,hlt,dlt,plt,flt,glt,mlt,ylt,uu,k1,ko,y8t,rn=G(()=>{"use strict";var e,t,r,i,n,a,o,l,u,h,d,p,f,g,m,y,k,C,S,E,_,b,v,x,w,R,$,M,N,j,I,z,F,O,B,Y,U,H,J,te,ie,ue,se,le,Te,oe,fe,ge,ke,Ne,xe,$e,Me,He,Ge,ze,Ve,et,he,ye,be,Ie,pe,Qe,W,Ae,K,de,ee,ve,X,me,Ce,Be,Re,Ke,De,Ze,Fe,Je,Le,ct,qe,mt,St,Lt,Bt,qt,tr,Et,_t,Kt,$t,Ot,Jt,Br,At,jr,br,Hr,Ei,Li,Cr,Ti,vt,wt,Wt,kr,Ir,Lr,di,ki,qr,fi,gt,Mt,kt,Qt,Pe,ii,Ye,ti,Na,ka,Ao,Qo,Zo,nn,fs,gs,ms,An,We;FGe=Object.create,jv=Object.defineProperty,zGe=Object.getOwnPropertyDescriptor,q$=Object.getOwnPropertyNames,qGe=Object.getPrototypeOf,WGe=Object.prototype.hasOwnProperty,L=s((q,T)=>jv(q,"name",{value:T,configurable:!0}),"__name"),jGe=s((q,T)=>s(function(){return q&&(T=(0,q[q$(q)[0]])(q=0)),T},"__init"),"__esm"),ci=s((q,T)=>s(function(){return T||(0,q[q$(q)[0]])((T={exports:{}}).exports,T),T.exports},"__require"),"__commonJS"),Vd=s((q,T)=>{for(var A in T)jv(q,A,{get:T[A],enumerable:!0})},"__export"),AC=s((q,T,A,D)=>{if(T&&typeof T=="object"||typeof T=="function")for(let P of q$(T))!WGe.call(q,P)&&P!==A&&jv(q,P,{get:s(()=>T[P],"get"),enumerable:!(D=zGe(T,P))||D.enumerable});return q},"__copyProps"),RC=s((q,T,A)=>(AC(q,T,"default"),A&&AC(A,T,"default")),"__reExport"),W$=s((q,T,A)=>(A=q!=null?FGe(qGe(q)):{},AC(T||!q||!q.__esModule?jv(A,"default",{value:q,enumerable:!0}):A,q)),"__toESM"),j$=s(q=>AC(jv({},"__esModule",{value:!0}),q),"__toCommonJS"),$C={},Vd($C,{AnnotatedTextEdit:s(()=>Ud,"AnnotatedTextEdit"),ChangeAnnotation:s(()=>Bm,"ChangeAnnotation"),ChangeAnnotationIdentifier:s(()=>Xs,"ChangeAnnotationIdentifier"),CodeAction:s(()=>Sae,"CodeAction"),CodeActionContext:s(()=>Eae,"CodeActionContext"),CodeActionKind:s(()=>wae,"CodeActionKind"),CodeActionTriggerKind:s(()=>PC,"CodeActionTriggerKind"),CodeDescription:s(()=>tae,"CodeDescription"),CodeLens:s(()=>_ae,"CodeLens"),Color:s(()=>U$,"Color"),ColorInformation:s(()=>Xne,"ColorInformation"),ColorPresentation:s(()=>Kne,"ColorPresentation"),Command:s(()=>Om,"Command"),CompletionItem:s(()=>dae,"CompletionItem"),CompletionItemKind:s(()=>sae,"CompletionItemKind"),CompletionItemLabelDetails:s(()=>hae,"CompletionItemLabelDetails"),CompletionItemTag:s(()=>lae,"CompletionItemTag"),CompletionList:s(()=>pae,"CompletionList"),CreateFile:s(()=>Vv,"CreateFile"),DeleteFile:s(()=>Yv,"DeleteFile"),Diagnostic:s(()=>MC,"Diagnostic"),DiagnosticRelatedInformation:s(()=>Y$,"DiagnosticRelatedInformation"),DiagnosticSeverity:s(()=>Jne,"DiagnosticSeverity"),DiagnosticTag:s(()=>eae,"DiagnosticTag"),DocumentHighlight:s(()=>vae,"DocumentHighlight"),DocumentHighlightKind:s(()=>yae,"DocumentHighlightKind"),DocumentLink:s(()=>Rae,"DocumentLink"),DocumentSymbol:s(()=>Cae,"DocumentSymbol"),DocumentUri:s(()=>Yne,"DocumentUri"),EOL:s(()=>UGe,"EOL"),FoldingRange:s(()=>Zne,"FoldingRange"),FoldingRangeKind:s(()=>Qne,"FoldingRangeKind"),FormattingOptions:s(()=>Aae,"FormattingOptions"),Hover:s(()=>fae,"Hover"),InlayHint:s(()=>Pae,"InlayHint"),InlayHintKind:s(()=>X$,"InlayHintKind"),InlayHintLabelPart:s(()=>K$,"InlayHintLabelPart"),InlineCompletionContext:s(()=>Vae,"InlineCompletionContext"),InlineCompletionItem:s(()=>zae,"InlineCompletionItem"),InlineCompletionList:s(()=>qae,"InlineCompletionList"),InlineCompletionTriggerKind:s(()=>Wae,"InlineCompletionTriggerKind"),InlineValueContext:s(()=>Bae,"InlineValueContext"),InlineValueEvaluatableExpression:s(()=>Oae,"InlineValueEvaluatableExpression"),InlineValueText:s(()=>Dae,"InlineValueText"),InlineValueVariableLookup:s(()=>Nae,"InlineValueVariableLookup"),InsertReplaceEdit:s(()=>cae,"InsertReplaceEdit"),InsertTextFormat:s(()=>oae,"InsertTextFormat"),InsertTextMode:s(()=>uae,"InsertTextMode"),Location:s(()=>IC,"Location"),LocationLink:s(()=>Gne,"LocationLink"),MarkedString:s(()=>BC,"MarkedString"),MarkupContent:s(()=>Hv,"MarkupContent"),MarkupKind:s(()=>G$,"MarkupKind"),OptionalVersionedTextDocumentIdentifier:s(()=>OC,"OptionalVersionedTextDocumentIdentifier"),ParameterInformation:s(()=>gae,"ParameterInformation"),Position:s(()=>tn,"Position"),Range:s(()=>Pi,"Range"),RenameFile:s(()=>Uv,"RenameFile"),SelectedCompletionInfo:s(()=>jae,"SelectedCompletionInfo"),SelectionRange:s(()=>$ae,"SelectionRange"),SemanticTokenModifiers:s(()=>Iae,"SemanticTokenModifiers"),SemanticTokenTypes:s(()=>Lae,"SemanticTokenTypes"),SemanticTokens:s(()=>Mae,"SemanticTokens"),SignatureInformation:s(()=>mae,"SignatureInformation"),StringValue:s(()=>Fae,"StringValue"),SymbolInformation:s(()=>Tae,"SymbolInformation"),SymbolKind:s(()=>bae,"SymbolKind"),SymbolTag:s(()=>xae,"SymbolTag"),TextDocument:s(()=>Yae,"TextDocument"),TextDocumentEdit:s(()=>DC,"TextDocumentEdit"),TextDocumentIdentifier:s(()=>iae,"TextDocumentIdentifier"),TextDocumentItem:s(()=>aae,"TextDocumentItem"),TextEdit:s(()=>ph,"TextEdit"),URI:s(()=>V$,"URI"),VersionedTextDocumentIdentifier:s(()=>nae,"VersionedTextDocumentIdentifier"),WorkspaceChange:s(()=>VGe,"WorkspaceChange"),WorkspaceEdit:s(()=>H$,"WorkspaceEdit"),WorkspaceFolder:s(()=>Uae,"WorkspaceFolder"),WorkspaceSymbol:s(()=>kae,"WorkspaceSymbol"),integer:s(()=>Hne,"integer"),uinteger:s(()=>LC,"uinteger")}),Gv=jGe({"../../node_modules/.pnpm/vscode-languageserver-types@3.17.5/node_modules/vscode-languageserver-types/lib/esm/main.js"(){"use strict";var q,T,A,D;(function(P){function V(Z){return typeof Z=="string"}s(V,"is"),L(V,"is"),P.is=V})(Yne||(Yne={})),(function(P){function V(Z){return typeof Z=="string"}s(V,"is"),L(V,"is"),P.is=V})(V$||(V$={})),(function(P){P.MIN_VALUE=-2147483648,P.MAX_VALUE=2147483647;function V(Z){return typeof Z=="number"&&P.MIN_VALUE<=Z&&Z<=P.MAX_VALUE}s(V,"is"),L(V,"is"),P.is=V})(Hne||(Hne={})),(function(P){P.MIN_VALUE=0,P.MAX_VALUE=2147483647;function V(Z){return typeof Z=="number"&&P.MIN_VALUE<=Z&&Z<=P.MAX_VALUE}s(V,"is"),L(V,"is"),P.is=V})(LC||(LC={})),(function(P){function V(re,Q){return re===Number.MAX_VALUE&&(re=LC.MAX_VALUE),Q===Number.MAX_VALUE&&(Q=LC.MAX_VALUE),{line:re,character:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&ft.uinteger(Q.line)&&ft.uinteger(Q.character)}s(Z,"is"),L(Z,"is"),P.is=Z})(tn||(tn={})),(function(P){function V(re,Q,ae,we){if(ft.uinteger(re)&&ft.uinteger(Q)&&ft.uinteger(ae)&&ft.uinteger(we))return{start:tn.create(re,Q),end:tn.create(ae,we)};if(tn.is(re)&&tn.is(Q))return{start:re,end:Q};throw new Error(`Range#create called with invalid arguments[${re}, ${Q}, ${ae}, ${we}]`)}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&tn.is(Q.start)&&tn.is(Q.end)}s(Z,"is"),L(Z,"is"),P.is=Z})(Pi||(Pi={})),(function(P){function V(re,Q){return{uri:re,range:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&Pi.is(Q.range)&&(ft.string(Q.uri)||ft.undefined(Q.uri))}s(Z,"is"),L(Z,"is"),P.is=Z})(IC||(IC={})),(function(P){function V(re,Q,ae,we){return{targetUri:re,targetRange:Q,targetSelectionRange:ae,originSelectionRange:we}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&Pi.is(Q.targetRange)&&ft.string(Q.targetUri)&&Pi.is(Q.targetSelectionRange)&&(Pi.is(Q.originSelectionRange)||ft.undefined(Q.originSelectionRange))}s(Z,"is"),L(Z,"is"),P.is=Z})(Gne||(Gne={})),(function(P){function V(re,Q,ae,we){return{red:re,green:Q,blue:ae,alpha:we}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&ft.numberRange(Q.red,0,1)&&ft.numberRange(Q.green,0,1)&&ft.numberRange(Q.blue,0,1)&&ft.numberRange(Q.alpha,0,1)}s(Z,"is"),L(Z,"is"),P.is=Z})(U$||(U$={})),(function(P){function V(re,Q){return{range:re,color:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&Pi.is(Q.range)&&U$.is(Q.color)}s(Z,"is"),L(Z,"is"),P.is=Z})(Xne||(Xne={})),(function(P){function V(re,Q,ae){return{label:re,textEdit:Q,additionalTextEdits:ae}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&ft.string(Q.label)&&(ft.undefined(Q.textEdit)||ph.is(Q))&&(ft.undefined(Q.additionalTextEdits)||ft.typedArray(Q.additionalTextEdits,ph.is))}s(Z,"is"),L(Z,"is"),P.is=Z})(Kne||(Kne={})),(function(P){P.Comment="comment",P.Imports="imports",P.Region="region"})(Qne||(Qne={})),(function(P){function V(re,Q,ae,we,Ue,je){let ut={startLine:re,endLine:Q};return ft.defined(ae)&&(ut.startCharacter=ae),ft.defined(we)&&(ut.endCharacter=we),ft.defined(Ue)&&(ut.kind=Ue),ft.defined(je)&&(ut.collapsedText=je),ut}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&ft.uinteger(Q.startLine)&&ft.uinteger(Q.startLine)&&(ft.undefined(Q.startCharacter)||ft.uinteger(Q.startCharacter))&&(ft.undefined(Q.endCharacter)||ft.uinteger(Q.endCharacter))&&(ft.undefined(Q.kind)||ft.string(Q.kind))}s(Z,"is"),L(Z,"is"),P.is=Z})(Zne||(Zne={})),(function(P){function V(re,Q){return{location:re,message:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&IC.is(Q.location)&&ft.string(Q.message)}s(Z,"is"),L(Z,"is"),P.is=Z})(Y$||(Y$={})),(function(P){P.Error=1,P.Warning=2,P.Information=3,P.Hint=4})(Jne||(Jne={})),(function(P){P.Unnecessary=1,P.Deprecated=2})(eae||(eae={})),(function(P){function V(Z){let re=Z;return ft.objectLiteral(re)&&ft.string(re.href)}s(V,"is"),L(V,"is"),P.is=V})(tae||(tae={})),(function(P){function V(re,Q,ae,we,Ue,je){let ut={range:re,message:Q};return ft.defined(ae)&&(ut.severity=ae),ft.defined(we)&&(ut.code=we),ft.defined(Ue)&&(ut.source=Ue),ft.defined(je)&&(ut.relatedInformation=je),ut}s(V,"create"),L(V,"create"),P.create=V;function Z(re){var Q;let ae=re;return ft.defined(ae)&&Pi.is(ae.range)&&ft.string(ae.message)&&(ft.number(ae.severity)||ft.undefined(ae.severity))&&(ft.integer(ae.code)||ft.string(ae.code)||ft.undefined(ae.code))&&(ft.undefined(ae.codeDescription)||ft.string((Q=ae.codeDescription)===null||Q===void 0?void 0:Q.href))&&(ft.string(ae.source)||ft.undefined(ae.source))&&(ft.undefined(ae.relatedInformation)||ft.typedArray(ae.relatedInformation,Y$.is))}s(Z,"is"),L(Z,"is"),P.is=Z})(MC||(MC={})),(function(P){function V(re,Q,...ae){let we={title:re,command:Q};return ft.defined(ae)&&ae.length>0&&(we.arguments=ae),we}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&ft.string(Q.title)&&ft.string(Q.command)}s(Z,"is"),L(Z,"is"),P.is=Z})(Om||(Om={})),(function(P){function V(ae,we){return{range:ae,newText:we}}s(V,"replace"),L(V,"replace"),P.replace=V;function Z(ae,we){return{range:{start:ae,end:ae},newText:we}}s(Z,"insert"),L(Z,"insert"),P.insert=Z;function re(ae){return{range:ae,newText:""}}s(re,"del"),L(re,"del"),P.del=re;function Q(ae){let we=ae;return ft.objectLiteral(we)&&ft.string(we.newText)&&Pi.is(we.range)}s(Q,"is"),L(Q,"is"),P.is=Q})(ph||(ph={})),(function(P){function V(re,Q,ae){let we={label:re};return Q!==void 0&&(we.needsConfirmation=Q),ae!==void 0&&(we.description=ae),we}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&ft.string(Q.label)&&(ft.boolean(Q.needsConfirmation)||Q.needsConfirmation===void 0)&&(ft.string(Q.description)||Q.description===void 0)}s(Z,"is"),L(Z,"is"),P.is=Z})(Bm||(Bm={})),(function(P){function V(Z){let re=Z;return ft.string(re)}s(V,"is"),L(V,"is"),P.is=V})(Xs||(Xs={})),(function(P){function V(ae,we,Ue){return{range:ae,newText:we,annotationId:Ue}}s(V,"replace"),L(V,"replace"),P.replace=V;function Z(ae,we,Ue){return{range:{start:ae,end:ae},newText:we,annotationId:Ue}}s(Z,"insert"),L(Z,"insert"),P.insert=Z;function re(ae,we){return{range:ae,newText:"",annotationId:we}}s(re,"del"),L(re,"del"),P.del=re;function Q(ae){let we=ae;return ph.is(we)&&(Bm.is(we.annotationId)||Xs.is(we.annotationId))}s(Q,"is"),L(Q,"is"),P.is=Q})(Ud||(Ud={})),(function(P){function V(re,Q){return{textDocument:re,edits:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&OC.is(Q.textDocument)&&Array.isArray(Q.edits)}s(Z,"is"),L(Z,"is"),P.is=Z})(DC||(DC={})),(function(P){function V(re,Q,ae){let we={kind:"create",uri:re};return Q!==void 0&&(Q.overwrite!==void 0||Q.ignoreIfExists!==void 0)&&(we.options=Q),ae!==void 0&&(we.annotationId=ae),we}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return Q&&Q.kind==="create"&&ft.string(Q.uri)&&(Q.options===void 0||(Q.options.overwrite===void 0||ft.boolean(Q.options.overwrite))&&(Q.options.ignoreIfExists===void 0||ft.boolean(Q.options.ignoreIfExists)))&&(Q.annotationId===void 0||Xs.is(Q.annotationId))}s(Z,"is"),L(Z,"is"),P.is=Z})(Vv||(Vv={})),(function(P){function V(re,Q,ae,we){let Ue={kind:"rename",oldUri:re,newUri:Q};return ae!==void 0&&(ae.overwrite!==void 0||ae.ignoreIfExists!==void 0)&&(Ue.options=ae),we!==void 0&&(Ue.annotationId=we),Ue}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return Q&&Q.kind==="rename"&&ft.string(Q.oldUri)&&ft.string(Q.newUri)&&(Q.options===void 0||(Q.options.overwrite===void 0||ft.boolean(Q.options.overwrite))&&(Q.options.ignoreIfExists===void 0||ft.boolean(Q.options.ignoreIfExists)))&&(Q.annotationId===void 0||Xs.is(Q.annotationId))}s(Z,"is"),L(Z,"is"),P.is=Z})(Uv||(Uv={})),(function(P){function V(re,Q,ae){let we={kind:"delete",uri:re};return Q!==void 0&&(Q.recursive!==void 0||Q.ignoreIfNotExists!==void 0)&&(we.options=Q),ae!==void 0&&(we.annotationId=ae),we}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return Q&&Q.kind==="delete"&&ft.string(Q.uri)&&(Q.options===void 0||(Q.options.recursive===void 0||ft.boolean(Q.options.recursive))&&(Q.options.ignoreIfNotExists===void 0||ft.boolean(Q.options.ignoreIfNotExists)))&&(Q.annotationId===void 0||Xs.is(Q.annotationId))}s(Z,"is"),L(Z,"is"),P.is=Z})(Yv||(Yv={})),(function(P){function V(Z){let re=Z;return re&&(re.changes!==void 0||re.documentChanges!==void 0)&&(re.documentChanges===void 0||re.documentChanges.every(Q=>ft.string(Q.kind)?Vv.is(Q)||Uv.is(Q)||Yv.is(Q):DC.is(Q)))}s(V,"is"),L(V,"is"),P.is=V})(H$||(H$={})),NC=(q=class{constructor(V,Z){this.edits=V,this.changeAnnotations=Z}insert(V,Z,re){let Q,ae;if(re===void 0?Q=ph.insert(V,Z):Xs.is(re)?(ae=re,Q=Ud.insert(V,Z,re)):(this.assertChangeAnnotations(this.changeAnnotations),ae=this.changeAnnotations.manage(re),Q=Ud.insert(V,Z,ae)),this.edits.push(Q),ae!==void 0)return ae}replace(V,Z,re){let Q,ae;if(re===void 0?Q=ph.replace(V,Z):Xs.is(re)?(ae=re,Q=Ud.replace(V,Z,re)):(this.assertChangeAnnotations(this.changeAnnotations),ae=this.changeAnnotations.manage(re),Q=Ud.replace(V,Z,ae)),this.edits.push(Q),ae!==void 0)return ae}delete(V,Z){let re,Q;if(Z===void 0?re=ph.del(V):Xs.is(Z)?(Q=Z,re=Ud.del(V,Z)):(this.assertChangeAnnotations(this.changeAnnotations),Q=this.changeAnnotations.manage(Z),re=Ud.del(V,Q)),this.edits.push(re),Q!==void 0)return Q}add(V){this.edits.push(V)}all(){return this.edits}clear(){this.edits.splice(0,this.edits.length)}assertChangeAnnotations(V){if(V===void 0)throw new Error("Text edit change is not configured to manage change annotations.")}},s(q,"TextEditChangeImpl"),L(q,"TextEditChangeImpl"),q),rae=(T=class{constructor(V){this._annotations=V===void 0?Object.create(null):V,this._counter=0,this._size=0}all(){return this._annotations}get size(){return this._size}manage(V,Z){let re;if(Xs.is(V)?re=V:(re=this.nextId(),Z=V),this._annotations[re]!==void 0)throw new Error(`Id ${re} is already in use.`);if(Z===void 0)throw new Error(`No annotation provided for id ${re}`);return this._annotations[re]=Z,this._size++,re}nextId(){return this._counter++,this._counter.toString()}},s(T,"ChangeAnnotations"),L(T,"ChangeAnnotations"),T),VGe=(A=class{constructor(V){this._textEditChanges=Object.create(null),V!==void 0?(this._workspaceEdit=V,V.documentChanges?(this._changeAnnotations=new rae(V.changeAnnotations),V.changeAnnotations=this._changeAnnotations.all(),V.documentChanges.forEach(Z=>{if(DC.is(Z)){let re=new NC(Z.edits,this._changeAnnotations);this._textEditChanges[Z.textDocument.uri]=re}})):V.changes&&Object.keys(V.changes).forEach(Z=>{let re=new NC(V.changes[Z]);this._textEditChanges[Z]=re})):this._workspaceEdit={}}get edit(){return this.initDocumentChanges(),this._changeAnnotations!==void 0&&(this._changeAnnotations.size===0?this._workspaceEdit.changeAnnotations=void 0:this._workspaceEdit.changeAnnotations=this._changeAnnotations.all()),this._workspaceEdit}getTextEditChange(V){if(OC.is(V)){if(this.initDocumentChanges(),this._workspaceEdit.documentChanges===void 0)throw new Error("Workspace edit is not configured for document changes.");let Z={uri:V.uri,version:V.version},re=this._textEditChanges[Z.uri];if(!re){let Q=[],ae={textDocument:Z,edits:Q};this._workspaceEdit.documentChanges.push(ae),re=new NC(Q,this._changeAnnotations),this._textEditChanges[Z.uri]=re}return re}else{if(this.initChanges(),this._workspaceEdit.changes===void 0)throw new Error("Workspace edit is not configured for normal text edit changes.");let Z=this._textEditChanges[V];if(!Z){let re=[];this._workspaceEdit.changes[V]=re,Z=new NC(re),this._textEditChanges[V]=Z}return Z}}initDocumentChanges(){this._workspaceEdit.documentChanges===void 0&&this._workspaceEdit.changes===void 0&&(this._changeAnnotations=new rae,this._workspaceEdit.documentChanges=[],this._workspaceEdit.changeAnnotations=this._changeAnnotations.all())}initChanges(){this._workspaceEdit.documentChanges===void 0&&this._workspaceEdit.changes===void 0&&(this._workspaceEdit.changes=Object.create(null))}createFile(V,Z,re){if(this.initDocumentChanges(),this._workspaceEdit.documentChanges===void 0)throw new Error("Workspace edit is not configured for document changes.");let Q;Bm.is(Z)||Xs.is(Z)?Q=Z:re=Z;let ae,we;if(Q===void 0?ae=Vv.create(V,re):(we=Xs.is(Q)?Q:this._changeAnnotations.manage(Q),ae=Vv.create(V,re,we)),this._workspaceEdit.documentChanges.push(ae),we!==void 0)return we}renameFile(V,Z,re,Q){if(this.initDocumentChanges(),this._workspaceEdit.documentChanges===void 0)throw new Error("Workspace edit is not configured for document changes.");let ae;Bm.is(re)||Xs.is(re)?ae=re:Q=re;let we,Ue;if(ae===void 0?we=Uv.create(V,Z,Q):(Ue=Xs.is(ae)?ae:this._changeAnnotations.manage(ae),we=Uv.create(V,Z,Q,Ue)),this._workspaceEdit.documentChanges.push(we),Ue!==void 0)return Ue}deleteFile(V,Z,re){if(this.initDocumentChanges(),this._workspaceEdit.documentChanges===void 0)throw new Error("Workspace edit is not configured for document changes.");let Q;Bm.is(Z)||Xs.is(Z)?Q=Z:re=Z;let ae,we;if(Q===void 0?ae=Yv.create(V,re):(we=Xs.is(Q)?Q:this._changeAnnotations.manage(Q),ae=Yv.create(V,re,we)),this._workspaceEdit.documentChanges.push(ae),we!==void 0)return we}},s(A,"WorkspaceChange"),L(A,"WorkspaceChange"),A),(function(P){function V(re){return{uri:re}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&ft.string(Q.uri)}s(Z,"is"),L(Z,"is"),P.is=Z})(iae||(iae={})),(function(P){function V(re,Q){return{uri:re,version:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&ft.string(Q.uri)&&ft.integer(Q.version)}s(Z,"is"),L(Z,"is"),P.is=Z})(nae||(nae={})),(function(P){function V(re,Q){return{uri:re,version:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&ft.string(Q.uri)&&(Q.version===null||ft.integer(Q.version))}s(Z,"is"),L(Z,"is"),P.is=Z})(OC||(OC={})),(function(P){function V(re,Q,ae,we){return{uri:re,languageId:Q,version:ae,text:we}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&ft.string(Q.uri)&&ft.string(Q.languageId)&&ft.integer(Q.version)&&ft.string(Q.text)}s(Z,"is"),L(Z,"is"),P.is=Z})(aae||(aae={})),(function(P){P.PlainText="plaintext",P.Markdown="markdown";function V(Z){let re=Z;return re===P.PlainText||re===P.Markdown}s(V,"is"),L(V,"is"),P.is=V})(G$||(G$={})),(function(P){function V(Z){let re=Z;return ft.objectLiteral(Z)&&G$.is(re.kind)&&ft.string(re.value)}s(V,"is"),L(V,"is"),P.is=V})(Hv||(Hv={})),(function(P){P.Text=1,P.Method=2,P.Function=3,P.Constructor=4,P.Field=5,P.Variable=6,P.Class=7,P.Interface=8,P.Module=9,P.Property=10,P.Unit=11,P.Value=12,P.Enum=13,P.Keyword=14,P.Snippet=15,P.Color=16,P.File=17,P.Reference=18,P.Folder=19,P.EnumMember=20,P.Constant=21,P.Struct=22,P.Event=23,P.Operator=24,P.TypeParameter=25})(sae||(sae={})),(function(P){P.PlainText=1,P.Snippet=2})(oae||(oae={})),(function(P){P.Deprecated=1})(lae||(lae={})),(function(P){function V(re,Q,ae){return{newText:re,insert:Q,replace:ae}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return Q&&ft.string(Q.newText)&&Pi.is(Q.insert)&&Pi.is(Q.replace)}s(Z,"is"),L(Z,"is"),P.is=Z})(cae||(cae={})),(function(P){P.asIs=1,P.adjustIndentation=2})(uae||(uae={})),(function(P){function V(Z){let re=Z;return re&&(ft.string(re.detail)||re.detail===void 0)&&(ft.string(re.description)||re.description===void 0)}s(V,"is"),L(V,"is"),P.is=V})(hae||(hae={})),(function(P){function V(Z){return{label:Z}}s(V,"create"),L(V,"create"),P.create=V})(dae||(dae={})),(function(P){function V(Z,re){return{items:Z||[],isIncomplete:!!re}}s(V,"create"),L(V,"create"),P.create=V})(pae||(pae={})),(function(P){function V(re){return re.replace(/[\\`*_{}[\]()#+\-.!]/g,"\\$&")}s(V,"fromPlainText"),L(V,"fromPlainText"),P.fromPlainText=V;function Z(re){let Q=re;return ft.string(Q)||ft.objectLiteral(Q)&&ft.string(Q.language)&&ft.string(Q.value)}s(Z,"is"),L(Z,"is"),P.is=Z})(BC||(BC={})),(function(P){function V(Z){let re=Z;return!!re&&ft.objectLiteral(re)&&(Hv.is(re.contents)||BC.is(re.contents)||ft.typedArray(re.contents,BC.is))&&(Z.range===void 0||Pi.is(Z.range))}s(V,"is"),L(V,"is"),P.is=V})(fae||(fae={})),(function(P){function V(Z,re){return re?{label:Z,documentation:re}:{label:Z}}s(V,"create"),L(V,"create"),P.create=V})(gae||(gae={})),(function(P){function V(Z,re,...Q){let ae={label:Z};return ft.defined(re)&&(ae.documentation=re),ft.defined(Q)?ae.parameters=Q:ae.parameters=[],ae}s(V,"create"),L(V,"create"),P.create=V})(mae||(mae={})),(function(P){P.Text=1,P.Read=2,P.Write=3})(yae||(yae={})),(function(P){function V(Z,re){let Q={range:Z};return ft.number(re)&&(Q.kind=re),Q}s(V,"create"),L(V,"create"),P.create=V})(vae||(vae={})),(function(P){P.File=1,P.Module=2,P.Namespace=3,P.Package=4,P.Class=5,P.Method=6,P.Property=7,P.Field=8,P.Constructor=9,P.Enum=10,P.Interface=11,P.Function=12,P.Variable=13,P.Constant=14,P.String=15,P.Number=16,P.Boolean=17,P.Array=18,P.Object=19,P.Key=20,P.Null=21,P.EnumMember=22,P.Struct=23,P.Event=24,P.Operator=25,P.TypeParameter=26})(bae||(bae={})),(function(P){P.Deprecated=1})(xae||(xae={})),(function(P){function V(Z,re,Q,ae,we){let Ue={name:Z,kind:re,location:{uri:ae,range:Q}};return we&&(Ue.containerName=we),Ue}s(V,"create"),L(V,"create"),P.create=V})(Tae||(Tae={})),(function(P){function V(Z,re,Q,ae){return ae!==void 0?{name:Z,kind:re,location:{uri:Q,range:ae}}:{name:Z,kind:re,location:{uri:Q}}}s(V,"create"),L(V,"create"),P.create=V})(kae||(kae={})),(function(P){function V(re,Q,ae,we,Ue,je){let ut={name:re,detail:Q,kind:ae,range:we,selectionRange:Ue};return je!==void 0&&(ut.children=je),ut}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return Q&&ft.string(Q.name)&&ft.number(Q.kind)&&Pi.is(Q.range)&&Pi.is(Q.selectionRange)&&(Q.detail===void 0||ft.string(Q.detail))&&(Q.deprecated===void 0||ft.boolean(Q.deprecated))&&(Q.children===void 0||Array.isArray(Q.children))&&(Q.tags===void 0||Array.isArray(Q.tags))}s(Z,"is"),L(Z,"is"),P.is=Z})(Cae||(Cae={})),(function(P){P.Empty="",P.QuickFix="quickfix",P.Refactor="refactor",P.RefactorExtract="refactor.extract",P.RefactorInline="refactor.inline",P.RefactorRewrite="refactor.rewrite",P.Source="source",P.SourceOrganizeImports="source.organizeImports",P.SourceFixAll="source.fixAll"})(wae||(wae={})),(function(P){P.Invoked=1,P.Automatic=2})(PC||(PC={})),(function(P){function V(re,Q,ae){let we={diagnostics:re};return Q!=null&&(we.only=Q),ae!=null&&(we.triggerKind=ae),we}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&ft.typedArray(Q.diagnostics,MC.is)&&(Q.only===void 0||ft.typedArray(Q.only,ft.string))&&(Q.triggerKind===void 0||Q.triggerKind===PC.Invoked||Q.triggerKind===PC.Automatic)}s(Z,"is"),L(Z,"is"),P.is=Z})(Eae||(Eae={})),(function(P){function V(re,Q,ae){let we={title:re},Ue=!0;return typeof Q=="string"?(Ue=!1,we.kind=Q):Om.is(Q)?we.command=Q:we.edit=Q,Ue&&ae!==void 0&&(we.kind=ae),we}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return Q&&ft.string(Q.title)&&(Q.diagnostics===void 0||ft.typedArray(Q.diagnostics,MC.is))&&(Q.kind===void 0||ft.string(Q.kind))&&(Q.edit!==void 0||Q.command!==void 0)&&(Q.command===void 0||Om.is(Q.command))&&(Q.isPreferred===void 0||ft.boolean(Q.isPreferred))&&(Q.edit===void 0||H$.is(Q.edit))}s(Z,"is"),L(Z,"is"),P.is=Z})(Sae||(Sae={})),(function(P){function V(re,Q){let ae={range:re};return ft.defined(Q)&&(ae.data=Q),ae}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&Pi.is(Q.range)&&(ft.undefined(Q.command)||Om.is(Q.command))}s(Z,"is"),L(Z,"is"),P.is=Z})(_ae||(_ae={})),(function(P){function V(re,Q){return{tabSize:re,insertSpaces:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&ft.uinteger(Q.tabSize)&&ft.boolean(Q.insertSpaces)}s(Z,"is"),L(Z,"is"),P.is=Z})(Aae||(Aae={})),(function(P){function V(re,Q,ae){return{range:re,target:Q,data:ae}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&Pi.is(Q.range)&&(ft.undefined(Q.target)||ft.string(Q.target))}s(Z,"is"),L(Z,"is"),P.is=Z})(Rae||(Rae={})),(function(P){function V(re,Q){return{range:re,parent:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&Pi.is(Q.range)&&(Q.parent===void 0||P.is(Q.parent))}s(Z,"is"),L(Z,"is"),P.is=Z})($ae||($ae={})),(function(P){P.namespace="namespace",P.type="type",P.class="class",P.enum="enum",P.interface="interface",P.struct="struct",P.typeParameter="typeParameter",P.parameter="parameter",P.variable="variable",P.property="property",P.enumMember="enumMember",P.event="event",P.function="function",P.method="method",P.macro="macro",P.keyword="keyword",P.modifier="modifier",P.comment="comment",P.string="string",P.number="number",P.regexp="regexp",P.operator="operator",P.decorator="decorator"})(Lae||(Lae={})),(function(P){P.declaration="declaration",P.definition="definition",P.readonly="readonly",P.static="static",P.deprecated="deprecated",P.abstract="abstract",P.async="async",P.modification="modification",P.documentation="documentation",P.defaultLibrary="defaultLibrary"})(Iae||(Iae={})),(function(P){function V(Z){let re=Z;return ft.objectLiteral(re)&&(re.resultId===void 0||typeof re.resultId=="string")&&Array.isArray(re.data)&&(re.data.length===0||typeof re.data[0]=="number")}s(V,"is"),L(V,"is"),P.is=V})(Mae||(Mae={})),(function(P){function V(re,Q){return{range:re,text:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return Q!=null&&Pi.is(Q.range)&&ft.string(Q.text)}s(Z,"is"),L(Z,"is"),P.is=Z})(Dae||(Dae={})),(function(P){function V(re,Q,ae){return{range:re,variableName:Q,caseSensitiveLookup:ae}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return Q!=null&&Pi.is(Q.range)&&ft.boolean(Q.caseSensitiveLookup)&&(ft.string(Q.variableName)||Q.variableName===void 0)}s(Z,"is"),L(Z,"is"),P.is=Z})(Nae||(Nae={})),(function(P){function V(re,Q){return{range:re,expression:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return Q!=null&&Pi.is(Q.range)&&(ft.string(Q.expression)||Q.expression===void 0)}s(Z,"is"),L(Z,"is"),P.is=Z})(Oae||(Oae={})),(function(P){function V(re,Q){return{frameId:re,stoppedLocation:Q}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.defined(Q)&&Pi.is(re.stoppedLocation)}s(Z,"is"),L(Z,"is"),P.is=Z})(Bae||(Bae={})),(function(P){P.Type=1,P.Parameter=2;function V(Z){return Z===1||Z===2}s(V,"is"),L(V,"is"),P.is=V})(X$||(X$={})),(function(P){function V(re){return{value:re}}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&(Q.tooltip===void 0||ft.string(Q.tooltip)||Hv.is(Q.tooltip))&&(Q.location===void 0||IC.is(Q.location))&&(Q.command===void 0||Om.is(Q.command))}s(Z,"is"),L(Z,"is"),P.is=Z})(K$||(K$={})),(function(P){function V(re,Q,ae){let we={position:re,label:Q};return ae!==void 0&&(we.kind=ae),we}s(V,"create"),L(V,"create"),P.create=V;function Z(re){let Q=re;return ft.objectLiteral(Q)&&tn.is(Q.position)&&(ft.string(Q.label)||ft.typedArray(Q.label,K$.is))&&(Q.kind===void 0||X$.is(Q.kind))&&Q.textEdits===void 0||ft.typedArray(Q.textEdits,ph.is)&&(Q.tooltip===void 0||ft.string(Q.tooltip)||Hv.is(Q.tooltip))&&(Q.paddingLeft===void 0||ft.boolean(Q.paddingLeft))&&(Q.paddingRight===void 0||ft.boolean(Q.paddingRight))}s(Z,"is"),L(Z,"is"),P.is=Z})(Pae||(Pae={})),(function(P){function V(Z){return{kind:"snippet",value:Z}}s(V,"createSnippet"),L(V,"createSnippet"),P.createSnippet=V})(Fae||(Fae={})),(function(P){function V(Z,re,Q,ae){return{insertText:Z,filterText:re,range:Q,command:ae}}s(V,"create"),L(V,"create"),P.create=V})(zae||(zae={})),(function(P){function V(Z){return{items:Z}}s(V,"create"),L(V,"create"),P.create=V})(qae||(qae={})),(function(P){P.Invoked=0,P.Automatic=1})(Wae||(Wae={})),(function(P){function V(Z,re){return{range:Z,text:re}}s(V,"create"),L(V,"create"),P.create=V})(jae||(jae={})),(function(P){function V(Z,re){return{triggerKind:Z,selectedCompletionInfo:re}}s(V,"create"),L(V,"create"),P.create=V})(Vae||(Vae={})),(function(P){function V(Z){let re=Z;return ft.objectLiteral(re)&&V$.is(re.uri)&&ft.string(re.name)}s(V,"is"),L(V,"is"),P.is=V})(Uae||(Uae={})),UGe=[` +`,`\r +`,"\r"],(function(P){function V(ae,we,Ue,je){return new YGe(ae,we,Ue,je)}s(V,"create"),L(V,"create"),P.create=V;function Z(ae){let we=ae;return!!(ft.defined(we)&&ft.string(we.uri)&&(ft.undefined(we.languageId)||ft.string(we.languageId))&&ft.uinteger(we.lineCount)&&ft.func(we.getText)&&ft.func(we.positionAt)&&ft.func(we.offsetAt))}s(Z,"is"),L(Z,"is"),P.is=Z;function re(ae,we){let Ue=ae.getText(),je=Q(we,(at,Rt)=>{let Zt=at.range.start.line-Rt.range.start.line;return Zt===0?at.range.start.character-Rt.range.start.character:Zt}),ut=Ue.length;for(let at=je.length-1;at>=0;at--){let Rt=je[at],Zt=ae.offsetAt(Rt.range.start),Yt=ae.offsetAt(Rt.range.end);if(Yt<=ut)Ue=Ue.substring(0,Zt)+Rt.newText+Ue.substring(Yt,Ue.length);else throw new Error("Overlapping edit");ut=Zt}return Ue}s(re,"applyEdits"),L(re,"applyEdits"),P.applyEdits=re;function Q(ae,we){if(ae.length<=1)return ae;let Ue=ae.length/2|0,je=ae.slice(0,Ue),ut=ae.slice(Ue);Q(je,we),Q(ut,we);let at=0,Rt=0,Zt=0;for(;at0&&V.push(Z.length),this._lineOffsets=V}return this._lineOffsets}positionAt(V){V=Math.max(Math.min(V,this._content.length),0);let Z=this.getLineOffsets(),re=0,Q=Z.length;if(Q===0)return tn.create(0,V);for(;reV?Q=we:re=we+1}let ae=re-1;return tn.create(ae,V-Z[ae])}offsetAt(V){let Z=this.getLineOffsets();if(V.line>=Z.length)return this._content.length;if(V.line<0)return 0;let re=Z[V.line],Q=V.line+1"u"}s(re,"undefined2"),L(re,"undefined"),P.undefined=re;function Q(Yt){return Yt===!0||Yt===!1}s(Q,"boolean"),L(Q,"boolean"),P.boolean=Q;function ae(Yt){return V.call(Yt)==="[object String]"}s(ae,"string"),L(ae,"string"),P.string=ae;function we(Yt){return V.call(Yt)==="[object Number]"}s(we,"number"),L(we,"number"),P.number=we;function Ue(Yt,xr,mr){return V.call(Yt)==="[object Number]"&&xr<=Yt&&Yt<=mr}s(Ue,"numberRange"),L(Ue,"numberRange"),P.numberRange=Ue;function je(Yt){return V.call(Yt)==="[object Number]"&&-2147483648<=Yt&&Yt<=2147483647}s(je,"integer2"),L(je,"integer"),P.integer=je;function ut(Yt){return V.call(Yt)==="[object Number]"&&0<=Yt&&Yt<=2147483647}s(ut,"uinteger2"),L(ut,"uinteger"),P.uinteger=ut;function at(Yt){return V.call(Yt)==="[object Function]"}s(at,"func"),L(at,"func"),P.func=at;function Rt(Yt){return Yt!==null&&typeof Yt=="object"}s(Rt,"objectLiteral"),L(Rt,"objectLiteral"),P.objectLiteral=Rt;function Zt(Yt,xr){return Array.isArray(Yt)&&Yt.every(xr)}s(Zt,"typedArray"),L(Zt,"typedArray"),P.typedArray=Zt})(ft||(ft={}))}}),Uf=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/ral.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0});var T;function A(){if(T===void 0)throw new Error("No runtime abstraction layer installed");return T}s(A,"RAL"),L(A,"RAL"),(function(D){function P(V){if(V===void 0)throw new Error("No runtime abstraction layer provided");T=V}s(P,"install"),L(P,"install"),D.install=P})(A||(A={})),q.default=A}}),Xv=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/is.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.stringArray=q.array=q.func=q.error=q.number=q.string=q.boolean=void 0;function T(Q){return Q===!0||Q===!1}s(T,"boolean"),L(T,"boolean"),q.boolean=T;function A(Q){return typeof Q=="string"||Q instanceof String}s(A,"string"),L(A,"string"),q.string=A;function D(Q){return typeof Q=="number"||Q instanceof Number}s(D,"number"),L(D,"number"),q.number=D;function P(Q){return Q instanceof Error}s(P,"error"),L(P,"error"),q.error=P;function V(Q){return typeof Q=="function"}s(V,"func"),L(V,"func"),q.func=V;function Z(Q){return Array.isArray(Q)}s(Z,"array"),L(Z,"array"),q.array=Z;function re(Q){return Z(Q)&&Q.every(ae=>A(ae))}s(re,"stringArray"),L(re,"stringArray"),q.stringArray=re}}),Pm=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/events.js"(q){"use strict";var V,Z;Object.defineProperty(q,"__esModule",{value:!0}),q.Emitter=q.Event=void 0;var T=Uf(),A;(function(re){let Q={dispose(){}};re.None=function(){return Q}})(A||(q.Event=A={}));var D=(V=class{add(Q,ae=null,we){this._callbacks||(this._callbacks=[],this._contexts=[]),this._callbacks.push(Q),this._contexts.push(ae),Array.isArray(we)&&we.push({dispose:L(()=>this.remove(Q,ae),"dispose")})}remove(Q,ae=null){if(!this._callbacks)return;let we=!1;for(let Ue=0,je=this._callbacks.length;Ue{this._callbacks||(this._callbacks=new D),this._options&&this._options.onFirstListenerAdd&&this._callbacks.isEmpty()&&this._options.onFirstListenerAdd(this),this._callbacks.add(Q,ae);let Ue={dispose:L(()=>{this._callbacks&&(this._callbacks.remove(Q,ae),Ue.dispose=Z._noop,this._options&&this._options.onLastListenerRemove&&this._callbacks.isEmpty()&&this._options.onLastListenerRemove(this))},"dispose")};return Array.isArray(we)&&we.push(Ue),Ue}),this._event}fire(Q){this._callbacks&&this._callbacks.invoke.call(this._callbacks,Q)}dispose(){this._callbacks&&(this._callbacks.dispose(),this._callbacks=void 0)}},s(Z,"_Emitter"),L(Z,"Emitter"),Z);q.Emitter=P,P._noop=function(){}}}),FC=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/cancellation.js"(q){"use strict";var Q,ae;Object.defineProperty(q,"__esModule",{value:!0}),q.CancellationTokenSource=q.CancellationToken=void 0;var T=Uf(),A=Xv(),D=Pm(),P;(function(we){we.None=Object.freeze({isCancellationRequested:!1,onCancellationRequested:D.Event.None}),we.Cancelled=Object.freeze({isCancellationRequested:!0,onCancellationRequested:D.Event.None});function Ue(je){let ut=je;return ut&&(ut===we.None||ut===we.Cancelled||A.boolean(ut.isCancellationRequested)&&!!ut.onCancellationRequested)}s(Ue,"is"),L(Ue,"is"),we.is=Ue})(P||(q.CancellationToken=P={}));var V=Object.freeze(function(we,Ue){let je=(0,T.default)().timer.setTimeout(we.bind(Ue),0);return{dispose(){je.dispose()}}}),Z=(Q=class{constructor(){this._isCancelled=!1}cancel(){this._isCancelled||(this._isCancelled=!0,this._emitter&&(this._emitter.fire(void 0),this.dispose()))}get isCancellationRequested(){return this._isCancelled}get onCancellationRequested(){return this._isCancelled?V:(this._emitter||(this._emitter=new D.Emitter),this._emitter.event)}dispose(){this._emitter&&(this._emitter.dispose(),this._emitter=void 0)}},s(Q,"MutableToken"),L(Q,"MutableToken"),Q),re=(ae=class{get token(){return this._token||(this._token=new Z),this._token}cancel(){this._token?this._token.cancel():this._token=P.Cancelled}dispose(){this._token?this._token instanceof Z&&this._token.dispose():this._token=P.None}},s(ae,"CancellationTokenSource3"),L(ae,"CancellationTokenSource"),ae);q.CancellationTokenSource=re}}),Hae=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/messages.js"(q){"use strict";var Ut,yt,ht,Tt,dt,pt,Ct,rr,hr,Gt,Pr,wr,ui,oi,Ci,ji,ua,yn,jn,ys,ha,Mi,Os,on,vs;Object.defineProperty(q,"__esModule",{value:!0}),q.Message=q.NotificationType9=q.NotificationType8=q.NotificationType7=q.NotificationType6=q.NotificationType5=q.NotificationType4=q.NotificationType3=q.NotificationType2=q.NotificationType1=q.NotificationType0=q.NotificationType=q.RequestType9=q.RequestType8=q.RequestType7=q.RequestType6=q.RequestType5=q.RequestType4=q.RequestType3=q.RequestType2=q.RequestType1=q.RequestType=q.RequestType0=q.AbstractMessageSignature=q.ParameterStructures=q.ResponseError=q.ErrorCodes=void 0;var T=Xv(),A;(function(Vn){Vn.ParseError=-32700,Vn.InvalidRequest=-32600,Vn.MethodNotFound=-32601,Vn.InvalidParams=-32602,Vn.InternalError=-32603,Vn.jsonrpcReservedErrorRangeStart=-32099,Vn.serverErrorStart=-32099,Vn.MessageWriteError=-32099,Vn.MessageReadError=-32098,Vn.PendingResponseRejected=-32097,Vn.ConnectionInactive=-32096,Vn.ServerNotInitialized=-32002,Vn.UnknownErrorCode=-32001,Vn.jsonrpcReservedErrorRangeEnd=-32e3,Vn.serverErrorEnd=-32e3})(A||(q.ErrorCodes=A={}));var D=(Ut=class extends Error{constructor(_r,Oa,yu){super(Oa),this.code=T.number(_r)?_r:A.UnknownErrorCode,this.data=yu,Object.setPrototypeOf(this,Ut.prototype)}toJson(){let _r={code:this.code,message:this.message};return this.data!==void 0&&(_r.data=this.data),_r}},s(Ut,"_ResponseError"),L(Ut,"ResponseError"),Ut);q.ResponseError=D;var P=(yt=class{constructor(_r){this.kind=_r}static is(_r){return _r===yt.auto||_r===yt.byName||_r===yt.byPosition}toString(){return this.kind}},s(yt,"_ParameterStructures"),L(yt,"ParameterStructures"),yt);q.ParameterStructures=P,P.auto=new P("auto"),P.byPosition=new P("byPosition"),P.byName=new P("byName");var V=(ht=class{constructor(_r,Oa){this.method=_r,this.numberOfParams=Oa}get parameterStructures(){return P.auto}},s(ht,"AbstractMessageSignature"),L(ht,"AbstractMessageSignature"),ht);q.AbstractMessageSignature=V;var Z=(Tt=class extends V{constructor(_r){super(_r,0)}},s(Tt,"RequestType0"),L(Tt,"RequestType0"),Tt);q.RequestType0=Z;var re=(dt=class extends V{constructor(_r,Oa=P.auto){super(_r,1),this._parameterStructures=Oa}get parameterStructures(){return this._parameterStructures}},s(dt,"RequestType"),L(dt,"RequestType"),dt);q.RequestType=re;var Q=(pt=class extends V{constructor(_r,Oa=P.auto){super(_r,1),this._parameterStructures=Oa}get parameterStructures(){return this._parameterStructures}},s(pt,"RequestType1"),L(pt,"RequestType1"),pt);q.RequestType1=Q;var ae=(Ct=class extends V{constructor(_r){super(_r,2)}},s(Ct,"RequestType2"),L(Ct,"RequestType2"),Ct);q.RequestType2=ae;var we=(rr=class extends V{constructor(_r){super(_r,3)}},s(rr,"RequestType3"),L(rr,"RequestType3"),rr);q.RequestType3=we;var Ue=(hr=class extends V{constructor(_r){super(_r,4)}},s(hr,"RequestType4"),L(hr,"RequestType4"),hr);q.RequestType4=Ue;var je=(Gt=class extends V{constructor(_r){super(_r,5)}},s(Gt,"RequestType5"),L(Gt,"RequestType5"),Gt);q.RequestType5=je;var ut=(Pr=class extends V{constructor(_r){super(_r,6)}},s(Pr,"RequestType6"),L(Pr,"RequestType6"),Pr);q.RequestType6=ut;var at=(wr=class extends V{constructor(_r){super(_r,7)}},s(wr,"RequestType7"),L(wr,"RequestType7"),wr);q.RequestType7=at;var Rt=(ui=class extends V{constructor(_r){super(_r,8)}},s(ui,"RequestType8"),L(ui,"RequestType8"),ui);q.RequestType8=Rt;var Zt=(oi=class extends V{constructor(_r){super(_r,9)}},s(oi,"RequestType9"),L(oi,"RequestType9"),oi);q.RequestType9=Zt;var Yt=(Ci=class extends V{constructor(_r,Oa=P.auto){super(_r,1),this._parameterStructures=Oa}get parameterStructures(){return this._parameterStructures}},s(Ci,"NotificationType"),L(Ci,"NotificationType"),Ci);q.NotificationType=Yt;var xr=(ji=class extends V{constructor(_r){super(_r,0)}},s(ji,"NotificationType0"),L(ji,"NotificationType0"),ji);q.NotificationType0=xr;var mr=(ua=class extends V{constructor(_r,Oa=P.auto){super(_r,1),this._parameterStructures=Oa}get parameterStructures(){return this._parameterStructures}},s(ua,"NotificationType1"),L(ua,"NotificationType1"),ua);q.NotificationType1=mr;var sr=(yn=class extends V{constructor(_r){super(_r,2)}},s(yn,"NotificationType2"),L(yn,"NotificationType2"),yn);q.NotificationType2=sr;var qn=(jn=class extends V{constructor(_r){super(_r,3)}},s(jn,"NotificationType3"),L(jn,"NotificationType3"),jn);q.NotificationType3=qn;var ia=(ys=class extends V{constructor(_r){super(_r,4)}},s(ys,"NotificationType4"),L(ys,"NotificationType4"),ys);q.NotificationType4=ia;var Ri=(ha=class extends V{constructor(_r){super(_r,5)}},s(ha,"NotificationType5"),L(ha,"NotificationType5"),ha);q.NotificationType5=Ri;var Ca=(Mi=class extends V{constructor(_r){super(_r,6)}},s(Mi,"NotificationType6"),L(Mi,"NotificationType6"),Mi);q.NotificationType6=Ca;var Qn=(Os=class extends V{constructor(_r){super(_r,7)}},s(Os,"NotificationType7"),L(Os,"NotificationType7"),Os);q.NotificationType7=Qn;var Wn=(on=class extends V{constructor(_r){super(_r,8)}},s(on,"NotificationType8"),L(on,"NotificationType8"),on);q.NotificationType8=Wn;var sn=(vs=class extends V{constructor(_r){super(_r,9)}},s(vs,"NotificationType9"),L(vs,"NotificationType9"),vs);q.NotificationType9=sn;var Zs;(function(Vn){function _r(Cc){let Un=Cc;return Un&&T.string(Un.method)&&(T.string(Un.id)||T.number(Un.id))}s(_r,"isRequest"),L(_r,"isRequest"),Vn.isRequest=_r;function Oa(Cc){let Un=Cc;return Un&&T.string(Un.method)&&Cc.id===void 0}s(Oa,"isNotification"),L(Oa,"isNotification"),Vn.isNotification=Oa;function yu(Cc){let Un=Cc;return Un&&(Un.result!==void 0||!!Un.error)&&(T.string(Un.id)||T.number(Un.id)||Un.id===null)}s(yu,"isResponse"),L(yu,"isResponse"),Vn.isResponse=yu})(Zs||(q.Message=Zs={}))}}),Gae=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/linkedMap.js"(q){"use strict";var V,Z;var T;Object.defineProperty(q,"__esModule",{value:!0}),q.LRUCache=q.LinkedMap=q.Touch=void 0;var A;(function(re){re.None=0,re.First=1,re.AsOld=re.First,re.Last=2,re.AsNew=re.Last})(A||(q.Touch=A={}));var D=(V=class{constructor(){this[T]="LinkedMap",this._map=new Map,this._head=void 0,this._tail=void 0,this._size=0,this._state=0}clear(){this._map.clear(),this._head=void 0,this._tail=void 0,this._size=0,this._state++}isEmpty(){return!this._head&&!this._tail}get size(){return this._size}get first(){return this._head?.value}get last(){return this._tail?.value}has(Q){return this._map.has(Q)}get(Q,ae=A.None){let we=this._map.get(Q);if(we)return ae!==A.None&&this.touch(we,ae),we.value}set(Q,ae,we=A.None){let Ue=this._map.get(Q);if(Ue)Ue.value=ae,we!==A.None&&this.touch(Ue,we);else{switch(Ue={key:Q,value:ae,next:void 0,previous:void 0},we){case A.None:this.addItemLast(Ue);break;case A.First:this.addItemFirst(Ue);break;case A.Last:this.addItemLast(Ue);break;default:this.addItemLast(Ue);break}this._map.set(Q,Ue),this._size++}return this}delete(Q){return!!this.remove(Q)}remove(Q){let ae=this._map.get(Q);if(ae)return this._map.delete(Q),this.removeItem(ae),this._size--,ae.value}shift(){if(!this._head&&!this._tail)return;if(!this._head||!this._tail)throw new Error("Invalid list");let Q=this._head;return this._map.delete(Q.key),this.removeItem(Q),this._size--,Q.value}forEach(Q,ae){let we=this._state,Ue=this._head;for(;Ue;){if(ae?Q.bind(ae)(Ue.value,Ue.key,this):Q(Ue.value,Ue.key,this),this._state!==we)throw new Error("LinkedMap got modified during iteration.");Ue=Ue.next}}keys(){let Q=this._state,ae=this._head,we={[Symbol.iterator]:()=>we,next:L(()=>{if(this._state!==Q)throw new Error("LinkedMap got modified during iteration.");if(ae){let Ue={value:ae.key,done:!1};return ae=ae.next,Ue}else return{value:void 0,done:!0}},"next")};return we}values(){let Q=this._state,ae=this._head,we={[Symbol.iterator]:()=>we,next:L(()=>{if(this._state!==Q)throw new Error("LinkedMap got modified during iteration.");if(ae){let Ue={value:ae.value,done:!1};return ae=ae.next,Ue}else return{value:void 0,done:!0}},"next")};return we}entries(){let Q=this._state,ae=this._head,we={[Symbol.iterator]:()=>we,next:L(()=>{if(this._state!==Q)throw new Error("LinkedMap got modified during iteration.");if(ae){let Ue={value:[ae.key,ae.value],done:!1};return ae=ae.next,Ue}else return{value:void 0,done:!0}},"next")};return we}[(T=Symbol.toStringTag,Symbol.iterator)](){return this.entries()}trimOld(Q){if(Q>=this.size)return;if(Q===0){this.clear();return}let ae=this._head,we=this.size;for(;ae&&we>Q;)this._map.delete(ae.key),ae=ae.next,we--;this._head=ae,this._size=we,ae&&(ae.previous=void 0),this._state++}addItemFirst(Q){if(!this._head&&!this._tail)this._tail=Q;else if(this._head)Q.next=this._head,this._head.previous=Q;else throw new Error("Invalid list");this._head=Q,this._state++}addItemLast(Q){if(!this._head&&!this._tail)this._head=Q;else if(this._tail)Q.previous=this._tail,this._tail.next=Q;else throw new Error("Invalid list");this._tail=Q,this._state++}removeItem(Q){if(Q===this._head&&Q===this._tail)this._head=void 0,this._tail=void 0;else if(Q===this._head){if(!Q.next)throw new Error("Invalid list");Q.next.previous=void 0,this._head=Q.next}else if(Q===this._tail){if(!Q.previous)throw new Error("Invalid list");Q.previous.next=void 0,this._tail=Q.previous}else{let ae=Q.next,we=Q.previous;if(!ae||!we)throw new Error("Invalid list");ae.previous=we,we.next=ae}Q.next=void 0,Q.previous=void 0,this._state++}touch(Q,ae){if(!this._head||!this._tail)throw new Error("Invalid list");if(!(ae!==A.First&&ae!==A.Last)){if(ae===A.First){if(Q===this._head)return;let we=Q.next,Ue=Q.previous;Q===this._tail?(Ue.next=void 0,this._tail=Ue):(we.previous=Ue,Ue.next=we),Q.previous=void 0,Q.next=this._head,this._head.previous=Q,this._head=Q,this._state++}else if(ae===A.Last){if(Q===this._tail)return;let we=Q.next,Ue=Q.previous;Q===this._head?(we.previous=void 0,this._head=we):(we.previous=Ue,Ue.next=we),Q.next=void 0,Q.previous=this._tail,this._tail.next=Q,this._tail=Q,this._state++}}}toJSON(){let Q=[];return this.forEach((ae,we)=>{Q.push([we,ae])}),Q}fromJSON(Q){this.clear();for(let[ae,we]of Q)this.set(ae,we)}},s(V,"LinkedMap"),L(V,"LinkedMap"),V);q.LinkedMap=D;var P=(Z=class extends D{constructor(Q,ae=1){super(),this._limit=Q,this._ratio=Math.min(Math.max(0,ae),1)}get limit(){return this._limit}set limit(Q){this._limit=Q,this.checkTrim()}get ratio(){return this._ratio}set ratio(Q){this._ratio=Math.min(Math.max(0,Q),1),this.checkTrim()}get(Q,ae=A.AsNew){return super.get(Q,ae)}peek(Q){return super.get(Q,A.None)}set(Q,ae){return super.set(Q,ae,A.Last),this.checkTrim(),this}checkTrim(){this.size>this._limit&&this.trimOld(Math.round(this._limit*this._ratio))}},s(Z,"LRUCache"),L(Z,"LRUCache"),Z);q.LRUCache=P}}),HGe=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/disposable.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.Disposable=void 0;var T;(function(A){function D(P){return{dispose:P}}s(D,"create"),L(D,"create"),A.create=D})(T||(q.Disposable=T={}))}}),GGe=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/sharedArrayCancellation.js"(q){"use strict";var re,Q,ae,we;Object.defineProperty(q,"__esModule",{value:!0}),q.SharedArrayReceiverStrategy=q.SharedArraySenderStrategy=void 0;var T=FC(),A;(function(Ue){Ue.Continue=0,Ue.Cancelled=1})(A||(A={}));var D=(re=class{constructor(){this.buffers=new Map}enableCancellation(je){if(je.id===null)return;let ut=new SharedArrayBuffer(4),at=new Int32Array(ut,0,1);at[0]=A.Continue,this.buffers.set(je.id,ut),je.$cancellationData=ut}async sendCancellation(je,ut){let at=this.buffers.get(ut);if(at===void 0)return;let Rt=new Int32Array(at,0,1);Atomics.store(Rt,0,A.Cancelled)}cleanup(je){this.buffers.delete(je)}dispose(){this.buffers.clear()}},s(re,"SharedArraySenderStrategy"),L(re,"SharedArraySenderStrategy"),re);q.SharedArraySenderStrategy=D;var P=(Q=class{constructor(je){this.data=new Int32Array(je,0,1)}get isCancellationRequested(){return Atomics.load(this.data,0)===A.Cancelled}get onCancellationRequested(){throw new Error("Cancellation over SharedArrayBuffer doesn't support cancellation events")}},s(Q,"SharedArrayBufferCancellationToken"),L(Q,"SharedArrayBufferCancellationToken"),Q),V=(ae=class{constructor(je){this.token=new P(je)}cancel(){}dispose(){}},s(ae,"SharedArrayBufferCancellationTokenSource"),L(ae,"SharedArrayBufferCancellationTokenSource"),ae),Z=(we=class{constructor(){this.kind="request"}createCancellationTokenSource(je){let ut=je.$cancellationData;return ut===void 0?new T.CancellationTokenSource:new V(ut)}},s(we,"SharedArrayReceiverStrategy"),L(we,"SharedArrayReceiverStrategy"),we);q.SharedArrayReceiverStrategy=Z}}),Xae=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/semaphore.js"(q){"use strict";var D;Object.defineProperty(q,"__esModule",{value:!0}),q.Semaphore=void 0;var T=Uf(),A=(D=class{constructor(V=1){if(V<=0)throw new Error("Capacity must be greater than 0");this._capacity=V,this._active=0,this._waiting=[]}lock(V){return new Promise((Z,re)=>{this._waiting.push({thunk:V,resolve:Z,reject:re}),this.runNext()})}get active(){return this._active}runNext(){this._waiting.length===0||this._active===this._capacity||(0,T.default)().timer.setImmediate(()=>this.doRunNext())}doRunNext(){if(this._waiting.length===0||this._active===this._capacity)return;let V=this._waiting.shift();if(this._active++,this._active>this._capacity)throw new Error("To many thunks active");try{let Z=V.thunk();Z instanceof Promise?Z.then(re=>{this._active--,V.resolve(re),this.runNext()},re=>{this._active--,V.reject(re),this.runNext()}):(this._active--,V.resolve(Z),this.runNext())}catch(Z){this._active--,V.reject(Z),this.runNext()}}},s(D,"Semaphore"),L(D,"Semaphore"),D);q.Semaphore=A}}),XGe=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/messageReader.js"(q){"use strict";var ae,we;Object.defineProperty(q,"__esModule",{value:!0}),q.ReadableStreamMessageReader=q.AbstractMessageReader=q.MessageReader=void 0;var T=Uf(),A=Xv(),D=Pm(),P=Xae(),V;(function(Ue){function je(ut){let at=ut;return at&&A.func(at.listen)&&A.func(at.dispose)&&A.func(at.onError)&&A.func(at.onClose)&&A.func(at.onPartialMessage)}s(je,"is"),L(je,"is"),Ue.is=je})(V||(q.MessageReader=V={}));var Z=(ae=class{constructor(){this.errorEmitter=new D.Emitter,this.closeEmitter=new D.Emitter,this.partialMessageEmitter=new D.Emitter}dispose(){this.errorEmitter.dispose(),this.closeEmitter.dispose()}get onError(){return this.errorEmitter.event}fireError(je){this.errorEmitter.fire(this.asError(je))}get onClose(){return this.closeEmitter.event}fireClose(){this.closeEmitter.fire(void 0)}get onPartialMessage(){return this.partialMessageEmitter.event}firePartialMessage(je){this.partialMessageEmitter.fire(je)}asError(je){return je instanceof Error?je:new Error(`Reader received error. Reason: ${A.string(je.message)?je.message:"unknown"}`)}},s(ae,"AbstractMessageReader"),L(ae,"AbstractMessageReader"),ae);q.AbstractMessageReader=Z;var re;(function(Ue){function je(ut){let at,Rt,Zt,Yt=new Map,xr,mr=new Map;if(ut===void 0||typeof ut=="string")at=ut??"utf-8";else{if(at=ut.charset??"utf-8",ut.contentDecoder!==void 0&&(Zt=ut.contentDecoder,Yt.set(Zt.name,Zt)),ut.contentDecoders!==void 0)for(let sr of ut.contentDecoders)Yt.set(sr.name,sr);if(ut.contentTypeDecoder!==void 0&&(xr=ut.contentTypeDecoder,mr.set(xr.name,xr)),ut.contentTypeDecoders!==void 0)for(let sr of ut.contentTypeDecoders)mr.set(sr.name,sr)}return xr===void 0&&(xr=(0,T.default)().applicationJson.decoder,mr.set(xr.name,xr)),{charset:at,contentDecoder:Zt,contentDecoders:Yt,contentTypeDecoder:xr,contentTypeDecoders:mr}}s(je,"fromOptions"),L(je,"fromOptions"),Ue.fromOptions=je})(re||(re={}));var Q=(we=class extends Z{constructor(je,ut){super(),this.readable=je,this.options=re.fromOptions(ut),this.buffer=(0,T.default)().messageBuffer.create(this.options.charset),this._partialMessageTimeout=1e4,this.nextMessageLength=-1,this.messageToken=0,this.readSemaphore=new P.Semaphore(1)}set partialMessageTimeout(je){this._partialMessageTimeout=je}get partialMessageTimeout(){return this._partialMessageTimeout}listen(je){this.nextMessageLength=-1,this.messageToken=0,this.partialMessageTimer=void 0,this.callback=je;let ut=this.readable.onData(at=>{this.onData(at)});return this.readable.onError(at=>this.fireError(at)),this.readable.onClose(()=>this.fireClose()),ut}onData(je){try{for(this.buffer.append(je);;){if(this.nextMessageLength===-1){let at=this.buffer.tryReadHeaders(!0);if(!at)return;let Rt=at.get("content-length");if(!Rt){this.fireError(new Error(`Header must provide a Content-Length property. +${JSON.stringify(Object.fromEntries(at))}`));return}let Zt=parseInt(Rt);if(isNaN(Zt)){this.fireError(new Error(`Content-Length value must be a number. Got ${Rt}`));return}this.nextMessageLength=Zt}let ut=this.buffer.tryReadBody(this.nextMessageLength);if(ut===void 0){this.setPartialMessageTimer();return}this.clearPartialMessageTimer(),this.nextMessageLength=-1,this.readSemaphore.lock(async()=>{let at=this.options.contentDecoder!==void 0?await this.options.contentDecoder.decode(ut):ut,Rt=await this.options.contentTypeDecoder.decode(at,this.options);this.callback(Rt)}).catch(at=>{this.fireError(at)})}}catch(ut){this.fireError(ut)}}clearPartialMessageTimer(){this.partialMessageTimer&&(this.partialMessageTimer.dispose(),this.partialMessageTimer=void 0)}setPartialMessageTimer(){this.clearPartialMessageTimer(),!(this._partialMessageTimeout<=0)&&(this.partialMessageTimer=(0,T.default)().timer.setTimeout((je,ut)=>{this.partialMessageTimer=void 0,je===this.messageToken&&(this.firePartialMessage({messageToken:je,waitingTime:ut}),this.setPartialMessageTimer())},this._partialMessageTimeout,this.messageToken,this._partialMessageTimeout))}},s(we,"ReadableStreamMessageReader"),L(we,"ReadableStreamMessageReader"),we);q.ReadableStreamMessageReader=Q}}),KGe=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/messageWriter.js"(q){"use strict";var Ue,je;Object.defineProperty(q,"__esModule",{value:!0}),q.WriteableStreamMessageWriter=q.AbstractMessageWriter=q.MessageWriter=void 0;var T=Uf(),A=Xv(),D=Xae(),P=Pm(),V="Content-Length: ",Z=`\r +`,re;(function(ut){function at(Rt){let Zt=Rt;return Zt&&A.func(Zt.dispose)&&A.func(Zt.onClose)&&A.func(Zt.onError)&&A.func(Zt.write)}s(at,"is"),L(at,"is"),ut.is=at})(re||(q.MessageWriter=re={}));var Q=(Ue=class{constructor(){this.errorEmitter=new P.Emitter,this.closeEmitter=new P.Emitter}dispose(){this.errorEmitter.dispose(),this.closeEmitter.dispose()}get onError(){return this.errorEmitter.event}fireError(at,Rt,Zt){this.errorEmitter.fire([this.asError(at),Rt,Zt])}get onClose(){return this.closeEmitter.event}fireClose(){this.closeEmitter.fire(void 0)}asError(at){return at instanceof Error?at:new Error(`Writer received error. Reason: ${A.string(at.message)?at.message:"unknown"}`)}},s(Ue,"AbstractMessageWriter"),L(Ue,"AbstractMessageWriter"),Ue);q.AbstractMessageWriter=Q;var ae;(function(ut){function at(Rt){return Rt===void 0||typeof Rt=="string"?{charset:Rt??"utf-8",contentTypeEncoder:(0,T.default)().applicationJson.encoder}:{charset:Rt.charset??"utf-8",contentEncoder:Rt.contentEncoder,contentTypeEncoder:Rt.contentTypeEncoder??(0,T.default)().applicationJson.encoder}}s(at,"fromOptions"),L(at,"fromOptions"),ut.fromOptions=at})(ae||(ae={}));var we=(je=class extends Q{constructor(at,Rt){super(),this.writable=at,this.options=ae.fromOptions(Rt),this.errorCount=0,this.writeSemaphore=new D.Semaphore(1),this.writable.onError(Zt=>this.fireError(Zt)),this.writable.onClose(()=>this.fireClose())}async write(at){return this.writeSemaphore.lock(async()=>this.options.contentTypeEncoder.encode(at,this.options).then(Rt=>this.options.contentEncoder!==void 0?this.options.contentEncoder.encode(Rt):Rt).then(Rt=>{let Zt=[];return Zt.push(V,Rt.byteLength.toString(),Z),Zt.push(Z),this.doWrite(at,Zt,Rt)},Rt=>{throw this.fireError(Rt),Rt}))}async doWrite(at,Rt,Zt){try{return await this.writable.write(Rt.join(""),"ascii"),this.writable.write(Zt)}catch(Yt){return this.handleError(Yt,at),Promise.reject(Yt)}}handleError(at,Rt){this.errorCount++,this.fireError(at,Rt,this.errorCount)}end(){this.writable.end()}},s(je,"WriteableStreamMessageWriter"),L(je,"WriteableStreamMessageWriter"),je);q.WriteableStreamMessageWriter=we}}),QGe=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/messageBuffer.js"(q){"use strict";var V;Object.defineProperty(q,"__esModule",{value:!0}),q.AbstractMessageBuffer=void 0;var T=13,A=10,D=`\r +`,P=(V=class{constructor(re="utf-8"){this._encoding=re,this._chunks=[],this._totalLength=0}get encoding(){return this._encoding}append(re){let Q=typeof re=="string"?this.fromString(re,this._encoding):re;this._chunks.push(Q),this._totalLength+=Q.byteLength}tryReadHeaders(re=!1){if(this._chunks.length===0)return;let Q=0,ae=0,we=0,Ue=0;e:for(;aethis._totalLength)throw new Error("Cannot read so many bytes!");if(this._chunks[0].byteLength===re){let Ue=this._chunks[0];return this._chunks.shift(),this._totalLength-=re,this.asNative(Ue)}if(this._chunks[0].byteLength>re){let Ue=this._chunks[0],je=this.asNative(Ue,re);return this._chunks[0]=Ue.slice(re),this._totalLength-=re,je}let Q=this.allocNative(re),ae=0,we=0;for(;re>0;){let Ue=this._chunks[we];if(Ue.byteLength>re){let je=Ue.slice(0,re);Q.set(je,ae),ae+=re,this._chunks[we]=Ue.slice(re),this._totalLength-=re,re-=re}else Q.set(Ue,ae),ae+=Ue.byteLength,this._chunks.shift(),this._totalLength-=Ue.byteLength,re-=Ue.byteLength}return Q}},s(V,"AbstractMessageBuffer"),L(V,"AbstractMessageBuffer"),V);q.AbstractMessageBuffer=P}}),ZGe=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/connection.js"(q){"use strict";var Ut,yt;Object.defineProperty(q,"__esModule",{value:!0}),q.createMessageConnection=q.ConnectionOptions=q.MessageStrategy=q.CancellationStrategy=q.CancellationSenderStrategy=q.CancellationReceiverStrategy=q.RequestCancellationReceiverStrategy=q.IdCancellationReceiverStrategy=q.ConnectionStrategy=q.ConnectionError=q.ConnectionErrors=q.LogTraceNotification=q.SetTraceNotification=q.TraceFormat=q.TraceValues=q.Trace=q.NullLogger=q.ProgressType=q.ProgressToken=void 0;var T=Uf(),A=Xv(),D=Hae(),P=Gae(),V=Pm(),Z=FC(),re;(function(ht){ht.type=new D.NotificationType("$/cancelRequest")})(re||(re={}));var Q;(function(ht){function Tt(dt){return typeof dt=="string"||typeof dt=="number"}s(Tt,"is"),L(Tt,"is"),ht.is=Tt})(Q||(q.ProgressToken=Q={}));var ae;(function(ht){ht.type=new D.NotificationType("$/progress")})(ae||(ae={}));var we=(Ut=class{constructor(){}},s(Ut,"ProgressType"),L(Ut,"ProgressType"),Ut);q.ProgressType=we;var Ue;(function(ht){function Tt(dt){return A.func(dt)}s(Tt,"is"),L(Tt,"is"),ht.is=Tt})(Ue||(Ue={})),q.NullLogger=Object.freeze({error:L(()=>{},"error"),warn:L(()=>{},"warn"),info:L(()=>{},"info"),log:L(()=>{},"log")});var je;(function(ht){ht[ht.Off=0]="Off",ht[ht.Messages=1]="Messages",ht[ht.Compact=2]="Compact",ht[ht.Verbose=3]="Verbose"})(je||(q.Trace=je={}));var ut;(function(ht){ht.Off="off",ht.Messages="messages",ht.Compact="compact",ht.Verbose="verbose"})(ut||(q.TraceValues=ut={})),(function(ht){function Tt(pt){if(!A.string(pt))return ht.Off;switch(pt=pt.toLowerCase(),pt){case"off":return ht.Off;case"messages":return ht.Messages;case"compact":return ht.Compact;case"verbose":return ht.Verbose;default:return ht.Off}}s(Tt,"fromString"),L(Tt,"fromString"),ht.fromString=Tt;function dt(pt){switch(pt){case ht.Off:return"off";case ht.Messages:return"messages";case ht.Compact:return"compact";case ht.Verbose:return"verbose";default:return"off"}}s(dt,"toString4"),L(dt,"toString"),ht.toString=dt})(je||(q.Trace=je={}));var at;(function(ht){ht.Text="text",ht.JSON="json"})(at||(q.TraceFormat=at={})),(function(ht){function Tt(dt){return A.string(dt)?(dt=dt.toLowerCase(),dt==="json"?ht.JSON:ht.Text):ht.Text}s(Tt,"fromString"),L(Tt,"fromString"),ht.fromString=Tt})(at||(q.TraceFormat=at={}));var Rt;(function(ht){ht.type=new D.NotificationType("$/setTrace")})(Rt||(q.SetTraceNotification=Rt={}));var Zt;(function(ht){ht.type=new D.NotificationType("$/logTrace")})(Zt||(q.LogTraceNotification=Zt={}));var Yt;(function(ht){ht[ht.Closed=1]="Closed",ht[ht.Disposed=2]="Disposed",ht[ht.AlreadyListening=3]="AlreadyListening"})(Yt||(q.ConnectionErrors=Yt={}));var xr=(yt=class extends Error{constructor(Tt,dt){super(dt),this.code=Tt,Object.setPrototypeOf(this,yt.prototype)}},s(yt,"_ConnectionError"),L(yt,"ConnectionError"),yt);q.ConnectionError=xr;var mr;(function(ht){function Tt(dt){let pt=dt;return pt&&A.func(pt.cancelUndispatched)}s(Tt,"is"),L(Tt,"is"),ht.is=Tt})(mr||(q.ConnectionStrategy=mr={}));var sr;(function(ht){function Tt(dt){let pt=dt;return pt&&(pt.kind===void 0||pt.kind==="id")&&A.func(pt.createCancellationTokenSource)&&(pt.dispose===void 0||A.func(pt.dispose))}s(Tt,"is"),L(Tt,"is"),ht.is=Tt})(sr||(q.IdCancellationReceiverStrategy=sr={}));var qn;(function(ht){function Tt(dt){let pt=dt;return pt&&pt.kind==="request"&&A.func(pt.createCancellationTokenSource)&&(pt.dispose===void 0||A.func(pt.dispose))}s(Tt,"is"),L(Tt,"is"),ht.is=Tt})(qn||(q.RequestCancellationReceiverStrategy=qn={}));var ia;(function(ht){ht.Message=Object.freeze({createCancellationTokenSource(dt){return new Z.CancellationTokenSource}});function Tt(dt){return sr.is(dt)||qn.is(dt)}s(Tt,"is"),L(Tt,"is"),ht.is=Tt})(ia||(q.CancellationReceiverStrategy=ia={}));var Ri;(function(ht){ht.Message=Object.freeze({sendCancellation(dt,pt){return dt.sendNotification(re.type,{id:pt})},cleanup(dt){}});function Tt(dt){let pt=dt;return pt&&A.func(pt.sendCancellation)&&A.func(pt.cleanup)}s(Tt,"is"),L(Tt,"is"),ht.is=Tt})(Ri||(q.CancellationSenderStrategy=Ri={}));var Ca;(function(ht){ht.Message=Object.freeze({receiver:ia.Message,sender:Ri.Message});function Tt(dt){let pt=dt;return pt&&ia.is(pt.receiver)&&Ri.is(pt.sender)}s(Tt,"is"),L(Tt,"is"),ht.is=Tt})(Ca||(q.CancellationStrategy=Ca={}));var Qn;(function(ht){function Tt(dt){let pt=dt;return pt&&A.func(pt.handleMessage)}s(Tt,"is"),L(Tt,"is"),ht.is=Tt})(Qn||(q.MessageStrategy=Qn={}));var Wn;(function(ht){function Tt(dt){let pt=dt;return pt&&(Ca.is(pt.cancellationStrategy)||mr.is(pt.connectionStrategy)||Qn.is(pt.messageStrategy))}s(Tt,"is"),L(Tt,"is"),ht.is=Tt})(Wn||(q.ConnectionOptions=Wn={}));var sn;(function(ht){ht[ht.New=1]="New",ht[ht.Listening=2]="Listening",ht[ht.Closed=3]="Closed",ht[ht.Disposed=4]="Disposed"})(sn||(sn={}));function Zs(ht,Tt,dt,pt){let Ct=dt!==void 0?dt:q.NullLogger,rr=0,hr=0,Gt=0,Pr="2.0",wr,ui=new Map,oi,Ci=new Map,ji=new Map,ua,yn=new P.LinkedMap,jn=new Map,ys=new Set,ha=new Map,Mi=je.Off,Os=at.Text,on,vs=sn.New,Vn=new V.Emitter,_r=new V.Emitter,Oa=new V.Emitter,yu=new V.Emitter,Cc=new V.Emitter,Un=pt&&pt.cancellationStrategy?pt.cancellationStrategy:Ca.Message;function K1(nt){if(nt===null)throw new Error("Can't send requests with id null since the response can't be correlated.");return"req-"+nt.toString()}s(K1,"createRequestQueueKey"),L(K1,"createRequestQueueKey");function hx(nt){return nt===null?"res-unknown-"+(++Gt).toString():"res-"+nt.toString()}s(hx,"createResponseQueueKey"),L(hx,"createResponseQueueKey");function dx(){return"not-"+(++hr).toString()}s(dx,"createNotificationQueueKey"),L(dx,"createNotificationQueueKey");function px(nt,Dt){D.Message.isRequest(Dt)?nt.set(K1(Dt.id),Dt):D.Message.isResponse(Dt)?nt.set(hx(Dt.id),Dt):nt.set(dx(),Dt)}s(px,"addMessageToQueue"),L(px,"addMessageToQueue");function fx(nt){}s(fx,"cancelUndispatched"),L(fx,"cancelUndispatched");function Q1(){return vs===sn.Listening}s(Q1,"isListening"),L(Q1,"isListening");function Z1(){return vs===sn.Closed}s(Z1,"isClosed"),L(Z1,"isClosed");function vu(){return vs===sn.Disposed}s(vu,"isDisposed"),L(vu,"isDisposed");function J1(){(vs===sn.New||vs===sn.Listening)&&(vs=sn.Closed,_r.fire(void 0))}s(J1,"closeHandler"),L(J1,"closeHandler");function gx(nt){Vn.fire([nt,void 0,void 0])}s(gx,"readErrorHandler"),L(gx,"readErrorHandler");function mx(nt){Vn.fire(nt)}s(mx,"writeErrorHandler"),L(mx,"writeErrorHandler"),ht.onClose(J1),ht.onError(gx),Tt.onClose(J1),Tt.onError(mx);function ey(){ua||yn.size===0||(ua=(0,T.default)().timer.setImmediate(()=>{ua=void 0,yx()}))}s(ey,"triggerMessageQueue"),L(ey,"triggerMessageQueue");function ty(nt){D.Message.isRequest(nt)?vx(nt):D.Message.isNotification(nt)?xx(nt):D.Message.isResponse(nt)?bx(nt):Tx(nt)}s(ty,"handleMessage"),L(ty,"handleMessage");function yx(){if(yn.size===0)return;let nt=yn.shift();try{let Dt=pt?.messageStrategy;Qn.is(Dt)?Dt.handleMessage(nt,ty):ty(nt)}finally{ey()}}s(yx,"processMessageQueue"),L(yx,"processMessageQueue");let QM=L(nt=>{try{if(D.Message.isNotification(nt)&&nt.method===re.type.method){let Dt=nt.params.id,or=K1(Dt),Dr=yn.get(or);if(D.Message.isRequest(Dr)){let $n=pt?.connectionStrategy,Ea=$n&&$n.cancelUndispatched?$n.cancelUndispatched(Dr,fx):void 0;if(Ea&&(Ea.error!==void 0||Ea.result!==void 0)){yn.delete(or),ha.delete(Dt),Ea.id=Dr.id,gp(Ea,nt.method,Date.now()),Tt.write(Ea).catch(()=>Ct.error("Sending response for canceled message failed."));return}}let Zn=ha.get(Dt);if(Zn!==void 0){Zn.cancel(),N0(nt);return}else ys.add(Dt)}px(yn,nt)}finally{ey()}},"callback");function vx(nt){if(vu())return;function Dt(Gi,da,hn){let bs={jsonrpc:Pr,id:nt.id};Gi instanceof D.ResponseError?bs.error=Gi.toJson():bs.result=Gi===void 0?null:Gi,gp(bs,da,hn),Tt.write(bs).catch(()=>Ct.error("Sending response failed."))}s(Dt,"reply"),L(Dt,"reply");function or(Gi,da,hn){let bs={jsonrpc:Pr,id:nt.id,error:Gi.toJson()};gp(bs,da,hn),Tt.write(bs).catch(()=>Ct.error("Sending response failed."))}s(or,"replyError"),L(or,"replyError");function Dr(Gi,da,hn){Gi===void 0&&(Gi=null);let bs={jsonrpc:Pr,id:nt.id,result:Gi};gp(bs,da,hn),Tt.write(bs).catch(()=>Ct.error("Sending response failed."))}s(Dr,"replySuccess"),L(Dr,"replySuccess"),wx(nt);let Zn=ui.get(nt.method),$n,Ea;Zn&&($n=Zn.type,Ea=Zn.handler);let Ba=Date.now();if(Ea||wr){let Gi=nt.id??String(Date.now()),da=sr.is(Un.receiver)?Un.receiver.createCancellationTokenSource(Gi):Un.receiver.createCancellationTokenSource(nt);nt.id!==null&&ys.has(nt.id)&&da.cancel(),nt.id!==null&&ha.set(Gi,da);try{let hn;if(Ea)if(nt.params===void 0){if($n!==void 0&&$n.numberOfParams!==0){or(new D.ResponseError(D.ErrorCodes.InvalidParams,`Request ${nt.method} defines ${$n.numberOfParams} params but received none.`),nt.method,Ba);return}hn=Ea(da.token)}else if(Array.isArray(nt.params)){if($n!==void 0&&$n.parameterStructures===D.ParameterStructures.byName){or(new D.ResponseError(D.ErrorCodes.InvalidParams,`Request ${nt.method} defines parameters by name but received parameters by position`),nt.method,Ba);return}hn=Ea(...nt.params,da.token)}else{if($n!==void 0&&$n.parameterStructures===D.ParameterStructures.byPosition){or(new D.ResponseError(D.ErrorCodes.InvalidParams,`Request ${nt.method} defines parameters by position but received parameters by name`),nt.method,Ba);return}hn=Ea(nt.params,da.token)}else wr&&(hn=wr(nt.method,nt.params,da.token));let bs=hn;hn?bs.then?bs.then(Jo=>{ha.delete(Gi),Dt(Jo,nt.method,Ba)},Jo=>{ha.delete(Gi),Jo instanceof D.ResponseError?or(Jo,nt.method,Ba):Jo&&A.string(Jo.message)?or(new D.ResponseError(D.ErrorCodes.InternalError,`Request ${nt.method} failed with message: ${Jo.message}`),nt.method,Ba):or(new D.ResponseError(D.ErrorCodes.InternalError,`Request ${nt.method} failed unexpectedly without providing any details.`),nt.method,Ba)}):(ha.delete(Gi),Dt(hn,nt.method,Ba)):(ha.delete(Gi),Dr(hn,nt.method,Ba))}catch(hn){ha.delete(Gi),hn instanceof D.ResponseError?Dt(hn,nt.method,Ba):hn&&A.string(hn.message)?or(new D.ResponseError(D.ErrorCodes.InternalError,`Request ${nt.method} failed with message: ${hn.message}`),nt.method,Ba):or(new D.ResponseError(D.ErrorCodes.InternalError,`Request ${nt.method} failed unexpectedly without providing any details.`),nt.method,Ba)}}else or(new D.ResponseError(D.ErrorCodes.MethodNotFound,`Unhandled method ${nt.method}`),nt.method,Ba)}s(vx,"handleRequest"),L(vx,"handleRequest");function bx(nt){if(!vu())if(nt.id===null)nt.error?Ct.error(`Received response message without id: Error is: +${JSON.stringify(nt.error,void 0,4)}`):Ct.error("Received response message without id. No further error information provided.");else{let Dt=nt.id,or=jn.get(Dt);if(Ex(nt,or),or!==void 0){jn.delete(Dt);try{if(nt.error){let Dr=nt.error;or.reject(new D.ResponseError(Dr.code,Dr.message,Dr.data))}else if(nt.result!==void 0)or.resolve(nt.result);else throw new Error("Should never happen.")}catch(Dr){Dr.message?Ct.error(`Response handler '${or.method}' failed with message: ${Dr.message}`):Ct.error(`Response handler '${or.method}' failed unexpectedly.`)}}}}s(bx,"handleResponse"),L(bx,"handleResponse");function xx(nt){if(vu())return;let Dt,or;if(nt.method===re.type.method){let Dr=nt.params.id;ys.delete(Dr),N0(nt);return}else{let Dr=Ci.get(nt.method);Dr&&(or=Dr.handler,Dt=Dr.type)}if(or||oi)try{if(N0(nt),or)if(nt.params===void 0)Dt!==void 0&&Dt.numberOfParams!==0&&Dt.parameterStructures!==D.ParameterStructures.byName&&Ct.error(`Notification ${nt.method} defines ${Dt.numberOfParams} params but received none.`),or();else if(Array.isArray(nt.params)){let Dr=nt.params;nt.method===ae.type.method&&Dr.length===2&&Q.is(Dr[0])?or({token:Dr[0],value:Dr[1]}):(Dt!==void 0&&(Dt.parameterStructures===D.ParameterStructures.byName&&Ct.error(`Notification ${nt.method} defines parameters by name but received parameters by position`),Dt.numberOfParams!==nt.params.length&&Ct.error(`Notification ${nt.method} defines ${Dt.numberOfParams} params but received ${Dr.length} arguments`)),or(...Dr))}else Dt!==void 0&&Dt.parameterStructures===D.ParameterStructures.byPosition&&Ct.error(`Notification ${nt.method} defines parameters by position but received parameters by name`),or(nt.params);else oi&&oi(nt.method,nt.params)}catch(Dr){Dr.message?Ct.error(`Notification handler '${nt.method}' failed with message: ${Dr.message}`):Ct.error(`Notification handler '${nt.method}' failed unexpectedly.`)}else Oa.fire(nt)}s(xx,"handleNotification"),L(xx,"handleNotification");function Tx(nt){if(!nt){Ct.error("Received empty message.");return}Ct.error(`Received message which is neither a response nor a notification message: +${JSON.stringify(nt,null,4)}`);let Dt=nt;if(A.string(Dt.id)||A.number(Dt.id)){let or=Dt.id,Dr=jn.get(or);Dr&&Dr.reject(new Error("The received response has neither a result nor an error property."))}}s(Tx,"handleInvalidMessage"),L(Tx,"handleInvalidMessage");function Vl(nt){if(nt!=null)switch(Mi){case je.Verbose:return JSON.stringify(nt,null,4);case je.Compact:return JSON.stringify(nt);default:return}}s(Vl,"stringifyTrace"),L(Vl,"stringifyTrace");function kx(nt){if(!(Mi===je.Off||!on))if(Os===at.Text){let Dt;(Mi===je.Verbose||Mi===je.Compact)&&nt.params&&(Dt=`Params: ${Vl(nt.params)} + +`),on.log(`Sending request '${nt.method} - (${nt.id})'.`,Dt)}else bu("send-request",nt)}s(kx,"traceSendingRequest"),L(kx,"traceSendingRequest");function Cx(nt){if(!(Mi===je.Off||!on))if(Os===at.Text){let Dt;(Mi===je.Verbose||Mi===je.Compact)&&(nt.params?Dt=`Params: ${Vl(nt.params)} + +`:Dt=`No parameters provided. + +`),on.log(`Sending notification '${nt.method}'.`,Dt)}else bu("send-notification",nt)}s(Cx,"traceSendingNotification"),L(Cx,"traceSendingNotification");function gp(nt,Dt,or){if(!(Mi===je.Off||!on))if(Os===at.Text){let Dr;(Mi===je.Verbose||Mi===je.Compact)&&(nt.error&&nt.error.data?Dr=`Error data: ${Vl(nt.error.data)} + +`:nt.result?Dr=`Result: ${Vl(nt.result)} + +`:nt.error===void 0&&(Dr=`No result returned. + +`)),on.log(`Sending response '${Dt} - (${nt.id})'. Processing request took ${Date.now()-or}ms`,Dr)}else bu("send-response",nt)}s(gp,"traceSendingResponse"),L(gp,"traceSendingResponse");function wx(nt){if(!(Mi===je.Off||!on))if(Os===at.Text){let Dt;(Mi===je.Verbose||Mi===je.Compact)&&nt.params&&(Dt=`Params: ${Vl(nt.params)} + +`),on.log(`Received request '${nt.method} - (${nt.id})'.`,Dt)}else bu("receive-request",nt)}s(wx,"traceReceivedRequest"),L(wx,"traceReceivedRequest");function N0(nt){if(!(Mi===je.Off||!on||nt.method===Zt.type.method))if(Os===at.Text){let Dt;(Mi===je.Verbose||Mi===je.Compact)&&(nt.params?Dt=`Params: ${Vl(nt.params)} + +`:Dt=`No parameters provided. + +`),on.log(`Received notification '${nt.method}'.`,Dt)}else bu("receive-notification",nt)}s(N0,"traceReceivedNotification"),L(N0,"traceReceivedNotification");function Ex(nt,Dt){if(!(Mi===je.Off||!on))if(Os===at.Text){let or;if((Mi===je.Verbose||Mi===je.Compact)&&(nt.error&&nt.error.data?or=`Error data: ${Vl(nt.error.data)} + +`:nt.result?or=`Result: ${Vl(nt.result)} + +`:nt.error===void 0&&(or=`No result returned. + +`)),Dt){let Dr=nt.error?` Request failed: ${nt.error.message} (${nt.error.code}).`:"";on.log(`Received response '${Dt.method} - (${nt.id})' in ${Date.now()-Dt.timerStart}ms.${Dr}`,or)}else on.log(`Received response ${nt.id} without active response promise.`,or)}else bu("receive-response",nt)}s(Ex,"traceReceivedResponse"),L(Ex,"traceReceivedResponse");function bu(nt,Dt){if(!on||Mi===je.Off)return;let or={isLSPMessage:!0,type:nt,message:Dt,timestamp:Date.now()};on.log(or)}s(bu,"logLSPMessage"),L(bu,"logLSPMessage");function Ph(){if(Z1())throw new xr(Yt.Closed,"Connection is closed.");if(vu())throw new xr(Yt.Disposed,"Connection is disposed.")}s(Ph,"throwIfClosedOrDisposed"),L(Ph,"throwIfClosedOrDisposed");function Sx(){if(Q1())throw new xr(Yt.AlreadyListening,"Connection is already listening")}s(Sx,"throwIfListening"),L(Sx,"throwIfListening");function _x(){if(!Q1())throw new Error("Call listen() first.")}s(_x,"throwIfNotListening"),L(_x,"throwIfNotListening");function Xe(nt){return nt===void 0?null:nt}s(Xe,"undefinedToNull"),L(Xe,"undefinedToNull");function wi(nt){if(nt!==null)return nt}s(wi,"nullToUndefined"),L(wi,"nullToUndefined");function Rn(nt){return nt!=null&&!Array.isArray(nt)&&typeof nt=="object"}s(Rn,"isNamedParam"),L(Rn,"isNamedParam");function li(nt,Dt){switch(nt){case D.ParameterStructures.auto:return Rn(Dt)?wi(Dt):[Xe(Dt)];case D.ParameterStructures.byName:if(!Rn(Dt))throw new Error("Received parameters by name but param is not an object literal.");return wi(Dt);case D.ParameterStructures.byPosition:return[Xe(Dt)];default:throw new Error(`Unknown parameter structure ${nt.toString()}`)}}s(li,"computeSingleParam"),L(li,"computeSingleParam");function wa(nt,Dt){let or,Dr=nt.numberOfParams;switch(Dr){case 0:or=void 0;break;case 1:or=li(nt.parameterStructures,Dt[0]);break;default:or=[];for(let Zn=0;Zn{Ph();let or,Dr;if(A.string(nt)){or=nt;let $n=Dt[0],Ea=0,Ba=D.ParameterStructures.auto;D.ParameterStructures.is($n)&&(Ea=1,Ba=$n);let Gi=Dt.length,da=Gi-Ea;switch(da){case 0:Dr=void 0;break;case 1:Dr=li(Ba,Dt[Ea]);break;default:if(Ba===D.ParameterStructures.byName)throw new Error(`Received ${da} parameters for 'by Name' notification parameter structure.`);Dr=Dt.slice(Ea,Gi).map(hn=>Xe(hn));break}}else{let $n=Dt;or=nt.method,Dr=wa(nt,$n)}let Zn={jsonrpc:Pr,method:or,params:Dr};return Cx(Zn),Tt.write(Zn).catch($n=>{throw Ct.error("Sending notification failed."),$n})},"sendNotification"),onNotification:L((nt,Dt)=>{Ph();let or;return A.func(nt)?oi=nt:Dt&&(A.string(nt)?(or=nt,Ci.set(nt,{type:void 0,handler:Dt})):(or=nt.method,Ci.set(nt.method,{type:nt,handler:Dt}))),{dispose:L(()=>{or!==void 0?Ci.delete(or):oi=void 0},"dispose")}},"onNotification"),onProgress:L((nt,Dt,or)=>{if(ji.has(Dt))throw new Error(`Progress handler for token ${Dt} already registered`);return ji.set(Dt,or),{dispose:L(()=>{ji.delete(Dt)},"dispose")}},"onProgress"),sendProgress:L((nt,Dt,or)=>Ax.sendNotification(ae.type,{token:Dt,value:or}),"sendProgress"),onUnhandledProgress:yu.event,sendRequest:L((nt,...Dt)=>{Ph(),_x();let or,Dr,Zn;if(A.string(nt)){or=nt;let Gi=Dt[0],da=Dt[Dt.length-1],hn=0,bs=D.ParameterStructures.auto;D.ParameterStructures.is(Gi)&&(hn=1,bs=Gi);let Jo=Dt.length;Z.CancellationToken.is(da)&&(Jo=Jo-1,Zn=da);let Fh=Jo-hn;switch(Fh){case 0:Dr=void 0;break;case 1:Dr=li(bs,Dt[hn]);break;default:if(bs===D.ParameterStructures.byName)throw new Error(`Received ${Fh} parameters for 'by Name' request parameter structure.`);Dr=Dt.slice(hn,Jo).map(vDt=>Xe(vDt));break}}else{let Gi=Dt;or=nt.method,Dr=wa(nt,Gi);let da=nt.numberOfParams;Zn=Z.CancellationToken.is(Gi[da])?Gi[da]:void 0}let $n=rr++,Ea;Zn&&(Ea=Zn.onCancellationRequested(()=>{let Gi=Un.sender.sendCancellation(Ax,$n);return Gi===void 0?(Ct.log(`Received no promise from cancellation strategy when cancelling id ${$n}`),Promise.resolve()):Gi.catch(()=>{Ct.log(`Sending cancellation messages for id ${$n} failed`)})}));let Ba={jsonrpc:Pr,id:$n,method:or,params:Dr};return kx(Ba),typeof Un.sender.enableCancellation=="function"&&Un.sender.enableCancellation(Ba),new Promise(async(Gi,da)=>{let hn=L(Fh=>{Gi(Fh),Un.sender.cleanup($n),Ea?.dispose()},"resolveWithCleanup"),bs=L(Fh=>{da(Fh),Un.sender.cleanup($n),Ea?.dispose()},"rejectWithCleanup"),Jo={method:or,timerStart:Date.now(),resolve:hn,reject:bs};try{await Tt.write(Ba),jn.set($n,Jo)}catch(Fh){throw Ct.error("Sending request failed."),Jo.reject(new D.ResponseError(D.ErrorCodes.MessageWriteError,Fh.message?Fh.message:"Unknown reason")),Fh}})},"sendRequest"),onRequest:L((nt,Dt)=>{Ph();let or=null;return Ue.is(nt)?(or=void 0,wr=nt):A.string(nt)?(or=null,Dt!==void 0&&(or=nt,ui.set(nt,{handler:Dt,type:void 0}))):Dt!==void 0&&(or=nt.method,ui.set(nt.method,{type:nt,handler:Dt})),{dispose:L(()=>{or!==null&&(or!==void 0?ui.delete(or):wr=void 0)},"dispose")}},"onRequest"),hasPendingResponse:L(()=>jn.size>0,"hasPendingResponse"),trace:L(async(nt,Dt,or)=>{let Dr=!1,Zn=at.Text;or!==void 0&&(A.boolean(or)?Dr=or:(Dr=or.sendNotification||!1,Zn=or.traceFormat||at.Text)),Mi=nt,Os=Zn,Mi===je.Off?on=void 0:on=Dt,Dr&&!Z1()&&!vu()&&await Ax.sendNotification(Rt.type,{value:je.toString(nt)})},"trace"),onError:Vn.event,onClose:_r.event,onUnhandledNotification:Oa.event,onDispose:Cc.event,end:L(()=>{Tt.end()},"end"),dispose:L(()=>{if(vu())return;vs=sn.Disposed,Cc.fire(void 0);let nt=new D.ResponseError(D.ErrorCodes.PendingResponseRejected,"Pending response rejected since connection got disposed");for(let Dt of jn.values())Dt.reject(nt);jn=new Map,ha=new Map,ys=new Set,yn=new P.LinkedMap,A.func(Tt.dispose)&&Tt.dispose(),A.func(ht.dispose)&&ht.dispose()},"dispose"),listen:L(()=>{Ph(),Sx(),vs=sn.Listening,ht.listen(QM)},"listen"),inspect:L(()=>{(0,T.default)().console.log("inspect")},"inspect")};return Ax.onNotification(Zt.type,nt=>{if(Mi===je.Off||!on)return;let Dt=Mi===je.Verbose||Mi===je.Compact;on.log(nt.message,Dt?nt.verbose:void 0)}),Ax.onNotification(ae.type,nt=>{let Dt=ji.get(nt.token);Dt?Dt(nt.value):yu.fire(nt)}),Ax}s(Zs,"createMessageConnection"),L(Zs,"createMessageConnection"),q.createMessageConnection=Zs}}),Q$=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/common/api.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.ProgressType=q.ProgressToken=q.createMessageConnection=q.NullLogger=q.ConnectionOptions=q.ConnectionStrategy=q.AbstractMessageBuffer=q.WriteableStreamMessageWriter=q.AbstractMessageWriter=q.MessageWriter=q.ReadableStreamMessageReader=q.AbstractMessageReader=q.MessageReader=q.SharedArrayReceiverStrategy=q.SharedArraySenderStrategy=q.CancellationToken=q.CancellationTokenSource=q.Emitter=q.Event=q.Disposable=q.LRUCache=q.Touch=q.LinkedMap=q.ParameterStructures=q.NotificationType9=q.NotificationType8=q.NotificationType7=q.NotificationType6=q.NotificationType5=q.NotificationType4=q.NotificationType3=q.NotificationType2=q.NotificationType1=q.NotificationType0=q.NotificationType=q.ErrorCodes=q.ResponseError=q.RequestType9=q.RequestType8=q.RequestType7=q.RequestType6=q.RequestType5=q.RequestType4=q.RequestType3=q.RequestType2=q.RequestType1=q.RequestType0=q.RequestType=q.Message=q.RAL=void 0,q.MessageStrategy=q.CancellationStrategy=q.CancellationSenderStrategy=q.CancellationReceiverStrategy=q.ConnectionError=q.ConnectionErrors=q.LogTraceNotification=q.SetTraceNotification=q.TraceFormat=q.TraceValues=q.Trace=void 0;var T=Hae();Object.defineProperty(q,"Message",{enumerable:!0,get:L(function(){return T.Message},"get")}),Object.defineProperty(q,"RequestType",{enumerable:!0,get:L(function(){return T.RequestType},"get")}),Object.defineProperty(q,"RequestType0",{enumerable:!0,get:L(function(){return T.RequestType0},"get")}),Object.defineProperty(q,"RequestType1",{enumerable:!0,get:L(function(){return T.RequestType1},"get")}),Object.defineProperty(q,"RequestType2",{enumerable:!0,get:L(function(){return T.RequestType2},"get")}),Object.defineProperty(q,"RequestType3",{enumerable:!0,get:L(function(){return T.RequestType3},"get")}),Object.defineProperty(q,"RequestType4",{enumerable:!0,get:L(function(){return T.RequestType4},"get")}),Object.defineProperty(q,"RequestType5",{enumerable:!0,get:L(function(){return T.RequestType5},"get")}),Object.defineProperty(q,"RequestType6",{enumerable:!0,get:L(function(){return T.RequestType6},"get")}),Object.defineProperty(q,"RequestType7",{enumerable:!0,get:L(function(){return T.RequestType7},"get")}),Object.defineProperty(q,"RequestType8",{enumerable:!0,get:L(function(){return T.RequestType8},"get")}),Object.defineProperty(q,"RequestType9",{enumerable:!0,get:L(function(){return T.RequestType9},"get")}),Object.defineProperty(q,"ResponseError",{enumerable:!0,get:L(function(){return T.ResponseError},"get")}),Object.defineProperty(q,"ErrorCodes",{enumerable:!0,get:L(function(){return T.ErrorCodes},"get")}),Object.defineProperty(q,"NotificationType",{enumerable:!0,get:L(function(){return T.NotificationType},"get")}),Object.defineProperty(q,"NotificationType0",{enumerable:!0,get:L(function(){return T.NotificationType0},"get")}),Object.defineProperty(q,"NotificationType1",{enumerable:!0,get:L(function(){return T.NotificationType1},"get")}),Object.defineProperty(q,"NotificationType2",{enumerable:!0,get:L(function(){return T.NotificationType2},"get")}),Object.defineProperty(q,"NotificationType3",{enumerable:!0,get:L(function(){return T.NotificationType3},"get")}),Object.defineProperty(q,"NotificationType4",{enumerable:!0,get:L(function(){return T.NotificationType4},"get")}),Object.defineProperty(q,"NotificationType5",{enumerable:!0,get:L(function(){return T.NotificationType5},"get")}),Object.defineProperty(q,"NotificationType6",{enumerable:!0,get:L(function(){return T.NotificationType6},"get")}),Object.defineProperty(q,"NotificationType7",{enumerable:!0,get:L(function(){return T.NotificationType7},"get")}),Object.defineProperty(q,"NotificationType8",{enumerable:!0,get:L(function(){return T.NotificationType8},"get")}),Object.defineProperty(q,"NotificationType9",{enumerable:!0,get:L(function(){return T.NotificationType9},"get")}),Object.defineProperty(q,"ParameterStructures",{enumerable:!0,get:L(function(){return T.ParameterStructures},"get")});var A=Gae();Object.defineProperty(q,"LinkedMap",{enumerable:!0,get:L(function(){return A.LinkedMap},"get")}),Object.defineProperty(q,"LRUCache",{enumerable:!0,get:L(function(){return A.LRUCache},"get")}),Object.defineProperty(q,"Touch",{enumerable:!0,get:L(function(){return A.Touch},"get")});var D=HGe();Object.defineProperty(q,"Disposable",{enumerable:!0,get:L(function(){return D.Disposable},"get")});var P=Pm();Object.defineProperty(q,"Event",{enumerable:!0,get:L(function(){return P.Event},"get")}),Object.defineProperty(q,"Emitter",{enumerable:!0,get:L(function(){return P.Emitter},"get")});var V=FC();Object.defineProperty(q,"CancellationTokenSource",{enumerable:!0,get:L(function(){return V.CancellationTokenSource},"get")}),Object.defineProperty(q,"CancellationToken",{enumerable:!0,get:L(function(){return V.CancellationToken},"get")});var Z=GGe();Object.defineProperty(q,"SharedArraySenderStrategy",{enumerable:!0,get:L(function(){return Z.SharedArraySenderStrategy},"get")}),Object.defineProperty(q,"SharedArrayReceiverStrategy",{enumerable:!0,get:L(function(){return Z.SharedArrayReceiverStrategy},"get")});var re=XGe();Object.defineProperty(q,"MessageReader",{enumerable:!0,get:L(function(){return re.MessageReader},"get")}),Object.defineProperty(q,"AbstractMessageReader",{enumerable:!0,get:L(function(){return re.AbstractMessageReader},"get")}),Object.defineProperty(q,"ReadableStreamMessageReader",{enumerable:!0,get:L(function(){return re.ReadableStreamMessageReader},"get")});var Q=KGe();Object.defineProperty(q,"MessageWriter",{enumerable:!0,get:L(function(){return Q.MessageWriter},"get")}),Object.defineProperty(q,"AbstractMessageWriter",{enumerable:!0,get:L(function(){return Q.AbstractMessageWriter},"get")}),Object.defineProperty(q,"WriteableStreamMessageWriter",{enumerable:!0,get:L(function(){return Q.WriteableStreamMessageWriter},"get")});var ae=QGe();Object.defineProperty(q,"AbstractMessageBuffer",{enumerable:!0,get:L(function(){return ae.AbstractMessageBuffer},"get")});var we=ZGe();Object.defineProperty(q,"ConnectionStrategy",{enumerable:!0,get:L(function(){return we.ConnectionStrategy},"get")}),Object.defineProperty(q,"ConnectionOptions",{enumerable:!0,get:L(function(){return we.ConnectionOptions},"get")}),Object.defineProperty(q,"NullLogger",{enumerable:!0,get:L(function(){return we.NullLogger},"get")}),Object.defineProperty(q,"createMessageConnection",{enumerable:!0,get:L(function(){return we.createMessageConnection},"get")}),Object.defineProperty(q,"ProgressToken",{enumerable:!0,get:L(function(){return we.ProgressToken},"get")}),Object.defineProperty(q,"ProgressType",{enumerable:!0,get:L(function(){return we.ProgressType},"get")}),Object.defineProperty(q,"Trace",{enumerable:!0,get:L(function(){return we.Trace},"get")}),Object.defineProperty(q,"TraceValues",{enumerable:!0,get:L(function(){return we.TraceValues},"get")}),Object.defineProperty(q,"TraceFormat",{enumerable:!0,get:L(function(){return we.TraceFormat},"get")}),Object.defineProperty(q,"SetTraceNotification",{enumerable:!0,get:L(function(){return we.SetTraceNotification},"get")}),Object.defineProperty(q,"LogTraceNotification",{enumerable:!0,get:L(function(){return we.LogTraceNotification},"get")}),Object.defineProperty(q,"ConnectionErrors",{enumerable:!0,get:L(function(){return we.ConnectionErrors},"get")}),Object.defineProperty(q,"ConnectionError",{enumerable:!0,get:L(function(){return we.ConnectionError},"get")}),Object.defineProperty(q,"CancellationReceiverStrategy",{enumerable:!0,get:L(function(){return we.CancellationReceiverStrategy},"get")}),Object.defineProperty(q,"CancellationSenderStrategy",{enumerable:!0,get:L(function(){return we.CancellationSenderStrategy},"get")}),Object.defineProperty(q,"CancellationStrategy",{enumerable:!0,get:L(function(){return we.CancellationStrategy},"get")}),Object.defineProperty(q,"MessageStrategy",{enumerable:!0,get:L(function(){return we.MessageStrategy},"get")});var Ue=Uf();q.RAL=Ue.default}}),JGe=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/browser/ril.js"(q){"use strict";var Q,ae,we;Object.defineProperty(q,"__esModule",{value:!0});var T=Q$(),A=(Q=class extends T.AbstractMessageBuffer{constructor(je="utf-8"){super(je),this.asciiDecoder=new TextDecoder("ascii")}emptyBuffer(){return Q.emptyBuffer}fromString(je,ut){return new TextEncoder().encode(je)}toString(je,ut){return ut==="ascii"?this.asciiDecoder.decode(je):new TextDecoder(ut).decode(je)}asNative(je,ut){return ut===void 0?je:je.slice(0,ut)}allocNative(je){return new Uint8Array(je)}},s(Q,"_MessageBuffer"),L(Q,"MessageBuffer"),Q);A.emptyBuffer=new Uint8Array(0);var D=(ae=class{constructor(je){this.socket=je,this._onData=new T.Emitter,this._messageListener=ut=>{ut.data.arrayBuffer().then(at=>{this._onData.fire(new Uint8Array(at))},()=>{(0,T.RAL)().console.error("Converting blob to array buffer failed.")})},this.socket.addEventListener("message",this._messageListener)}onClose(je){return this.socket.addEventListener("close",je),T.Disposable.create(()=>this.socket.removeEventListener("close",je))}onError(je){return this.socket.addEventListener("error",je),T.Disposable.create(()=>this.socket.removeEventListener("error",je))}onEnd(je){return this.socket.addEventListener("end",je),T.Disposable.create(()=>this.socket.removeEventListener("end",je))}onData(je){return this._onData.event(je)}},s(ae,"ReadableStreamWrapper"),L(ae,"ReadableStreamWrapper"),ae),P=(we=class{constructor(je){this.socket=je}onClose(je){return this.socket.addEventListener("close",je),T.Disposable.create(()=>this.socket.removeEventListener("close",je))}onError(je){return this.socket.addEventListener("error",je),T.Disposable.create(()=>this.socket.removeEventListener("error",je))}onEnd(je){return this.socket.addEventListener("end",je),T.Disposable.create(()=>this.socket.removeEventListener("end",je))}write(je,ut){if(typeof je=="string"){if(ut!==void 0&&ut!=="utf-8")throw new Error(`In a Browser environments only utf-8 text encoding is supported. But got encoding: ${ut}`);this.socket.send(je)}else this.socket.send(je);return Promise.resolve()}end(){this.socket.close()}},s(we,"WritableStreamWrapper"),L(we,"WritableStreamWrapper"),we),V=new TextEncoder,Z=Object.freeze({messageBuffer:Object.freeze({create:L(Ue=>new A(Ue),"create")}),applicationJson:Object.freeze({encoder:Object.freeze({name:"application/json",encode:L((Ue,je)=>{if(je.charset!=="utf-8")throw new Error(`In a Browser environments only utf-8 text encoding is supported. But got encoding: ${je.charset}`);return Promise.resolve(V.encode(JSON.stringify(Ue,void 0,0)))},"encode")}),decoder:Object.freeze({name:"application/json",decode:L((Ue,je)=>{if(!(Ue instanceof Uint8Array))throw new Error("In a Browser environments only Uint8Arrays are supported.");return Promise.resolve(JSON.parse(new TextDecoder(je.charset).decode(Ue)))},"decode")})}),stream:Object.freeze({asReadableStream:L(Ue=>new D(Ue),"asReadableStream"),asWritableStream:L(Ue=>new P(Ue),"asWritableStream")}),console,timer:Object.freeze({setTimeout(Ue,je,...ut){let at=setTimeout(Ue,je,...ut);return{dispose:L(()=>clearTimeout(at),"dispose")}},setImmediate(Ue,...je){let ut=setTimeout(Ue,0,...je);return{dispose:L(()=>clearTimeout(ut),"dispose")}},setInterval(Ue,je,...ut){let at=setInterval(Ue,je,...ut);return{dispose:L(()=>clearInterval(at),"dispose")}}})});function re(){return Z}s(re,"RIL"),L(re,"RIL"),(function(Ue){function je(){T.RAL.install(Z)}s(je,"install"),L(je,"install"),Ue.install=je})(re||(re={})),q.default=re}}),Fm=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/lib/browser/main.js"(q){"use strict";var Q,ae;var T=q&&q.__createBinding||(Object.create?(function(we,Ue,je,ut){ut===void 0&&(ut=je);var at=Object.getOwnPropertyDescriptor(Ue,je);(!at||("get"in at?!Ue.__esModule:at.writable||at.configurable))&&(at={enumerable:!0,get:L(function(){return Ue[je]},"get")}),Object.defineProperty(we,ut,at)}):(function(we,Ue,je,ut){ut===void 0&&(ut=je),we[ut]=Ue[je]})),A=q&&q.__exportStar||function(we,Ue){for(var je in we)je!=="default"&&!Object.prototype.hasOwnProperty.call(Ue,je)&&T(Ue,we,je)};Object.defineProperty(q,"__esModule",{value:!0}),q.createMessageConnection=q.BrowserMessageWriter=q.BrowserMessageReader=void 0;var D=JGe();D.default.install();var P=Q$();A(Q$(),q);var V=(Q=class extends P.AbstractMessageReader{constructor(Ue){super(),this._onData=new P.Emitter,this._messageListener=je=>{this._onData.fire(je.data)},Ue.addEventListener("error",je=>this.fireError(je)),Ue.onmessage=this._messageListener}listen(Ue){return this._onData.event(Ue)}},s(Q,"BrowserMessageReader"),L(Q,"BrowserMessageReader"),Q);q.BrowserMessageReader=V;var Z=(ae=class extends P.AbstractMessageWriter{constructor(Ue){super(),this.port=Ue,this.errorCount=0,Ue.addEventListener("error",je=>this.fireError(je))}write(Ue){try{return this.port.postMessage(Ue),Promise.resolve()}catch(je){return this.handleError(je,Ue),Promise.reject(je)}}handleError(Ue,je){this.errorCount++,this.fireError(Ue,je,this.errorCount)}end(){}},s(ae,"BrowserMessageWriter"),L(ae,"BrowserMessageWriter"),ae);q.BrowserMessageWriter=Z;function re(we,Ue,je,ut){return je===void 0&&(je=P.NullLogger),P.ConnectionStrategy.is(ut)&&(ut={connectionStrategy:ut}),(0,P.createMessageConnection)(we,Ue,je,ut)}s(re,"createMessageConnection"),L(re,"createMessageConnection"),q.createMessageConnection=re}}),Kae=ci({"../../node_modules/.pnpm/vscode-jsonrpc@8.2.0/node_modules/vscode-jsonrpc/browser.js"(q,T){"use strict";T.exports=Fm()}}),ra=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/messages.js"(q){"use strict";var Q,ae,we,Ue,je;Object.defineProperty(q,"__esModule",{value:!0}),q.ProtocolNotificationType=q.ProtocolNotificationType0=q.ProtocolRequestType=q.ProtocolRequestType0=q.RegistrationType=q.MessageDirection=void 0;var T=Fm(),A;(function(ut){ut.clientToServer="clientToServer",ut.serverToClient="serverToClient",ut.both="both"})(A||(q.MessageDirection=A={}));var D=(Q=class{constructor(at){this.method=at}},s(Q,"RegistrationType"),L(Q,"RegistrationType"),Q);q.RegistrationType=D;var P=(ae=class extends T.RequestType0{constructor(at){super(at)}},s(ae,"ProtocolRequestType0"),L(ae,"ProtocolRequestType0"),ae);q.ProtocolRequestType0=P;var V=(we=class extends T.RequestType{constructor(at){super(at,T.ParameterStructures.byName)}},s(we,"ProtocolRequestType"),L(we,"ProtocolRequestType"),we);q.ProtocolRequestType=V;var Z=(Ue=class extends T.NotificationType0{constructor(at){super(at)}},s(Ue,"ProtocolNotificationType0"),L(Ue,"ProtocolNotificationType0"),Ue);q.ProtocolNotificationType0=Z;var re=(je=class extends T.NotificationType{constructor(at){super(at,T.ParameterStructures.byName)}},s(je,"ProtocolNotificationType"),L(je,"ProtocolNotificationType"),je);q.ProtocolNotificationType=re}}),Z$=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/utils/is.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.objectLiteral=q.typedArray=q.stringArray=q.array=q.func=q.error=q.number=q.string=q.boolean=void 0;function T(we){return we===!0||we===!1}s(T,"boolean"),L(T,"boolean"),q.boolean=T;function A(we){return typeof we=="string"||we instanceof String}s(A,"string"),L(A,"string"),q.string=A;function D(we){return typeof we=="number"||we instanceof Number}s(D,"number"),L(D,"number"),q.number=D;function P(we){return we instanceof Error}s(P,"error"),L(P,"error"),q.error=P;function V(we){return typeof we=="function"}s(V,"func"),L(V,"func"),q.func=V;function Z(we){return Array.isArray(we)}s(Z,"array"),L(Z,"array"),q.array=Z;function re(we){return Z(we)&&we.every(Ue=>A(Ue))}s(re,"stringArray"),L(re,"stringArray"),q.stringArray=re;function Q(we,Ue){return Array.isArray(we)&&we.every(Ue)}s(Q,"typedArray"),L(Q,"typedArray"),q.typedArray=Q;function ae(we){return we!==null&&typeof we=="object"}s(ae,"objectLiteral"),L(ae,"objectLiteral"),q.objectLiteral=ae}}),eXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.implementation.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.ImplementationRequest=void 0;var T=ra(),A;(function(D){D.method="textDocument/implementation",D.messageDirection=T.MessageDirection.clientToServer,D.type=new T.ProtocolRequestType(D.method)})(A||(q.ImplementationRequest=A={}))}}),tXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.typeDefinition.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.TypeDefinitionRequest=void 0;var T=ra(),A;(function(D){D.method="textDocument/typeDefinition",D.messageDirection=T.MessageDirection.clientToServer,D.type=new T.ProtocolRequestType(D.method)})(A||(q.TypeDefinitionRequest=A={}))}}),rXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.workspaceFolder.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.DidChangeWorkspaceFoldersNotification=q.WorkspaceFoldersRequest=void 0;var T=ra(),A;(function(P){P.method="workspace/workspaceFolders",P.messageDirection=T.MessageDirection.serverToClient,P.type=new T.ProtocolRequestType0(P.method)})(A||(q.WorkspaceFoldersRequest=A={}));var D;(function(P){P.method="workspace/didChangeWorkspaceFolders",P.messageDirection=T.MessageDirection.clientToServer,P.type=new T.ProtocolNotificationType(P.method)})(D||(q.DidChangeWorkspaceFoldersNotification=D={}))}}),iXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.configuration.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.ConfigurationRequest=void 0;var T=ra(),A;(function(D){D.method="workspace/configuration",D.messageDirection=T.MessageDirection.serverToClient,D.type=new T.ProtocolRequestType(D.method)})(A||(q.ConfigurationRequest=A={}))}}),nXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.colorProvider.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.ColorPresentationRequest=q.DocumentColorRequest=void 0;var T=ra(),A;(function(P){P.method="textDocument/documentColor",P.messageDirection=T.MessageDirection.clientToServer,P.type=new T.ProtocolRequestType(P.method)})(A||(q.DocumentColorRequest=A={}));var D;(function(P){P.method="textDocument/colorPresentation",P.messageDirection=T.MessageDirection.clientToServer,P.type=new T.ProtocolRequestType(P.method)})(D||(q.ColorPresentationRequest=D={}))}}),aXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.foldingRange.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.FoldingRangeRefreshRequest=q.FoldingRangeRequest=void 0;var T=ra(),A;(function(P){P.method="textDocument/foldingRange",P.messageDirection=T.MessageDirection.clientToServer,P.type=new T.ProtocolRequestType(P.method)})(A||(q.FoldingRangeRequest=A={}));var D;(function(P){P.method="workspace/foldingRange/refresh",P.messageDirection=T.MessageDirection.serverToClient,P.type=new T.ProtocolRequestType0(P.method)})(D||(q.FoldingRangeRefreshRequest=D={}))}}),sXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.declaration.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.DeclarationRequest=void 0;var T=ra(),A;(function(D){D.method="textDocument/declaration",D.messageDirection=T.MessageDirection.clientToServer,D.type=new T.ProtocolRequestType(D.method)})(A||(q.DeclarationRequest=A={}))}}),oXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.selectionRange.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.SelectionRangeRequest=void 0;var T=ra(),A;(function(D){D.method="textDocument/selectionRange",D.messageDirection=T.MessageDirection.clientToServer,D.type=new T.ProtocolRequestType(D.method)})(A||(q.SelectionRangeRequest=A={}))}}),lXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.progress.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.WorkDoneProgressCancelNotification=q.WorkDoneProgressCreateRequest=q.WorkDoneProgress=void 0;var T=Fm(),A=ra(),D;(function(Z){Z.type=new T.ProgressType;function re(Q){return Q===Z.type}s(re,"is"),L(re,"is"),Z.is=re})(D||(q.WorkDoneProgress=D={}));var P;(function(Z){Z.method="window/workDoneProgress/create",Z.messageDirection=A.MessageDirection.serverToClient,Z.type=new A.ProtocolRequestType(Z.method)})(P||(q.WorkDoneProgressCreateRequest=P={}));var V;(function(Z){Z.method="window/workDoneProgress/cancel",Z.messageDirection=A.MessageDirection.clientToServer,Z.type=new A.ProtocolNotificationType(Z.method)})(V||(q.WorkDoneProgressCancelNotification=V={}))}}),cXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.callHierarchy.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.CallHierarchyOutgoingCallsRequest=q.CallHierarchyIncomingCallsRequest=q.CallHierarchyPrepareRequest=void 0;var T=ra(),A;(function(V){V.method="textDocument/prepareCallHierarchy",V.messageDirection=T.MessageDirection.clientToServer,V.type=new T.ProtocolRequestType(V.method)})(A||(q.CallHierarchyPrepareRequest=A={}));var D;(function(V){V.method="callHierarchy/incomingCalls",V.messageDirection=T.MessageDirection.clientToServer,V.type=new T.ProtocolRequestType(V.method)})(D||(q.CallHierarchyIncomingCallsRequest=D={}));var P;(function(V){V.method="callHierarchy/outgoingCalls",V.messageDirection=T.MessageDirection.clientToServer,V.type=new T.ProtocolRequestType(V.method)})(P||(q.CallHierarchyOutgoingCallsRequest=P={}))}}),uXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.semanticTokens.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.SemanticTokensRefreshRequest=q.SemanticTokensRangeRequest=q.SemanticTokensDeltaRequest=q.SemanticTokensRequest=q.SemanticTokensRegistrationType=q.TokenFormat=void 0;var T=ra(),A;(function(Q){Q.Relative="relative"})(A||(q.TokenFormat=A={}));var D;(function(Q){Q.method="textDocument/semanticTokens",Q.type=new T.RegistrationType(Q.method)})(D||(q.SemanticTokensRegistrationType=D={}));var P;(function(Q){Q.method="textDocument/semanticTokens/full",Q.messageDirection=T.MessageDirection.clientToServer,Q.type=new T.ProtocolRequestType(Q.method),Q.registrationMethod=D.method})(P||(q.SemanticTokensRequest=P={}));var V;(function(Q){Q.method="textDocument/semanticTokens/full/delta",Q.messageDirection=T.MessageDirection.clientToServer,Q.type=new T.ProtocolRequestType(Q.method),Q.registrationMethod=D.method})(V||(q.SemanticTokensDeltaRequest=V={}));var Z;(function(Q){Q.method="textDocument/semanticTokens/range",Q.messageDirection=T.MessageDirection.clientToServer,Q.type=new T.ProtocolRequestType(Q.method),Q.registrationMethod=D.method})(Z||(q.SemanticTokensRangeRequest=Z={}));var re;(function(Q){Q.method="workspace/semanticTokens/refresh",Q.messageDirection=T.MessageDirection.serverToClient,Q.type=new T.ProtocolRequestType0(Q.method)})(re||(q.SemanticTokensRefreshRequest=re={}))}}),hXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.showDocument.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.ShowDocumentRequest=void 0;var T=ra(),A;(function(D){D.method="window/showDocument",D.messageDirection=T.MessageDirection.serverToClient,D.type=new T.ProtocolRequestType(D.method)})(A||(q.ShowDocumentRequest=A={}))}}),dXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.linkedEditingRange.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.LinkedEditingRangeRequest=void 0;var T=ra(),A;(function(D){D.method="textDocument/linkedEditingRange",D.messageDirection=T.MessageDirection.clientToServer,D.type=new T.ProtocolRequestType(D.method)})(A||(q.LinkedEditingRangeRequest=A={}))}}),pXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.fileOperations.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.WillDeleteFilesRequest=q.DidDeleteFilesNotification=q.DidRenameFilesNotification=q.WillRenameFilesRequest=q.DidCreateFilesNotification=q.WillCreateFilesRequest=q.FileOperationPatternKind=void 0;var T=ra(),A;(function(ae){ae.file="file",ae.folder="folder"})(A||(q.FileOperationPatternKind=A={}));var D;(function(ae){ae.method="workspace/willCreateFiles",ae.messageDirection=T.MessageDirection.clientToServer,ae.type=new T.ProtocolRequestType(ae.method)})(D||(q.WillCreateFilesRequest=D={}));var P;(function(ae){ae.method="workspace/didCreateFiles",ae.messageDirection=T.MessageDirection.clientToServer,ae.type=new T.ProtocolNotificationType(ae.method)})(P||(q.DidCreateFilesNotification=P={}));var V;(function(ae){ae.method="workspace/willRenameFiles",ae.messageDirection=T.MessageDirection.clientToServer,ae.type=new T.ProtocolRequestType(ae.method)})(V||(q.WillRenameFilesRequest=V={}));var Z;(function(ae){ae.method="workspace/didRenameFiles",ae.messageDirection=T.MessageDirection.clientToServer,ae.type=new T.ProtocolNotificationType(ae.method)})(Z||(q.DidRenameFilesNotification=Z={}));var re;(function(ae){ae.method="workspace/didDeleteFiles",ae.messageDirection=T.MessageDirection.clientToServer,ae.type=new T.ProtocolNotificationType(ae.method)})(re||(q.DidDeleteFilesNotification=re={}));var Q;(function(ae){ae.method="workspace/willDeleteFiles",ae.messageDirection=T.MessageDirection.clientToServer,ae.type=new T.ProtocolRequestType(ae.method)})(Q||(q.WillDeleteFilesRequest=Q={}))}}),fXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.moniker.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.MonikerRequest=q.MonikerKind=q.UniquenessLevel=void 0;var T=ra(),A;(function(V){V.document="document",V.project="project",V.group="group",V.scheme="scheme",V.global="global"})(A||(q.UniquenessLevel=A={}));var D;(function(V){V.$import="import",V.$export="export",V.local="local"})(D||(q.MonikerKind=D={}));var P;(function(V){V.method="textDocument/moniker",V.messageDirection=T.MessageDirection.clientToServer,V.type=new T.ProtocolRequestType(V.method)})(P||(q.MonikerRequest=P={}))}}),gXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.typeHierarchy.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.TypeHierarchySubtypesRequest=q.TypeHierarchySupertypesRequest=q.TypeHierarchyPrepareRequest=void 0;var T=ra(),A;(function(V){V.method="textDocument/prepareTypeHierarchy",V.messageDirection=T.MessageDirection.clientToServer,V.type=new T.ProtocolRequestType(V.method)})(A||(q.TypeHierarchyPrepareRequest=A={}));var D;(function(V){V.method="typeHierarchy/supertypes",V.messageDirection=T.MessageDirection.clientToServer,V.type=new T.ProtocolRequestType(V.method)})(D||(q.TypeHierarchySupertypesRequest=D={}));var P;(function(V){V.method="typeHierarchy/subtypes",V.messageDirection=T.MessageDirection.clientToServer,V.type=new T.ProtocolRequestType(V.method)})(P||(q.TypeHierarchySubtypesRequest=P={}))}}),mXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.inlineValue.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.InlineValueRefreshRequest=q.InlineValueRequest=void 0;var T=ra(),A;(function(P){P.method="textDocument/inlineValue",P.messageDirection=T.MessageDirection.clientToServer,P.type=new T.ProtocolRequestType(P.method)})(A||(q.InlineValueRequest=A={}));var D;(function(P){P.method="workspace/inlineValue/refresh",P.messageDirection=T.MessageDirection.serverToClient,P.type=new T.ProtocolRequestType0(P.method)})(D||(q.InlineValueRefreshRequest=D={}))}}),yXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.inlayHint.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.InlayHintRefreshRequest=q.InlayHintResolveRequest=q.InlayHintRequest=void 0;var T=ra(),A;(function(V){V.method="textDocument/inlayHint",V.messageDirection=T.MessageDirection.clientToServer,V.type=new T.ProtocolRequestType(V.method)})(A||(q.InlayHintRequest=A={}));var D;(function(V){V.method="inlayHint/resolve",V.messageDirection=T.MessageDirection.clientToServer,V.type=new T.ProtocolRequestType(V.method)})(D||(q.InlayHintResolveRequest=D={}));var P;(function(V){V.method="workspace/inlayHint/refresh",V.messageDirection=T.MessageDirection.serverToClient,V.type=new T.ProtocolRequestType0(V.method)})(P||(q.InlayHintRefreshRequest=P={}))}}),vXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.diagnostic.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.DiagnosticRefreshRequest=q.WorkspaceDiagnosticRequest=q.DocumentDiagnosticRequest=q.DocumentDiagnosticReportKind=q.DiagnosticServerCancellationData=void 0;var T=Fm(),A=Z$(),D=ra(),P;(function(ae){function we(Ue){let je=Ue;return je&&A.boolean(je.retriggerRequest)}s(we,"is"),L(we,"is"),ae.is=we})(P||(q.DiagnosticServerCancellationData=P={}));var V;(function(ae){ae.Full="full",ae.Unchanged="unchanged"})(V||(q.DocumentDiagnosticReportKind=V={}));var Z;(function(ae){ae.method="textDocument/diagnostic",ae.messageDirection=D.MessageDirection.clientToServer,ae.type=new D.ProtocolRequestType(ae.method),ae.partialResult=new T.ProgressType})(Z||(q.DocumentDiagnosticRequest=Z={}));var re;(function(ae){ae.method="workspace/diagnostic",ae.messageDirection=D.MessageDirection.clientToServer,ae.type=new D.ProtocolRequestType(ae.method),ae.partialResult=new T.ProgressType})(re||(q.WorkspaceDiagnosticRequest=re={}));var Q;(function(ae){ae.method="workspace/diagnostic/refresh",ae.messageDirection=D.MessageDirection.serverToClient,ae.type=new D.ProtocolRequestType0(ae.method)})(Q||(q.DiagnosticRefreshRequest=Q={}))}}),bXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/protocol.notebook.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.DidCloseNotebookDocumentNotification=q.DidSaveNotebookDocumentNotification=q.DidChangeNotebookDocumentNotification=q.NotebookCellArrayChange=q.DidOpenNotebookDocumentNotification=q.NotebookDocumentSyncRegistrationType=q.NotebookDocument=q.NotebookCell=q.ExecutionSummary=q.NotebookCellKind=void 0;var T=(Gv(),j$($C)),A=Z$(),D=ra(),P;(function(at){at.Markup=1,at.Code=2;function Rt(Zt){return Zt===1||Zt===2}s(Rt,"is"),L(Rt,"is"),at.is=Rt})(P||(q.NotebookCellKind=P={}));var V;(function(at){function Rt(xr,mr){let sr={executionOrder:xr};return(mr===!0||mr===!1)&&(sr.success=mr),sr}s(Rt,"create"),L(Rt,"create"),at.create=Rt;function Zt(xr){let mr=xr;return A.objectLiteral(mr)&&T.uinteger.is(mr.executionOrder)&&(mr.success===void 0||A.boolean(mr.success))}s(Zt,"is"),L(Zt,"is"),at.is=Zt;function Yt(xr,mr){return xr===mr?!0:xr==null||mr===null||mr===void 0?!1:xr.executionOrder===mr.executionOrder&&xr.success===mr.success}s(Yt,"equals"),L(Yt,"equals"),at.equals=Yt})(V||(q.ExecutionSummary=V={}));var Z;(function(at){function Rt(mr,sr){return{kind:mr,document:sr}}s(Rt,"create"),L(Rt,"create"),at.create=Rt;function Zt(mr){let sr=mr;return A.objectLiteral(sr)&&P.is(sr.kind)&&T.DocumentUri.is(sr.document)&&(sr.metadata===void 0||A.objectLiteral(sr.metadata))}s(Zt,"is"),L(Zt,"is"),at.is=Zt;function Yt(mr,sr){let qn=new Set;return mr.document!==sr.document&&qn.add("document"),mr.kind!==sr.kind&&qn.add("kind"),mr.executionSummary!==sr.executionSummary&&qn.add("executionSummary"),(mr.metadata!==void 0||sr.metadata!==void 0)&&!xr(mr.metadata,sr.metadata)&&qn.add("metadata"),(mr.executionSummary!==void 0||sr.executionSummary!==void 0)&&!V.equals(mr.executionSummary,sr.executionSummary)&&qn.add("executionSummary"),qn}s(Yt,"diff"),L(Yt,"diff"),at.diff=Yt;function xr(mr,sr){if(mr===sr)return!0;if(mr==null||sr===null||sr===void 0||typeof mr!=typeof sr||typeof mr!="object")return!1;let qn=Array.isArray(mr),ia=Array.isArray(sr);if(qn!==ia)return!1;if(qn&&ia){if(mr.length!==sr.length)return!1;for(let Ri=0;Ri0}s(wi,"hasId"),L(wi,"hasId"),Xe.hasId=wi})(pt||(q.StaticRegistrationOptions=pt={}));var Ct;(function(Xe){function wi(Rn){let li=Rn;return li&&(li.documentSelector===null||Zs.is(li.documentSelector))}s(wi,"is"),L(wi,"is"),Xe.is=wi})(Ct||(q.TextDocumentRegistrationOptions=Ct={}));var rr;(function(Xe){function wi(li){let wa=li;return D.objectLiteral(wa)&&(wa.workDoneProgress===void 0||D.boolean(wa.workDoneProgress))}s(wi,"is"),L(wi,"is"),Xe.is=wi;function Rn(li){let wa=li;return wa&&D.boolean(wa.workDoneProgress)}s(Rn,"hasWorkDoneProgress"),L(Rn,"hasWorkDoneProgress"),Xe.hasWorkDoneProgress=Rn})(rr||(q.WorkDoneProgressOptions=rr={}));var hr;(function(Xe){Xe.method="initialize",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(hr||(q.InitializeRequest=hr={}));var Gt;(function(Xe){Xe.unknownProtocolVersion=1})(Gt||(q.InitializeErrorCodes=Gt={}));var Pr;(function(Xe){Xe.method="initialized",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolNotificationType(Xe.method)})(Pr||(q.InitializedNotification=Pr={}));var wr;(function(Xe){Xe.method="shutdown",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType0(Xe.method)})(wr||(q.ShutdownRequest=wr={}));var ui;(function(Xe){Xe.method="exit",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolNotificationType0(Xe.method)})(ui||(q.ExitNotification=ui={}));var oi;(function(Xe){Xe.method="workspace/didChangeConfiguration",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolNotificationType(Xe.method)})(oi||(q.DidChangeConfigurationNotification=oi={}));var Ci;(function(Xe){Xe.Error=1,Xe.Warning=2,Xe.Info=3,Xe.Log=4,Xe.Debug=5})(Ci||(q.MessageType=Ci={}));var ji;(function(Xe){Xe.method="window/showMessage",Xe.messageDirection=T.MessageDirection.serverToClient,Xe.type=new T.ProtocolNotificationType(Xe.method)})(ji||(q.ShowMessageNotification=ji={}));var ua;(function(Xe){Xe.method="window/showMessageRequest",Xe.messageDirection=T.MessageDirection.serverToClient,Xe.type=new T.ProtocolRequestType(Xe.method)})(ua||(q.ShowMessageRequest=ua={}));var yn;(function(Xe){Xe.method="window/logMessage",Xe.messageDirection=T.MessageDirection.serverToClient,Xe.type=new T.ProtocolNotificationType(Xe.method)})(yn||(q.LogMessageNotification=yn={}));var jn;(function(Xe){Xe.method="telemetry/event",Xe.messageDirection=T.MessageDirection.serverToClient,Xe.type=new T.ProtocolNotificationType(Xe.method)})(jn||(q.TelemetryEventNotification=jn={}));var ys;(function(Xe){Xe.None=0,Xe.Full=1,Xe.Incremental=2})(ys||(q.TextDocumentSyncKind=ys={}));var ha;(function(Xe){Xe.method="textDocument/didOpen",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolNotificationType(Xe.method)})(ha||(q.DidOpenTextDocumentNotification=ha={}));var Mi;(function(Xe){function wi(li){let wa=li;return wa!=null&&typeof wa.text=="string"&&wa.range!==void 0&&(wa.rangeLength===void 0||typeof wa.rangeLength=="number")}s(wi,"isIncremental"),L(wi,"isIncremental"),Xe.isIncremental=wi;function Rn(li){let wa=li;return wa!=null&&typeof wa.text=="string"&&wa.range===void 0&&wa.rangeLength===void 0}s(Rn,"isFull"),L(Rn,"isFull"),Xe.isFull=Rn})(Mi||(q.TextDocumentContentChangeEvent=Mi={}));var Os;(function(Xe){Xe.method="textDocument/didChange",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolNotificationType(Xe.method)})(Os||(q.DidChangeTextDocumentNotification=Os={}));var on;(function(Xe){Xe.method="textDocument/didClose",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolNotificationType(Xe.method)})(on||(q.DidCloseTextDocumentNotification=on={}));var vs;(function(Xe){Xe.method="textDocument/didSave",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolNotificationType(Xe.method)})(vs||(q.DidSaveTextDocumentNotification=vs={}));var Vn;(function(Xe){Xe.Manual=1,Xe.AfterDelay=2,Xe.FocusOut=3})(Vn||(q.TextDocumentSaveReason=Vn={}));var _r;(function(Xe){Xe.method="textDocument/willSave",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolNotificationType(Xe.method)})(_r||(q.WillSaveTextDocumentNotification=_r={}));var Oa;(function(Xe){Xe.method="textDocument/willSaveWaitUntil",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(Oa||(q.WillSaveTextDocumentWaitUntilRequest=Oa={}));var yu;(function(Xe){Xe.method="workspace/didChangeWatchedFiles",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolNotificationType(Xe.method)})(yu||(q.DidChangeWatchedFilesNotification=yu={}));var Cc;(function(Xe){Xe.Created=1,Xe.Changed=2,Xe.Deleted=3})(Cc||(q.FileChangeType=Cc={}));var Un;(function(Xe){function wi(Rn){let li=Rn;return D.objectLiteral(li)&&(A.URI.is(li.baseUri)||A.WorkspaceFolder.is(li.baseUri))&&D.string(li.pattern)}s(wi,"is"),L(wi,"is"),Xe.is=wi})(Un||(q.RelativePattern=Un={}));var K1;(function(Xe){Xe.Create=1,Xe.Change=2,Xe.Delete=4})(K1||(q.WatchKind=K1={}));var hx;(function(Xe){Xe.method="textDocument/publishDiagnostics",Xe.messageDirection=T.MessageDirection.serverToClient,Xe.type=new T.ProtocolNotificationType(Xe.method)})(hx||(q.PublishDiagnosticsNotification=hx={}));var dx;(function(Xe){Xe.Invoked=1,Xe.TriggerCharacter=2,Xe.TriggerForIncompleteCompletions=3})(dx||(q.CompletionTriggerKind=dx={}));var px;(function(Xe){Xe.method="textDocument/completion",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(px||(q.CompletionRequest=px={}));var fx;(function(Xe){Xe.method="completionItem/resolve",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(fx||(q.CompletionResolveRequest=fx={}));var Q1;(function(Xe){Xe.method="textDocument/hover",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(Q1||(q.HoverRequest=Q1={}));var Z1;(function(Xe){Xe.Invoked=1,Xe.TriggerCharacter=2,Xe.ContentChange=3})(Z1||(q.SignatureHelpTriggerKind=Z1={}));var vu;(function(Xe){Xe.method="textDocument/signatureHelp",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(vu||(q.SignatureHelpRequest=vu={}));var J1;(function(Xe){Xe.method="textDocument/definition",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(J1||(q.DefinitionRequest=J1={}));var gx;(function(Xe){Xe.method="textDocument/references",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(gx||(q.ReferencesRequest=gx={}));var mx;(function(Xe){Xe.method="textDocument/documentHighlight",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(mx||(q.DocumentHighlightRequest=mx={}));var ey;(function(Xe){Xe.method="textDocument/documentSymbol",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(ey||(q.DocumentSymbolRequest=ey={}));var ty;(function(Xe){Xe.method="textDocument/codeAction",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(ty||(q.CodeActionRequest=ty={}));var yx;(function(Xe){Xe.method="codeAction/resolve",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(yx||(q.CodeActionResolveRequest=yx={}));var QM;(function(Xe){Xe.method="workspace/symbol",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(QM||(q.WorkspaceSymbolRequest=QM={}));var vx;(function(Xe){Xe.method="workspaceSymbol/resolve",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(vx||(q.WorkspaceSymbolResolveRequest=vx={}));var bx;(function(Xe){Xe.method="textDocument/codeLens",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(bx||(q.CodeLensRequest=bx={}));var xx;(function(Xe){Xe.method="codeLens/resolve",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(xx||(q.CodeLensResolveRequest=xx={}));var Tx;(function(Xe){Xe.method="workspace/codeLens/refresh",Xe.messageDirection=T.MessageDirection.serverToClient,Xe.type=new T.ProtocolRequestType0(Xe.method)})(Tx||(q.CodeLensRefreshRequest=Tx={}));var Vl;(function(Xe){Xe.method="textDocument/documentLink",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(Vl||(q.DocumentLinkRequest=Vl={}));var kx;(function(Xe){Xe.method="documentLink/resolve",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(kx||(q.DocumentLinkResolveRequest=kx={}));var Cx;(function(Xe){Xe.method="textDocument/formatting",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(Cx||(q.DocumentFormattingRequest=Cx={}));var gp;(function(Xe){Xe.method="textDocument/rangeFormatting",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(gp||(q.DocumentRangeFormattingRequest=gp={}));var wx;(function(Xe){Xe.method="textDocument/rangesFormatting",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(wx||(q.DocumentRangesFormattingRequest=wx={}));var N0;(function(Xe){Xe.method="textDocument/onTypeFormatting",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(N0||(q.DocumentOnTypeFormattingRequest=N0={}));var Ex;(function(Xe){Xe.Identifier=1})(Ex||(q.PrepareSupportDefaultBehavior=Ex={}));var bu;(function(Xe){Xe.method="textDocument/rename",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(bu||(q.RenameRequest=bu={}));var Ph;(function(Xe){Xe.method="textDocument/prepareRename",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(Ph||(q.PrepareRenameRequest=Ph={}));var Sx;(function(Xe){Xe.method="workspace/executeCommand",Xe.messageDirection=T.MessageDirection.clientToServer,Xe.type=new T.ProtocolRequestType(Xe.method)})(Sx||(q.ExecuteCommandRequest=Sx={}));var _x;(function(Xe){Xe.method="workspace/applyEdit",Xe.messageDirection=T.MessageDirection.serverToClient,Xe.type=new T.ProtocolRequestType("workspace/applyEdit")})(_x||(q.ApplyWorkspaceEditRequest=_x={}))}}),kXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/connection.js"(q){"use strict";Object.defineProperty(q,"__esModule",{value:!0}),q.createProtocolConnection=void 0;var T=Fm();function A(D,P,V,Z){return T.ConnectionStrategy.is(Z)&&(Z={connectionStrategy:Z}),(0,T.createMessageConnection)(D,P,V,Z)}s(A,"createProtocolConnection"),L(A,"createProtocolConnection"),q.createProtocolConnection=A}}),CXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/common/api.js"(q){"use strict";var T=q&&q.__createBinding||(Object.create?(function(V,Z,re,Q){Q===void 0&&(Q=re);var ae=Object.getOwnPropertyDescriptor(Z,re);(!ae||("get"in ae?!Z.__esModule:ae.writable||ae.configurable))&&(ae={enumerable:!0,get:L(function(){return Z[re]},"get")}),Object.defineProperty(V,Q,ae)}):(function(V,Z,re,Q){Q===void 0&&(Q=re),V[Q]=Z[re]})),A=q&&q.__exportStar||function(V,Z){for(var re in V)re!=="default"&&!Object.prototype.hasOwnProperty.call(Z,re)&&T(Z,V,re)};Object.defineProperty(q,"__esModule",{value:!0}),q.LSPErrorCodes=q.createProtocolConnection=void 0,A(Fm(),q),A((Gv(),j$($C)),q),A(ra(),q),A(TXe(),q);var D=kXe();Object.defineProperty(q,"createProtocolConnection",{enumerable:!0,get:L(function(){return D.createProtocolConnection},"get")});var P;(function(V){V.lspReservedErrorRangeStart=-32899,V.RequestFailed=-32803,V.ServerCancelled=-32802,V.ContentModified=-32801,V.RequestCancelled=-32800,V.lspReservedErrorRangeEnd=-32800})(P||(q.LSPErrorCodes=P={}))}}),wXe=ci({"../../node_modules/.pnpm/vscode-languageserver-protocol@3.17.5/node_modules/vscode-languageserver-protocol/lib/browser/main.js"(q){"use strict";var T=q&&q.__createBinding||(Object.create?(function(V,Z,re,Q){Q===void 0&&(Q=re);var ae=Object.getOwnPropertyDescriptor(Z,re);(!ae||("get"in ae?!Z.__esModule:ae.writable||ae.configurable))&&(ae={enumerable:!0,get:L(function(){return Z[re]},"get")}),Object.defineProperty(V,Q,ae)}):(function(V,Z,re,Q){Q===void 0&&(Q=re),V[Q]=Z[re]})),A=q&&q.__exportStar||function(V,Z){for(var re in V)re!=="default"&&!Object.prototype.hasOwnProperty.call(Z,re)&&T(Z,V,re)};Object.defineProperty(q,"__esModule",{value:!0}),q.createProtocolConnection=void 0;var D=Kae();A(Kae(),q),A(CXe(),q);function P(V,Z,re,Q){return(0,D.createMessageConnection)(V,Z,re,Q)}s(P,"createProtocolConnection"),L(P,"createProtocolConnection"),q.createProtocolConnection=P}}),Qae={},Vd(Qae,{AbstractAstReflection:s(()=>eL,"AbstractAstReflection"),AbstractCstNode:s(()=>n9,"AbstractCstNode"),AbstractLangiumParser:s(()=>s9,"AbstractLangiumParser"),AbstractParserErrorMessageProvider:s(()=>Yle,"AbstractParserErrorMessageProvider"),AbstractThreadedAsyncParser:s(()=>rot,"AbstractThreadedAsyncParser"),AstUtils:s(()=>tL,"AstUtils"),BiMap:s(()=>Zw,"BiMap"),Cancellation:s(()=>Sn,"Cancellation"),CompositeCstNodeImpl:s(()=>Gw,"CompositeCstNodeImpl"),ContextCache:s(()=>e5,"ContextCache"),CstNodeBuilder:s(()=>Wle,"CstNodeBuilder"),CstUtils:s(()=>J$,"CstUtils"),DEFAULT_TOKENIZE_OPTIONS:s(()=>g9,"DEFAULT_TOKENIZE_OPTIONS"),DONE_RESULT:s(()=>mo,"DONE_RESULT"),DatatypeSymbol:s(()=>Xw,"DatatypeSymbol"),DefaultAstNodeDescriptionProvider:s(()=>hce,"DefaultAstNodeDescriptionProvider"),DefaultAstNodeLocator:s(()=>pce,"DefaultAstNodeLocator"),DefaultAsyncParser:s(()=>wce,"DefaultAsyncParser"),DefaultCommentProvider:s(()=>Cce,"DefaultCommentProvider"),DefaultConfigurationProvider:s(()=>fce,"DefaultConfigurationProvider"),DefaultDocumentBuilder:s(()=>gce,"DefaultDocumentBuilder"),DefaultDocumentValidator:s(()=>uce,"DefaultDocumentValidator"),DefaultHydrator:s(()=>Sce,"DefaultHydrator"),DefaultIndexManager:s(()=>mce,"DefaultIndexManager"),DefaultJsonSerializer:s(()=>sce,"DefaultJsonSerializer"),DefaultLangiumDocumentFactory:s(()=>Qle,"DefaultLangiumDocumentFactory"),DefaultLangiumDocuments:s(()=>Zle,"DefaultLangiumDocuments"),DefaultLangiumProfiler:s(()=>oot,"DefaultLangiumProfiler"),DefaultLexer:s(()=>m9,"DefaultLexer"),DefaultLexerErrorMessageProvider:s(()=>vce,"DefaultLexerErrorMessageProvider"),DefaultLinker:s(()=>Jle,"DefaultLinker"),DefaultNameProvider:s(()=>ece,"DefaultNameProvider"),DefaultReferenceDescriptionProvider:s(()=>dce,"DefaultReferenceDescriptionProvider"),DefaultReferences:s(()=>tce,"DefaultReferences"),DefaultScopeComputation:s(()=>rce,"DefaultScopeComputation"),DefaultScopeProvider:s(()=>ace,"DefaultScopeProvider"),DefaultServiceRegistry:s(()=>oce,"DefaultServiceRegistry"),DefaultTokenBuilder:s(()=>Kw,"DefaultTokenBuilder"),DefaultValueConverter:s(()=>l9,"DefaultValueConverter"),DefaultWorkspaceLock:s(()=>Ece,"DefaultWorkspaceLock"),DefaultWorkspaceManager:s(()=>yce,"DefaultWorkspaceManager"),Deferred:s(()=>wh,"Deferred"),Disposable:s(()=>a1,"Disposable"),DisposableCache:s(()=>Jw,"DisposableCache"),DocumentCache:s(()=>nce,"DocumentCache"),DocumentState:s(()=>_i,"DocumentState"),DocumentValidator:s(()=>vc,"DocumentValidator"),EMPTY_SCOPE:s(()=>Zst,"EMPTY_SCOPE"),EMPTY_STREAM:s(()=>zm,"EMPTY_STREAM"),EmptyFileSystem:s(()=>$s,"EmptyFileSystem"),EmptyFileSystemProvider:s(()=>Lce,"EmptyFileSystemProvider"),ErrorWithLocation:s(()=>ew,"ErrorWithLocation"),GrammarAST:s(()=>Zae,"GrammarAST"),GrammarUtils:s(()=>nL,"GrammarUtils"),IndentationAwareLexer:s(()=>not,"IndentationAwareLexer"),IndentationAwareTokenBuilder:s(()=>$ce,"IndentationAwareTokenBuilder"),JSDocDocumentationProvider:s(()=>kce,"JSDocDocumentationProvider"),LangiumCompletionParser:s(()=>Hle,"LangiumCompletionParser"),LangiumParser:s(()=>Ule,"LangiumParser"),LangiumParserErrorMessageProvider:s(()=>o9,"LangiumParserErrorMessageProvider"),LeafCstNodeImpl:s(()=>Hw,"LeafCstNodeImpl"),LexingMode:s(()=>s1,"LexingMode"),MapScope:s(()=>Qst,"MapScope"),Module:s(()=>_ce,"Module"),MultiMap:s(()=>Eh,"MultiMap"),MultiMapScope:s(()=>ice,"MultiMapScope"),OperationCancelled:s(()=>ou,"OperationCancelled"),ParserWorker:s(()=>iot,"ParserWorker"),ProfilingTask:s(()=>Ice,"ProfilingTask"),Reduction:s(()=>zC,"Reduction"),RefResolving:s(()=>p0,"RefResolving"),RegExpUtils:s(()=>aL,"RegExpUtils"),RootCstNodeImpl:s(()=>a9,"RootCstNodeImpl"),SimpleCache:s(()=>d9,"SimpleCache"),StreamImpl:s(()=>eu,"StreamImpl"),StreamScope:s(()=>h9,"StreamScope"),TextDocument:s(()=>c9,"TextDocument"),TreeStreamImpl:s(()=>qm,"TreeStreamImpl"),URI:s(()=>yl,"URI"),UriTrie:s(()=>u9,"UriTrie"),UriUtils:s(()=>Uo,"UriUtils"),VALIDATE_EACH_NODE:s(()=>cce,"VALIDATE_EACH_NODE"),ValidationCategory:s(()=>f9,"ValidationCategory"),ValidationRegistry:s(()=>lce,"ValidationRegistry"),ValueConverter:s(()=>Ch,"ValueConverter"),WorkspaceCache:s(()=>p9,"WorkspaceCache"),assertCondition:s(()=>dR,"assertCondition"),assertUnreachable:s(()=>qd,"assertUnreachable"),createCompletionParser:s(()=>b$,"createCompletionParser"),createDefaultCoreModule:s(()=>us,"createDefaultCoreModule"),createDefaultSharedCoreModule:s(()=>hs,"createDefaultSharedCoreModule"),createGrammarConfig:s(()=>$R,"createGrammarConfig"),createLangiumParser:s(()=>x$,"createLangiumParser"),createParser:s(()=>mC,"createParser"),delayNextTick:s(()=>bC,"delayNextTick"),diagnosticData:s(()=>Vf,"diagnosticData"),eagerLoad:s(()=>P$,"eagerLoad"),getDiagnosticRange:s(()=>R$,"getDiagnosticRange"),indentationBuilderDefaultOptions:s(()=>b9,"indentationBuilderDefaultOptions"),inject:s(()=>Bi,"inject"),interruptAndCheck:s(()=>cs,"interruptAndCheck"),isAstNode:s(()=>Da,"isAstNode"),isAstNodeDescription:s(()=>B8,"isAstNodeDescription"),isAstNodeWithComment:s(()=>_$,"isAstNodeWithComment"),isCompositeCstNode:s(()=>sh,"isCompositeCstNode"),isIMultiModeLexerDefinition:s(()=>CC,"isIMultiModeLexerDefinition"),isJSDoc:s(()=>I$,"isJSDoc"),isLeafCstNode:s(()=>wf,"isLeafCstNode"),isLinkingError:s(()=>Cf,"isLinkingError"),isMultiReference:s(()=>Zc,"isMultiReference"),isNamed:s(()=>S$,"isNamed"),isOperationCancelled:s(()=>jf,"isOperationCancelled"),isReference:s(()=>fo,"isReference"),isRootCstNode:s(()=>Mk,"isRootCstNode"),isTokenTypeArray:s(()=>kC,"isTokenTypeArray"),isTokenTypeDictionary:s(()=>wC,"isTokenTypeDictionary"),loadGrammarFromJson:s(()=>ds,"loadGrammarFromJson"),parseJSDoc:s(()=>L$,"parseJSDoc"),prepareLangiumParser:s(()=>T$,"prepareLangiumParser"),setInterruptionPeriod:s(()=>k$,"setInterruptionPeriod"),startCancelableOperation:s(()=>xC,"startCancelableOperation"),stream:s(()=>an,"stream"),toDiagnosticData:s(()=>$$,"toDiagnosticData"),toDiagnosticSeverity:s(()=>Wv,"toDiagnosticSeverity")}),J$={},Vd(J$,{DefaultNameRegexp:s(()=>iL,"DefaultNameRegexp"),RangeComparison:s(()=>mh,"RangeComparison"),compareRange:s(()=>aR,"compareRange"),findCommentNode:s(()=>oR,"findCommentNode"),findDeclarationNodeAtOffset:s(()=>IK,"findDeclarationNodeAtOffset"),findLeafNodeAtOffset:s(()=>Vk,"findLeafNodeAtOffset"),findLeafNodeBeforeOffset:s(()=>lR,"findLeafNodeBeforeOffset"),flattenCst:s(()=>LK,"flattenCst"),getDatatypeNode:s(()=>$K,"getDatatypeNode"),getInteriorNodes:s(()=>NK,"getInteriorNodes"),getNextNode:s(()=>MK,"getNextNode"),getPreviousNode:s(()=>uR,"getPreviousNode"),getStartlineNode:s(()=>DK,"getStartlineNode"),inRange:s(()=>sR,"inRange"),isChildNode:s(()=>nR,"isChildNode"),isCommentNode:s(()=>jk,"isCommentNode"),streamCst:s(()=>Sm,"streamCst"),toDocumentSegment:s(()=>_m,"toDocumentSegment"),tokenToRange:s(()=>Tv,"tokenToRange")}),s(Da,"isAstNode"),L(Da,"isAstNode"),s(fo,"isReference"),L(fo,"isReference"),s(Zc,"isMultiReference"),L(Zc,"isMultiReference"),s(B8,"isAstNodeDescription"),L(B8,"isAstNodeDescription"),s(Cf,"isLinkingError"),L(Cf,"isLinkingError"),eL=(e=class{constructor(){this.subtypes={},this.allSubtypes={}}getAllTypes(){return Object.keys(this.types)}getReferenceType(T){let A=this.types[T.container.$type];if(!A)throw new Error(`Type ${T.container.$type||"undefined"} not found.`);let D=A.properties[T.property]?.referenceType;if(!D)throw new Error(`Property ${T.property||"undefined"} of type ${T.container.$type} is not a reference.`);return D}getTypeMetaData(T){return this.types[T]||{name:T,properties:{},superTypes:[]}}isInstance(T,A){return Da(T)&&this.isSubtype(T.$type,A)}isSubtype(T,A){if(T===A)return!0;let D=this.subtypes[T];D||(D=this.subtypes[T]={});let P=D[A];if(P!==void 0)return P;{let V=this.types[T],Z=V?V.superTypes.some(re=>this.isSubtype(re,A)):!1;return D[A]=Z,Z}}getAllSubTypes(T){let A=this.allSubtypes[T];if(A)return A;{let D=this.getAllTypes(),P=[];for(let V of D)this.isSubtype(V,T)&&P.push(V);return this.allSubtypes[T]=P,P}}},s(e,"AbstractAstReflection"),L(e,"AbstractAstReflection"),e),s(sh,"isCompositeCstNode"),L(sh,"isCompositeCstNode"),s(wf,"isLeafCstNode"),L(wf,"isLeafCstNode"),s(Mk,"isRootCstNode"),L(Mk,"isRootCstNode"),eu=(t=class{constructor(T,A){this.startFn=T,this.nextFn=A}iterator(){let T={state:this.startFn(),next:L(()=>this.nextFn(T.state),"next"),[Symbol.iterator]:()=>T};return T}[Symbol.iterator](){return this.iterator()}isEmpty(){return!!this.iterator().next().done}count(){let T=this.iterator(),A=0,D=T.next();for(;!D.done;)A++,D=T.next();return A}toArray(){let T=[],A=this.iterator(),D;do D=A.next(),D.value!==void 0&&T.push(D.value);while(!D.done);return T}toSet(){return new Set(this)}toMap(T,A){let D=this.map(P=>[T?T(P):P,A?A(P):P]);return new Map(D)}toString(){return this.join()}concat(T){return new t(()=>({first:this.startFn(),firstDone:!1,iterator:T[Symbol.iterator]()}),A=>{let D;if(!A.firstDone){do if(D=this.nextFn(A.first),!D.done)return D;while(!D.done);A.firstDone=!0}do if(D=A.iterator.next(),!D.done)return D;while(!D.done);return mo})}join(T=","){let A=this.iterator(),D="",P,V=!1;do P=A.next(),P.done||(V&&(D+=T),D+=dK(P.value)),V=!0;while(!P.done);return D}indexOf(T,A=0){let D=this.iterator(),P=0,V=D.next();for(;!V.done;){if(P>=A&&V.value===T)return P;V=D.next(),P++}return-1}every(T){let A=this.iterator(),D=A.next();for(;!D.done;){if(!T(D.value))return!1;D=A.next()}return!0}some(T){let A=this.iterator(),D=A.next();for(;!D.done;){if(T(D.value))return!0;D=A.next()}return!1}forEach(T){let A=this.iterator(),D=0,P=A.next();for(;!P.done;)T(P.value,D),P=A.next(),D++}map(T){return new t(this.startFn,A=>{let{done:D,value:P}=this.nextFn(A);return D?mo:{done:!1,value:T(P)}})}filter(T){return new t(this.startFn,A=>{let D;do if(D=this.nextFn(A),!D.done&&T(D.value))return D;while(!D.done);return mo})}nonNullable(){return this.filter(T=>T!=null)}reduce(T,A){let D=this.iterator(),P=A,V=D.next();for(;!V.done;)P===void 0?P=V.value:P=T(P,V.value),V=D.next();return P}reduceRight(T,A){return this.recursiveReduce(this.iterator(),T,A)}recursiveReduce(T,A,D){let P=T.next();if(P.done)return D;let V=this.recursiveReduce(T,A,D);return V===void 0?P.value:A(V,P.value)}find(T){let A=this.iterator(),D=A.next();for(;!D.done;){if(T(D.value))return D.value;D=A.next()}}findIndex(T){let A=this.iterator(),D=0,P=A.next();for(;!P.done;){if(T(P.value))return D;P=A.next(),D++}return-1}includes(T){let A=this.iterator(),D=A.next();for(;!D.done;){if(D.value===T)return!0;D=A.next()}return!1}flatMap(T){return new t(()=>({this:this.startFn()}),A=>{do{if(A.iterator){let V=A.iterator.next();if(V.done)A.iterator=void 0;else return V}let{done:D,value:P}=this.nextFn(A.this);if(!D){let V=T(P);if(vv(V))A.iterator=V[Symbol.iterator]();else return{done:!1,value:V}}}while(A.iterator);return mo})}flat(T){if(T===void 0&&(T=1),T<=0)return this;let A=T>1?this.flat(T-1):this;return new t(()=>({this:A.startFn()}),D=>{do{if(D.iterator){let Z=D.iterator.next();if(Z.done)D.iterator=void 0;else return Z}let{done:P,value:V}=A.nextFn(D.this);if(!P)if(vv(V))D.iterator=V[Symbol.iterator]();else return{done:!1,value:V}}while(D.iterator);return mo})}head(){let T=this.iterator().next();if(!T.done)return T.value}tail(T=1){return new t(()=>{let A=this.startFn();for(let D=0;D({size:0,state:this.startFn()}),A=>(A.size++,A.size>T?mo:this.nextFn(A.state)))}distinct(T){return new t(()=>({set:new Set,internalState:this.startFn()}),A=>{let D;do if(D=this.nextFn(A.internalState),!D.done){let P=T?T(D.value):D.value;if(!A.set.has(P))return A.set.add(P),D}while(!D.done);return mo})}exclude(T,A){let D=new Set;for(let P of T){let V=A?A(P):P;D.add(V)}return this.filter(P=>{let V=A?A(P):P;return!D.has(V)})}},s(t,"_StreamImpl"),L(t,"StreamImpl"),t),s(dK,"toString"),L(dK,"toString"),s(vv,"isIterable"),L(vv,"isIterable"),zm=new eu(()=>{},()=>mo),mo=Object.freeze({done:!0,value:void 0}),s(an,"stream"),L(an,"stream"),qm=(r=class extends eu{constructor(T,A,D){super(()=>({iterators:D?.includeRoot?[[T][Symbol.iterator]()]:[A(T)[Symbol.iterator]()],pruned:!1}),P=>{for(P.pruned&&(P.iterators.pop(),P.pruned=!1);P.iterators.length>0;){let V=P.iterators[P.iterators.length-1].next();if(V.done)P.iterators.pop();else return P.iterators.push(A(V.value)[Symbol.iterator]()),V}return mo})}iterator(){let T={state:this.startFn(),next:L(()=>this.nextFn(T.state),"next"),prune:L(()=>{T.state.pruned=!0},"prune"),[Symbol.iterator]:()=>T};return T}},s(r,"TreeStreamImpl"),L(r,"TreeStreamImpl"),r),(function(q){function T(V){return V.reduce((Z,re)=>Z+re,0)}s(T,"sum"),L(T,"sum"),q.sum=T;function A(V){return V.reduce((Z,re)=>Z*re,0)}s(A,"product"),L(A,"product"),q.product=A;function D(V){return V.reduce((Z,re)=>Math.min(Z,re))}s(D,"min2"),L(D,"min"),q.min=D;function P(V){return V.reduce((Z,re)=>Math.max(Z,re))}s(P,"max"),L(P,"max"),q.max=P})(zC||(zC={})),tL={},Vd(tL,{assignMandatoryProperties:s(()=>P8,"assignMandatoryProperties"),copyAstNode:s(()=>Ok,"copyAstNode"),findRootNode:s(()=>Cm,"findRootNode"),getContainerOfType:s(()=>Ef,"getContainerOfType"),getDocument:s(()=>lc,"getDocument"),getReferenceNodes:s(()=>Dk,"getReferenceNodes"),hasContainerOfType:s(()=>pK,"hasContainerOfType"),linkContentToContainer:s(()=>km,"linkContentToContainer"),streamAllContents:s(()=>oh,"streamAllContents"),streamAst:s(()=>cc,"streamAst"),streamContents:s(()=>bv,"streamContents"),streamReferences:s(()=>wm,"streamReferences")}),s(km,"linkContentToContainer"),L(km,"linkContentToContainer"),s(Ef,"getContainerOfType"),L(Ef,"getContainerOfType"),s(pK,"hasContainerOfType"),L(pK,"hasContainerOfType"),s(lc,"getDocument"),L(lc,"getDocument"),s(Cm,"findRootNode"),L(Cm,"findRootNode"),s(Dk,"getReferenceNodes"),L(Dk,"getReferenceNodes"),s(bv,"streamContents"),L(bv,"streamContents"),s(oh,"streamAllContents"),L(oh,"streamAllContents"),s(cc,"streamAst"),L(cc,"streamAst"),s(Nk,"isAstNodeInRange"),L(Nk,"isAstNodeInRange"),s(wm,"streamReferences"),L(wm,"streamReferences"),s(P8,"assignMandatoryProperties"),L(P8,"assignMandatoryProperties"),s(F8,"copyDefaultValue"),L(F8,"copyDefaultValue"),s(Ok,"copyAstNode"),L(Ok,"copyAstNode"),Zae={},Vd(Zae,{AbstractElement:s(()=>Po,"AbstractElement"),AbstractParserRule:s(()=>Kv,"AbstractParserRule"),AbstractRule:s(()=>Wm,"AbstractRule"),AbstractType:s(()=>hl,"AbstractType"),Action:s(()=>Yd,"Action"),Alternatives:s(()=>Qv,"Alternatives"),ArrayLiteral:s(()=>qC,"ArrayLiteral"),ArrayType:s(()=>WC,"ArrayType"),Assignment:s(()=>Hd,"Assignment"),BooleanLiteral:s(()=>jC,"BooleanLiteral"),CharacterRange:s(()=>Gd,"CharacterRange"),Condition:s(()=>Xd,"Condition"),Conjunction:s(()=>Zv,"Conjunction"),CrossReference:s(()=>Kd,"CrossReference"),Disjunction:s(()=>Jv,"Disjunction"),EndOfFile:s(()=>VC,"EndOfFile"),Grammar:s(()=>fh,"Grammar"),GrammarImport:s(()=>UC,"GrammarImport"),Group:s(()=>Yf,"Group"),InferredType:s(()=>YC,"InferredType"),InfixRule:s(()=>tu,"InfixRule"),InfixRuleOperatorList:s(()=>eb,"InfixRuleOperatorList"),InfixRuleOperators:s(()=>HC,"InfixRuleOperators"),Interface:s(()=>jm,"Interface"),Keyword:s(()=>Vm,"Keyword"),LangiumGrammarAstReflection:s(()=>rL,"LangiumGrammarAstReflection"),LangiumGrammarTerminals:s(()=>EXe,"LangiumGrammarTerminals"),NamedArgument:s(()=>Um,"NamedArgument"),NegatedToken:s(()=>Hf,"NegatedToken"),Negation:s(()=>GC,"Negation"),NumberLiteral:s(()=>XC,"NumberLiteral"),Parameter:s(()=>Ym,"Parameter"),ParameterReference:s(()=>KC,"ParameterReference"),ParserRule:s(()=>dc,"ParserRule"),ReferenceType:s(()=>tb,"ReferenceType"),RegexToken:s(()=>Gf,"RegexToken"),ReturnType:s(()=>QC,"ReturnType"),RuleCall:s(()=>Xf,"RuleCall"),SimpleType:s(()=>Hm,"SimpleType"),StringLiteral:s(()=>ZC,"StringLiteral"),TerminalAlternatives:s(()=>Kf,"TerminalAlternatives"),TerminalElement:s(()=>Fo,"TerminalElement"),TerminalGroup:s(()=>Qf,"TerminalGroup"),TerminalRule:s(()=>gh,"TerminalRule"),TerminalRuleCall:s(()=>Zf,"TerminalRuleCall"),Type:s(()=>rb,"Type"),TypeAttribute:s(()=>Jf,"TypeAttribute"),TypeDefinition:s(()=>e0,"TypeDefinition"),UnionType:s(()=>JC,"UnionType"),UnorderedGroup:s(()=>ib,"UnorderedGroup"),UntilToken:s(()=>t0,"UntilToken"),ValueLiteral:s(()=>r0,"ValueLiteral"),Wildcard:s(()=>Gm,"Wildcard"),isAbstractElement:s(()=>Bk,"isAbstractElement"),isAbstractParserRule:s(()=>Sf,"isAbstractParserRule"),isAbstractRule:s(()=>fK,"isAbstractRule"),isAbstractType:s(()=>gK,"isAbstractType"),isAction:s(()=>zd,"isAction"),isAlternatives:s(()=>Pk,"isAlternatives"),isArrayLiteral:s(()=>mK,"isArrayLiteral"),isArrayType:s(()=>z8,"isArrayType"),isAssignment:s(()=>lh,"isAssignment"),isBooleanLiteral:s(()=>q8,"isBooleanLiteral"),isCharacterRange:s(()=>W8,"isCharacterRange"),isCondition:s(()=>yK,"isCondition"),isConjunction:s(()=>j8,"isConjunction"),isCrossReference:s(()=>_f,"isCrossReference"),isDisjunction:s(()=>V8,"isDisjunction"),isEndOfFile:s(()=>U8,"isEndOfFile"),isGrammar:s(()=>vK,"isGrammar"),isGrammarImport:s(()=>bK,"isGrammarImport"),isGroup:s(()=>Af,"isGroup"),isInferredType:s(()=>xv,"isInferredType"),isInfixRule:s(()=>Em,"isInfixRule"),isInfixRuleOperatorList:s(()=>xK,"isInfixRuleOperatorList"),isInfixRuleOperators:s(()=>TK,"isInfixRuleOperators"),isInterface:s(()=>Y8,"isInterface"),isKeyword:s(()=>ch,"isKeyword"),isNamedArgument:s(()=>kK,"isNamedArgument"),isNegatedToken:s(()=>H8,"isNegatedToken"),isNegation:s(()=>G8,"isNegation"),isNumberLiteral:s(()=>CK,"isNumberLiteral"),isParameter:s(()=>wK,"isParameter"),isParameterReference:s(()=>X8,"isParameterReference"),isParserRule:s(()=>go,"isParserRule"),isReferenceType:s(()=>K8,"isReferenceType"),isRegexToken:s(()=>Q8,"isRegexToken"),isReturnType:s(()=>Z8,"isReturnType"),isRuleCall:s(()=>uh,"isRuleCall"),isSimpleType:s(()=>Fk,"isSimpleType"),isStringLiteral:s(()=>EK,"isStringLiteral"),isTerminalAlternatives:s(()=>J8,"isTerminalAlternatives"),isTerminalElement:s(()=>SK,"isTerminalElement"),isTerminalGroup:s(()=>eR,"isTerminalGroup"),isTerminalRule:s(()=>Ml,"isTerminalRule"),isTerminalRuleCall:s(()=>zk,"isTerminalRuleCall"),isType:s(()=>qk,"isType"),isTypeAttribute:s(()=>_K,"isTypeAttribute"),isTypeDefinition:s(()=>AK,"isTypeDefinition"),isUnionType:s(()=>tR,"isUnionType"),isUnorderedGroup:s(()=>Wk,"isUnorderedGroup"),isUntilToken:s(()=>rR,"isUntilToken"),isValueLiteral:s(()=>RK,"isValueLiteral"),isWildcard:s(()=>iR,"isWildcard"),reflection:s(()=>Yr,"reflection")}),EXe={ID:/\^?[_a-zA-Z][\w_]*/,STRING:/"(\\.|[^"\\])*"|'(\\.|[^'\\])*'/,NUMBER:/NaN|-?((\d*\.\d+|\d+)([Ee][+-]?\d+)?|Infinity)/,RegexLiteral:/\/(?![*+?])(?:[^\r\n\[/\\]|\\.|\[(?:[^\r\n\]\\]|\\.)*\])+\/[a-z]*/,WS:/\s+/,ML_COMMENT:/\/\*[\s\S]*?\*\//,SL_COMMENT:/\/\/[^\n\r]*/},Po={$type:"AbstractElement",cardinality:"cardinality"},s(Bk,"isAbstractElement"),L(Bk,"isAbstractElement"),Kv={$type:"AbstractParserRule"},s(Sf,"isAbstractParserRule"),L(Sf,"isAbstractParserRule"),Wm={$type:"AbstractRule"},s(fK,"isAbstractRule"),L(fK,"isAbstractRule"),hl={$type:"AbstractType"},s(gK,"isAbstractType"),L(gK,"isAbstractType"),Yd={$type:"Action",cardinality:"cardinality",feature:"feature",inferredType:"inferredType",operator:"operator",type:"type"},s(zd,"isAction"),L(zd,"isAction"),Qv={$type:"Alternatives",cardinality:"cardinality",elements:"elements"},s(Pk,"isAlternatives"),L(Pk,"isAlternatives"),qC={$type:"ArrayLiteral",elements:"elements"},s(mK,"isArrayLiteral"),L(mK,"isArrayLiteral"),WC={$type:"ArrayType",elementType:"elementType"},s(z8,"isArrayType"),L(z8,"isArrayType"),Hd={$type:"Assignment",cardinality:"cardinality",feature:"feature",operator:"operator",predicate:"predicate",terminal:"terminal"},s(lh,"isAssignment"),L(lh,"isAssignment"),jC={$type:"BooleanLiteral",true:"true"},s(q8,"isBooleanLiteral"),L(q8,"isBooleanLiteral"),Gd={$type:"CharacterRange",cardinality:"cardinality",left:"left",lookahead:"lookahead",parenthesized:"parenthesized",right:"right"},s(W8,"isCharacterRange"),L(W8,"isCharacterRange"),Xd={$type:"Condition"},s(yK,"isCondition"),L(yK,"isCondition"),Zv={$type:"Conjunction",left:"left",right:"right"},s(j8,"isConjunction"),L(j8,"isConjunction"),Kd={$type:"CrossReference",cardinality:"cardinality",deprecatedSyntax:"deprecatedSyntax",isMulti:"isMulti",terminal:"terminal",type:"type"},s(_f,"isCrossReference"),L(_f,"isCrossReference"),Jv={$type:"Disjunction",left:"left",right:"right"},s(V8,"isDisjunction"),L(V8,"isDisjunction"),VC={$type:"EndOfFile",cardinality:"cardinality"},s(U8,"isEndOfFile"),L(U8,"isEndOfFile"),fh={$type:"Grammar",imports:"imports",interfaces:"interfaces",isDeclared:"isDeclared",name:"name",rules:"rules",types:"types"},s(vK,"isGrammar"),L(vK,"isGrammar"),UC={$type:"GrammarImport",path:"path"},s(bK,"isGrammarImport"),L(bK,"isGrammarImport"),Yf={$type:"Group",cardinality:"cardinality",elements:"elements",guardCondition:"guardCondition",predicate:"predicate"},s(Af,"isGroup"),L(Af,"isGroup"),YC={$type:"InferredType",name:"name"},s(xv,"isInferredType"),L(xv,"isInferredType"),tu={$type:"InfixRule",call:"call",dataType:"dataType",inferredType:"inferredType",name:"name",operators:"operators",parameters:"parameters",returnType:"returnType"},s(Em,"isInfixRule"),L(Em,"isInfixRule"),eb={$type:"InfixRuleOperatorList",associativity:"associativity",operators:"operators"},s(xK,"isInfixRuleOperatorList"),L(xK,"isInfixRuleOperatorList"),HC={$type:"InfixRuleOperators",precedences:"precedences"},s(TK,"isInfixRuleOperators"),L(TK,"isInfixRuleOperators"),jm={$type:"Interface",attributes:"attributes",name:"name",superTypes:"superTypes"},s(Y8,"isInterface"),L(Y8,"isInterface"),Vm={$type:"Keyword",cardinality:"cardinality",predicate:"predicate",value:"value"},s(ch,"isKeyword"),L(ch,"isKeyword"),Um={$type:"NamedArgument",calledByName:"calledByName",parameter:"parameter",value:"value"},s(kK,"isNamedArgument"),L(kK,"isNamedArgument"),Hf={$type:"NegatedToken",cardinality:"cardinality",lookahead:"lookahead",parenthesized:"parenthesized",terminal:"terminal"},s(H8,"isNegatedToken"),L(H8,"isNegatedToken"),GC={$type:"Negation",value:"value"},s(G8,"isNegation"),L(G8,"isNegation"),XC={$type:"NumberLiteral",value:"value"},s(CK,"isNumberLiteral"),L(CK,"isNumberLiteral"),Ym={$type:"Parameter",name:"name"},s(wK,"isParameter"),L(wK,"isParameter"),KC={$type:"ParameterReference",parameter:"parameter"},s(X8,"isParameterReference"),L(X8,"isParameterReference"),dc={$type:"ParserRule",dataType:"dataType",definition:"definition",entry:"entry",fragment:"fragment",inferredType:"inferredType",name:"name",parameters:"parameters",returnType:"returnType"},s(go,"isParserRule"),L(go,"isParserRule"),tb={$type:"ReferenceType",isMulti:"isMulti",referenceType:"referenceType"},s(K8,"isReferenceType"),L(K8,"isReferenceType"),Gf={$type:"RegexToken",cardinality:"cardinality",lookahead:"lookahead",parenthesized:"parenthesized",regex:"regex"},s(Q8,"isRegexToken"),L(Q8,"isRegexToken"),QC={$type:"ReturnType",name:"name"},s(Z8,"isReturnType"),L(Z8,"isReturnType"),Xf={$type:"RuleCall",arguments:"arguments",cardinality:"cardinality",predicate:"predicate",rule:"rule"},s(uh,"isRuleCall"),L(uh,"isRuleCall"),Hm={$type:"SimpleType",primitiveType:"primitiveType",stringType:"stringType",typeRef:"typeRef"},s(Fk,"isSimpleType"),L(Fk,"isSimpleType"),ZC={$type:"StringLiteral",value:"value"},s(EK,"isStringLiteral"),L(EK,"isStringLiteral"),Kf={$type:"TerminalAlternatives",cardinality:"cardinality",elements:"elements",lookahead:"lookahead",parenthesized:"parenthesized"},s(J8,"isTerminalAlternatives"),L(J8,"isTerminalAlternatives"),Fo={$type:"TerminalElement",cardinality:"cardinality",lookahead:"lookahead",parenthesized:"parenthesized"},s(SK,"isTerminalElement"),L(SK,"isTerminalElement"),Qf={$type:"TerminalGroup",cardinality:"cardinality",elements:"elements",lookahead:"lookahead",parenthesized:"parenthesized"},s(eR,"isTerminalGroup"),L(eR,"isTerminalGroup"),gh={$type:"TerminalRule",definition:"definition",fragment:"fragment",hidden:"hidden",name:"name",type:"type"},s(Ml,"isTerminalRule"),L(Ml,"isTerminalRule"),Zf={$type:"TerminalRuleCall",cardinality:"cardinality",lookahead:"lookahead",parenthesized:"parenthesized",rule:"rule"},s(zk,"isTerminalRuleCall"),L(zk,"isTerminalRuleCall"),rb={$type:"Type",name:"name",type:"type"},s(qk,"isType"),L(qk,"isType"),Jf={$type:"TypeAttribute",defaultValue:"defaultValue",isOptional:"isOptional",name:"name",type:"type"},s(_K,"isTypeAttribute"),L(_K,"isTypeAttribute"),e0={$type:"TypeDefinition"},s(AK,"isTypeDefinition"),L(AK,"isTypeDefinition"),JC={$type:"UnionType",types:"types"},s(tR,"isUnionType"),L(tR,"isUnionType"),ib={$type:"UnorderedGroup",cardinality:"cardinality",elements:"elements"},s(Wk,"isUnorderedGroup"),L(Wk,"isUnorderedGroup"),t0={$type:"UntilToken",cardinality:"cardinality",lookahead:"lookahead",parenthesized:"parenthesized",terminal:"terminal"},s(rR,"isUntilToken"),L(rR,"isUntilToken"),r0={$type:"ValueLiteral"},s(RK,"isValueLiteral"),L(RK,"isValueLiteral"),Gm={$type:"Wildcard",cardinality:"cardinality",lookahead:"lookahead",parenthesized:"parenthesized"},s(iR,"isWildcard"),L(iR,"isWildcard"),rL=(i=class extends eL{constructor(){super(...arguments),this.types={AbstractElement:{name:Po.$type,properties:{cardinality:{name:Po.cardinality}},superTypes:[]},AbstractParserRule:{name:Kv.$type,properties:{},superTypes:[Wm.$type,hl.$type]},AbstractRule:{name:Wm.$type,properties:{},superTypes:[]},AbstractType:{name:hl.$type,properties:{},superTypes:[]},Action:{name:Yd.$type,properties:{cardinality:{name:Yd.cardinality},feature:{name:Yd.feature},inferredType:{name:Yd.inferredType},operator:{name:Yd.operator},type:{name:Yd.type,referenceType:hl.$type}},superTypes:[Po.$type]},Alternatives:{name:Qv.$type,properties:{cardinality:{name:Qv.cardinality},elements:{name:Qv.elements,defaultValue:[]}},superTypes:[Po.$type]},ArrayLiteral:{name:qC.$type,properties:{elements:{name:qC.elements,defaultValue:[]}},superTypes:[r0.$type]},ArrayType:{name:WC.$type,properties:{elementType:{name:WC.elementType}},superTypes:[e0.$type]},Assignment:{name:Hd.$type,properties:{cardinality:{name:Hd.cardinality},feature:{name:Hd.feature},operator:{name:Hd.operator},predicate:{name:Hd.predicate},terminal:{name:Hd.terminal}},superTypes:[Po.$type]},BooleanLiteral:{name:jC.$type,properties:{true:{name:jC.true,defaultValue:!1}},superTypes:[Xd.$type,r0.$type]},CharacterRange:{name:Gd.$type,properties:{cardinality:{name:Gd.cardinality},left:{name:Gd.left},lookahead:{name:Gd.lookahead},parenthesized:{name:Gd.parenthesized,defaultValue:!1},right:{name:Gd.right}},superTypes:[Fo.$type]},Condition:{name:Xd.$type,properties:{},superTypes:[]},Conjunction:{name:Zv.$type,properties:{left:{name:Zv.left},right:{name:Zv.right}},superTypes:[Xd.$type]},CrossReference:{name:Kd.$type,properties:{cardinality:{name:Kd.cardinality},deprecatedSyntax:{name:Kd.deprecatedSyntax,defaultValue:!1},isMulti:{name:Kd.isMulti,defaultValue:!1},terminal:{name:Kd.terminal},type:{name:Kd.type,referenceType:hl.$type}},superTypes:[Po.$type]},Disjunction:{name:Jv.$type,properties:{left:{name:Jv.left},right:{name:Jv.right}},superTypes:[Xd.$type]},EndOfFile:{name:VC.$type,properties:{cardinality:{name:VC.cardinality}},superTypes:[Po.$type]},Grammar:{name:fh.$type,properties:{imports:{name:fh.imports,defaultValue:[]},interfaces:{name:fh.interfaces,defaultValue:[]},isDeclared:{name:fh.isDeclared,defaultValue:!1},name:{name:fh.name},rules:{name:fh.rules,defaultValue:[]},types:{name:fh.types,defaultValue:[]}},superTypes:[]},GrammarImport:{name:UC.$type,properties:{path:{name:UC.path}},superTypes:[]},Group:{name:Yf.$type,properties:{cardinality:{name:Yf.cardinality},elements:{name:Yf.elements,defaultValue:[]},guardCondition:{name:Yf.guardCondition},predicate:{name:Yf.predicate}},superTypes:[Po.$type]},InferredType:{name:YC.$type,properties:{name:{name:YC.name}},superTypes:[hl.$type]},InfixRule:{name:tu.$type,properties:{call:{name:tu.call},dataType:{name:tu.dataType},inferredType:{name:tu.inferredType},name:{name:tu.name},operators:{name:tu.operators},parameters:{name:tu.parameters,defaultValue:[]},returnType:{name:tu.returnType,referenceType:hl.$type}},superTypes:[Kv.$type]},InfixRuleOperatorList:{name:eb.$type,properties:{associativity:{name:eb.associativity},operators:{name:eb.operators,defaultValue:[]}},superTypes:[]},InfixRuleOperators:{name:HC.$type,properties:{precedences:{name:HC.precedences,defaultValue:[]}},superTypes:[]},Interface:{name:jm.$type,properties:{attributes:{name:jm.attributes,defaultValue:[]},name:{name:jm.name},superTypes:{name:jm.superTypes,defaultValue:[],referenceType:hl.$type}},superTypes:[hl.$type]},Keyword:{name:Vm.$type,properties:{cardinality:{name:Vm.cardinality},predicate:{name:Vm.predicate},value:{name:Vm.value}},superTypes:[Po.$type]},NamedArgument:{name:Um.$type,properties:{calledByName:{name:Um.calledByName,defaultValue:!1},parameter:{name:Um.parameter,referenceType:Ym.$type},value:{name:Um.value}},superTypes:[]},NegatedToken:{name:Hf.$type,properties:{cardinality:{name:Hf.cardinality},lookahead:{name:Hf.lookahead},parenthesized:{name:Hf.parenthesized,defaultValue:!1},terminal:{name:Hf.terminal}},superTypes:[Fo.$type]},Negation:{name:GC.$type,properties:{value:{name:GC.value}},superTypes:[Xd.$type]},NumberLiteral:{name:XC.$type,properties:{value:{name:XC.value}},superTypes:[r0.$type]},Parameter:{name:Ym.$type,properties:{name:{name:Ym.name}},superTypes:[]},ParameterReference:{name:KC.$type,properties:{parameter:{name:KC.parameter,referenceType:Ym.$type}},superTypes:[Xd.$type]},ParserRule:{name:dc.$type,properties:{dataType:{name:dc.dataType},definition:{name:dc.definition},entry:{name:dc.entry,defaultValue:!1},fragment:{name:dc.fragment,defaultValue:!1},inferredType:{name:dc.inferredType},name:{name:dc.name},parameters:{name:dc.parameters,defaultValue:[]},returnType:{name:dc.returnType,referenceType:hl.$type}},superTypes:[Kv.$type]},ReferenceType:{name:tb.$type,properties:{isMulti:{name:tb.isMulti,defaultValue:!1},referenceType:{name:tb.referenceType}},superTypes:[e0.$type]},RegexToken:{name:Gf.$type,properties:{cardinality:{name:Gf.cardinality},lookahead:{name:Gf.lookahead},parenthesized:{name:Gf.parenthesized,defaultValue:!1},regex:{name:Gf.regex}},superTypes:[Fo.$type]},ReturnType:{name:QC.$type,properties:{name:{name:QC.name}},superTypes:[]},RuleCall:{name:Xf.$type,properties:{arguments:{name:Xf.arguments,defaultValue:[]},cardinality:{name:Xf.cardinality},predicate:{name:Xf.predicate},rule:{name:Xf.rule,referenceType:Wm.$type}},superTypes:[Po.$type]},SimpleType:{name:Hm.$type,properties:{primitiveType:{name:Hm.primitiveType},stringType:{name:Hm.stringType},typeRef:{name:Hm.typeRef,referenceType:hl.$type}},superTypes:[e0.$type]},StringLiteral:{name:ZC.$type,properties:{value:{name:ZC.value}},superTypes:[r0.$type]},TerminalAlternatives:{name:Kf.$type,properties:{cardinality:{name:Kf.cardinality},elements:{name:Kf.elements,defaultValue:[]},lookahead:{name:Kf.lookahead},parenthesized:{name:Kf.parenthesized,defaultValue:!1}},superTypes:[Fo.$type]},TerminalElement:{name:Fo.$type,properties:{cardinality:{name:Fo.cardinality},lookahead:{name:Fo.lookahead},parenthesized:{name:Fo.parenthesized,defaultValue:!1}},superTypes:[Po.$type]},TerminalGroup:{name:Qf.$type,properties:{cardinality:{name:Qf.cardinality},elements:{name:Qf.elements,defaultValue:[]},lookahead:{name:Qf.lookahead},parenthesized:{name:Qf.parenthesized,defaultValue:!1}},superTypes:[Fo.$type]},TerminalRule:{name:gh.$type,properties:{definition:{name:gh.definition},fragment:{name:gh.fragment,defaultValue:!1},hidden:{name:gh.hidden,defaultValue:!1},name:{name:gh.name},type:{name:gh.type}},superTypes:[Wm.$type]},TerminalRuleCall:{name:Zf.$type,properties:{cardinality:{name:Zf.cardinality},lookahead:{name:Zf.lookahead},parenthesized:{name:Zf.parenthesized,defaultValue:!1},rule:{name:Zf.rule,referenceType:gh.$type}},superTypes:[Fo.$type]},Type:{name:rb.$type,properties:{name:{name:rb.name},type:{name:rb.type}},superTypes:[hl.$type]},TypeAttribute:{name:Jf.$type,properties:{defaultValue:{name:Jf.defaultValue},isOptional:{name:Jf.isOptional,defaultValue:!1},name:{name:Jf.name},type:{name:Jf.type}},superTypes:[]},TypeDefinition:{name:e0.$type,properties:{},superTypes:[]},UnionType:{name:JC.$type,properties:{types:{name:JC.types,defaultValue:[]}},superTypes:[e0.$type]},UnorderedGroup:{name:ib.$type,properties:{cardinality:{name:ib.cardinality},elements:{name:ib.elements,defaultValue:[]}},superTypes:[Po.$type]},UntilToken:{name:t0.$type,properties:{cardinality:{name:t0.cardinality},lookahead:{name:t0.lookahead},parenthesized:{name:t0.parenthesized,defaultValue:!1},terminal:{name:t0.terminal}},superTypes:[Fo.$type]},ValueLiteral:{name:r0.$type,properties:{},superTypes:[]},Wildcard:{name:Gm.$type,properties:{cardinality:{name:Gm.cardinality},lookahead:{name:Gm.lookahead},parenthesized:{name:Gm.parenthesized,defaultValue:!1}},superTypes:[Fo.$type]}}}},s(i,"LangiumGrammarAstReflection"),L(i,"LangiumGrammarAstReflection"),i),Yr=new rL,s($K,"getDatatypeNode"),L($K,"getDatatypeNode"),s(Sm,"streamCst"),L(Sm,"streamCst"),s(LK,"flattenCst"),L(LK,"flattenCst"),s(nR,"isChildNode"),L(nR,"isChildNode"),s(Tv,"tokenToRange"),L(Tv,"tokenToRange"),s(_m,"toDocumentSegment"),L(_m,"toDocumentSegment"),(function(q){q[q.Before=0]="Before",q[q.After=1]="After",q[q.OverlapFront=2]="OverlapFront",q[q.OverlapBack=3]="OverlapBack",q[q.Inside=4]="Inside",q[q.Outside=5]="Outside"})(mh||(mh={})),s(aR,"compareRange"),L(aR,"compareRange"),s(sR,"inRange"),L(sR,"inRange"),iL=/^[\w\p{L}]$/u,s(IK,"findDeclarationNodeAtOffset"),L(IK,"findDeclarationNodeAtOffset"),s(oR,"findCommentNode"),L(oR,"findCommentNode"),s(jk,"isCommentNode"),L(jk,"isCommentNode"),s(Vk,"findLeafNodeAtOffset"),L(Vk,"findLeafNodeAtOffset"),s(lR,"findLeafNodeBeforeOffset"),L(lR,"findLeafNodeBeforeOffset"),s(cR,"binarySearch"),L(cR,"binarySearch"),s(uR,"getPreviousNode"),L(uR,"getPreviousNode"),s(MK,"getNextNode"),L(MK,"getNextNode"),s(DK,"getStartlineNode"),L(DK,"getStartlineNode"),s(NK,"getInteriorNodes"),L(NK,"getInteriorNodes"),s(OK,"getCommonParent"),L(OK,"getCommonParent"),s(hR,"getParentChain"),L(hR,"getParentChain"),nL={},Vd(nL,{findAssignment:s(()=>wR,"findAssignment"),findNameAssignment:s(()=>Qk,"findNameAssignment"),findNodeForKeyword:s(()=>CR,"findNodeForKeyword"),findNodeForProperty:s(()=>Gk,"findNodeForProperty"),findNodesForKeyword:s(()=>FK,"findNodesForKeyword"),findNodesForKeywordInternal:s(()=>Kk,"findNodesForKeywordInternal"),findNodesForProperty:s(()=>kR,"findNodesForProperty"),getActionAtElement:s(()=>SR,"getActionAtElement"),getActionType:s(()=>AR,"getActionType"),getAllReachableRules:s(()=>Hk,"getAllReachableRules"),getAllRulesUsedForCrossReferences:s(()=>PK,"getAllRulesUsedForCrossReferences"),getCrossReferenceTerminal:s(()=>xR,"getCrossReferenceTerminal"),getEntryRule:s(()=>yR,"getEntryRule"),getExplicitRuleType:s(()=>wv,"getExplicitRuleType"),getHiddenRules:s(()=>vR,"getHiddenRules"),getRuleType:s(()=>RR,"getRuleType"),getRuleTypeName:s(()=>VK,"getRuleTypeName"),getTypeName:s(()=>$f,"getTypeName"),isArrayCardinality:s(()=>qK,"isArrayCardinality"),isArrayOperator:s(()=>WK,"isArrayOperator"),isCommentTerminal:s(()=>TR,"isCommentTerminal"),isDataType:s(()=>jK,"isDataType"),isDataTypeRule:s(()=>Cv,"isDataTypeRule"),isOptionalCardinality:s(()=>zK,"isOptionalCardinality"),terminalRegex:s(()=>Ev,"terminalRegex")}),ew=(n=class extends Error{constructor(T,A){super(T?`${A} at ${T.range.start.line}:${T.range.start.character}`:A)}},s(n,"ErrorWithLocation"),L(n,"ErrorWithLocation"),n),s(qd,"assertUnreachable"),L(qd,"assertUnreachable"),s(dR,"assertCondition"),L(dR,"assertCondition"),aL={},Vd(aL,{NEWLINE_REGEXP:s(()=>tse,"NEWLINE_REGEXP"),escapeRegExp:s(()=>Rm,"escapeRegExp"),getTerminalParts:s(()=>BK,"getTerminalParts"),isMultilineComment:s(()=>fR,"isMultilineComment"),isWhitespace:s(()=>Yk,"isWhitespace"),partialMatches:s(()=>gR,"partialMatches"),partialRegExp:s(()=>mR,"partialRegExp"),whitespaceCharacters:s(()=>ise,"whitespaceCharacters")}),s(Jr,"cc"),L(Jr,"cc"),s(Uk,"insertToSet"),L(Uk,"insertToSet"),s(Am,"addFlag"),L(Am,"addFlag"),s(Rf,"ASSERT_EXISTS"),L(Rf,"ASSERT_EXISTS"),s(kv,"ASSERT_NEVER_REACH_HERE"),L(kv,"ASSERT_NEVER_REACH_HERE"),s(pR,"isCharacter"),L(pR,"isCharacter"),tw=[];for(let q=Jr("0");q<=Jr("9");q++)tw.push(q);rw=[Jr("_")].concat(tw);for(let q=Jr("a");q<=Jr("z");q++)rw.push(q);for(let q=Jr("A");q<=Jr("Z");q++)rw.push(q);Jae=[Jr(" "),Jr("\f"),Jr(` +`),Jr("\r"),Jr(" "),Jr("\v"),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr(" "),Jr("\u2028"),Jr("\u2029"),Jr(" "),Jr(" "),Jr(" "),Jr("\uFEFF")],SXe=/[0-9a-fA-F]/,iw=/[0-9]/,_Xe=/[1-9]/,ese=(a=class{constructor(){this.idx=0,this.input="",this.groupIdx=0}saveState(){return{idx:this.idx,input:this.input,groupIdx:this.groupIdx}}restoreState(T){this.idx=T.idx,this.input=T.input,this.groupIdx=T.groupIdx}pattern(T){this.idx=0,this.input=T,this.groupIdx=0,this.consumeChar("/");let A=this.disjunction();this.consumeChar("/");let D={type:"Flags",loc:{begin:this.idx,end:T.length},global:!1,ignoreCase:!1,multiLine:!1,unicode:!1,sticky:!1};for(;this.isRegExpFlag();)switch(this.popChar()){case"g":Am(D,"global");break;case"i":Am(D,"ignoreCase");break;case"m":Am(D,"multiLine");break;case"u":Am(D,"unicode");break;case"y":Am(D,"sticky");break}if(this.idx!==this.input.length)throw Error("Redundant input: "+this.input.substring(this.idx));return{type:"Pattern",flags:D,value:A,loc:this.loc(0)}}disjunction(){let T=[],A=this.idx;for(T.push(this.alternative());this.peekChar()==="|";)this.consumeChar("|"),T.push(this.alternative());return{type:"Disjunction",value:T,loc:this.loc(A)}}alternative(){let T=[],A=this.idx;for(;this.isTerm();)T.push(this.term());return{type:"Alternative",value:T,loc:this.loc(A)}}term(){return this.isAssertion()?this.assertion():this.atom()}assertion(){let T=this.idx;switch(this.popChar()){case"^":return{type:"StartAnchor",loc:this.loc(T)};case"$":return{type:"EndAnchor",loc:this.loc(T)};case"\\":switch(this.popChar()){case"b":return{type:"WordBoundary",loc:this.loc(T)};case"B":return{type:"NonWordBoundary",loc:this.loc(T)}}throw Error("Invalid Assertion Escape");case"(":this.consumeChar("?");let A;switch(this.popChar()){case"=":A="Lookahead";break;case"!":A="NegativeLookahead";break;case"<":{switch(this.popChar()){case"=":A="Lookbehind";break;case"!":A="NegativeLookbehind"}break}}Rf(A);let D=this.disjunction();return this.consumeChar(")"),{type:A,value:D,loc:this.loc(T)}}return kv()}quantifier(T=!1){let A,D=this.idx;switch(this.popChar()){case"*":A={atLeast:0,atMost:1/0};break;case"+":A={atLeast:1,atMost:1/0};break;case"?":A={atLeast:0,atMost:1};break;case"{":let P=this.integerIncludingZero();switch(this.popChar()){case"}":A={atLeast:P,atMost:P};break;case",":let V;this.isDigit()?(V=this.integerIncludingZero(),A={atLeast:P,atMost:V}):A={atLeast:P,atMost:1/0},this.consumeChar("}");break}if(T===!0&&A===void 0)return;Rf(A);break}if(!(T===!0&&A===void 0)&&Rf(A))return this.peekChar(0)==="?"?(this.consumeChar("?"),A.greedy=!1):A.greedy=!0,A.type="Quantifier",A.loc=this.loc(D),A}atom(){let T,A=this.idx;switch(this.peekChar()){case".":T=this.dotAll();break;case"\\":T=this.atomEscape();break;case"[":T=this.characterClass();break;case"(":T=this.group();break}return T===void 0&&this.isPatternCharacter()&&(T=this.patternCharacter()),Rf(T)?(T.loc=this.loc(A),this.isQuantifier()&&(T.quantifier=this.quantifier()),T):kv()}dotAll(){return this.consumeChar("."),{type:"Set",complement:!0,value:[Jr(` +`),Jr("\r"),Jr("\u2028"),Jr("\u2029")]}}atomEscape(){switch(this.consumeChar("\\"),this.peekChar()){case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":return this.decimalEscapeAtom();case"d":case"D":case"s":case"S":case"w":case"W":return this.characterClassEscape();case"f":case"n":case"r":case"t":case"v":return this.controlEscapeAtom();case"c":return this.controlLetterEscapeAtom();case"0":return this.nulCharacterAtom();case"x":return this.hexEscapeSequenceAtom();case"u":return this.regExpUnicodeEscapeSequenceAtom();default:return this.identityEscapeAtom()}}decimalEscapeAtom(){return{type:"GroupBackReference",value:this.positiveInteger()}}characterClassEscape(){let T,A=!1;switch(this.popChar()){case"d":T=tw;break;case"D":T=tw,A=!0;break;case"s":T=Jae;break;case"S":T=Jae,A=!0;break;case"w":T=rw;break;case"W":T=rw,A=!0;break}return Rf(T)?{type:"Set",value:T,complement:A}:kv()}controlEscapeAtom(){let T;switch(this.popChar()){case"f":T=Jr("\f");break;case"n":T=Jr(` +`);break;case"r":T=Jr("\r");break;case"t":T=Jr(" ");break;case"v":T=Jr("\v");break}return Rf(T)?{type:"Character",value:T}:kv()}controlLetterEscapeAtom(){this.consumeChar("c");let T=this.popChar();if(/[a-zA-Z]/.test(T)===!1)throw Error("Invalid ");return{type:"Character",value:T.toUpperCase().charCodeAt(0)-64}}nulCharacterAtom(){return this.consumeChar("0"),{type:"Character",value:Jr("\0")}}hexEscapeSequenceAtom(){return this.consumeChar("x"),this.parseHexDigits(2)}regExpUnicodeEscapeSequenceAtom(){return this.consumeChar("u"),this.parseHexDigits(4)}identityEscapeAtom(){let T=this.popChar();return{type:"Character",value:Jr(T)}}classPatternCharacterAtom(){switch(this.peekChar()){case` +`:case"\r":case"\u2028":case"\u2029":case"\\":case"]":throw Error("TBD");default:let T=this.popChar();return{type:"Character",value:Jr(T)}}}characterClass(){let T=[],A=!1;for(this.consumeChar("["),this.peekChar(0)==="^"&&(this.consumeChar("^"),A=!0);this.isClassAtom();){let D=this.classAtom(),P=D.type==="Character";if(pR(D)&&this.isRangeDash()){this.consumeChar("-");let V=this.classAtom(),Z=V.type==="Character";if(pR(V)){if(V.value=this.input.length)throw Error("Unexpected end of input");this.idx++}loc(T){return{begin:T,end:this.idx}}},s(a,"RegExpParser"),L(a,"RegExpParser"),a),nw=(o=class{visitChildren(T){for(let A in T){let D=T[A];T.hasOwnProperty(A)&&(D.type!==void 0?this.visit(D):Array.isArray(D)&&D.forEach(P=>{this.visit(P)},this))}}visit(T){switch(T.type){case"Pattern":this.visitPattern(T);break;case"Flags":this.visitFlags(T);break;case"Disjunction":this.visitDisjunction(T);break;case"Alternative":this.visitAlternative(T);break;case"StartAnchor":this.visitStartAnchor(T);break;case"EndAnchor":this.visitEndAnchor(T);break;case"WordBoundary":this.visitWordBoundary(T);break;case"NonWordBoundary":this.visitNonWordBoundary(T);break;case"Lookahead":this.visitLookahead(T);break;case"NegativeLookahead":this.visitNegativeLookahead(T);break;case"Lookbehind":this.visitLookbehind(T);break;case"NegativeLookbehind":this.visitNegativeLookbehind(T);break;case"Character":this.visitCharacter(T);break;case"Set":this.visitSet(T);break;case"Group":this.visitGroup(T);break;case"GroupBackReference":this.visitGroupBackReference(T);break;case"Quantifier":this.visitQuantifier(T);break}this.visitChildren(T)}visitPattern(T){}visitFlags(T){}visitDisjunction(T){}visitAlternative(T){}visitStartAnchor(T){}visitEndAnchor(T){}visitWordBoundary(T){}visitNonWordBoundary(T){}visitLookahead(T){}visitNegativeLookahead(T){}visitLookbehind(T){}visitNegativeLookbehind(T){}visitCharacter(T){}visitSet(T){}visitGroup(T){}visitGroupBackReference(T){}visitQuantifier(T){}},s(o,"BaseRegExpVisitor"),L(o,"BaseRegExpVisitor"),o),tse=/\r?\n/gm,rse=new ese,AXe=(l=class extends nw{constructor(){super(...arguments),this.isStarting=!0,this.endRegexpStack=[],this.multiline=!1}get endRegex(){return this.endRegexpStack.join("")}reset(T){this.multiline=!1,this.regex=T,this.startRegexp="",this.isStarting=!0,this.endRegexpStack=[]}visitGroup(T){T.quantifier&&(this.isStarting=!1,this.endRegexpStack=[])}visitCharacter(T){let A=String.fromCharCode(T.value);if(!this.multiline&&A===` +`&&(this.multiline=!0),T.quantifier)this.isStarting=!1,this.endRegexpStack=[];else{let D=Rm(A);this.endRegexpStack.push(D),this.isStarting&&(this.startRegexp+=D)}}visitSet(T){if(!this.multiline){let A=this.regex.substring(T.loc.begin,T.loc.end),D=new RegExp(A);this.multiline=!!` +`.match(D)}if(T.quantifier)this.isStarting=!1,this.endRegexpStack=[];else{let A=this.regex.substring(T.loc.begin,T.loc.end);this.endRegexpStack.push(A),this.isStarting&&(this.startRegexp+=A)}}visitChildren(T){T.type==="Group"&&T.quantifier||super.visitChildren(T)}},s(l,"TerminalRegExpVisitor"),L(l,"TerminalRegExpVisitor"),l),i0=new AXe,s(BK,"getTerminalParts"),L(BK,"getTerminalParts"),s(fR,"isMultilineComment"),L(fR,"isMultilineComment"),ise=`\f +\r \v              \u2028\u2029   \uFEFF`.split(""),s(Yk,"isWhitespace"),L(Yk,"isWhitespace"),s(Rm,"escapeRegExp"),L(Rm,"escapeRegExp"),s(gR,"partialMatches"),L(gR,"partialMatches"),s(mR,"partialRegExp"),L(mR,"partialRegExp"),s(yR,"getEntryRule"),L(yR,"getEntryRule"),s(vR,"getHiddenRules"),L(vR,"getHiddenRules"),s(Hk,"getAllReachableRules"),L(Hk,"getAllReachableRules"),s(bR,"ruleDfs"),L(bR,"ruleDfs"),s(PK,"getAllRulesUsedForCrossReferences"),L(PK,"getAllRulesUsedForCrossReferences"),s(xR,"getCrossReferenceTerminal"),L(xR,"getCrossReferenceTerminal"),s(TR,"isCommentTerminal"),L(TR,"isCommentTerminal"),s(kR,"findNodesForProperty"),L(kR,"findNodesForProperty"),s(Gk,"findNodeForProperty"),L(Gk,"findNodeForProperty"),s(Xk,"findNodesForPropertyInternal"),L(Xk,"findNodesForPropertyInternal"),s(FK,"findNodesForKeyword"),L(FK,"findNodesForKeyword"),s(CR,"findNodeForKeyword"),L(CR,"findNodeForKeyword"),s(Kk,"findNodesForKeywordInternal"),L(Kk,"findNodesForKeywordInternal"),s(wR,"findAssignment"),L(wR,"findAssignment"),s(Qk,"findNameAssignment"),L(Qk,"findNameAssignment"),s(ER,"findNameAssignmentInternal"),L(ER,"findNameAssignmentInternal"),s(SR,"getActionAtElement"),L(SR,"getActionAtElement"),s(zK,"isOptionalCardinality"),L(zK,"isOptionalCardinality"),s(qK,"isArrayCardinality"),L(qK,"isArrayCardinality"),s(WK,"isArrayOperator"),L(WK,"isArrayOperator"),s(Cv,"isDataTypeRule"),L(Cv,"isDataTypeRule"),s(_R,"isDataTypeRuleInternal"),L(_R,"isDataTypeRuleInternal"),s(jK,"isDataType"),L(jK,"isDataType"),s(Zk,"isDataTypeInternal"),L(Zk,"isDataTypeInternal"),s(wv,"getExplicitRuleType"),L(wv,"getExplicitRuleType"),s($f,"getTypeName"),L($f,"getTypeName"),s(AR,"getActionType"),L(AR,"getActionType"),s(VK,"getRuleTypeName"),L(VK,"getRuleTypeName"),s(RR,"getRuleType"),L(RR,"getRuleType"),s(Ev,"terminalRegex"),L(Ev,"terminalRegex"),sL=/[\s\S]/.source,s(Lf,"abstractElementToRegex"),L(Lf,"abstractElementToRegex"),s(UK,"terminalAlternativesToRegex"),L(UK,"terminalAlternativesToRegex"),s(YK,"terminalGroupToRegex"),L(YK,"terminalGroupToRegex"),s(HK,"untilTokenToRegex"),L(HK,"untilTokenToRegex"),s(GK,"negateTokenToRegex"),L(GK,"negateTokenToRegex"),s(XK,"characterRangeToRegex"),L(XK,"characterRangeToRegex"),s(Jk,"keywordToRegex"),L(Jk,"keywordToRegex"),s(Jc,"withCardinality"),L(Jc,"withCardinality"),s($R,"createGrammarConfig"),L($R,"createGrammarConfig"),RXe=typeof global=="object"&&global&&global.Object===Object&&global,nse=RXe,$Xe=typeof self=="object"&&self&&self.Object===Object&&self,LXe=nse||$Xe||Function("return this")(),ru=LXe,IXe=ru.Symbol,Dl=IXe,ase=Object.prototype,MXe=ase.hasOwnProperty,DXe=ase.toString,nb=Dl?Dl.toStringTag:void 0,s(KK,"getRawTag"),L(KK,"getRawTag"),NXe=KK,OXe=Object.prototype,BXe=OXe.toString,s(QK,"objectToString"),L(QK,"objectToString"),PXe=QK,FXe="[object Null]",zXe="[object Undefined]",sse=Dl?Dl.toStringTag:void 0,s(ZK,"baseGetTag"),L(ZK,"baseGetTag"),Qd=ZK,s(JK,"isObjectLike"),L(JK,"isObjectLike"),pc=JK,qXe="[object Symbol]",s(eQ,"isSymbol"),L(eQ,"isSymbol"),aw=eQ,s(tQ,"arrayMap"),L(tQ,"arrayMap"),ab=tQ,WXe=Array.isArray,qi=WXe,jXe=1/0,ose=Dl?Dl.prototype:void 0,lse=ose?ose.toString:void 0,s(LR,"baseToString"),L(LR,"baseToString"),VXe=LR,UXe=/\s/,s(rQ,"trimmedEndIndex"),L(rQ,"trimmedEndIndex"),YXe=rQ,HXe=/^\s+/,s(iQ,"baseTrim"),L(iQ,"baseTrim"),GXe=iQ,s(nQ,"isObject"),L(nQ,"isObject"),Nl=nQ,cse=NaN,XXe=/^[-+]0x[0-9a-f]+$/i,KXe=/^0b[01]+$/i,QXe=/^0o[0-7]+$/i,ZXe=parseInt,s(aQ,"toNumber"),L(aQ,"toNumber"),JXe=aQ,use=1/0,eKe=17976931348623157e292,s(sQ,"toFinite"),L(sQ,"toFinite"),tKe=sQ,s(oQ,"toInteger"),L(oQ,"toInteger"),sb=oQ,s(lQ,"identity"),L(lQ,"identity"),ob=lQ,rKe="[object AsyncFunction]",iKe="[object Function]",nKe="[object GeneratorFunction]",aKe="[object Proxy]",s(cQ,"isFunction"),L(cQ,"isFunction"),yh=cQ,sKe=ru["__core-js_shared__"],oL=sKe,hse=(function(){var q=/[^.]+$/.exec(oL&&oL.keys&&oL.keys.IE_PROTO||"");return q?"Symbol(src)_1."+q:""})(),s(uQ,"isMasked"),L(uQ,"isMasked"),oKe=uQ,lKe=Function.prototype,cKe=lKe.toString,s(hQ,"toSource"),L(hQ,"toSource"),n0=hQ,uKe=/[\\^$.*+?()[\]{}|]/g,hKe=/^\[object .+?Constructor\]$/,dKe=Function.prototype,pKe=Object.prototype,fKe=dKe.toString,gKe=pKe.hasOwnProperty,mKe=RegExp("^"+fKe.call(gKe).replace(uKe,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),s(dQ,"baseIsNative"),L(dQ,"baseIsNative"),yKe=dQ,s(pQ,"getValue"),L(pQ,"getValue"),vKe=pQ,s(fQ,"getNative"),L(fQ,"getNative"),a0=fQ,bKe=a0(ru,"WeakMap"),lL=bKe,dse=Object.create,xKe=(function(){function q(){}return s(q,"object"),L(q,"object"),function(T){if(!Nl(T))return{};if(dse)return dse(T);q.prototype=T;var A=new q;return q.prototype=void 0,A}})(),TKe=xKe,s(gQ,"apply"),L(gQ,"apply"),kKe=gQ,s(mQ,"noop"),L(mQ,"noop"),ps=mQ,s(yQ,"copyArray"),L(yQ,"copyArray"),CKe=yQ,wKe=800,EKe=16,SKe=Date.now,s(vQ,"shortOut"),L(vQ,"shortOut"),_Ke=vQ,s(bQ,"constant"),L(bQ,"constant"),AKe=bQ,RKe=(function(){try{var q=a0(Object,"defineProperty");return q({},"",{}),q}catch{}})(),sw=RKe,$Ke=sw?function(q,T){return sw(q,"toString",{configurable:!0,enumerable:!1,value:AKe(T),writable:!0})}:ob,LKe=$Ke,IKe=_Ke(LKe),MKe=IKe,s(xQ,"arrayEach"),L(xQ,"arrayEach"),pse=xQ,s(TQ,"baseFindIndex"),L(TQ,"baseFindIndex"),fse=TQ,s(kQ,"baseIsNaN"),L(kQ,"baseIsNaN"),DKe=kQ,s(CQ,"strictIndexOf"),L(CQ,"strictIndexOf"),NKe=CQ,s(wQ,"baseIndexOf"),L(wQ,"baseIndexOf"),cL=wQ,s(EQ,"arrayIncludes"),L(EQ,"arrayIncludes"),gse=EQ,OKe=9007199254740991,BKe=/^(?:0|[1-9]\d*)$/,s(SQ,"isIndex"),L(SQ,"isIndex"),ow=SQ,s(_Q,"baseAssignValue"),L(_Q,"baseAssignValue"),uL=_Q,s(AQ,"eq"),L(AQ,"eq"),lb=AQ,PKe=Object.prototype,FKe=PKe.hasOwnProperty,s(RQ,"assignValue"),L(RQ,"assignValue"),lw=RQ,s($Q,"copyObject"),L($Q,"copyObject"),cb=$Q,mse=Math.max,s(LQ,"overRest"),L(LQ,"overRest"),zKe=LQ,s(IQ,"baseRest"),L(IQ,"baseRest"),hL=IQ,qKe=9007199254740991,s(MQ,"isLength"),L(MQ,"isLength"),dL=MQ,s(DQ,"isArrayLike"),L(DQ,"isArrayLike"),iu=DQ,s(NQ,"isIterateeCall"),L(NQ,"isIterateeCall"),cw=NQ,s(OQ,"createAssigner"),L(OQ,"createAssigner"),WKe=OQ,jKe=Object.prototype,s(BQ,"isPrototype"),L(BQ,"isPrototype"),ub=BQ,s(PQ,"baseTimes"),L(PQ,"baseTimes"),VKe=PQ,UKe="[object Arguments]",s(FQ,"baseIsArguments"),L(FQ,"baseIsArguments"),yse=FQ,vse=Object.prototype,YKe=vse.hasOwnProperty,HKe=vse.propertyIsEnumerable,GKe=yse((function(){return arguments})())?yse:function(q){return pc(q)&&YKe.call(q,"callee")&&!HKe.call(q,"callee")},uw=GKe,s(zQ,"stubFalse"),L(zQ,"stubFalse"),XKe=zQ,bse=typeof exports=="object"&&exports&&!exports.nodeType&&exports,xse=bse&&typeof module=="object"&&module&&!module.nodeType&&module,KKe=xse&&xse.exports===bse,Tse=KKe?ru.Buffer:void 0,QKe=Tse?Tse.isBuffer:void 0,ZKe=QKe||XKe,hb=ZKe,JKe="[object Arguments]",eQe="[object Array]",tQe="[object Boolean]",rQe="[object Date]",iQe="[object Error]",nQe="[object Function]",aQe="[object Map]",sQe="[object Number]",oQe="[object Object]",lQe="[object RegExp]",cQe="[object Set]",uQe="[object String]",hQe="[object WeakMap]",dQe="[object ArrayBuffer]",pQe="[object DataView]",fQe="[object Float32Array]",gQe="[object Float64Array]",mQe="[object Int8Array]",yQe="[object Int16Array]",vQe="[object Int32Array]",bQe="[object Uint8Array]",xQe="[object Uint8ClampedArray]",TQe="[object Uint16Array]",kQe="[object Uint32Array]",Bn={},Bn[fQe]=Bn[gQe]=Bn[mQe]=Bn[yQe]=Bn[vQe]=Bn[bQe]=Bn[xQe]=Bn[TQe]=Bn[kQe]=!0,Bn[JKe]=Bn[eQe]=Bn[dQe]=Bn[tQe]=Bn[pQe]=Bn[rQe]=Bn[iQe]=Bn[nQe]=Bn[aQe]=Bn[sQe]=Bn[oQe]=Bn[lQe]=Bn[cQe]=Bn[uQe]=Bn[hQe]=!1,s(qQ,"baseIsTypedArray"),L(qQ,"baseIsTypedArray"),CQe=qQ,s(WQ,"baseUnary"),L(WQ,"baseUnary"),db=WQ,kse=typeof exports=="object"&&exports&&!exports.nodeType&&exports,pb=kse&&typeof module=="object"&&module&&!module.nodeType&&module,wQe=pb&&pb.exports===kse,pL=wQe&&nse.process,EQe=(function(){try{var q=pb&&pb.require&&pb.require("util").types;return q||pL&&pL.binding&&pL.binding("util")}catch{}})(),Zd=EQe,Cse=Zd&&Zd.isTypedArray,SQe=Cse?db(Cse):CQe,fL=SQe,_Qe=Object.prototype,AQe=_Qe.hasOwnProperty,s(jQ,"arrayLikeKeys"),L(jQ,"arrayLikeKeys"),wse=jQ,s(VQ,"overArg"),L(VQ,"overArg"),Ese=VQ,RQe=Ese(Object.keys,Object),$Qe=RQe,LQe=Object.prototype,IQe=LQe.hasOwnProperty,s(UQ,"baseKeys"),L(UQ,"baseKeys"),Sse=UQ,s(YQ,"keys"),L(YQ,"keys"),dl=YQ,MQe=Object.prototype,DQe=MQe.hasOwnProperty,NQe=WKe(function(q,T){if(ub(T)||iu(T)){cb(T,dl(T),q);return}for(var A in T)DQe.call(T,A)&&lw(q,A,T[A])}),pl=NQe,s(HQ,"nativeKeysIn"),L(HQ,"nativeKeysIn"),OQe=HQ,BQe=Object.prototype,PQe=BQe.hasOwnProperty,s(GQ,"baseKeysIn"),L(GQ,"baseKeysIn"),FQe=GQ,s(XQ,"keysIn"),L(XQ,"keysIn"),hw=XQ,zQe=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,qQe=/^\w*$/,s(KQ,"isKey"),L(KQ,"isKey"),gL=KQ,WQe=a0(Object,"create"),fb=WQe,s(QQ,"hashClear"),L(QQ,"hashClear"),jQe=QQ,s(ZQ,"hashDelete"),L(ZQ,"hashDelete"),VQe=ZQ,UQe="__lodash_hash_undefined__",YQe=Object.prototype,HQe=YQe.hasOwnProperty,s(JQ,"hashGet"),L(JQ,"hashGet"),GQe=JQ,XQe=Object.prototype,KQe=XQe.hasOwnProperty,s(eZ,"hashHas"),L(eZ,"hashHas"),QQe=eZ,ZQe="__lodash_hash_undefined__",s(tZ,"hashSet"),L(tZ,"hashSet"),JQe=tZ,s(If,"Hash"),L(If,"Hash"),If.prototype.clear=jQe,If.prototype.delete=VQe,If.prototype.get=GQe,If.prototype.has=QQe,If.prototype.set=JQe,_se=If,s(rZ,"listCacheClear"),L(rZ,"listCacheClear"),eZe=rZ,s(iZ,"assocIndexOf"),L(iZ,"assocIndexOf"),dw=iZ,tZe=Array.prototype,rZe=tZe.splice,s(nZ,"listCacheDelete"),L(nZ,"listCacheDelete"),iZe=nZ,s(aZ,"listCacheGet"),L(aZ,"listCacheGet"),nZe=aZ,s(sZ,"listCacheHas"),L(sZ,"listCacheHas"),aZe=sZ,s(oZ,"listCacheSet"),L(oZ,"listCacheSet"),sZe=oZ,s(Mf,"ListCache"),L(Mf,"ListCache"),Mf.prototype.clear=eZe,Mf.prototype.delete=iZe,Mf.prototype.get=nZe,Mf.prototype.has=aZe,Mf.prototype.set=sZe,pw=Mf,oZe=a0(ru,"Map"),gb=oZe,s(lZ,"mapCacheClear"),L(lZ,"mapCacheClear"),lZe=lZ,s(cZ,"isKeyable"),L(cZ,"isKeyable"),cZe=cZ,s(uZ,"getMapData"),L(uZ,"getMapData"),fw=uZ,s(hZ,"mapCacheDelete"),L(hZ,"mapCacheDelete"),uZe=hZ,s(dZ,"mapCacheGet"),L(dZ,"mapCacheGet"),hZe=dZ,s(pZ,"mapCacheHas"),L(pZ,"mapCacheHas"),dZe=pZ,s(fZ,"mapCacheSet"),L(fZ,"mapCacheSet"),pZe=fZ,s(Df,"MapCache"),L(Df,"MapCache"),Df.prototype.clear=lZe,Df.prototype.delete=uZe,Df.prototype.get=hZe,Df.prototype.has=dZe,Df.prototype.set=pZe,gw=Df,fZe="Expected a function",s(eC,"memoize"),L(eC,"memoize"),eC.Cache=gw,gZe=eC,mZe=500,s(gZ,"memoizeCapped"),L(gZ,"memoizeCapped"),yZe=gZ,vZe=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,bZe=/\\(\\)?/g,xZe=yZe(function(q){var T=[];return q.charCodeAt(0)===46&&T.push(""),q.replace(vZe,function(A,D,P,V){T.push(P?V.replace(bZe,"$1"):D||A)}),T}),TZe=xZe,s(mZ,"toString2"),L(mZ,"toString"),kZe=mZ,s(yZ,"castPath"),L(yZ,"castPath"),mw=yZ,CZe=1/0,s(vZ,"toKey"),L(vZ,"toKey"),mb=vZ,s(bZ,"baseGet"),L(bZ,"baseGet"),mL=bZ,s(xZ,"get"),L(xZ,"get"),wZe=xZ,s(TZ,"arrayPush"),L(TZ,"arrayPush"),yL=TZ,Ase=Dl?Dl.isConcatSpreadable:void 0,s(kZ,"isFlattenable"),L(kZ,"isFlattenable"),EZe=kZ,s(IR,"baseFlatten"),L(IR,"baseFlatten"),vL=IR,s(CZ,"flatten"),L(CZ,"flatten"),fc=CZ,SZe=Ese(Object.getPrototypeOf,Object),Rse=SZe,s(wZ,"baseSlice"),L(wZ,"baseSlice"),$se=wZ,s(EZ,"arrayReduce"),L(EZ,"arrayReduce"),_Ze=EZ,s(SZ,"stackClear"),L(SZ,"stackClear"),AZe=SZ,s(_Z,"stackDelete"),L(_Z,"stackDelete"),RZe=_Z,s(AZ,"stackGet"),L(AZ,"stackGet"),$Ze=AZ,s(RZ,"stackHas"),L(RZ,"stackHas"),LZe=RZ,IZe=200,s($Z,"stackSet"),L($Z,"stackSet"),MZe=$Z,s(Nf,"Stack"),L(Nf,"Stack"),Nf.prototype.clear=AZe,Nf.prototype.delete=RZe,Nf.prototype.get=$Ze,Nf.prototype.has=LZe,Nf.prototype.set=MZe,yb=Nf,s(LZ,"baseAssign"),L(LZ,"baseAssign"),DZe=LZ,s(IZ,"baseAssignIn"),L(IZ,"baseAssignIn"),NZe=IZ,Lse=typeof exports=="object"&&exports&&!exports.nodeType&&exports,Ise=Lse&&typeof module=="object"&&module&&!module.nodeType&&module,OZe=Ise&&Ise.exports===Lse,Mse=OZe?ru.Buffer:void 0,Dse=Mse?Mse.allocUnsafe:void 0,s(MZ,"cloneBuffer"),L(MZ,"cloneBuffer"),BZe=MZ,s(DZ,"arrayFilter"),L(DZ,"arrayFilter"),bL=DZ,s(NZ,"stubArray"),L(NZ,"stubArray"),Nse=NZ,PZe=Object.prototype,FZe=PZe.propertyIsEnumerable,Ose=Object.getOwnPropertySymbols,zZe=Ose?function(q){return q==null?[]:(q=Object(q),bL(Ose(q),function(T){return FZe.call(q,T)}))}:Nse,xL=zZe,s(OZ,"copySymbols"),L(OZ,"copySymbols"),qZe=OZ,WZe=Object.getOwnPropertySymbols,jZe=WZe?function(q){for(var T=[];q;)yL(T,xL(q)),q=Rse(q);return T}:Nse,Bse=jZe,s(BZ,"copySymbolsIn"),L(BZ,"copySymbolsIn"),VZe=BZ,s(PZ,"baseGetAllKeys"),L(PZ,"baseGetAllKeys"),Pse=PZ,s(FZ,"getAllKeys"),L(FZ,"getAllKeys"),TL=FZ,s(zZ,"getAllKeysIn"),L(zZ,"getAllKeysIn"),Fse=zZ,UZe=a0(ru,"DataView"),kL=UZe,YZe=a0(ru,"Promise"),CL=YZe,HZe=a0(ru,"Set"),Xm=HZe,zse="[object Map]",GZe="[object Object]",qse="[object Promise]",Wse="[object Set]",jse="[object WeakMap]",Vse="[object DataView]",XZe=n0(kL),KZe=n0(gb),QZe=n0(CL),ZZe=n0(Xm),JZe=n0(lL),s0=Qd,(kL&&s0(new kL(new ArrayBuffer(1)))!=Vse||gb&&s0(new gb)!=zse||CL&&s0(CL.resolve())!=qse||Xm&&s0(new Xm)!=Wse||lL&&s0(new lL)!=jse)&&(s0=L(function(q){var T=Qd(q),A=T==GZe?q.constructor:void 0,D=A?n0(A):"";if(D)switch(D){case XZe:return Vse;case KZe:return zse;case QZe:return qse;case ZZe:return Wse;case JZe:return jse}return T},"getTag")),Km=s0,eJe=Object.prototype,tJe=eJe.hasOwnProperty,s(qZ,"initCloneArray"),L(qZ,"initCloneArray"),rJe=qZ,iJe=ru.Uint8Array,yw=iJe,s(WZ,"cloneArrayBuffer"),L(WZ,"cloneArrayBuffer"),wL=WZ,s(jZ,"cloneDataView"),L(jZ,"cloneDataView"),nJe=jZ,aJe=/\w*$/,s(VZ,"cloneRegExp"),L(VZ,"cloneRegExp"),sJe=VZ,Use=Dl?Dl.prototype:void 0,Yse=Use?Use.valueOf:void 0,s(UZ,"cloneSymbol"),L(UZ,"cloneSymbol"),oJe=UZ,s(YZ,"cloneTypedArray"),L(YZ,"cloneTypedArray"),lJe=YZ,cJe="[object Boolean]",uJe="[object Date]",hJe="[object Map]",dJe="[object Number]",pJe="[object RegExp]",fJe="[object Set]",gJe="[object String]",mJe="[object Symbol]",yJe="[object ArrayBuffer]",vJe="[object DataView]",bJe="[object Float32Array]",xJe="[object Float64Array]",TJe="[object Int8Array]",kJe="[object Int16Array]",CJe="[object Int32Array]",wJe="[object Uint8Array]",EJe="[object Uint8ClampedArray]",SJe="[object Uint16Array]",_Je="[object Uint32Array]",s(HZ,"initCloneByTag"),L(HZ,"initCloneByTag"),AJe=HZ,s(GZ,"initCloneObject"),L(GZ,"initCloneObject"),RJe=GZ,$Je="[object Map]",s(XZ,"baseIsMap"),L(XZ,"baseIsMap"),LJe=XZ,Hse=Zd&&Zd.isMap,IJe=Hse?db(Hse):LJe,MJe=IJe,DJe="[object Set]",s(KZ,"baseIsSet"),L(KZ,"baseIsSet"),NJe=KZ,Gse=Zd&&Zd.isSet,OJe=Gse?db(Gse):NJe,BJe=OJe,PJe=1,FJe=2,zJe=4,Xse="[object Arguments]",qJe="[object Array]",WJe="[object Boolean]",jJe="[object Date]",VJe="[object Error]",Kse="[object Function]",UJe="[object GeneratorFunction]",YJe="[object Map]",HJe="[object Number]",Qse="[object Object]",GJe="[object RegExp]",XJe="[object Set]",KJe="[object String]",QJe="[object Symbol]",ZJe="[object WeakMap]",JJe="[object ArrayBuffer]",eet="[object DataView]",tet="[object Float32Array]",ret="[object Float64Array]",iet="[object Int8Array]",net="[object Int16Array]",aet="[object Int32Array]",set="[object Uint8Array]",oet="[object Uint8ClampedArray]",cet="[object Uint16Array]",uet="[object Uint32Array]",wn={},wn[Xse]=wn[qJe]=wn[JJe]=wn[eet]=wn[WJe]=wn[jJe]=wn[tet]=wn[ret]=wn[iet]=wn[net]=wn[aet]=wn[YJe]=wn[HJe]=wn[Qse]=wn[GJe]=wn[XJe]=wn[KJe]=wn[QJe]=wn[set]=wn[oet]=wn[cet]=wn[uet]=!0,wn[VJe]=wn[Kse]=wn[ZJe]=!1,s(Sv,"baseClone"),L(Sv,"baseClone"),het=Sv,det=4,s(QZ,"clone"),L(QZ,"clone"),_s=QZ,s(ZZ,"compact"),L(ZZ,"compact"),vb=ZZ,pet="__lodash_hash_undefined__",s(JZ,"setCacheAdd"),L(JZ,"setCacheAdd"),fet=JZ,s(eJ,"setCacheHas"),L(eJ,"setCacheHas"),get=eJ,s(_v,"SetCache"),L(_v,"SetCache"),_v.prototype.add=_v.prototype.push=fet,_v.prototype.has=get,EL=_v,s(tJ,"arraySome"),L(tJ,"arraySome"),Zse=tJ,s(rJ,"cacheHas"),L(rJ,"cacheHas"),SL=rJ,met=1,yet=2,s(iJ,"equalArrays"),L(iJ,"equalArrays"),Jse=iJ,s(nJ,"mapToArray"),L(nJ,"mapToArray"),vet=nJ,s(aJ,"setToArray"),L(aJ,"setToArray"),_L=aJ,bet=1,xet=2,Tet="[object Boolean]",ket="[object Date]",Cet="[object Error]",wet="[object Map]",Eet="[object Number]",_et="[object RegExp]",Aet="[object Set]",Ret="[object String]",$et="[object Symbol]",Let="[object ArrayBuffer]",Iet="[object DataView]",eoe=Dl?Dl.prototype:void 0,AL=eoe?eoe.valueOf:void 0,s(sJ,"equalByTag"),L(sJ,"equalByTag"),Met=sJ,Det=1,Net=Object.prototype,Oet=Net.hasOwnProperty,s(oJ,"equalObjects"),L(oJ,"equalObjects"),Bet=oJ,Pet=1,toe="[object Arguments]",roe="[object Array]",vw="[object Object]",Fet=Object.prototype,ioe=Fet.hasOwnProperty,s(lJ,"baseIsEqualDeep"),L(lJ,"baseIsEqualDeep"),zet=lJ,s(MR,"baseIsEqual"),L(MR,"baseIsEqual"),noe=MR,qet=1,Wet=2,s(cJ,"baseIsMatch"),L(cJ,"baseIsMatch"),jet=cJ,s(uJ,"isStrictComparable"),L(uJ,"isStrictComparable"),aoe=uJ,s(hJ,"getMatchData"),L(hJ,"getMatchData"),Vet=hJ,s(dJ,"matchesStrictComparable"),L(dJ,"matchesStrictComparable"),soe=dJ,s(pJ,"baseMatches"),L(pJ,"baseMatches"),Uet=pJ,s(fJ,"baseHasIn"),L(fJ,"baseHasIn"),Yet=fJ,s(gJ,"hasPath"),L(gJ,"hasPath"),ooe=gJ,s(mJ,"hasIn"),L(mJ,"hasIn"),Het=mJ,Get=1,Xet=2,s(yJ,"baseMatchesProperty"),L(yJ,"baseMatchesProperty"),Ket=yJ,s(vJ,"baseProperty"),L(vJ,"baseProperty"),Qet=vJ,s(bJ,"basePropertyDeep"),L(bJ,"basePropertyDeep"),Zet=bJ,s(xJ,"property"),L(xJ,"property"),Jet=xJ,s(TJ,"baseIteratee"),L(TJ,"baseIteratee"),nu=TJ,s(kJ,"arrayAggregator"),L(kJ,"arrayAggregator"),ett=kJ,s(CJ,"createBaseFor"),L(CJ,"createBaseFor"),ttt=CJ,rtt=ttt(),itt=rtt,s(wJ,"baseForOwn"),L(wJ,"baseForOwn"),ntt=wJ,s(EJ,"createBaseEach"),L(EJ,"createBaseEach"),att=EJ,stt=att(ntt),o0=stt,s(SJ,"baseAggregator"),L(SJ,"baseAggregator"),ott=SJ,s(_J,"createAggregator"),L(_J,"createAggregator"),ltt=_J,loe=Object.prototype,ctt=loe.hasOwnProperty,utt=hL(function(q,T){q=Object(q);var A=-1,D=T.length,P=D>2?T[2]:void 0;for(P&&cw(T[0],T[1],P)&&(D=1);++A{A.accept(T)})}},s(u,"AbstractProduction"),L(u,"AbstractProduction"),u),vo=(h=class extends au{constructor(T){super([]),this.idx=1,pl(this,yc(T,A=>A!==void 0))}set definition(T){}get definition(){return this.referencedRule!==void 0?this.referencedRule.definition:[]}accept(T){T.visit(this)}},s(h,"NonTerminal"),L(h,"NonTerminal"),h),Zm=(d=class extends au{constructor(T){super(T.definition),this.orgText="",pl(this,yc(T,A=>A!==void 0))}},s(d,"Rule"),L(d,"Rule"),d),qo=(p=class extends au{constructor(T){super(T.definition),this.ignoreAmbiguities=!1,pl(this,yc(T,A=>A!==void 0))}},s(p,"Alternative"),L(p,"Alternative"),p),Rs=(f=class extends au{constructor(T){super(T.definition),this.idx=1,pl(this,yc(T,A=>A!==void 0))}},s(f,"Option"),L(f,"Option"),f),gl=(g=class extends au{constructor(T){super(T.definition),this.idx=1,pl(this,yc(T,A=>A!==void 0))}},s(g,"RepetitionMandatory"),L(g,"RepetitionMandatory"),g),ml=(m=class extends au{constructor(T){super(T.definition),this.idx=1,pl(this,yc(T,A=>A!==void 0))}},s(m,"RepetitionMandatoryWithSeparator"),L(m,"RepetitionMandatoryWithSeparator"),m),la=(y=class extends au{constructor(T){super(T.definition),this.idx=1,pl(this,yc(T,A=>A!==void 0))}},s(y,"Repetition"),L(y,"Repetition"),y),Wo=(k=class extends au{constructor(T){super(T.definition),this.idx=1,pl(this,yc(T,A=>A!==void 0))}},s(k,"RepetitionWithSeparator"),L(k,"RepetitionWithSeparator"),k),jo=(C=class extends au{get definition(){return this._definition}set definition(T){this._definition=T}constructor(T){super(T.definition),this.idx=1,this.ignoreAmbiguities=!1,this.hasPredicates=!1,pl(this,yc(T,A=>A!==void 0))}},s(C,"Alternation"),L(C,"Alternation"),C),Pn=(S=class{constructor(T){this.idx=1,pl(this,yc(T,A=>A!==void 0))}accept(T){T.visit(this)}},s(S,"Terminal"),L(S,"Terminal"),S),s(mee,"serializeGrammar"),L(mee,"serializeGrammar"),s(Av,"serializeProduction"),L(Av,"serializeProduction"),Jm=(E=class{visit(T){let A=T;switch(A.constructor){case vo:return this.visitNonTerminal(A);case qo:return this.visitAlternative(A);case Rs:return this.visitOption(A);case gl:return this.visitRepetitionMandatory(A);case ml:return this.visitRepetitionMandatoryWithSeparator(A);case Wo:return this.visitRepetitionWithSeparator(A);case la:return this.visitRepetition(A);case jo:return this.visitAlternation(A);case Pn:return this.visitTerminal(A);case Zm:return this.visitRule(A);default:throw Error("non exhaustive match")}}visitNonTerminal(T){}visitAlternative(T){}visitOption(T){}visitRepetition(T){}visitRepetitionMandatory(T){}visitRepetitionMandatoryWithSeparator(T){}visitRepetitionWithSeparator(T){}visitAlternation(T){}visitTerminal(T){}visitRule(T){}},s(E,"GAstVisitor"),L(E,"GAstVisitor"),E),s(yee,"isSequenceProd"),L(yee,"isSequenceProd"),s(Rv,"isOptionalProd"),L(Rv,"isOptionalProd"),s(vee,"isBranchingProd"),L(vee,"isBranchingProd"),s(uc,"getProductionDslName"),L(uc,"getProductionDslName"),Tw=(_=class{walk(T,A=[]){ei(T.definition,(D,P)=>{let V=As(T.definition,P+1);if(D instanceof vo)this.walkProdRef(D,V,A);else if(D instanceof Pn)this.walkTerminal(D,V,A);else if(D instanceof qo)this.walkFlat(D,V,A);else if(D instanceof Rs)this.walkOption(D,V,A);else if(D instanceof gl)this.walkAtLeastOne(D,V,A);else if(D instanceof ml)this.walkAtLeastOneSep(D,V,A);else if(D instanceof Wo)this.walkManySep(D,V,A);else if(D instanceof la)this.walkMany(D,V,A);else if(D instanceof jo)this.walkOr(D,V,A);else throw Error("non exhaustive match")})}walkTerminal(T,A,D){}walkProdRef(T,A,D){}walkFlat(T,A,D){let P=A.concat(D);this.walk(T,P)}walkOption(T,A,D){let P=A.concat(D);this.walk(T,P)}walkAtLeastOne(T,A,D){let P=[new Rs({definition:T.definition})].concat(A,D);this.walk(T,P)}walkAtLeastOneSep(T,A,D){let P=BR(T,A,D);this.walk(T,P)}walkMany(T,A,D){let P=[new Rs({definition:T.definition})].concat(A,D);this.walk(T,P)}walkManySep(T,A,D){let P=BR(T,A,D);this.walk(T,P)}walkOr(T,A,D){let P=A.concat(D);ei(T.definition,V=>{let Z=new qo({definition:[V]});this.walk(Z,P)})}},s(_,"RestWalker"),L(_,"RestWalker"),_),s(BR,"restForRepetitionWithSeparator"),L(BR,"restForRepetitionWithSeparator"),s($m,"first"),L($m,"first"),s(bee,"firstForSequence"),L(bee,"firstForSequence"),s(xee,"firstForBranching"),L(xee,"firstForBranching"),s(Tee,"firstForTerminal"),L(Tee,"firstForTerminal"),goe="_~IN~_",Qtt=(b=class extends Tw{constructor(T){super(),this.topProd=T,this.follows={}}startWalking(){return this.walk(this.topProd),this.follows}walkTerminal(T,A,D){}walkProdRef(T,A,D){let P=Cee(T.referencedRule,T.idx)+this.topProd.name,V=A.concat(D),Z=new qo({definition:V}),re=$m(Z);this.follows[P]=re}},s(b,"ResyncFollowsWalker"),L(b,"ResyncFollowsWalker"),b),s(kee,"computeAllProdsFollows"),L(kee,"computeAllProdsFollows"),s(Cee,"buildBetweenProdsFollowPrefix"),L(Cee,"buildBetweenProdsFollowPrefix"),kw={},Ztt=new ese,s($v,"getRegExpAst"),L($v,"getRegExpAst"),s(wee,"clearRegExpParserCache"),L(wee,"clearRegExpParserCache"),moe="Complement Sets are not supported for first char optimization",Cw=`Unable to use "first char" lexer optimizations: +`,s(Eee,"getOptimizedStartCodesIndices"),L(Eee,"getOptimizedStartCodesIndices"),s(rC,"firstCharOptimizedIndices"),L(rC,"firstCharOptimizedIndices"),s(Lv,"addOptimizedIdxToResult"),L(Lv,"addOptimizedIdxToResult"),s(See,"handleIgnoreCase"),L(See,"handleIgnoreCase"),s(PR,"findCode"),L(PR,"findCode"),s(iC,"isWholeOptional"),L(iC,"isWholeOptional"),Jtt=(v=class extends nw{constructor(T){super(),this.targetCharCodes=T,this.found=!1}visitChildren(T){if(this.found!==!0){switch(T.type){case"Lookahead":this.visitLookahead(T);return;case"NegativeLookahead":this.visitNegativeLookahead(T);return;case"Lookbehind":this.visitLookbehind(T);return;case"NegativeLookbehind":this.visitNegativeLookbehind(T);return}super.visitChildren(T)}}visitCharacter(T){yo(this.targetCharCodes,T.value)&&(this.found=!0)}visitSet(T){T.complement?PR(T,this.targetCharCodes)===void 0&&(this.found=!0):PR(T,this.targetCharCodes)!==void 0&&(this.found=!0)}},s(v,"CharCodeFinder"),L(v,"CharCodeFinder"),v),s(nC,"canMatchCharCode"),L(nC,"canMatchCharCode"),c0="PATTERN",xb="defaultMode",ww="modes",s(_ee,"analyzeTokenTypes"),L(_ee,"analyzeTokenTypes"),s(Aee,"validatePatterns"),L(Aee,"validatePatterns"),s(Ree,"validateRegExpPattern"),L(Ree,"validateRegExpPattern"),s($ee,"findMissingPatterns"),L($ee,"findMissingPatterns"),s(Lee,"findInvalidPatterns"),L(Lee,"findInvalidPatterns"),ert=/[^\\][$]/,s(Iee,"findEndOfInputAnchor"),L(Iee,"findEndOfInputAnchor"),s(Mee,"findEmptyMatchRegExps"),L(Mee,"findEmptyMatchRegExps"),trt=/[^\\[][\^]|^\^/,s(Dee,"findStartOfInputAnchor"),L(Dee,"findStartOfInputAnchor"),s(Nee,"findUnsupportedFlags"),L(Nee,"findUnsupportedFlags"),s(Oee,"findDuplicatePatterns"),L(Oee,"findDuplicatePatterns"),s(Bee,"findInvalidGroupType"),L(Bee,"findInvalidGroupType"),s(Pee,"findModesThatDoNotExist"),L(Pee,"findModesThatDoNotExist"),s(Fee,"findUnreachablePatterns"),L(Fee,"findUnreachablePatterns"),s(zee,"tryToMatchStrToPattern"),L(zee,"tryToMatchStrToPattern"),s(qee,"noMetaChar"),L(qee,"noMetaChar"),s(Wee,"usesLookAheadOrBehind"),L(Wee,"usesLookAheadOrBehind"),s(FR,"addStickyFlag"),L(FR,"addStickyFlag"),s(jee,"performRuntimeChecks"),L(jee,"performRuntimeChecks"),s(Vee,"performWarningRuntimeChecks"),L(Vee,"performWarningRuntimeChecks"),s(Uee,"cloneEmptyGroups"),L(Uee,"cloneEmptyGroups"),s(zR,"isCustomPattern"),L(zR,"isCustomPattern"),s(Yee,"isShortPattern"),L(Yee,"isShortPattern"),rrt={test:L(function(q){let T=q.length;for(let A=this.lastIndex;A${q.image}<- The Mode Stack is empty`},buildUnexpectedCharactersMessage(q,T,A,D,P,V){return`unexpected character: ->${q.charAt(T)}<- at offset: ${T}, skipped ${A} characters.`}},(function(q){q[q.MISSING_PATTERN=0]="MISSING_PATTERN",q[q.INVALID_PATTERN=1]="INVALID_PATTERN",q[q.EOI_ANCHOR_FOUND=2]="EOI_ANCHOR_FOUND",q[q.UNSUPPORTED_FLAGS_FOUND=3]="UNSUPPORTED_FLAGS_FOUND",q[q.DUPLICATE_PATTERNS_FOUND=4]="DUPLICATE_PATTERNS_FOUND",q[q.INVALID_GROUP_TYPE_FOUND=5]="INVALID_GROUP_TYPE_FOUND",q[q.PUSH_MODE_DOES_NOT_EXIST=6]="PUSH_MODE_DOES_NOT_EXIST",q[q.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE=7]="MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE",q[q.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY=8]="MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY",q[q.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST=9]="MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST",q[q.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED=10]="LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED",q[q.SOI_ANCHOR_FOUND=11]="SOI_ANCHOR_FOUND",q[q.EMPTY_MATCH_PATTERN=12]="EMPTY_MATCH_PATTERN",q[q.NO_LINE_BREAKS_FLAGS=13]="NO_LINE_BREAKS_FLAGS",q[q.UNREACHABLE_PATTERN=14]="UNREACHABLE_PATTERN",q[q.IDENTIFY_TERMINATOR=15]="IDENTIFY_TERMINATOR",q[q.CUSTOM_LINE_BREAK=16]="CUSTOM_LINE_BREAK",q[q.MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE=17]="MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE"})(xa||(xa={})),kb={deferDefinitionErrorsHandling:!1,positionTracking:"full",lineTerminatorsPattern:/\n|\r\n?/g,lineTerminatorCharacters:[` +`,"\r"],ensureOptimizations:!1,safeMode:!1,errorMessageProvider:LL,traceInitPerf:!1,skipValidations:!1,recoveryEnabled:!0},Object.freeze(kb),bo=(x=class{constructor(T,A=kb){if(this.lexerDefinition=T,this.lexerDefinitionErrors=[],this.lexerDefinitionWarning=[],this.patternIdxToConfig={},this.charCodeToPatternIdxToConfig={},this.modes=[],this.emptyGroups={},this.trackStartLines=!0,this.trackEndLines=!0,this.hasCustom=!1,this.canModeBeOptimized={},this.TRACE_INIT=(P,V)=>{if(this.traceInitPerf===!0){this.traceInitIndent++;let Z=new Array(this.traceInitIndent+1).join(" ");this.traceInitIndent <${P}>`);let{time:re,value:Q}=NR(V),ae=re>10?console.warn:console.log;return this.traceInitIndent time: ${re}ms`),this.traceInitIndent--,Q}else return V()},typeof A=="boolean")throw Error(`The second argument to the Lexer constructor is now an ILexerConfig Object. +a boolean 2nd argument is no longer supported`);this.config=pl({},kb,A);let D=this.config.traceInitPerf;D===!0?(this.traceInitMaxIdent=1/0,this.traceInitPerf=!0):typeof D=="number"&&(this.traceInitMaxIdent=D,this.traceInitPerf=!0),this.traceInitIndent=-1,this.TRACE_INIT("Lexer Constructor",()=>{let P,V=!0;this.TRACE_INIT("Lexer Config handling",()=>{if(this.config.lineTerminatorsPattern===kb.lineTerminatorsPattern)this.config.lineTerminatorsPattern=rrt;else if(this.config.lineTerminatorCharacters===kb.lineTerminatorCharacters)throw Error(`Error: Missing property on the Lexer config. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#MISSING_LINE_TERM_CHARS`);if(A.safeMode&&A.ensureOptimizations)throw Error('"safeMode" and "ensureOptimizations" flags are mutually exclusive.');this.trackStartLines=/full|onlyStart/i.test(this.config.positionTracking),this.trackEndLines=/full/i.test(this.config.positionTracking),qi(T)?P={modes:{defaultMode:_s(T)},defaultMode:xb}:(V=!1,P=_s(T))}),this.config.skipValidations===!1&&(this.TRACE_INIT("performRuntimeChecks",()=>{this.lexerDefinitionErrors=this.lexerDefinitionErrors.concat(jee(P,this.trackStartLines,this.config.lineTerminatorCharacters))}),this.TRACE_INIT("performWarningRuntimeChecks",()=>{this.lexerDefinitionWarning=this.lexerDefinitionWarning.concat(Vee(P,this.trackStartLines,this.config.lineTerminatorCharacters))})),P.modes=P.modes?P.modes:{},ei(P.modes,(re,Q)=>{P.modes[Q]=xw(re,ae=>bh(ae))});let Z=dl(P.modes);if(ei(P.modes,(re,Q)=>{this.TRACE_INIT(`Mode: <${Q}> processing`,()=>{if(this.modes.push(Q),this.config.skipValidations===!1&&this.TRACE_INIT("validatePatterns",()=>{this.lexerDefinitionErrors=this.lexerDefinitionErrors.concat(Aee(re,Z))}),En(this.lexerDefinitionErrors)){Im(re);let ae;this.TRACE_INIT("analyzeTokenTypes",()=>{ae=_ee(re,{lineTerminatorCharacters:this.config.lineTerminatorCharacters,positionTracking:A.positionTracking,ensureOptimizations:A.ensureOptimizations,safeMode:A.safeMode,tracer:this.TRACE_INIT})}),this.patternIdxToConfig[Q]=ae.patternIdxToConfig,this.charCodeToPatternIdxToConfig[Q]=ae.charCodeToPatternIdxToConfig,this.emptyGroups=pl({},this.emptyGroups,ae.emptyGroups),this.hasCustom=ae.hasCustom||this.hasCustom,this.canModeBeOptimized[Q]=ae.canBeOptimized}})}),this.defaultMode=P.defaultMode,!En(this.lexerDefinitionErrors)&&!this.config.deferDefinitionErrorsHandling){let re=Fr(this.lexerDefinitionErrors,Q=>Q.message).join(`----------------------- +`);throw new Error(`Errors detected in definition of Lexer: +`+re)}ei(this.lexerDefinitionWarning,re=>{DR(re.message)}),this.TRACE_INIT("Choosing sub-methods implementations",()=>{if(V&&(this.handleModes=ps),this.trackStartLines===!1&&(this.computeNewColumn=ob),this.trackEndLines===!1&&(this.updateTokenEndLineColumnLocation=ps),/full/i.test(this.config.positionTracking))this.createTokenInstance=this.createFullToken;else if(/onlyStart/i.test(this.config.positionTracking))this.createTokenInstance=this.createStartOnlyToken;else if(/onlyOffset/i.test(this.config.positionTracking))this.createTokenInstance=this.createOffsetOnlyToken;else throw Error(`Invalid config option: "${this.config.positionTracking}"`);this.hasCustom?(this.addToken=this.addTokenUsingPush,this.handlePayload=this.handlePayloadWithCustom):(this.addToken=this.addTokenUsingMemberAccess,this.handlePayload=this.handlePayloadNoCustom)}),this.TRACE_INIT("Failed Optimization Warnings",()=>{let re=fl(this.canModeBeOptimized,(Q,ae,we)=>(ae===!1&&Q.push(we),Q),[]);if(A.ensureOptimizations&&!En(re))throw Error(`Lexer Modes: < ${re.join(", ")} > cannot be optimized. + Disable the "ensureOptimizations" lexer config flag to silently ignore this and run the lexer in an un-optimized mode. + Or inspect the console log for details on how to resolve these issues.`)}),this.TRACE_INIT("clearRegExpParserCache",()=>{wee()}),this.TRACE_INIT("toFastProperties",()=>{OR(this)})})}tokenize(T,A=this.defaultMode){if(!En(this.lexerDefinitionErrors)){let D=Fr(this.lexerDefinitionErrors,P=>P.message).join(`----------------------- +`);throw new Error(`Unable to Tokenize because Errors detected in definition of Lexer: +`+D)}return this.tokenizeInternal(T,A)}tokenizeInternal(T,A){let D,P,V,Z,re,Q,ae,we,Ue,je,ut,at,Rt,Zt,Yt,xr=T,mr=xr.length,sr=0,qn=0,ia=this.hasCustom?0:Math.floor(T.length/10),Ri=new Array(ia),Ca=[],Qn=this.trackStartLines?1:void 0,Wn=this.trackStartLines?1:void 0,sn=Uee(this.emptyGroups),Zs=this.trackStartLines,Ut=this.config.lineTerminatorsPattern,yt=0,ht=[],Tt=[],dt=[],pt=[];Object.freeze(pt);let Ct=!1,rr=L(wr=>{if(dt.length===1&&wr.tokenType.PUSH_MODE===void 0){let ui=this.config.errorMessageProvider.buildUnableToPopLexerModeMessage(wr);Ca.push({offset:wr.startOffset,line:wr.startLine,column:wr.startColumn,length:wr.image.length,message:ui})}else{dt.pop();let ui=l0(dt);ht=this.patternIdxToConfig[ui],Tt=this.charCodeToPatternIdxToConfig[ui],yt=ht.length;let oi=this.canModeBeOptimized[ui]&&this.config.safeMode===!1;Tt&&oi?Ct=!0:Ct=!1}},"pop_mode");function hr(wr){dt.push(wr),Tt=this.charCodeToPatternIdxToConfig[wr],ht=this.patternIdxToConfig[wr],yt=ht.length,yt=ht.length;let ui=this.canModeBeOptimized[wr]&&this.config.safeMode===!1;Tt&&ui?Ct=!0:Ct=!1}s(hr,"push_mode"),L(hr,"push_mode"),hr.call(this,A);let Gt,Pr=this.config.recoveryEnabled;for(;srQ.length){Q=Z,Ue=Z.length,ae=we,Gt=yn;break}}}break}}if(Ue!==-1){if(je=Gt.group,je!==void 0&&(Q=Q!==null?Q:T.substring(sr,sr+Ue),ut=Gt.tokenTypeIdx,at=this.createTokenInstance(Q,sr,ut,Gt.tokenType,Qn,Wn,Ue),this.handlePayload(at,ae),je===!1?qn=this.addToken(Ri,qn,at):sn[je].push(at)),Zs===!0&&Gt.canLineTerminator===!0){let Ci=0,ji,ua;Ut.lastIndex=0;do Q=Q!==null?Q:T.substring(sr,sr+Ue),ji=Ut.test(Q),ji===!0&&(ua=Ut.lastIndex-1,Ci++);while(ji===!0);Ci!==0?(Qn=Qn+Ci,Wn=Ue-ua,this.updateTokenEndLineColumnLocation(at,je,ua,Ci,Qn,Wn,Ue)):Wn=this.computeNewColumn(Wn,Ue)}else Wn=this.computeNewColumn(Wn,Ue);sr=sr+Ue,this.handleModes(Gt,rr,hr,at)}else{let Ci=sr,ji=Qn,ua=Wn,yn=Pr===!1;for(;yn===!1&&sr ${Of(q)} <--`:`token of type --> ${q.name} <--`} but found --> '${T.image}' <--`},buildNotAllInputParsedMessage({firstRedundant:q,ruleName:T}){return"Redundant input, expecting EOF but found: "+q.image},buildNoViableAltMessage({expectedPathsPerAlt:q,actual:T,previous:A,customUserDescription:D,ruleName:P}){let V="Expecting: ",Z=` +but found: '`+mc(T).image+"'";if(D)return V+D+Z;{let re=fl(q,(we,Ue)=>we.concat(Ue),[]),Q=Fr(re,we=>`[${Fr(we,Ue=>Of(Ue)).join(", ")}]`),ae=`one of these possible Token sequences: +${Fr(Q,(we,Ue)=>` ${Ue+1}. ${we}`).join(` +`)}`;return V+ae+Z}},buildEarlyExitMessage({expectedIterationPaths:q,actual:T,customUserDescription:A,ruleName:D}){let P="Expecting: ",V=` +but found: '`+mc(T).image+"'";if(A)return P+A+V;{let Z=`expecting at least one iteration which starts with one of these possible Token sequences:: + <${Fr(q,re=>`[${Fr(re,Q=>Of(Q)).join(",")}]`).join(" ,")}>`;return P+Z+V}}},Object.freeze(e1),nrt={buildRuleNotFoundError(q,T){return"Invalid grammar, reference to a rule which is not defined: ->"+T.nonTerminalName+`<- +inside top level rule: ->`+q.name+"<-"}},u0={buildDuplicateFoundError(q,T){function A(we){return we instanceof Pn?we.terminalType.name:we instanceof vo?we.nonTerminalName:""}s(A,"getExtraProductionArgument2"),L(A,"getExtraProductionArgument");let D=q.name,P=mc(T),V=P.idx,Z=uc(P),re=A(P),Q=V>0,ae=`->${Z}${Q?V:""}<- ${re?`with argument: ->${re}<-`:""} + appears more than once (${T.length} times) in the top level rule: ->${D}<-. + For further details see: https://chevrotain.io/docs/FAQ.html#NUMERICAL_SUFFIXES + `;return ae=ae.replace(/[ \t]+/g," "),ae=ae.replace(/\s\s+/g,` +`),ae},buildNamespaceConflictError(q){return`Namespace conflict found in grammar. +The grammar has both a Terminal(Token) and a Non-Terminal(Rule) named: <${q.name}>. +To resolve this make sure each Terminal and Non-Terminal names are unique +This is easy to accomplish by using the convention that Terminal names start with an uppercase letter +and Non-Terminal names start with a lower case letter.`},buildAlternationPrefixAmbiguityError(q){let T=Fr(q.prefixPath,D=>Of(D)).join(", "),A=q.alternation.idx===0?"":q.alternation.idx;return`Ambiguous alternatives: <${q.ambiguityIndices.join(" ,")}> due to common lookahead prefix +in inside <${q.topLevelRule.name}> Rule, +<${T}> may appears as a prefix path in all these alternatives. +See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#COMMON_PREFIX +For Further details.`},buildAlternationAmbiguityError(q){let T=q.alternation.idx===0?"":q.alternation.idx,A=q.prefixPath.length===0,D=`Ambiguous Alternatives Detected: <${q.ambiguityIndices.join(" ,")}> in inside <${q.topLevelRule.name}> Rule, +`;if(A)D+=`These alternatives are all empty (match no tokens), making them indistinguishable. +Only the last alternative may be empty. +`;else{let P=Fr(q.prefixPath,V=>Of(V)).join(", ");D+=`<${P}> may appears as a prefix path in all these alternatives. +`}return D+=`See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES +For Further details.`,D},buildEmptyRepetitionError(q){let T=uc(q.repetition);return q.repetition.idx!==0&&(T+=q.repetition.idx),`The repetition <${T}> within Rule <${q.topLevelRule.name}> can never consume any tokens. +This could lead to an infinite loop.`},buildTokenNameError(q){return"deprecated"},buildEmptyAlternationError(q){return`Ambiguous empty alternative: <${q.emptyChoiceIdx+1}> in inside <${q.topLevelRule.name}> Rule. +Only the last alternative may be an empty alternative.`},buildTooManyAlternativesError(q){return`An Alternation cannot have more than 256 alternatives: + inside <${q.topLevelRule.name}> Rule. + has ${q.alternation.definition.length+1} alternatives.`},buildLeftRecursionError(q){let T=q.topLevelRule.name,A=Fr(q.leftRecursionPath,P=>P.name),D=`${T} --> ${A.concat([T]).join(" --> ")}`;return`Left Recursion found in grammar. +rule: <${T}> can be invoked from itself (directly or indirectly) +without consuming any Tokens. The grammar path that causes this is: + ${D} + To fix this refactor your grammar to remove the left recursion. +see: https://en.wikipedia.org/wiki/LL_parser#Left_factoring.`},buildInvalidRuleNameError(q){return"deprecated"},buildDuplicateRuleNameError(q){let T;return q.topLevelRule instanceof Zm?T=q.topLevelRule.name:T=q.topLevelRule,`Duplicate definition, rule: ->${T}<- is already defined in the grammar: ->${q.grammarName}<-`}},s(ite,"resolveGrammar"),L(ite,"resolveGrammar"),art=(w=class extends Jm{constructor(T,A){super(),this.nameToTopRule=T,this.errMsgProvider=A,this.errors=[]}resolveRefs(){ei(Ha(this.nameToTopRule),T=>{this.currTopLevel=T,T.accept(this)})}visitNonTerminal(T){let A=this.nameToTopRule[T.nonTerminalName];if(A)T.referencedRule=A;else{let D=this.errMsgProvider.buildRuleNotFoundError(this.currTopLevel,T);this.errors.push({message:D,type:Vo.UNRESOLVED_SUBRULE_REF,ruleName:this.currTopLevel.name,unresolvedRefName:T.nonTerminalName})}}},s(w,"GastRefResolverVisitor"),L(w,"GastRefResolverVisitor"),w),srt=(R=class extends Tw{constructor(T,A){super(),this.topProd=T,this.path=A,this.possibleTokTypes=[],this.nextProductionName="",this.nextProductionOccurrence=0,this.found=!1,this.isAtEndOfPath=!1}startWalking(){if(this.found=!1,this.path.ruleStack[0]!==this.topProd.name)throw Error("The path does not start with the walker's top Rule!");return this.ruleStack=_s(this.path.ruleStack).reverse(),this.occurrenceStack=_s(this.path.occurrenceStack).reverse(),this.ruleStack.pop(),this.occurrenceStack.pop(),this.updateExpectedNext(),this.walk(this.topProd),this.possibleTokTypes}walk(T,A=[]){this.found||super.walk(T,A)}walkProdRef(T,A,D){if(T.referencedRule.name===this.nextProductionName&&T.idx===this.nextProductionOccurrence){let P=A.concat(D);this.updateExpectedNext(),this.walk(T.referencedRule,P)}}updateExpectedNext(){En(this.ruleStack)?(this.nextProductionName="",this.nextProductionOccurrence=0,this.isAtEndOfPath=!0):(this.nextProductionName=this.ruleStack.pop(),this.nextProductionOccurrence=this.occurrenceStack.pop())}},s(R,"AbstractNextPossibleTokensWalker"),L(R,"AbstractNextPossibleTokensWalker"),R),ort=($=class extends srt{constructor(T,A){super(T,A),this.path=A,this.nextTerminalName="",this.nextTerminalOccurrence=0,this.nextTerminalName=this.path.lastTok.name,this.nextTerminalOccurrence=this.path.lastTokOccurrence}walkTerminal(T,A,D){if(this.isAtEndOfPath&&T.terminalType.name===this.nextTerminalName&&T.idx===this.nextTerminalOccurrence&&!this.found){let P=A.concat(D),V=new qo({definition:P});this.possibleTokTypes=$m(V),this.found=!0}}},s($,"NextAfterTokenWalker"),L($,"NextAfterTokenWalker"),$),Sw=(M=class extends Tw{constructor(T,A){super(),this.topRule=T,this.occurrence=A,this.result={token:void 0,occurrence:void 0,isEndOfRule:void 0}}startWalking(){return this.walk(this.topRule),this.result}},s(M,"AbstractNextTerminalAfterProductionWalker"),L(M,"AbstractNextTerminalAfterProductionWalker"),M),lrt=(N=class extends Sw{walkMany(T,A,D){if(T.idx===this.occurrence){let P=mc(A.concat(D));this.result.isEndOfRule=P===void 0,P instanceof Pn&&(this.result.token=P.terminalType,this.result.occurrence=P.idx)}else super.walkMany(T,A,D)}},s(N,"NextTerminalAfterManyWalker"),L(N,"NextTerminalAfterManyWalker"),N),_oe=(j=class extends Sw{walkManySep(T,A,D){if(T.idx===this.occurrence){let P=mc(A.concat(D));this.result.isEndOfRule=P===void 0,P instanceof Pn&&(this.result.token=P.terminalType,this.result.occurrence=P.idx)}else super.walkManySep(T,A,D)}},s(j,"NextTerminalAfterManySepWalker"),L(j,"NextTerminalAfterManySepWalker"),j),crt=(I=class extends Sw{walkAtLeastOne(T,A,D){if(T.idx===this.occurrence){let P=mc(A.concat(D));this.result.isEndOfRule=P===void 0,P instanceof Pn&&(this.result.token=P.terminalType,this.result.occurrence=P.idx)}else super.walkAtLeastOne(T,A,D)}},s(I,"NextTerminalAfterAtLeastOneWalker"),L(I,"NextTerminalAfterAtLeastOneWalker"),I),Aoe=(z=class extends Sw{walkAtLeastOneSep(T,A,D){if(T.idx===this.occurrence){let P=mc(A.concat(D));this.result.isEndOfRule=P===void 0,P instanceof Pn&&(this.result.token=P.terminalType,this.result.occurrence=P.idx)}else super.walkAtLeastOneSep(T,A,D)}},s(z,"NextTerminalAfterAtLeastOneSepWalker"),L(z,"NextTerminalAfterAtLeastOneSepWalker"),z),s(sC,"possiblePathsFrom"),L(sC,"possiblePathsFrom"),s(GR,"nextPossibleTokensAfter"),L(GR,"nextPossibleTokensAfter"),s(nte,"expandTopLevelRule"),L(nte,"expandTopLevelRule"),(function(q){q[q.OPTION=0]="OPTION",q[q.REPETITION=1]="REPETITION",q[q.REPETITION_MANDATORY=2]="REPETITION_MANDATORY",q[q.REPETITION_MANDATORY_WITH_SEPARATOR=3]="REPETITION_MANDATORY_WITH_SEPARATOR",q[q.REPETITION_WITH_SEPARATOR=4]="REPETITION_WITH_SEPARATOR",q[q.ALTERNATION=5]="ALTERNATION"})(ca||(ca={})),s(oC,"getProdType"),L(oC,"getProdType"),s(XR,"getLookaheadPaths"),L(XR,"getLookaheadPaths"),s(ate,"buildLookaheadFuncForOr"),L(ate,"buildLookaheadFuncForOr"),s(ste,"buildLookaheadFuncForOptionalProd"),L(ste,"buildLookaheadFuncForOptionalProd"),s(ote,"buildAlternativesLookAheadFunc"),L(ote,"buildAlternativesLookAheadFunc"),s(lte,"buildSingleAlternativeLookaheadFunction"),L(lte,"buildSingleAlternativeLookaheadFunction"),urt=(F=class extends Tw{constructor(T,A,D){super(),this.topProd=T,this.targetOccurrence=A,this.targetProdType=D}startWalking(){return this.walk(this.topProd),this.restDef}checkIsTarget(T,A,D,P){return T.idx===this.targetOccurrence&&this.targetProdType===A?(this.restDef=D.concat(P),!0):!1}walkOption(T,A,D){this.checkIsTarget(T,ca.OPTION,A,D)||super.walkOption(T,A,D)}walkAtLeastOne(T,A,D){this.checkIsTarget(T,ca.REPETITION_MANDATORY,A,D)||super.walkOption(T,A,D)}walkAtLeastOneSep(T,A,D){this.checkIsTarget(T,ca.REPETITION_MANDATORY_WITH_SEPARATOR,A,D)||super.walkOption(T,A,D)}walkMany(T,A,D){this.checkIsTarget(T,ca.REPETITION,A,D)||super.walkOption(T,A,D)}walkManySep(T,A,D){this.checkIsTarget(T,ca.REPETITION_WITH_SEPARATOR,A,D)||super.walkOption(T,A,D)}},s(F,"RestDefinitionFinderWalker"),L(F,"RestDefinitionFinderWalker"),F),Roe=(O=class extends Jm{constructor(T,A,D){super(),this.targetOccurrence=T,this.targetProdType=A,this.targetRef=D,this.result=[]}checkIsTarget(T,A){T.idx===this.targetOccurrence&&this.targetProdType===A&&(this.targetRef===void 0||T===this.targetRef)&&(this.result=T.definition)}visitOption(T){this.checkIsTarget(T,ca.OPTION)}visitRepetition(T){this.checkIsTarget(T,ca.REPETITION)}visitRepetitionMandatory(T){this.checkIsTarget(T,ca.REPETITION_MANDATORY)}visitRepetitionMandatoryWithSeparator(T){this.checkIsTarget(T,ca.REPETITION_MANDATORY_WITH_SEPARATOR)}visitRepetitionWithSeparator(T){this.checkIsTarget(T,ca.REPETITION_WITH_SEPARATOR)}visitAlternation(T){this.checkIsTarget(T,ca.ALTERNATION)}},s(O,"InsideDefinitionFinderVisitor"),L(O,"InsideDefinitionFinderVisitor"),O),s(KR,"initializeArrayOfArrays"),L(KR,"initializeArrayOfArrays"),s(lC,"pathToHashKeys"),L(lC,"pathToHashKeys"),s(cte,"isUniquePrefixHash"),L(cte,"isUniquePrefixHash"),s(QR,"lookAheadSequenceFromAlternatives"),L(QR,"lookAheadSequenceFromAlternatives"),s(Dv,"getLookaheadPathsForOr"),L(Dv,"getLookaheadPathsForOr"),s(Nv,"getLookaheadPathsForOptionalProd"),L(Nv,"getLookaheadPathsForOptionalProd"),s(cC,"containsPath"),L(cC,"containsPath"),s(ute,"isStrictPrefixOfPath"),L(ute,"isStrictPrefixOfPath"),s(ZR,"areTokenCategoriesNotUsed"),L(ZR,"areTokenCategoriesNotUsed"),s(hte,"validateLookahead"),L(hte,"validateLookahead"),s(dte,"validateGrammar"),L(dte,"validateGrammar"),s(pte,"validateDuplicateProductions"),L(pte,"validateDuplicateProductions"),s(fte,"identifyProductionForDuplicates"),L(fte,"identifyProductionForDuplicates"),s(JR,"getExtraProductionArgument"),L(JR,"getExtraProductionArgument"),hrt=(B=class extends Jm{constructor(){super(...arguments),this.allProductions=[]}visitNonTerminal(T){this.allProductions.push(T)}visitOption(T){this.allProductions.push(T)}visitRepetitionWithSeparator(T){this.allProductions.push(T)}visitRepetitionMandatory(T){this.allProductions.push(T)}visitRepetitionMandatoryWithSeparator(T){this.allProductions.push(T)}visitRepetition(T){this.allProductions.push(T)}visitAlternation(T){this.allProductions.push(T)}visitTerminal(T){this.allProductions.push(T)}},s(B,"OccurrenceValidationCollector"),L(B,"OccurrenceValidationCollector"),B),s(gte,"validateRuleDoesNotAlreadyExist"),L(gte,"validateRuleDoesNotAlreadyExist"),s(mte,"validateRuleIsOverridden"),L(mte,"validateRuleIsOverridden"),s(e$,"validateNoLeftRecursion"),L(e$,"validateNoLeftRecursion"),s(Ov,"getFirstNoneTerminal"),L(Ov,"getFirstNoneTerminal"),IL=(Y=class extends Jm{constructor(){super(...arguments),this.alternations=[]}visitAlternation(T){this.alternations.push(T)}},s(Y,"OrCollector"),L(Y,"OrCollector"),Y),s(yte,"validateEmptyOrAlternative"),L(yte,"validateEmptyOrAlternative"),s(vte,"validateAmbiguousAlternationAlternatives"),L(vte,"validateAmbiguousAlternationAlternatives"),drt=(U=class extends Jm{constructor(){super(...arguments),this.allProductions=[]}visitRepetitionWithSeparator(T){this.allProductions.push(T)}visitRepetitionMandatory(T){this.allProductions.push(T)}visitRepetitionMandatoryWithSeparator(T){this.allProductions.push(T)}visitRepetition(T){this.allProductions.push(T)}},s(U,"RepetitionCollector"),L(U,"RepetitionCollector"),U),s(bte,"validateTooManyAlts"),L(bte,"validateTooManyAlts"),s(xte,"validateSomeNonEmptyLookaheadPath"),L(xte,"validateSomeNonEmptyLookaheadPath"),s(Tte,"checkAlternativesAmbiguities"),L(Tte,"checkAlternativesAmbiguities"),s(kte,"checkPrefixAlternativesAmbiguities"),L(kte,"checkPrefixAlternativesAmbiguities"),s(Cte,"checkTerminalAndNoneTerminalsNameSpace"),L(Cte,"checkTerminalAndNoneTerminalsNameSpace"),s(wte,"resolveGrammar2"),L(wte,"resolveGrammar"),s(Ete,"validateGrammar2"),L(Ete,"validateGrammar"),$oe="MismatchedTokenException",Loe="NoViableAltException",Ioe="EarlyExitException",Moe="NotAllInputParsedException",Doe=[$oe,Loe,Ioe,Moe],Object.freeze(Doe),s(Bv,"isRecognitionException"),L(Bv,"isRecognitionException"),_w=(H=class extends Error{constructor(T,A){super(T),this.token=A,this.resyncedTokens=[],Object.setPrototypeOf(this,new.target.prototype),Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}},s(H,"RecognitionException"),L(H,"RecognitionException"),H),Noe=(J=class extends _w{constructor(T,A,D){super(T,A),this.previousToken=D,this.name=$oe}},s(J,"MismatchedTokenException"),L(J,"MismatchedTokenException"),J),prt=(te=class extends _w{constructor(T,A,D){super(T,A),this.previousToken=D,this.name=Loe}},s(te,"NoViableAltException"),L(te,"NoViableAltException"),te),frt=(ie=class extends _w{constructor(T,A){super(T,A),this.name=Moe}},s(ie,"NotAllInputParsedException"),L(ie,"NotAllInputParsedException"),ie),grt=(ue=class extends _w{constructor(T,A,D){super(T,A),this.previousToken=D,this.name=Ioe}},s(ue,"EarlyExitException"),L(ue,"EarlyExitException"),ue),ML={},Ooe="InRuleRecoveryException",mrt=(se=class extends Error{constructor(T){super(T),this.name=Ooe}},s(se,"InRuleRecoveryException"),L(se,"InRuleRecoveryException"),se),yrt=(le=class{initRecoverable(T){this.firstAfterRepMap={},this.resyncFollows={},this.recoveryEnabled=Kr(T,"recoveryEnabled")?T.recoveryEnabled:xh.recoveryEnabled,this.recoveryEnabled&&(this.attemptInRepetitionRecovery=Ste)}getTokenToInsert(T){let A=Mv(T,"",NaN,NaN,NaN,NaN,NaN,NaN);return A.isInsertedInRecovery=!0,A}canTokenTypeBeInsertedInRecovery(T){return!0}canTokenTypeBeDeletedInRecovery(T){return!0}tryInRepetitionRecovery(T,A,D,P){let V=this.findReSyncTokenType(),Z=this.exportLexerState(),re=[],Q=!1,ae=this.LA(1),we=this.LA(1),Ue=L(()=>{let je=this.LA(0),ut=this.errorMessageProvider.buildMismatchTokenMessage({expected:P,actual:ae,previous:je,ruleName:this.getCurrRuleFullName()}),at=new Noe(ut,ae,this.LA(0));at.resyncedTokens=bb(re),this.SAVE_ERROR(at)},"generateErrorMessage");for(;!Q;)if(this.tokenMatcher(we,P)){Ue();return}else if(D.call(this)){Ue(),T.apply(this,A);return}else this.tokenMatcher(we,V)?Q=!0:(we=this.SKIP_TOKEN(),this.addToResyncTokens(we,re));this.importLexerState(Z)}shouldInRepetitionRecoveryBeTried(T,A,D){return!(D===!1||this.tokenMatcher(this.LA(1),T)||this.isBackTracking()||this.canPerformInRuleRecovery(T,this.getFollowsForInRuleRecovery(T,A)))}getFollowsForInRuleRecovery(T,A){let D=this.getCurrentGrammarPath(T,A);return this.getNextPossibleTokenTypes(D)}tryInRuleRecovery(T,A){if(this.canRecoverWithSingleTokenInsertion(T,A))return this.getTokenToInsert(T);if(this.canRecoverWithSingleTokenDeletion(T)){let D=this.SKIP_TOKEN();return this.consumeToken(),D}throw new mrt("sad sad panda")}canPerformInRuleRecovery(T,A){return this.canRecoverWithSingleTokenInsertion(T,A)||this.canRecoverWithSingleTokenDeletion(T)}canRecoverWithSingleTokenInsertion(T,A){if(!this.canTokenTypeBeInsertedInRecovery(T)||En(A))return!1;let D=this.LA(1);return Qm(A,P=>this.tokenMatcher(D,P))!==void 0}canRecoverWithSingleTokenDeletion(T){return this.canTokenTypeBeDeletedInRecovery(T)?this.tokenMatcher(this.LA(2),T):!1}isInCurrentRuleReSyncSet(T){let A=this.getCurrFollowKey(),D=this.getFollowSetFromFollowKey(A);return yo(D,T)}findReSyncTokenType(){let T=this.flattenFollowSet(),A=this.LA(1),D=2;for(;;){let P=Qm(T,V=>HR(A,V));if(P!==void 0)return P;A=this.LA(D),D++}}getCurrFollowKey(){if(this.RULE_STACK.length===1)return ML;let T=this.getLastExplicitRuleShortName(),A=this.getLastExplicitRuleOccurrenceIndex(),D=this.getPreviousExplicitRuleShortName();return{ruleName:this.shortRuleNameToFullName(T),idxInCallingRule:A,inRule:this.shortRuleNameToFullName(D)}}buildFullFollowKeyStack(){let T=this.RULE_STACK,A=this.RULE_OCCURRENCE_STACK;return Fr(T,(D,P)=>P===0?ML:{ruleName:this.shortRuleNameToFullName(D),idxInCallingRule:A[P],inRule:this.shortRuleNameToFullName(T[P-1])})}flattenFollowSet(){let T=Fr(this.buildFullFollowKeyStack(),A=>this.getFollowSetFromFollowKey(A));return fc(T)}getFollowSetFromFollowKey(T){if(T===ML)return[Jd];let A=T.ruleName+T.idxInCallingRule+goe+T.inRule;return this.resyncFollows[A]}addToResyncTokens(T,A){return this.tokenMatcher(T,Jd)||A.push(T),A}reSyncTo(T){let A=[],D=this.LA(1);for(;this.tokenMatcher(D,T)===!1;)D=this.SKIP_TOKEN(),this.addToResyncTokens(D,A);return bb(A)}attemptInRepetitionRecovery(T,A,D,P,V,Z,re){}getCurrentGrammarPath(T,A){let D=this.getHumanReadableRuleStack(),P=_s(this.RULE_OCCURRENCE_STACK);return{ruleStack:D,occurrenceStack:P,lastTok:T,lastTokOccurrence:A}}getHumanReadableRuleStack(){return Fr(this.RULE_STACK,T=>this.shortRuleNameToFullName(T))}},s(le,"Recoverable"),L(le,"Recoverable"),le),s(Ste,"attemptInRepetitionRecovery"),L(Ste,"attemptInRepetitionRecovery"),vrt=4,ep=8,brt=8,Boe=1<e$(A,A,u0))}validateEmptyOrAlternatives(T){return Bl(T,A=>yte(A,u0))}validateAmbiguousAlternationAlternatives(T,A){return Bl(T,D=>vte(D,A,u0))}validateSomeNonEmptyLookaheadPath(T,A){return xte(T,A,u0)}buildLookaheadForAlternation(T){return ate(T.prodOccurrence,T.rule,T.maxLookahead,T.hasPredicates,T.dynamicTokensEnabled,ote)}buildLookaheadForOptional(T){return ste(T.prodOccurrence,T.rule,T.maxLookahead,T.dynamicTokensEnabled,oC(T.prodType),lte)}},s(Te,"LLkLookaheadStrategy"),L(Te,"LLkLookaheadStrategy"),Te),xrt=(oe=class{initLooksAhead(T){this.dynamicTokensEnabled=Kr(T,"dynamicTokensEnabled")?T.dynamicTokensEnabled:xh.dynamicTokensEnabled,this.maxLookahead=Kr(T,"maxLookahead")?T.maxLookahead:xh.maxLookahead,this.lookaheadStrategy=Kr(T,"lookaheadStrategy")?T.lookaheadStrategy:new BL({maxLookahead:this.maxLookahead}),this.lookAheadFuncsCache=new Map}preComputeLookaheadFunctions(T){ei(T,A=>{this.TRACE_INIT(`${A.name} Rule Lookahead`,()=>{let{alternation:D,repetition:P,option:V,repetitionMandatory:Z,repetitionMandatoryWithSeparator:re,repetitionWithSeparator:Q}=_te(A);ei(D,ae=>{let we=ae.idx===0?"":ae.idx;this.TRACE_INIT(`${uc(ae)}${we}`,()=>{let Ue=this.lookaheadStrategy.buildLookaheadForAlternation({prodOccurrence:ae.idx,rule:A,maxLookahead:ae.maxLookahead||this.maxLookahead,hasPredicates:ae.hasPredicates,dynamicTokensEnabled:this.dynamicTokensEnabled}),je=uC(this.fullRuleNameToShort[A.name],Boe,ae.idx);this.setLaFuncCache(je,Ue)})}),ei(P,ae=>{this.computeLookaheadFunc(A,ae.idx,DL,"Repetition",ae.maxLookahead,uc(ae))}),ei(V,ae=>{this.computeLookaheadFunc(A,ae.idx,Poe,"Option",ae.maxLookahead,uc(ae))}),ei(Z,ae=>{this.computeLookaheadFunc(A,ae.idx,NL,"RepetitionMandatory",ae.maxLookahead,uc(ae))}),ei(re,ae=>{this.computeLookaheadFunc(A,ae.idx,Aw,"RepetitionMandatoryWithSeparator",ae.maxLookahead,uc(ae))}),ei(Q,ae=>{this.computeLookaheadFunc(A,ae.idx,OL,"RepetitionWithSeparator",ae.maxLookahead,uc(ae))})})})}computeLookaheadFunc(T,A,D,P,V,Z){this.TRACE_INIT(`${Z}${A===0?"":A}`,()=>{let re=this.lookaheadStrategy.buildLookaheadForOptional({prodOccurrence:A,rule:T,maxLookahead:V||this.maxLookahead,dynamicTokensEnabled:this.dynamicTokensEnabled,prodType:P}),Q=uC(this.fullRuleNameToShort[T.name],D,A);this.setLaFuncCache(Q,re)})}getKeyForAutomaticLookahead(T,A){let D=this.getLastExplicitRuleShortName();return uC(D,T,A)}getLaFuncFromCache(T){return this.lookAheadFuncsCache.get(T)}setLaFuncCache(T,A){this.lookAheadFuncsCache.set(T,A)}},s(oe,"LooksAhead"),L(oe,"LooksAhead"),oe),Trt=(fe=class extends Jm{constructor(){super(...arguments),this.dslMethods={option:[],alternation:[],repetition:[],repetitionWithSeparator:[],repetitionMandatory:[],repetitionMandatoryWithSeparator:[]}}reset(){this.dslMethods={option:[],alternation:[],repetition:[],repetitionWithSeparator:[],repetitionMandatory:[],repetitionMandatoryWithSeparator:[]}}visitOption(T){this.dslMethods.option.push(T)}visitRepetitionWithSeparator(T){this.dslMethods.repetitionWithSeparator.push(T)}visitRepetitionMandatory(T){this.dslMethods.repetitionMandatory.push(T)}visitRepetitionMandatoryWithSeparator(T){this.dslMethods.repetitionMandatoryWithSeparator.push(T)}visitRepetition(T){this.dslMethods.repetition.push(T)}visitAlternation(T){this.dslMethods.alternation.push(T)}},s(fe,"DslMethodsCollectorVisitor"),L(fe,"DslMethodsCollectorVisitor"),fe),Rw=new Trt,s(_te,"collectMethods"),L(_te,"collectMethods"),s(t$,"setNodeLocationOnlyOffset"),L(t$,"setNodeLocationOnlyOffset"),s(r$,"setNodeLocationFull"),L(r$,"setNodeLocationFull"),s(Ate,"addTerminalToCst"),L(Ate,"addTerminalToCst"),s(Rte,"addNoneTerminalToCst"),L(Rte,"addNoneTerminalToCst"),krt="name",s(i$,"defineNameProp"),L(i$,"defineNameProp"),s($te,"defaultVisit"),L($te,"defaultVisit"),s(Lte,"createBaseSemanticVisitorConstructor"),L(Lte,"createBaseSemanticVisitorConstructor"),s(Ite,"createBaseVisitorConstructorWithDefaults"),L(Ite,"createBaseVisitorConstructorWithDefaults"),(function(q){q[q.REDUNDANT_METHOD=0]="REDUNDANT_METHOD",q[q.MISSING_METHOD=1]="MISSING_METHOD"})(Foe||(Foe={})),s(Mte,"validateVisitor"),L(Mte,"validateVisitor"),s(Dte,"validateMissingCstMethods"),L(Dte,"validateMissingCstMethods"),Crt=(ge=class{initTreeBuilder(T){if(this.CST_STACK=[],this.outputCst=T.outputCst,this.nodeLocationTracking=Kr(T,"nodeLocationTracking")?T.nodeLocationTracking:xh.nodeLocationTracking,!this.outputCst)this.cstInvocationStateUpdate=ps,this.cstFinallyStateUpdate=ps,this.cstPostTerminal=ps,this.cstPostNonTerminal=ps,this.cstPostRule=ps;else if(/full/i.test(this.nodeLocationTracking))this.recoveryEnabled?(this.setNodeLocationFromToken=r$,this.setNodeLocationFromNode=r$,this.cstPostRule=ps,this.setInitialNodeLocation=this.setInitialNodeLocationFullRecovery):(this.setNodeLocationFromToken=ps,this.setNodeLocationFromNode=ps,this.cstPostRule=this.cstPostRuleFull,this.setInitialNodeLocation=this.setInitialNodeLocationFullRegular);else if(/onlyOffset/i.test(this.nodeLocationTracking))this.recoveryEnabled?(this.setNodeLocationFromToken=t$,this.setNodeLocationFromNode=t$,this.cstPostRule=ps,this.setInitialNodeLocation=this.setInitialNodeLocationOnlyOffsetRecovery):(this.setNodeLocationFromToken=ps,this.setNodeLocationFromNode=ps,this.cstPostRule=this.cstPostRuleOnlyOffset,this.setInitialNodeLocation=this.setInitialNodeLocationOnlyOffsetRegular);else if(/none/i.test(this.nodeLocationTracking))this.setNodeLocationFromToken=ps,this.setNodeLocationFromNode=ps,this.cstPostRule=ps,this.setInitialNodeLocation=ps;else throw Error(`Invalid config option: "${T.nodeLocationTracking}"`)}setInitialNodeLocationOnlyOffsetRecovery(T){T.location={startOffset:NaN,endOffset:NaN}}setInitialNodeLocationOnlyOffsetRegular(T){T.location={startOffset:this.LA(1).startOffset,endOffset:NaN}}setInitialNodeLocationFullRecovery(T){T.location={startOffset:NaN,startLine:NaN,startColumn:NaN,endOffset:NaN,endLine:NaN,endColumn:NaN}}setInitialNodeLocationFullRegular(T){let A=this.LA(1);T.location={startOffset:A.startOffset,startLine:A.startLine,startColumn:A.startColumn,endOffset:NaN,endLine:NaN,endColumn:NaN}}cstInvocationStateUpdate(T){let A={name:T,children:Object.create(null)};this.setInitialNodeLocation(A),this.CST_STACK.push(A)}cstFinallyStateUpdate(){this.CST_STACK.pop()}cstPostRuleFull(T){let A=this.LA(0),D=T.location;D.startOffset<=A.startOffset?(D.endOffset=A.endOffset,D.endLine=A.endLine,D.endColumn=A.endColumn):(D.startOffset=NaN,D.startLine=NaN,D.startColumn=NaN)}cstPostRuleOnlyOffset(T){let A=this.LA(0),D=T.location;D.startOffset<=A.startOffset?D.endOffset=A.endOffset:D.startOffset=NaN}cstPostTerminal(T,A){let D=this.CST_STACK[this.CST_STACK.length-1];Ate(D,A,T),this.setNodeLocationFromToken(D.location,A)}cstPostNonTerminal(T,A){let D=this.CST_STACK[this.CST_STACK.length-1];Rte(D,A,T),this.setNodeLocationFromNode(D.location,T.location)}getBaseCstVisitorConstructor(){if(bh(this.baseCstVisitorConstructor)){let T=Lte(this.className,dl(this.gastProductionsCache));return this.baseCstVisitorConstructor=T,T}return this.baseCstVisitorConstructor}getBaseCstVisitorConstructorWithDefaults(){if(bh(this.baseCstVisitorWithDefaultsConstructor)){let T=Ite(this.className,dl(this.gastProductionsCache),this.getBaseCstVisitorConstructor());return this.baseCstVisitorWithDefaultsConstructor=T,T}return this.baseCstVisitorWithDefaultsConstructor}getLastExplicitRuleShortName(){let T=this.RULE_STACK;return T[T.length-1]}getPreviousExplicitRuleShortName(){let T=this.RULE_STACK;return T[T.length-2]}getLastExplicitRuleOccurrenceIndex(){let T=this.RULE_OCCURRENCE_STACK;return T[T.length-1]}},s(ge,"TreeBuilder"),L(ge,"TreeBuilder"),ge),wrt=(ke=class{initLexerAdapter(){this.tokVector=[],this.tokVectorLength=0,this.currIdx=-1}set input(T){if(this.selfAnalysisDone!==!0)throw Error("Missing invocation at the end of the Parser's constructor.");this.reset(),this.tokVector=T,this.tokVectorLength=T.length}get input(){return this.tokVector}SKIP_TOKEN(){return this.currIdx<=this.tokVector.length-2?(this.consumeToken(),this.LA(1)):Lw}LA(T){let A=this.currIdx+T;return A<0||this.tokVectorLength<=A?Lw:this.tokVector[A]}consumeToken(){this.currIdx++}exportLexerState(){return this.currIdx}importLexerState(T){this.currIdx=T}resetLexerState(){this.currIdx=-1}moveToTerminatedState(){this.currIdx=this.tokVector.length-1}getLexerPosition(){return this.exportLexerState()}},s(ke,"LexerAdapter"),L(ke,"LexerAdapter"),ke),Ert=(Ne=class{ACTION(T){return T.call(this)}consume(T,A,D){return this.consumeInternal(A,T,D)}subrule(T,A,D){return this.subruleInternal(A,T,D)}option(T,A){return this.optionInternal(A,T)}or(T,A){return this.orInternal(A,T)}many(T,A){return this.manyInternal(T,A)}atLeastOne(T,A){return this.atLeastOneInternal(T,A)}CONSUME(T,A){return this.consumeInternal(T,0,A)}CONSUME1(T,A){return this.consumeInternal(T,1,A)}CONSUME2(T,A){return this.consumeInternal(T,2,A)}CONSUME3(T,A){return this.consumeInternal(T,3,A)}CONSUME4(T,A){return this.consumeInternal(T,4,A)}CONSUME5(T,A){return this.consumeInternal(T,5,A)}CONSUME6(T,A){return this.consumeInternal(T,6,A)}CONSUME7(T,A){return this.consumeInternal(T,7,A)}CONSUME8(T,A){return this.consumeInternal(T,8,A)}CONSUME9(T,A){return this.consumeInternal(T,9,A)}SUBRULE(T,A){return this.subruleInternal(T,0,A)}SUBRULE1(T,A){return this.subruleInternal(T,1,A)}SUBRULE2(T,A){return this.subruleInternal(T,2,A)}SUBRULE3(T,A){return this.subruleInternal(T,3,A)}SUBRULE4(T,A){return this.subruleInternal(T,4,A)}SUBRULE5(T,A){return this.subruleInternal(T,5,A)}SUBRULE6(T,A){return this.subruleInternal(T,6,A)}SUBRULE7(T,A){return this.subruleInternal(T,7,A)}SUBRULE8(T,A){return this.subruleInternal(T,8,A)}SUBRULE9(T,A){return this.subruleInternal(T,9,A)}OPTION(T){return this.optionInternal(T,0)}OPTION1(T){return this.optionInternal(T,1)}OPTION2(T){return this.optionInternal(T,2)}OPTION3(T){return this.optionInternal(T,3)}OPTION4(T){return this.optionInternal(T,4)}OPTION5(T){return this.optionInternal(T,5)}OPTION6(T){return this.optionInternal(T,6)}OPTION7(T){return this.optionInternal(T,7)}OPTION8(T){return this.optionInternal(T,8)}OPTION9(T){return this.optionInternal(T,9)}OR(T){return this.orInternal(T,0)}OR1(T){return this.orInternal(T,1)}OR2(T){return this.orInternal(T,2)}OR3(T){return this.orInternal(T,3)}OR4(T){return this.orInternal(T,4)}OR5(T){return this.orInternal(T,5)}OR6(T){return this.orInternal(T,6)}OR7(T){return this.orInternal(T,7)}OR8(T){return this.orInternal(T,8)}OR9(T){return this.orInternal(T,9)}MANY(T){this.manyInternal(0,T)}MANY1(T){this.manyInternal(1,T)}MANY2(T){this.manyInternal(2,T)}MANY3(T){this.manyInternal(3,T)}MANY4(T){this.manyInternal(4,T)}MANY5(T){this.manyInternal(5,T)}MANY6(T){this.manyInternal(6,T)}MANY7(T){this.manyInternal(7,T)}MANY8(T){this.manyInternal(8,T)}MANY9(T){this.manyInternal(9,T)}MANY_SEP(T){this.manySepFirstInternal(0,T)}MANY_SEP1(T){this.manySepFirstInternal(1,T)}MANY_SEP2(T){this.manySepFirstInternal(2,T)}MANY_SEP3(T){this.manySepFirstInternal(3,T)}MANY_SEP4(T){this.manySepFirstInternal(4,T)}MANY_SEP5(T){this.manySepFirstInternal(5,T)}MANY_SEP6(T){this.manySepFirstInternal(6,T)}MANY_SEP7(T){this.manySepFirstInternal(7,T)}MANY_SEP8(T){this.manySepFirstInternal(8,T)}MANY_SEP9(T){this.manySepFirstInternal(9,T)}AT_LEAST_ONE(T){this.atLeastOneInternal(0,T)}AT_LEAST_ONE1(T){return this.atLeastOneInternal(1,T)}AT_LEAST_ONE2(T){this.atLeastOneInternal(2,T)}AT_LEAST_ONE3(T){this.atLeastOneInternal(3,T)}AT_LEAST_ONE4(T){this.atLeastOneInternal(4,T)}AT_LEAST_ONE5(T){this.atLeastOneInternal(5,T)}AT_LEAST_ONE6(T){this.atLeastOneInternal(6,T)}AT_LEAST_ONE7(T){this.atLeastOneInternal(7,T)}AT_LEAST_ONE8(T){this.atLeastOneInternal(8,T)}AT_LEAST_ONE9(T){this.atLeastOneInternal(9,T)}AT_LEAST_ONE_SEP(T){this.atLeastOneSepFirstInternal(0,T)}AT_LEAST_ONE_SEP1(T){this.atLeastOneSepFirstInternal(1,T)}AT_LEAST_ONE_SEP2(T){this.atLeastOneSepFirstInternal(2,T)}AT_LEAST_ONE_SEP3(T){this.atLeastOneSepFirstInternal(3,T)}AT_LEAST_ONE_SEP4(T){this.atLeastOneSepFirstInternal(4,T)}AT_LEAST_ONE_SEP5(T){this.atLeastOneSepFirstInternal(5,T)}AT_LEAST_ONE_SEP6(T){this.atLeastOneSepFirstInternal(6,T)}AT_LEAST_ONE_SEP7(T){this.atLeastOneSepFirstInternal(7,T)}AT_LEAST_ONE_SEP8(T){this.atLeastOneSepFirstInternal(8,T)}AT_LEAST_ONE_SEP9(T){this.atLeastOneSepFirstInternal(9,T)}RULE(T,A,D=Iw){if(yo(this.definedRulesNames,T)){let V={message:u0.buildDuplicateRuleNameError({topLevelRule:T,grammarName:this.className}),type:Vo.DUPLICATE_RULE_NAME,ruleName:T};this.definitionErrors.push(V)}this.definedRulesNames.push(T);let P=this.defineRule(T,A,D);return this[T]=P,P}OVERRIDE_RULE(T,A,D=Iw){let P=mte(T,this.definedRulesNames,this.className);this.definitionErrors=this.definitionErrors.concat(P);let V=this.defineRule(T,A,D);return this[T]=V,V}BACKTRACK(T,A){return function(){this.isBackTrackingStack.push(1);let D=this.saveRecogState();try{return T.apply(this,A),!0}catch(P){if(Bv(P))return!1;throw P}finally{this.reloadRecogState(D),this.isBackTrackingStack.pop()}}}getGAstProductions(){return this.gastProductionsCache}getSerializedGastProductions(){return mee(Ha(this.gastProductionsCache))}},s(Ne,"RecognizerApi"),L(Ne,"RecognizerApi"),Ne),Srt=(xe=class{initRecognizerEngine(T,A){if(this.className=this.constructor.name,this.shortRuleNameToFull={},this.fullRuleNameToShort={},this.ruleShortNameIdx=256,this.tokenMatcher=Iv,this.subruleIdx=0,this.definedRulesNames=[],this.tokensMap={},this.isBackTrackingStack=[],this.RULE_STACK=[],this.RULE_OCCURRENCE_STACK=[],this.gastProductionsCache={},Kr(A,"serializedGrammar"))throw Error(`The Parser's configuration can no longer contain a property. + See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_6-0-0 + For Further details.`);if(qi(T)){if(En(T))throw Error(`A Token Vocabulary cannot be empty. + Note that the first argument for the parser constructor + is no longer a Token vector (since v4.0).`);if(typeof T[0].startOffset=="number")throw Error(`The Parser constructor no longer accepts a token vector as the first argument. + See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_4-0-0 + For Further details.`)}if(qi(T))this.tokensMap=fl(T,(V,Z)=>(V[Z.name]=Z,V),{});else if(Kr(T,"modes")&&gc(fc(Ha(T.modes)),tte)){let V=fc(Ha(T.modes)),Z=$L(V);this.tokensMap=fl(Z,(re,Q)=>(re[Q.name]=Q,re),{})}else if(Nl(T))this.tokensMap=_s(T);else throw new Error(" argument must be An Array of Token constructors, A dictionary of Token constructors or an IMultiModeLexerDefinition");this.tokensMap.EOF=Jd;let D=Kr(T,"modes")?fc(Ha(T.modes)):Ha(T),P=gc(D,V=>En(V.categoryMatches));this.tokenMatcher=P?Iv:Lm,Im(Ha(this.tokensMap))}defineRule(T,A,D){if(this.selfAnalysisDone)throw Error(`Grammar rule <${T}> may not be defined after the 'performSelfAnalysis' method has been called' +Make sure that all grammar rule definitions are done before 'performSelfAnalysis' is called.`);let P=Kr(D,"resyncEnabled")?D.resyncEnabled:Iw.resyncEnabled,V=Kr(D,"recoveryValueFunc")?D.recoveryValueFunc:Iw.recoveryValueFunc,Z=this.ruleShortNameIdx<Z.call(this)&&re.call(this),"lookAheadFunc")}}else V=T;if(P.call(this)===!0)return V.call(this)}atLeastOneInternal(T,A){let D=this.getKeyForAutomaticLookahead(NL,T);return this.atLeastOneInternalLogic(T,A,D)}atLeastOneInternalLogic(T,A,D){let P=this.getLaFuncFromCache(D),V;if(typeof A!="function"){V=A.DEF;let Z=A.GATE;if(Z!==void 0){let re=P;P=L(()=>Z.call(this)&&re.call(this),"lookAheadFunc")}}else V=A;if(P.call(this)===!0){let Z=this.doSingleRepetition(V);for(;P.call(this)===!0&&Z===!0;)Z=this.doSingleRepetition(V)}else throw this.raiseEarlyExitException(T,ca.REPETITION_MANDATORY,A.ERR_MSG);this.attemptInRepetitionRecovery(this.atLeastOneInternal,[T,A],P,NL,T,crt)}atLeastOneSepFirstInternal(T,A){let D=this.getKeyForAutomaticLookahead(Aw,T);this.atLeastOneSepFirstInternalLogic(T,A,D)}atLeastOneSepFirstInternalLogic(T,A,D){let P=A.DEF,V=A.SEP;if(this.getLaFuncFromCache(D).call(this)===!0){P.call(this);let Z=L(()=>this.tokenMatcher(this.LA(1),V),"separatorLookAheadFunc");for(;this.tokenMatcher(this.LA(1),V)===!0;)this.CONSUME(V),P.call(this);this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal,[T,V,Z,P,Aoe],Z,Aw,T,Aoe)}else throw this.raiseEarlyExitException(T,ca.REPETITION_MANDATORY_WITH_SEPARATOR,A.ERR_MSG)}manyInternal(T,A){let D=this.getKeyForAutomaticLookahead(DL,T);return this.manyInternalLogic(T,A,D)}manyInternalLogic(T,A,D){let P=this.getLaFuncFromCache(D),V;if(typeof A!="function"){V=A.DEF;let re=A.GATE;if(re!==void 0){let Q=P;P=L(()=>re.call(this)&&Q.call(this),"lookaheadFunction")}}else V=A;let Z=!0;for(;P.call(this)===!0&&Z===!0;)Z=this.doSingleRepetition(V);this.attemptInRepetitionRecovery(this.manyInternal,[T,A],P,DL,T,lrt,Z)}manySepFirstInternal(T,A){let D=this.getKeyForAutomaticLookahead(OL,T);this.manySepFirstInternalLogic(T,A,D)}manySepFirstInternalLogic(T,A,D){let P=A.DEF,V=A.SEP;if(this.getLaFuncFromCache(D).call(this)===!0){P.call(this);let Z=L(()=>this.tokenMatcher(this.LA(1),V),"separatorLookAheadFunc");for(;this.tokenMatcher(this.LA(1),V)===!0;)this.CONSUME(V),P.call(this);this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal,[T,V,Z,P,_oe],Z,OL,T,_oe)}}repetitionSepSecondInternal(T,A,D,P,V){for(;D();)this.CONSUME(A),P.call(this);this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal,[T,A,D,P,V],D,Aw,T,V)}doSingleRepetition(T){let A=this.getLexerPosition();return T.call(this),this.getLexerPosition()>A}orInternal(T,A){let D=this.getKeyForAutomaticLookahead(Boe,A),P=qi(T)?T:T.DEF,V=this.getLaFuncFromCache(D).call(this,P);if(V!==void 0)return P[V].ALT.call(this);this.raiseNoAltException(A,T.ERR_MSG)}ruleFinallyStateUpdate(){if(this.RULE_STACK.pop(),this.RULE_OCCURRENCE_STACK.pop(),this.cstFinallyStateUpdate(),this.RULE_STACK.length===0&&this.isAtEndOfInput()===!1){let T=this.LA(1),A=this.errorMessageProvider.buildNotAllInputParsedMessage({firstRedundant:T,ruleName:this.getCurrRuleFullName()});this.SAVE_ERROR(new frt(A,T))}}subruleInternal(T,A,D){let P;try{let V=D!==void 0?D.ARGS:void 0;return this.subruleIdx=A,P=T.apply(this,V),this.cstPostNonTerminal(P,D!==void 0&&D.LABEL!==void 0?D.LABEL:T.ruleName),P}catch(V){throw this.subruleInternalError(V,D,T.ruleName)}}subruleInternalError(T,A,D){throw Bv(T)&&T.partialCstResult!==void 0&&(this.cstPostNonTerminal(T.partialCstResult,A!==void 0&&A.LABEL!==void 0?A.LABEL:D),delete T.partialCstResult),T}consumeInternal(T,A,D){let P;try{let V=this.LA(1);this.tokenMatcher(V,T)===!0?(this.consumeToken(),P=V):this.consumeInternalError(T,V,D)}catch(V){P=this.consumeInternalRecovery(T,A,V)}return this.cstPostTerminal(D!==void 0&&D.LABEL!==void 0?D.LABEL:T.name,P),P}consumeInternalError(T,A,D){let P,V=this.LA(0);throw D!==void 0&&D.ERR_MSG?P=D.ERR_MSG:P=this.errorMessageProvider.buildMismatchTokenMessage({expected:T,actual:A,previous:V,ruleName:this.getCurrRuleFullName()}),this.SAVE_ERROR(new Noe(P,A,V))}consumeInternalRecovery(T,A,D){if(this.recoveryEnabled&&D.name==="MismatchedTokenException"&&!this.isBackTracking()){let P=this.getFollowsForInRuleRecovery(T,A);try{return this.tryInRuleRecovery(T,P)}catch(V){throw V.name===Ooe?D:V}}else throw D}saveRecogState(){let T=this.errors,A=_s(this.RULE_STACK);return{errors:T,lexerState:this.exportLexerState(),RULE_STACK:A,CST_STACK:this.CST_STACK}}reloadRecogState(T){this.errors=T.errors,this.importLexerState(T.lexerState),this.RULE_STACK=T.RULE_STACK}ruleInvocationStateUpdate(T,A,D){this.RULE_OCCURRENCE_STACK.push(D),this.RULE_STACK.push(T),this.cstInvocationStateUpdate(A)}isBackTracking(){return this.isBackTrackingStack.length!==0}getCurrRuleFullName(){let T=this.getLastExplicitRuleShortName();return this.shortRuleNameToFull[T]}shortRuleNameToFullName(T){return this.shortRuleNameToFull[T]}isAtEndOfInput(){return this.tokenMatcher(this.LA(1),Jd)}reset(){this.resetLexerState(),this.subruleIdx=0,this.isBackTrackingStack=[],this.errors=[],this.RULE_STACK=[],this.CST_STACK=[],this.RULE_OCCURRENCE_STACK=[]}},s(xe,"RecognizerEngine"),L(xe,"RecognizerEngine"),xe),_rt=($e=class{initErrorHandler(T){this._errors=[],this.errorMessageProvider=Kr(T,"errorMessageProvider")?T.errorMessageProvider:xh.errorMessageProvider}SAVE_ERROR(T){if(Bv(T))return T.context={ruleStack:this.getHumanReadableRuleStack(),ruleOccurrenceStack:_s(this.RULE_OCCURRENCE_STACK)},this._errors.push(T),T;throw Error("Trying to save an Error which is not a RecognitionException")}get errors(){return _s(this._errors)}set errors(T){this._errors=T}raiseEarlyExitException(T,A,D){let P=this.getCurrRuleFullName(),V=this.getGAstProductions()[P],Z=Nv(T,V,A,this.maxLookahead)[0],re=[];for(let ae=1;ae<=this.maxLookahead;ae++)re.push(this.LA(ae));let Q=this.errorMessageProvider.buildEarlyExitMessage({expectedIterationPaths:Z,actual:re,previous:this.LA(0),customUserDescription:D,ruleName:P});throw this.SAVE_ERROR(new grt(Q,this.LA(1),this.LA(0)))}raiseNoAltException(T,A){let D=this.getCurrRuleFullName(),P=this.getGAstProductions()[D],V=Dv(T,P,this.maxLookahead),Z=[];for(let ae=1;ae<=this.maxLookahead;ae++)Z.push(this.LA(ae));let re=this.LA(0),Q=this.errorMessageProvider.buildNoViableAltMessage({expectedPathsPerAlt:V,actual:Z,previous:re,customUserDescription:A,ruleName:this.getCurrRuleFullName()});throw this.SAVE_ERROR(new prt(Q,this.LA(1),re))}},s($e,"ErrorHandler"),L($e,"ErrorHandler"),$e),Art=(Me=class{initContentAssist(){}computeContentAssist(T,A){let D=this.gastProductionsCache[T];if(bh(D))throw Error(`Rule ->${T}<- does not exist in this grammar.`);return GR([D],A,this.tokenMatcher,this.maxLookahead)}getNextPossibleTokenTypes(T){let A=mc(T.ruleStack),D=this.getGAstProductions()[A];return new ort(D,T).startWalking()}},s(Me,"ContentAssist"),L(Me,"ContentAssist"),Me),$w={description:"This Object indicates the Parser is during Recording Phase"},Object.freeze($w),zoe=!0,qoe=Math.pow(2,ep)-1,Woe=Mm({name:"RECORDING_PHASE_TOKEN",pattern:bo.NA}),Im([Woe]),joe=Mv(Woe,`This IToken indicates the Parser is in Recording Phase + See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details`,-1,-1,-1,-1,-1,-1),Object.freeze(joe),Rrt={name:`This CSTNode indicates the Parser is in Recording Phase + See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details`,children:{}},$rt=(He=class{initGastRecorder(T){this.recordingProdStack=[],this.RECORDING_PHASE=!1}enableRecording(){this.RECORDING_PHASE=!0,this.TRACE_INIT("Enable Recording",()=>{for(let T=0;T<10;T++){let A=T>0?T:"";this[`CONSUME${A}`]=function(D,P){return this.consumeInternalRecord(D,T,P)},this[`SUBRULE${A}`]=function(D,P){return this.subruleInternalRecord(D,T,P)},this[`OPTION${A}`]=function(D){return this.optionInternalRecord(D,T)},this[`OR${A}`]=function(D){return this.orInternalRecord(D,T)},this[`MANY${A}`]=function(D){this.manyInternalRecord(T,D)},this[`MANY_SEP${A}`]=function(D){this.manySepFirstInternalRecord(T,D)},this[`AT_LEAST_ONE${A}`]=function(D){this.atLeastOneInternalRecord(T,D)},this[`AT_LEAST_ONE_SEP${A}`]=function(D){this.atLeastOneSepFirstInternalRecord(T,D)}}this.consume=function(T,A,D){return this.consumeInternalRecord(A,T,D)},this.subrule=function(T,A,D){return this.subruleInternalRecord(A,T,D)},this.option=function(T,A){return this.optionInternalRecord(A,T)},this.or=function(T,A){return this.orInternalRecord(A,T)},this.many=function(T,A){this.manyInternalRecord(T,A)},this.atLeastOne=function(T,A){this.atLeastOneInternalRecord(T,A)},this.ACTION=this.ACTION_RECORD,this.BACKTRACK=this.BACKTRACK_RECORD,this.LA=this.LA_RECORD})}disableRecording(){this.RECORDING_PHASE=!1,this.TRACE_INIT("Deleting Recording methods",()=>{let T=this;for(let A=0;A<10;A++){let D=A>0?A:"";delete T[`CONSUME${D}`],delete T[`SUBRULE${D}`],delete T[`OPTION${D}`],delete T[`OR${D}`],delete T[`MANY${D}`],delete T[`MANY_SEP${D}`],delete T[`AT_LEAST_ONE${D}`],delete T[`AT_LEAST_ONE_SEP${D}`]}delete T.consume,delete T.subrule,delete T.option,delete T.or,delete T.many,delete T.atLeastOne,delete T.ACTION,delete T.BACKTRACK,delete T.LA})}ACTION_RECORD(T){}BACKTRACK_RECORD(T,A){return()=>!0}LA_RECORD(T){return Lw}topLevelRuleRecord(T,A){try{let D=new Zm({definition:[],name:T});return D.name=T,this.recordingProdStack.push(D),A.call(this),this.recordingProdStack.pop(),D}catch(D){if(D.KNOWN_RECORDER_ERROR!==!0)try{D.message=D.message+` + This error was thrown during the "grammar recording phase" For more info see: + https://chevrotain.io/docs/guide/internals.html#grammar-recording`}catch{throw D}throw D}}optionInternalRecord(T,A){return Dm.call(this,Rs,T,A)}atLeastOneInternalRecord(T,A){Dm.call(this,gl,A,T)}atLeastOneSepFirstInternalRecord(T,A){Dm.call(this,ml,A,T,zoe)}manyInternalRecord(T,A){Dm.call(this,la,A,T)}manySepFirstInternalRecord(T,A){Dm.call(this,Wo,A,T,zoe)}orInternalRecord(T,A){return Nte.call(this,T,A)}subruleInternalRecord(T,A,D){if(Pv(A),!T||Kr(T,"ruleName")===!1){let re=new Error(` argument is invalid expecting a Parser method reference but got: <${JSON.stringify(T)}> + inside top level rule: <${this.recordingProdStack[0].name}>`);throw re.KNOWN_RECORDER_ERROR=!0,re}let P=l0(this.recordingProdStack),V=T.ruleName,Z=new vo({idx:A,nonTerminalName:V,label:D?.LABEL,referencedRule:void 0});return P.definition.push(Z),this.outputCst?Rrt:$w}consumeInternalRecord(T,A,D){if(Pv(A),!VR(T)){let Z=new Error(` argument is invalid expecting a TokenType reference but got: <${JSON.stringify(T)}> + inside top level rule: <${this.recordingProdStack[0].name}>`);throw Z.KNOWN_RECORDER_ERROR=!0,Z}let P=l0(this.recordingProdStack),V=new Pn({idx:A,terminalType:T,label:D?.LABEL});return P.definition.push(V),joe}},s(He,"GastRecorder"),L(He,"GastRecorder"),He),s(Dm,"recordProd"),L(Dm,"recordProd"),s(Nte,"recordOrProd"),L(Nte,"recordOrProd"),s(n$,"getIdxSuffix"),L(n$,"getIdxSuffix"),s(Pv,"assertMethodIdxIsValid"),L(Pv,"assertMethodIdxIsValid"),Lrt=(Ge=class{initPerformanceTracer(T){if(Kr(T,"traceInitPerf")){let A=T.traceInitPerf,D=typeof A=="number";this.traceInitMaxIdent=D?A:1/0,this.traceInitPerf=D?A>0:A}else this.traceInitMaxIdent=0,this.traceInitPerf=xh.traceInitPerf;this.traceInitIndent=-1}TRACE_INIT(T,A){if(this.traceInitPerf===!0){this.traceInitIndent++;let D=new Array(this.traceInitIndent+1).join(" ");this.traceInitIndent <${T}>`);let{time:P,value:V}=NR(A),Z=P>10?console.warn:console.log;return this.traceInitIndent time: ${P}ms`),this.traceInitIndent--,V}else return A()}},s(Ge,"PerformanceTracer"),L(Ge,"PerformanceTracer"),Ge),s(Ote,"applyMixins"),L(Ote,"applyMixins"),Lw=Mv(Jd,"",NaN,NaN,NaN,NaN,NaN,NaN),Object.freeze(Lw),xh=Object.freeze({recoveryEnabled:!1,maxLookahead:3,dynamicTokensEnabled:!1,outputCst:!0,errorMessageProvider:e1,nodeLocationTracking:"none",traceInitPerf:!1,skipValidations:!1}),Iw=Object.freeze({recoveryValueFunc:L(()=>{},"recoveryValueFunc"),resyncEnabled:!0}),(function(q){q[q.INVALID_RULE_NAME=0]="INVALID_RULE_NAME",q[q.DUPLICATE_RULE_NAME=1]="DUPLICATE_RULE_NAME",q[q.INVALID_RULE_OVERRIDE=2]="INVALID_RULE_OVERRIDE",q[q.DUPLICATE_PRODUCTIONS=3]="DUPLICATE_PRODUCTIONS",q[q.UNRESOLVED_SUBRULE_REF=4]="UNRESOLVED_SUBRULE_REF",q[q.LEFT_RECURSION=5]="LEFT_RECURSION",q[q.NONE_LAST_EMPTY_ALT=6]="NONE_LAST_EMPTY_ALT",q[q.AMBIGUOUS_ALTS=7]="AMBIGUOUS_ALTS",q[q.CONFLICT_TOKENS_RULES_NAMESPACE=8]="CONFLICT_TOKENS_RULES_NAMESPACE",q[q.INVALID_TOKEN_NAME=9]="INVALID_TOKEN_NAME",q[q.NO_NON_EMPTY_LOOKAHEAD=10]="NO_NON_EMPTY_LOOKAHEAD",q[q.AMBIGUOUS_PREFIX_ALTS=11]="AMBIGUOUS_PREFIX_ALTS",q[q.TOO_MANY_ALTS=12]="TOO_MANY_ALTS",q[q.CUSTOM_LOOKAHEAD_VALIDATION=13]="CUSTOM_LOOKAHEAD_VALIDATION"})(Vo||(Vo={})),s(a$,"EMPTY_ALT"),L(a$,"EMPTY_ALT"),PL=(ze=class{static performSelfAnalysis(T){throw Error("The **static** `performSelfAnalysis` method has been deprecated. \nUse the **instance** method with the same name instead.")}performSelfAnalysis(){this.TRACE_INIT("performSelfAnalysis",()=>{let T;this.selfAnalysisDone=!0;let A=this.className;this.TRACE_INIT("toFastProps",()=>{OR(this)}),this.TRACE_INIT("Grammar Recording",()=>{try{this.enableRecording(),ei(this.definedRulesNames,P=>{let V=this[P].originalGrammarAction,Z;this.TRACE_INIT(`${P} Rule`,()=>{Z=this.topLevelRuleRecord(P,V)}),this.gastProductionsCache[P]=Z})}finally{this.disableRecording()}});let D=[];if(this.TRACE_INIT("Grammar Resolving",()=>{D=wte({rules:Ha(this.gastProductionsCache)}),this.definitionErrors=this.definitionErrors.concat(D)}),this.TRACE_INIT("Grammar Validations",()=>{if(En(D)&&this.skipValidations===!1){let P=Ete({rules:Ha(this.gastProductionsCache),tokenTypes:Ha(this.tokensMap),errMsgProvider:u0,grammarName:A}),V=hte({lookaheadStrategy:this.lookaheadStrategy,rules:Ha(this.gastProductionsCache),tokenTypes:Ha(this.tokensMap),grammarName:A});this.definitionErrors=this.definitionErrors.concat(P,V)}}),En(this.definitionErrors)&&(this.recoveryEnabled&&this.TRACE_INIT("computeAllProdsFollows",()=>{let P=kee(Ha(this.gastProductionsCache));this.resyncFollows=P}),this.TRACE_INIT("ComputeLookaheadFunctions",()=>{var P,V;(V=(P=this.lookaheadStrategy).initialize)===null||V===void 0||V.call(P,{rules:Ha(this.gastProductionsCache)}),this.preComputeLookaheadFunctions(Ha(this.gastProductionsCache))})),!ze.DEFER_DEFINITION_ERRORS_HANDLING&&!En(this.definitionErrors))throw T=Fr(this.definitionErrors,P=>P.message),new Error(`Parser Definition Errors detected: + ${T.join(` +------------------------------- +`)}`)})}constructor(T,A){this.definitionErrors=[],this.selfAnalysisDone=!1;let D=this;if(D.initErrorHandler(A),D.initLexerAdapter(),D.initLooksAhead(A),D.initRecognizerEngine(T,A),D.initRecoverable(A),D.initTreeBuilder(A),D.initContentAssist(),D.initGastRecorder(A),D.initPerformanceTracer(A),Kr(A,"ignoredIssues"))throw new Error(`The IParserConfig property has been deprecated. + Please use the flag on the relevant DSL method instead. + See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#IGNORING_AMBIGUITIES + For further details.`);this.skipValidations=Kr(A,"skipValidations")?A.skipValidations:xh.skipValidations}},s(ze,"_Parser"),L(ze,"Parser"),ze),PL.DEFER_DEFINITION_ERRORS_HANDLING=!1,Ote(PL,[yrt,xrt,Crt,wrt,Srt,Ert,_rt,Art,$rt,Lrt]),Irt=(Ve=class extends PL{constructor(T,A=xh){let D=_s(A);D.outputCst=!1,super(T,D)}},s(Ve,"EmbeddedActionsParser"),L(Ve,"EmbeddedActionsParser"),Ve),s(Bte,"arrayMap2"),L(Bte,"arrayMap"),Voe=Bte,s(Pte,"listCacheClear2"),L(Pte,"listCacheClear"),Mrt=Pte,s(Fte,"eq2"),L(Fte,"eq"),Uoe=Fte,s(zte,"assocIndexOf2"),L(zte,"assocIndexOf"),Mw=zte,Drt=Array.prototype,Nrt=Drt.splice,s(qte,"listCacheDelete2"),L(qte,"listCacheDelete"),Ort=qte,s(Wte,"listCacheGet2"),L(Wte,"listCacheGet"),Brt=Wte,s(jte,"listCacheHas2"),L(jte,"listCacheHas"),Prt=jte,s(Vte,"listCacheSet2"),L(Vte,"listCacheSet"),Frt=Vte,s(Bf,"ListCache2"),L(Bf,"ListCache"),Bf.prototype.clear=Mrt,Bf.prototype.delete=Ort,Bf.prototype.get=Brt,Bf.prototype.has=Prt,Bf.prototype.set=Frt,Dw=Bf,s(Ute,"stackClear2"),L(Ute,"stackClear"),zrt=Ute,s(Yte,"stackDelete2"),L(Yte,"stackDelete"),qrt=Yte,s(Hte,"stackGet2"),L(Hte,"stackGet"),Wrt=Hte,s(Gte,"stackHas2"),L(Gte,"stackHas"),jrt=Gte,Vrt=typeof global=="object"&&global&&global.Object===Object&&global,Yoe=Vrt,Urt=typeof self=="object"&&self&&self.Object===Object&&self,Yrt=Yoe||Urt||Function("return this")(),Th=Yrt,Hrt=Th.Symbol,su=Hrt,Hoe=Object.prototype,Grt=Hoe.hasOwnProperty,Xrt=Hoe.toString,Cb=su?su.toStringTag:void 0,s(Xte,"getRawTag2"),L(Xte,"getRawTag"),Krt=Xte,Qrt=Object.prototype,Zrt=Qrt.toString,s(Kte,"objectToString2"),L(Kte,"objectToString"),Jrt=Kte,eit="[object Null]",tit="[object Undefined]",Goe=su?su.toStringTag:void 0,s(Qte,"baseGetTag2"),L(Qte,"baseGetTag"),t1=Qte,s(Zte,"isObject2"),L(Zte,"isObject"),FL=Zte,rit="[object AsyncFunction]",iit="[object Function]",nit="[object GeneratorFunction]",ait="[object Proxy]",s(Jte,"isFunction2"),L(Jte,"isFunction"),Xoe=Jte,sit=Th["__core-js_shared__"],zL=sit,Koe=(function(){var q=/[^.]+$/.exec(zL&&zL.keys&&zL.keys.IE_PROTO||"");return q?"Symbol(src)_1."+q:""})(),s(ere,"isMasked2"),L(ere,"isMasked"),oit=ere,lit=Function.prototype,cit=lit.toString,s(tre,"toSource2"),L(tre,"toSource"),h0=tre,uit=/[\\^$.*+?()[\]{}|]/g,hit=/^\[object .+?Constructor\]$/,dit=Function.prototype,pit=Object.prototype,fit=dit.toString,git=pit.hasOwnProperty,mit=RegExp("^"+fit.call(git).replace(uit,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),s(rre,"baseIsNative2"),L(rre,"baseIsNative"),yit=rre,s(ire,"getValue2"),L(ire,"getValue"),vit=ire,s(nre,"getNative2"),L(nre,"getNative"),r1=nre,bit=r1(Th,"Map"),wb=bit,xit=r1(Object,"create"),Eb=xit,s(are,"hashClear2"),L(are,"hashClear"),Tit=are,s(sre,"hashDelete2"),L(sre,"hashDelete"),kit=sre,Cit="__lodash_hash_undefined__",wit=Object.prototype,Eit=wit.hasOwnProperty,s(ore,"hashGet2"),L(ore,"hashGet"),Sit=ore,_it=Object.prototype,Ait=_it.hasOwnProperty,s(lre,"hashHas2"),L(lre,"hashHas"),Rit=lre,$it="__lodash_hash_undefined__",s(cre,"hashSet2"),L(cre,"hashSet"),Lit=cre,s(Pf,"Hash2"),L(Pf,"Hash"),Pf.prototype.clear=Tit,Pf.prototype.delete=kit,Pf.prototype.get=Sit,Pf.prototype.has=Rit,Pf.prototype.set=Lit,Qoe=Pf,s(ure,"mapCacheClear2"),L(ure,"mapCacheClear"),Iit=ure,s(hre,"isKeyable2"),L(hre,"isKeyable"),Mit=hre,s(dre,"getMapData2"),L(dre,"getMapData"),Nw=dre,s(pre,"mapCacheDelete2"),L(pre,"mapCacheDelete"),Dit=pre,s(fre,"mapCacheGet2"),L(fre,"mapCacheGet"),Nit=fre,s(gre,"mapCacheHas2"),L(gre,"mapCacheHas"),Oit=gre,s(mre,"mapCacheSet2"),L(mre,"mapCacheSet"),Bit=mre,s(Ff,"MapCache2"),L(Ff,"MapCache"),Ff.prototype.clear=Iit,Ff.prototype.delete=Dit,Ff.prototype.get=Nit,Ff.prototype.has=Oit,Ff.prototype.set=Bit,Ow=Ff,Pit=200,s(yre,"stackSet2"),L(yre,"stackSet"),Fit=yre,s(zf,"Stack2"),L(zf,"Stack"),zf.prototype.clear=zrt,zf.prototype.delete=qrt,zf.prototype.get=Wrt,zf.prototype.has=jrt,zf.prototype.set=Fit,Bw=zf,zit="__lodash_hash_undefined__",s(vre,"setCacheAdd2"),L(vre,"setCacheAdd"),qit=vre,s(bre,"setCacheHas2"),L(bre,"setCacheHas"),Wit=bre,s(Fv,"SetCache2"),L(Fv,"SetCache"),Fv.prototype.add=Fv.prototype.push=qit,Fv.prototype.has=Wit,Zoe=Fv,s(xre,"arraySome2"),L(xre,"arraySome"),jit=xre,s(Tre,"cacheHas2"),L(Tre,"cacheHas"),Joe=Tre,Vit=1,Uit=2,s(kre,"equalArrays2"),L(kre,"equalArrays"),ele=kre,Yit=Th.Uint8Array,tle=Yit,s(Cre,"mapToArray2"),L(Cre,"mapToArray"),Hit=Cre,s(wre,"setToArray2"),L(wre,"setToArray"),qL=wre,Git=1,Xit=2,Kit="[object Boolean]",Qit="[object Date]",Zit="[object Error]",Jit="[object Map]",ent="[object Number]",tnt="[object RegExp]",rnt="[object Set]",int="[object String]",nnt="[object Symbol]",ant="[object ArrayBuffer]",snt="[object DataView]",rle=su?su.prototype:void 0,WL=rle?rle.valueOf:void 0,s(Ere,"equalByTag2"),L(Ere,"equalByTag"),ont=Ere,s(Sre,"arrayPush2"),L(Sre,"arrayPush"),ile=Sre,lnt=Array.isArray,xo=lnt,s(_re,"baseGetAllKeys2"),L(_re,"baseGetAllKeys"),cnt=_re,s(Are,"arrayFilter2"),L(Are,"arrayFilter"),nle=Are,s(Rre,"stubArray2"),L(Rre,"stubArray"),unt=Rre,hnt=Object.prototype,dnt=hnt.propertyIsEnumerable,ale=Object.getOwnPropertySymbols,pnt=ale?function(q){return q==null?[]:(q=Object(q),nle(ale(q),function(T){return dnt.call(q,T)}))}:unt,fnt=pnt,s($re,"baseTimes2"),L($re,"baseTimes"),gnt=$re,s(Lre,"isObjectLike2"),L(Lre,"isObjectLike"),i1=Lre,mnt="[object Arguments]",s(Ire,"baseIsArguments2"),L(Ire,"baseIsArguments"),sle=Ire,ole=Object.prototype,ynt=ole.hasOwnProperty,vnt=ole.propertyIsEnumerable,bnt=sle((function(){return arguments})())?sle:function(q){return i1(q)&&ynt.call(q,"callee")&&!vnt.call(q,"callee")},Pw=bnt,s(Mre,"stubFalse2"),L(Mre,"stubFalse"),xnt=Mre,lle=typeof exports=="object"&&exports&&!exports.nodeType&&exports,cle=lle&&typeof module=="object"&&module&&!module.nodeType&&module,Tnt=cle&&cle.exports===lle,ule=Tnt?Th.Buffer:void 0,knt=ule?ule.isBuffer:void 0,Cnt=knt||xnt,Fw=Cnt,wnt=9007199254740991,Ent=/^(?:0|[1-9]\d*)$/,s(Dre,"isIndex2"),L(Dre,"isIndex"),hle=Dre,Snt=9007199254740991,s(Nre,"isLength2"),L(Nre,"isLength"),jL=Nre,_nt="[object Arguments]",Ant="[object Array]",Rnt="[object Boolean]",$nt="[object Date]",Lnt="[object Error]",Int="[object Function]",Mnt="[object Map]",Dnt="[object Number]",Nnt="[object Object]",Ont="[object RegExp]",Bnt="[object Set]",Pnt="[object String]",Fnt="[object WeakMap]",znt="[object ArrayBuffer]",qnt="[object DataView]",Wnt="[object Float32Array]",jnt="[object Float64Array]",Vnt="[object Int8Array]",Unt="[object Int16Array]",Ynt="[object Int32Array]",Hnt="[object Uint8Array]",Gnt="[object Uint8ClampedArray]",Xnt="[object Uint16Array]",Knt="[object Uint32Array]",Fn={},Fn[Wnt]=Fn[jnt]=Fn[Vnt]=Fn[Unt]=Fn[Ynt]=Fn[Hnt]=Fn[Gnt]=Fn[Xnt]=Fn[Knt]=!0,Fn[_nt]=Fn[Ant]=Fn[znt]=Fn[Rnt]=Fn[qnt]=Fn[$nt]=Fn[Lnt]=Fn[Int]=Fn[Mnt]=Fn[Dnt]=Fn[Nnt]=Fn[Ont]=Fn[Bnt]=Fn[Pnt]=Fn[Fnt]=!1,s(Ore,"baseIsTypedArray2"),L(Ore,"baseIsTypedArray"),Qnt=Ore,s(Bre,"baseUnary2"),L(Bre,"baseUnary"),Znt=Bre,dle=typeof exports=="object"&&exports&&!exports.nodeType&&exports,Sb=dle&&typeof module=="object"&&module&&!module.nodeType&&module,Jnt=Sb&&Sb.exports===dle,VL=Jnt&&Yoe.process,eat=(function(){try{var q=Sb&&Sb.require&&Sb.require("util").types;return q||VL&&VL.binding&&VL.binding("util")}catch{}})(),ple=eat,fle=ple&&ple.isTypedArray,tat=fle?Znt(fle):Qnt,UL=tat,rat=Object.prototype,iat=rat.hasOwnProperty,s(Pre,"arrayLikeKeys2"),L(Pre,"arrayLikeKeys"),nat=Pre,aat=Object.prototype,s(Fre,"isPrototype2"),L(Fre,"isPrototype"),gle=Fre,s(zre,"overArg2"),L(zre,"overArg"),sat=zre,oat=sat(Object.keys,Object),lat=oat,cat=Object.prototype,uat=cat.hasOwnProperty,s(qre,"baseKeys2"),L(qre,"baseKeys"),mle=qre,s(Wre,"isArrayLike2"),L(Wre,"isArrayLike"),zw=Wre,s(jre,"keys2"),L(jre,"keys"),YL=jre,s(Vre,"getAllKeys2"),L(Vre,"getAllKeys"),yle=Vre,hat=1,dat=Object.prototype,pat=dat.hasOwnProperty,s(Ure,"equalObjects2"),L(Ure,"equalObjects"),fat=Ure,gat=r1(Th,"DataView"),HL=gat,mat=r1(Th,"Promise"),GL=mat,yat=r1(Th,"Set"),n1=yat,vat=r1(Th,"WeakMap"),XL=vat,vle="[object Map]",bat="[object Object]",ble="[object Promise]",xle="[object Set]",Tle="[object WeakMap]",kle="[object DataView]",xat=h0(HL),Tat=h0(wb),kat=h0(GL),Cat=h0(n1),wat=h0(XL),d0=t1,(HL&&d0(new HL(new ArrayBuffer(1)))!=kle||wb&&d0(new wb)!=vle||GL&&d0(GL.resolve())!=ble||n1&&d0(new n1)!=xle||XL&&d0(new XL)!=Tle)&&(d0=L(function(q){var T=t1(q),A=T==bat?q.constructor:void 0,D=A?h0(A):"";if(D)switch(D){case xat:return kle;case Tat:return vle;case kat:return ble;case Cat:return xle;case wat:return Tle}return T},"getTag")),KL=d0,Eat=1,Cle="[object Arguments]",wle="[object Array]",qw="[object Object]",Sat=Object.prototype,Ele=Sat.hasOwnProperty,s(Yre,"baseIsEqualDeep2"),L(Yre,"baseIsEqualDeep"),_at=Yre,s(s$,"baseIsEqual2"),L(s$,"baseIsEqual"),Sle=s$,Aat=1,Rat=2,s(Hre,"baseIsMatch2"),L(Hre,"baseIsMatch"),$at=Hre,s(Gre,"isStrictComparable2"),L(Gre,"isStrictComparable"),_le=Gre,s(Xre,"getMatchData2"),L(Xre,"getMatchData"),Lat=Xre,s(Kre,"matchesStrictComparable2"),L(Kre,"matchesStrictComparable"),Ale=Kre,s(Qre,"baseMatches2"),L(Qre,"baseMatches"),Iat=Qre,Mat="[object Symbol]",s(Zre,"isSymbol2"),L(Zre,"isSymbol"),Ww=Zre,Dat=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Nat=/^\w*$/,s(Jre,"isKey2"),L(Jre,"isKey"),QL=Jre,Oat="Expected a function",s(hC,"memoize2"),L(hC,"memoize"),hC.Cache=Ow,Bat=hC,Pat=500,s(eie,"memoizeCapped2"),L(eie,"memoizeCapped"),Fat=eie,zat=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,qat=/\\(\\)?/g,Wat=Fat(function(q){var T=[];return q.charCodeAt(0)===46&&T.push(""),q.replace(zat,function(A,D,P,V){T.push(P?V.replace(qat,"$1"):D||A)}),T}),jat=Wat,Vat=1/0,Rle=su?su.prototype:void 0,$le=Rle?Rle.toString:void 0,s(o$,"baseToString2"),L(o$,"baseToString"),Uat=o$,s(tie,"toString3"),L(tie,"toString"),Yat=tie,s(rie,"castPath2"),L(rie,"castPath"),Lle=rie,Hat=1/0,s(iie,"toKey2"),L(iie,"toKey"),jw=iie,s(nie,"baseGet2"),L(nie,"baseGet"),Ile=nie,s(aie,"get2"),L(aie,"get"),Gat=aie,s(sie,"baseHasIn2"),L(sie,"baseHasIn"),Xat=sie,s(oie,"hasPath2"),L(oie,"hasPath"),Kat=oie,s(lie,"hasIn2"),L(lie,"hasIn"),Qat=lie,Zat=1,Jat=2,s(cie,"baseMatchesProperty2"),L(cie,"baseMatchesProperty"),est=cie,s(uie,"identity2"),L(uie,"identity"),ZL=uie,s(hie,"baseProperty2"),L(hie,"baseProperty"),tst=hie,s(die,"basePropertyDeep2"),L(die,"basePropertyDeep"),rst=die,s(pie,"property2"),L(pie,"property"),ist=pie,s(fie,"baseIteratee2"),L(fie,"baseIteratee"),Vw=fie,s(gie,"createBaseFor2"),L(gie,"createBaseFor"),nst=gie,ast=nst(),sst=ast,s(mie,"baseForOwn2"),L(mie,"baseForOwn"),ost=mie,s(yie,"createBaseEach2"),L(yie,"createBaseEach"),lst=yie,cst=lst(ost),Uw=cst,s(vie,"baseMap2"),L(vie,"baseMap"),ust=vie,s(bie,"map2"),L(bie,"map"),kh=bie,s(xie,"baseFilter2"),L(xie,"baseFilter"),hst=xie,s(Tie,"filter2"),L(Tie,"filter"),dst=Tie,s(qf,"buildATNKey"),L(qf,"buildATNKey"),tp=1,pst=2,Mle=4,Dle=5,_b=7,fst=8,gst=9,mst=10,yst=11,Nle=12,JL=(et=class{constructor(T){this.target=T}isEpsilon(){return!1}},s(et,"AbstractTransition"),L(et,"AbstractTransition"),et),e9=(he=class extends JL{constructor(T,A){super(T),this.tokenType=A}},s(he,"AtomTransition"),L(he,"AtomTransition"),he),Ole=(ye=class extends JL{constructor(T){super(T)}isEpsilon(){return!0}},s(ye,"EpsilonTransition"),L(ye,"EpsilonTransition"),ye),t9=(be=class extends JL{constructor(T,A,D){super(T),this.rule=A,this.followState=D}isEpsilon(){return!0}},s(be,"RuleTransition"),L(be,"RuleTransition"),be),s(kie,"createATN"),L(kie,"createATN"),s(Cie,"createRuleStartAndStopATNStates"),L(Cie,"createRuleStartAndStopATNStates"),s(l$,"atom"),L(l$,"atom"),s(wie,"repetition"),L(wie,"repetition"),s(Eie,"repetitionSep"),L(Eie,"repetitionSep"),s(Sie,"repetitionMandatory"),L(Sie,"repetitionMandatory"),s(_ie,"repetitionMandatorySep"),L(_ie,"repetitionMandatorySep"),s(Aie,"alternation"),L(Aie,"alternation"),s(Rie,"option"),L(Rie,"option"),s(Wd,"block"),L(Wd,"block"),s(c$,"plus"),L(c$,"plus"),s(u$,"star"),L(u$,"star"),s($ie,"optional"),L($ie,"optional"),s(dh,"defineDecisionState"),L(dh,"defineDecisionState"),s(Wf,"makeAlts"),L(Wf,"makeAlts"),s(Lie,"getProdType2"),L(Lie,"getProdType"),s(Iie,"makeBlock"),L(Iie,"makeBlock"),s(dC,"tokenRef"),L(dC,"tokenRef"),s(Mie,"ruleRef"),L(Mie,"ruleRef"),s(Die,"buildRuleHandle"),L(Die,"buildRuleHandle"),s(ba,"epsilon"),L(ba,"epsilon"),s(Ya,"newState"),L(Ya,"newState"),s(pC,"addTransition"),L(pC,"addTransition"),s(Nie,"removeState"),L(Nie,"removeState"),Yw={},r9=(Ie=class{constructor(){this.map={},this.configs=[]}get size(){return this.configs.length}finalize(){this.map={}}add(T){let A=h$(T);A in this.map||(this.map[A]=this.configs.length,this.configs.push(T))}get elements(){return this.configs}get alts(){return kh(this.configs,T=>T.alt)}get key(){let T="";for(let A in this.map)T+=A+":";return T}},s(Ie,"ATNConfigSet"),L(Ie,"ATNConfigSet"),Ie),s(h$,"getATNConfigKey"),L(h$,"getATNConfigKey"),s(Oie,"baseExtremum"),L(Oie,"baseExtremum"),vst=Oie,s(Bie,"baseLt"),L(Bie,"baseLt"),bst=Bie,s(Pie,"min"),L(Pie,"min"),xst=Pie,Ble=su?su.isConcatSpreadable:void 0,s(Fie,"isFlattenable2"),L(Fie,"isFlattenable"),Tst=Fie,s(d$,"baseFlatten2"),L(d$,"baseFlatten"),Ple=d$,s(zie,"flatMap2"),L(zie,"flatMap"),kst=zie,s(qie,"baseFindIndex2"),L(qie,"baseFindIndex"),Cst=qie,s(Wie,"baseIsNaN2"),L(Wie,"baseIsNaN"),wst=Wie,s(jie,"strictIndexOf2"),L(jie,"strictIndexOf"),Est=jie,s(Vie,"baseIndexOf2"),L(Vie,"baseIndexOf"),Sst=Vie,s(Uie,"arrayIncludes2"),L(Uie,"arrayIncludes"),_st=Uie,s(Yie,"arrayIncludesWith2"),L(Yie,"arrayIncludesWith"),Ast=Yie,s(Hie,"noop2"),L(Hie,"noop"),Rst=Hie,$st=1/0,Lst=n1&&1/qL(new n1([,-0]))[1]==$st?function(q){return new n1(q)}:Rst,Ist=Lst,Mst=200,s(Gie,"baseUniq2"),L(Gie,"baseUniq"),Dst=Gie,s(Xie,"uniqBy"),L(Xie,"uniqBy"),Nst=Xie,s(Kie,"flatten2"),L(Kie,"flatten"),Ost=Kie,s(Qie,"arrayEach2"),L(Qie,"arrayEach"),Bst=Qie,s(Zie,"castFunction2"),L(Zie,"castFunction"),Pst=Zie,s(Jie,"forEach2"),L(Jie,"forEach"),i9=Jie,Fst="[object Map]",zst="[object Set]",qst=Object.prototype,Wst=qst.hasOwnProperty,s(ene,"isEmpty2"),L(ene,"isEmpty"),jst=ene,s(tne,"arrayReduce2"),L(tne,"arrayReduce"),Vst=tne,s(rne,"baseReduce2"),L(rne,"baseReduce"),Ust=rne,s(ine,"reduce2"),L(ine,"reduce"),Fle=ine,s(nne,"createDFACache"),L(nne,"createDFACache"),zle=(pe=class{constructor(){this.predicates=[]}is(T){return T>=this.predicates.length||this.predicates[T]}set(T,A){this.predicates[T]=A}toString(){let T="",A=this.predicates.length;for(let D=0;Dconsole.log(D))}initialize(T){this.atn=kie(T.rules),this.dfas=ane(this.atn)}validateAmbiguousAlternationAlternatives(){return[]}validateEmptyOrAlternatives(){return[]}buildLookaheadForAlternation(T){let{prodOccurrence:A,rule:D,hasPredicates:P,dynamicTokensEnabled:V}=T,Z=this.dfas,re=this.logging,Q=qf(D,"Alternation",A),ae=this.atn.decisionMap[Q].decision,we=kh(XR({maxLookahead:1,occurrence:A,prodType:"Alternation",rule:D}),Ue=>kh(Ue,je=>je[0]));if(p$(we,!1)&&!V){let Ue=Fle(we,(je,ut,at)=>(i9(ut,Rt=>{Rt&&(je[Rt.tokenTypeIdx]=at,i9(Rt.categoryMatches,Zt=>{je[Zt]=at}))}),je),{});return P?function(je){var ut;let at=this.LA(1),Rt=Ue[at.tokenTypeIdx];if(je!==void 0&&Rt!==void 0){let Zt=(ut=je[Rt])===null||ut===void 0?void 0:ut.GATE;if(Zt!==void 0&&Zt.call(this)===!1)return}return Rt}:function(){let je=this.LA(1);return Ue[je.tokenTypeIdx]}}else return P?function(Ue){let je=new zle,ut=Ue===void 0?0:Ue.length;for(let Rt=0;Rtkh(Ue,je=>je[0]));if(p$(we)&&we[0][0]&&!V){let Ue=we[0],je=Ost(Ue);if(je.length===1&&jst(je[0].categoryMatches)){let ut=je[0].tokenTypeIdx;return function(){return this.LA(1).tokenTypeIdx===ut}}else{let ut=Fle(je,(at,Rt)=>(Rt!==void 0&&(at[Rt.tokenTypeIdx]=!0,i9(Rt.categoryMatches,Zt=>{at[Zt]=!0})),at),{});return function(){let at=this.LA(1);return ut[at.tokenTypeIdx]===!0}}}return function(){let Ue=fC.call(this,Z,ae,qle,re);return typeof Ue=="object"?!1:Ue===0}}},s(Qe,"LLStarLookaheadStrategy"),L(Qe,"LLStarLookaheadStrategy"),Qe),s(p$,"isLL1Sequence"),L(p$,"isLL1Sequence"),s(ane,"initATNSimulator"),L(ane,"initATNSimulator"),s(fC,"adaptivePredict"),L(fC,"adaptivePredict"),s(sne,"performLookahead"),L(sne,"performLookahead"),s(one,"computeLookaheadTarget"),L(one,"computeLookaheadTarget"),s(lne,"reportLookaheadAmbiguity"),L(lne,"reportLookaheadAmbiguity"),s(cne,"buildAmbiguityError"),L(cne,"buildAmbiguityError"),s(une,"getProductionDslName2"),L(une,"getProductionDslName"),s(hne,"buildAdaptivePredictError"),L(hne,"buildAdaptivePredictError"),s(dne,"getExistingTargetState"),L(dne,"getExistingTargetState"),s(pne,"computeReachSet"),L(pne,"computeReachSet"),s(fne,"getReachableTarget"),L(fne,"getReachableTarget"),s(gne,"getUniqueAlt"),L(gne,"getUniqueAlt"),s(f$,"newDFAState"),L(f$,"newDFAState"),s(g$,"addDFAEdge"),L(g$,"addDFAEdge"),s(m$,"addDFAState"),L(m$,"addDFAState"),s(mne,"computeStartState"),L(mne,"computeStartState"),s(zv,"closure"),L(zv,"closure"),s(yne,"getEpsilonTarget"),L(yne,"getEpsilonTarget"),s(vne,"hasConfigInRuleStopState"),L(vne,"hasConfigInRuleStopState"),s(bne,"allConfigsInRuleStopStates"),L(bne,"allConfigsInRuleStopStates"),s(xne,"hasConflictTerminatingPrediction"),L(xne,"hasConflictTerminatingPrediction"),s(Tne,"getConflictingAltSets"),L(Tne,"getConflictingAltSets"),s(kne,"hasConflictingAltSet"),L(kne,"hasConflictingAltSet"),s(Cne,"hasStateAssociatedWithOneAlt"),L(Cne,"hasStateAssociatedWithOneAlt"),Gv(),Wle=(W=class{constructor(){this.nodeStack=[]}get current(){return this.nodeStack[this.nodeStack.length-1]??this.rootNode}buildRootNode(T){return this.rootNode=new a9(T),this.rootNode.root=this.rootNode,this.nodeStack=[this.rootNode],this.rootNode}buildCompositeNode(T){let A=new Gw;return A.grammarSource=T,A.root=this.rootNode,this.current.content.push(A),this.nodeStack.push(A),A}buildLeafNode(T,A){let D=new Hw(T.startOffset,T.image.length,Tv(T),T.tokenType,!A);return D.grammarSource=A,D.root=this.rootNode,this.current.content.push(D),D}removeNode(T){let A=T.container;if(A){let D=A.content.indexOf(T);D>=0&&A.content.splice(D,1)}}addHiddenNodes(T){let A=[];for(let V of T){let Z=new Hw(V.startOffset,V.image.length,Tv(V),V.tokenType,!0);Z.root=this.rootNode,A.push(Z)}let D=this.current,P=!1;if(D.content.length>0){D.content.push(...A);return}for(;D.container;){let V=D.container.content.indexOf(D);if(V>0){D.container.content.splice(V,0,...A),P=!0;break}D=D.container}P||this.rootNode.content.unshift(...A)}construct(T){let A=this.current;typeof T.$type=="string"&&!T.$infixName&&(this.current.astNode=T),T.$cstNode=A;let D=this.nodeStack.pop();D?.content.length===0&&this.removeNode(D)}},s(W,"CstNodeBuilder"),L(W,"CstNodeBuilder"),W),n9=(Ae=class{get hidden(){return!1}get astNode(){let T=typeof this._astNode?.$type=="string"?this._astNode:this.container?.astNode;if(!T)throw new Error("This node has no associated AST element");return T}set astNode(T){this._astNode=T}get text(){return this.root.fullText.substring(this.offset,this.end)}},s(Ae,"AbstractCstNode"),L(Ae,"AbstractCstNode"),Ae),Hw=(K=class extends n9{get offset(){return this._offset}get length(){return this._length}get end(){return this._offset+this._length}get hidden(){return this._hidden}get tokenType(){return this._tokenType}get range(){return this._range}constructor(T,A,D,P,V=!1){super(),this._hidden=V,this._offset=T,this._tokenType=P,this._length=A,this._range=D}},s(K,"LeafCstNodeImpl"),L(K,"LeafCstNodeImpl"),K),Gw=(de=class extends n9{constructor(){super(...arguments),this.content=new Hst(this)}get offset(){return this.firstNonHiddenNode?.offset??0}get length(){return this.end-this.offset}get end(){return this.lastNonHiddenNode?.end??0}get range(){let T=this.firstNonHiddenNode,A=this.lastNonHiddenNode;if(T&&A){if(this._rangeCache===void 0){let{range:D}=T,{range:P}=A;this._rangeCache={start:D.start,end:P.end.line=0;T--){let A=this.content[T];if(!A.hidden)return A}return this.content[this.content.length-1]}},s(de,"CompositeCstNodeImpl"),L(de,"CompositeCstNodeImpl"),de),Hst=(ee=class extends Array{constructor(T){super(),this.parent=T,Object.setPrototypeOf(this,ee.prototype)}push(...T){return this.addParents(T),super.push(...T)}unshift(...T){return this.addParents(T),super.unshift(...T)}splice(T,A,...D){return this.addParents(D),super.splice(T,A,...D)}addParents(T){for(let A of T)A.container=this.parent}},s(ee,"_CstNodeContainer"),L(ee,"CstNodeContainer"),ee),a9=(ve=class extends Gw{get text(){return this._text.substring(this.offset,this.end)}get fullText(){return this._text}constructor(T){super(),this._text="",this._text=T??""}},s(ve,"RootCstNodeImpl"),L(ve,"RootCstNodeImpl"),ve),Xw=Symbol("Datatype"),s(gC,"isDataTypeNode"),L(gC,"isDataTypeNode"),jle="​",Vle=L(q=>q.endsWith(jle)?q:q+jle,"withRuleSuffix"),s9=(X=class{constructor(T){this._unorderedGroups=new Map,this.allRules=new Map,this.lexer=T.parser.Lexer;let A=this.lexer.definition,D=T.LanguageMetaData.mode==="production";T.shared.profilers.LangiumProfiler?.isActive("parsing")?this.wrapper=new Xst(A,{...T.parser.ParserConfig,skipValidations:D,errorMessageProvider:T.parser.ParserErrorMessageProvider},T.shared.profilers.LangiumProfiler.createTask("parsing",T.LanguageMetaData.languageId)):this.wrapper=new Gle(A,{...T.parser.ParserConfig,skipValidations:D,errorMessageProvider:T.parser.ParserErrorMessageProvider})}alternatives(T,A){this.wrapper.wrapOr(T,A)}optional(T,A){this.wrapper.wrapOption(T,A)}many(T,A){this.wrapper.wrapMany(T,A)}atLeastOne(T,A){this.wrapper.wrapAtLeastOne(T,A)}getRule(T){return this.allRules.get(T)}isRecording(){return this.wrapper.IS_RECORDING}get unorderedGroups(){return this._unorderedGroups}getRuleStack(){return this.wrapper.RULE_STACK}finalize(){this.wrapper.wrapSelfAnalysis()}},s(X,"AbstractLangiumParser"),L(X,"AbstractLangiumParser"),X),Ule=(me=class extends s9{get current(){return this.stack[this.stack.length-1]}constructor(T){super(T),this.nodeBuilder=new Wle,this.stack=[],this.assignmentMap=new Map,this.operatorPrecedence=new Map,this.linker=T.references.Linker,this.converter=T.parser.ValueConverter,this.astReflection=T.shared.AstReflection}rule(T,A){let D=this.computeRuleType(T),P;Em(T)&&(P=T.name,this.registerPrecedenceMap(T));let V=this.wrapper.DEFINE_RULE(Vle(T.name),this.startImplementation(D,P,A).bind(this));return this.allRules.set(T.name,V),go(T)&&T.entry&&(this.mainRule=V),V}registerPrecedenceMap(T){let A=T.name,D=new Map;for(let P=0;P0&&(A=this.construct()),A===void 0)throw new Error("No result from parser");if(this.stack.length>0)throw new Error("Parser stack is not empty after parsing");return A}startImplementation(T,A,D){return P=>{let V=!this.isRecording()&&T!==void 0;if(V){let Z={$type:T};this.stack.push(Z),T===Xw?Z.value="":A!==void 0&&(Z.$infixName=A)}return D(P),V?this.construct():void 0}}extractHiddenTokens(T){let A=this.lexerResult.hidden;if(!A.length)return[];let D=T.startOffset;for(let P=0;PD)return A.splice(0,P);return A.splice(0,A.length)}consume(T,A,D){let P=this.wrapper.wrapConsume(T,A);if(!this.isRecording()&&this.isValidToken(P)){let V=this.extractHiddenTokens(P);this.nodeBuilder.addHiddenNodes(V);let Z=this.nodeBuilder.buildLeafNode(P,D),{assignment:re,crossRef:Q}=this.getAssignment(D),ae=this.current;if(re){let we=ch(D)?P.image:this.converter.convert(P.image,Z);this.assign(re.operator,re.feature,we,Z,Q)}else if(gC(ae)){let we=P.image;ch(D)||(we=this.converter.convert(we,Z).toString()),ae.value+=we}}}isValidToken(T){return!T.isInsertedInRecovery&&!isNaN(T.startOffset)&&typeof T.endOffset=="number"&&!isNaN(T.endOffset)}subrule(T,A,D,P,V){let Z;!this.isRecording()&&!D&&(Z=this.nodeBuilder.buildCompositeNode(P));let re;try{re=this.wrapper.wrapSubrule(T,A,V)}finally{this.isRecording()||(re===void 0&&!D&&(re=this.construct()),re!==void 0&&Z&&Z.length>0&&this.performSubruleAssignment(re,P,Z))}}performSubruleAssignment(T,A,D){let{assignment:P,crossRef:V}=this.getAssignment(A);if(P)this.assign(P.operator,P.feature,T,D,V);else if(!P){let Z=this.current;if(gC(Z))Z.value+=T.toString();else if(typeof T=="object"&&T){let re=this.assignWithoutOverride(T,Z);this.stack.pop(),this.stack.push(re)}}}action(T,A){if(!this.isRecording()){let D=this.current;if(A.feature&&A.operator){D=this.construct(),this.nodeBuilder.removeNode(D.$cstNode),this.nodeBuilder.buildCompositeNode(A).content.push(D.$cstNode);let P={$type:T};this.stack.push(P),this.assign(A.operator,A.feature,D,D.$cstNode)}else D.$type=T}}construct(){if(this.isRecording())return;let T=this.stack.pop();return this.nodeBuilder.construct(T),"$infixName"in T?this.constructInfix(T,this.operatorPrecedence.get(T.$infixName)):gC(T)?this.converter.convert(T.value,T.$cstNode):(P8(this.astReflection,T),T)}constructInfix(T,A){let D=T.parts;if(!Array.isArray(D)||D.length===0)return;let P=T.operators;if(!Array.isArray(P)||D.length<2)return D[0];let V=0,Z=-1;for(let Rt=0;RtZ?(Z=Yt.precedence,V=Rt):Yt.precedence===Z&&(Yt.rightAssoc||(V=Rt))}let re=P.slice(0,V),Q=P.slice(V+1),ae=D.slice(0,V+1),we=D.slice(V+1),Ue={$infixName:T.$infixName,$type:T.$type,$cstNode:T.$cstNode,parts:ae,operators:re},je={$infixName:T.$infixName,$type:T.$type,$cstNode:T.$cstNode,parts:we,operators:Q},ut=this.constructInfix(Ue,A),at=this.constructInfix(je,A);return{$type:T.$type,$cstNode:T.$cstNode,left:ut,operator:P[V],right:at}}getAssignment(T){if(!this.assignmentMap.has(T)){let A=Ef(T,lh);this.assignmentMap.set(T,{assignment:A,crossRef:A&&_f(A.terminal)?A.terminal.isMulti?"multi":"single":void 0})}return this.assignmentMap.get(T)}assign(T,A,D,P,V){let Z=this.current,re;switch(V==="single"&&typeof D=="string"?re=this.linker.buildReference(Z,A,P,D):V==="multi"&&typeof D=="string"?re=this.linker.buildMultiReference(Z,A,P,D):re=D,T){case"=":{Z[A]=re;break}case"?=":{Z[A]=!0;break}case"+=":Array.isArray(Z[A])||(Z[A]=[]),Z[A].push(re)}}assignWithoutOverride(T,A){for(let[P,V]of Object.entries(A)){let Z=T[P];Z===void 0?T[P]=V:Array.isArray(Z)&&Array.isArray(V)&&(V.push(...Z),T[P]=V)}let D=T.$cstNode;return D&&(D.astNode=void 0,T.$cstNode=void 0),T}get definitionErrors(){return this.wrapper.definitionErrors}},s(me,"LangiumParser"),L(me,"LangiumParser"),me),Yle=(Ce=class{buildMismatchTokenMessage(T){return e1.buildMismatchTokenMessage(T)}buildNotAllInputParsedMessage(T){return e1.buildNotAllInputParsedMessage(T)}buildNoViableAltMessage(T){return e1.buildNoViableAltMessage(T)}buildEarlyExitMessage(T){return e1.buildEarlyExitMessage(T)}},s(Ce,"AbstractParserErrorMessageProvider"),L(Ce,"AbstractParserErrorMessageProvider"),Ce),o9=(Be=class extends Yle{buildMismatchTokenMessage({expected:T,actual:A}){return`Expecting ${T.LABEL?"`"+T.LABEL+"`":T.name.endsWith(":KW")?`keyword '${T.name.substring(0,T.name.length-3)}'`:`token of type '${T.name}'`} but found \`${A.image}\`.`}buildNotAllInputParsedMessage({firstRedundant:T}){return`Expecting end of file but found \`${T.image}\`.`}},s(Be,"LangiumParserErrorMessageProvider"),L(Be,"LangiumParserErrorMessageProvider"),Be),Hle=(Re=class extends s9{constructor(){super(...arguments),this.tokens=[],this.elementStack=[],this.lastElementStack=[],this.nextTokenIndex=0,this.stackSize=0}action(){}construct(){}parse(T){this.resetState();let A=this.lexer.tokenize(T,{mode:"partial"});return this.tokens=A.tokens,this.wrapper.input=[...this.tokens],this.mainRule.call(this.wrapper,{}),this.unorderedGroups.clear(),{tokens:this.tokens,elementStack:[...this.lastElementStack],tokenIndex:this.nextTokenIndex}}rule(T,A){let D=this.wrapper.DEFINE_RULE(Vle(T.name),this.startImplementation(A).bind(this));return this.allRules.set(T.name,D),T.entry&&(this.mainRule=D),D}resetState(){this.elementStack=[],this.lastElementStack=[],this.nextTokenIndex=0,this.stackSize=0}startImplementation(T){return A=>{let D=this.keepStackSize();try{T(A)}finally{this.resetStackSize(D)}}}removeUnexpectedElements(){this.elementStack.splice(this.stackSize)}keepStackSize(){let T=this.elementStack.length;return this.stackSize=T,T}resetStackSize(T){this.removeUnexpectedElements(),this.stackSize=T}consume(T,A,D){this.wrapper.wrapConsume(T,A),this.isRecording()||(this.lastElementStack=[...this.elementStack,D],this.nextTokenIndex=this.currIdx+1)}subrule(T,A,D,P,V){this.before(P),this.wrapper.wrapSubrule(T,A,V),this.after(P)}before(T){this.isRecording()||this.elementStack.push(T)}after(T){if(!this.isRecording()){let A=this.elementStack.lastIndexOf(T);A>=0&&this.elementStack.splice(A)}}get currIdx(){return this.wrapper.currIdx}},s(Re,"LangiumCompletionParser"),L(Re,"LangiumCompletionParser"),Re),Gst={recoveryEnabled:!0,nodeLocationTracking:"full",skipValidations:!0,errorMessageProvider:new o9},Gle=(Ke=class extends Irt{constructor(T,A){let D=A&&"maxLookahead"in A;super(T,{...Gst,lookaheadStrategy:D?new BL({maxLookahead:A.maxLookahead}):new Yst({logging:A.skipValidations?()=>{}:void 0}),...A})}get IS_RECORDING(){return this.RECORDING_PHASE}DEFINE_RULE(T,A,D){return this.RULE(T,A,D)}wrapSelfAnalysis(){this.performSelfAnalysis()}wrapConsume(T,A){return this.consume(T,A,void 0)}wrapSubrule(T,A,D){return this.subrule(T,A,{ARGS:[D]})}wrapOr(T,A){this.or(T,A)}wrapOption(T,A){this.option(T,A)}wrapMany(T,A){this.many(T,A)}wrapAtLeastOne(T,A){this.atLeastOne(T,A)}rule(T){return T.call(this,{})}},s(Ke,"ChevrotainWrapper"),L(Ke,"ChevrotainWrapper"),Ke),Xst=(De=class extends Gle{constructor(T,A,D){super(T,A),this.task=D}rule(T){this.task.start(),this.task.startSubTask(this.ruleName(T));try{return super.rule(T)}finally{this.task.stopSubTask(this.ruleName(T)),this.task.stop()}}ruleName(T){return T.ruleName}subrule(T,A,D){this.task.startSubTask(this.ruleName(A));try{return super.subrule(T,A,D)}finally{this.task.stopSubTask(this.ruleName(A))}}},s(De,"ProfilerWrapper"),L(De,"ProfilerWrapper"),De),s(mC,"createParser"),L(mC,"createParser"),s(wne,"buildRules"),L(wne,"buildRules"),s(Ene,"buildInfixRule"),L(Ene,"buildInfixRule"),s(jd,"buildElement"),L(jd,"buildElement"),s(Sne,"buildAction"),L(Sne,"buildAction"),s(_ne,"buildRuleCall"),L(_ne,"buildRuleCall"),s(Ane,"buildRuleCallPredicate"),L(Ane,"buildRuleCallPredicate"),s(hc,"buildPredicate"),L(hc,"buildPredicate"),s(Rne,"buildAlternatives"),L(Rne,"buildAlternatives"),s($ne,"buildUnorderedGroup"),L($ne,"buildUnorderedGroup"),s(Lne,"buildGroup"),L(Lne,"buildGroup"),s(qv,"getGuardCondition"),L(qv,"getGuardCondition"),s(y$,"buildCrossReference"),L(y$,"buildCrossReference"),s(Ine,"buildKeyword"),L(Ine,"buildKeyword"),s(v$,"wrap"),L(v$,"wrap"),s(yC,"getRule"),L(yC,"getRule"),s(Mne,"getRuleName"),L(Mne,"getRuleName"),s(vC,"getToken"),L(vC,"getToken"),s(b$,"createCompletionParser"),L(b$,"createCompletionParser"),s(x$,"createLangiumParser"),L(x$,"createLangiumParser"),s(T$,"prepareLangiumParser"),L(T$,"prepareLangiumParser"),Kw=(Ze=class{constructor(){this.diagnostics=[]}buildTokens(T,A){let D=an(Hk(T,!1)),P=this.buildTerminalTokens(D),V=this.buildKeywordTokens(D,P,A);return V.push(...P),V}flushLexingReport(T){return{diagnostics:this.popDiagnostics()}}popDiagnostics(){let T=[...this.diagnostics];return this.diagnostics=[],T}buildTerminalTokens(T){return T.filter(Ml).filter(A=>!A.fragment).map(A=>this.buildTerminalToken(A)).toArray()}buildTerminalToken(T){let A=Ev(T),D=this.requiresCustomPattern(A)?this.regexPatternFunction(A):A,P={name:T.name,PATTERN:D};return typeof D=="function"&&(P.LINE_BREAKS=!0),T.hidden&&(P.GROUP=Yk(A)?bo.SKIPPED:"hidden"),P}requiresCustomPattern(T){return!!(T.flags.includes("u")||T.flags.includes("s"))}regexPatternFunction(T){let A=new RegExp(T,T.flags+"y");return(D,P)=>(A.lastIndex=P,A.exec(D))}buildKeywordTokens(T,A,D){return T.filter(Sf).flatMap(P=>oh(P).filter(ch)).distinct(P=>P.value).toArray().sort((P,V)=>V.value.length-P.value.length).map(P=>this.buildKeywordToken(P,A,!!D?.caseInsensitive))}buildKeywordToken(T,A,D){let P=this.buildKeywordPattern(T,D),V={name:T.value,PATTERN:P,LONGER_ALT:this.findLongerAlt(T,A)};return typeof P=="function"&&(V.LINE_BREAKS=!0),V}buildKeywordPattern(T,A){return A?new RegExp(Rm(T.value),"i"):T.value}findLongerAlt(T,A){return A.reduce((D,P)=>{let V=P?.PATTERN;return V?.source&&gR("^"+V.source+"$",T.value)&&D.push(P),D},[])}},s(Ze,"DefaultTokenBuilder"),L(Ze,"DefaultTokenBuilder"),Ze),l9=(Fe=class{convert(T,A){let D=A.grammarSource;if(_f(D)&&(D=xR(D)),uh(D)){let P=D.rule.ref;if(!P)throw new Error("This cst node was not parsed by a rule.");return this.runConverter(P,T,A)}return T}runConverter(T,A,D){switch(T.name.toUpperCase()){case"INT":return Ch.convertInt(A);case"STRING":return Ch.convertString(A);case"ID":return Ch.convertID(A)}switch(RR(T)?.toLowerCase()){case"number":return Ch.convertNumber(A);case"boolean":return Ch.convertBoolean(A);case"bigint":return Ch.convertBigint(A);case"date":return Ch.convertDate(A);default:return A}}},s(Fe,"DefaultValueConverter"),L(Fe,"DefaultValueConverter"),Fe),(function(q){function T(ae){let we="";for(let Ue=1;Ue{this.resolve=D=>(T(D),this),this.reject=D=>(A(D),this)})}},s(Je,"Deferred"),L(Je,"Deferred"),Je),Kle=(Le=class{constructor(T,A,D,P){this._uri=T,this._languageId=A,this._version=D,this._content=P,this._lineOffsets=void 0}get uri(){return this._uri}get languageId(){return this._languageId}get version(){return this._version}getText(T){if(T){let A=this.offsetAt(T.start),D=this.offsetAt(T.end);return this._content.substring(A,D)}return this._content}update(T,A){for(let D of T)if(Le.isIncremental(D)){let P=E$(D.range),V=this.offsetAt(P.start),Z=this.offsetAt(P.end);this._content=this._content.substring(0,V)+D.text+this._content.substring(Z,this._content.length);let re=Math.max(P.start.line,0),Q=Math.max(P.end.line,0),ae=this._lineOffsets,we=C$(D.text,!1,V);if(Q-re===we.length)for(let je=0,ut=we.length;jeT?P=Z:D=Z+1}let V=D-1;return T=this.ensureBeforeEOL(T,A[V]),{line:V,character:T-A[V]}}offsetAt(T){let A=this.getLineOffsets();if(T.line>=A.length)return this._content.length;if(T.line<0)return 0;let D=A[T.line];if(T.character<=0)return D;let P=T.line+1A&&w$(this._content.charCodeAt(T-1));)T--;return T}get lineCount(){return this.getLineOffsets().length}static isIncremental(T){let A=T;return A!=null&&typeof A.text=="string"&&A.range!==void 0&&(A.rangeLength===void 0||typeof A.rangeLength=="number")}static isFull(T){let A=T;return A!=null&&typeof A.text=="string"&&A.range===void 0&&A.rangeLength===void 0}},s(Le,"_FullTextDocument"),L(Le,"FullTextDocument"),Le),(function(q){function T(P,V,Z,re){return new Kle(P,V,Z,re)}s(T,"create"),L(T,"create"),q.create=T;function A(P,V,Z){if(P instanceof Kle)return P.update(V,Z),P;throw new Error("TextDocument.update: document must be created by TextDocument.create")}s(A,"update"),L(A,"update"),q.update=A;function D(P,V){let Z=P.getText(),re=TC(V.map(Dne),(we,Ue)=>{let je=we.range.start.line-Ue.range.start.line;return je===0?we.range.start.character-Ue.range.start.character:je}),Q=0,ae=[];for(let we of re){let Ue=P.offsetAt(we.range.start);if(UeQ&&ae.push(Z.substring(Q,Ue)),we.newText.length&&ae.push(we.newText),Q=P.offsetAt(we.range.end)}return ae.push(Z.substr(Q)),ae.join("")}s(D,"applyEdits"),L(D,"applyEdits"),q.applyEdits=D})(c9||(c9={})),s(TC,"mergeSort"),L(TC,"mergeSort"),s(C$,"computeLineOffsets"),L(C$,"computeLineOffsets"),s(w$,"isEOL"),L(w$,"isEOL"),s(E$,"getWellformedRange"),L(E$,"getWellformedRange"),s(Dne,"getWellformedEdit"),L(Dne,"getWellformedEdit"),(()=>{"use strict";var q={975:Ut=>{function yt(dt){if(typeof dt!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(dt))}s(yt,"e2"),L(yt,"e");function ht(dt,pt){for(var Ct,rr="",hr=0,Gt=-1,Pr=0,wr=0;wr<=dt.length;++wr){if(wr2){var ui=rr.lastIndexOf("/");if(ui!==rr.length-1){ui===-1?(rr="",hr=0):hr=(rr=rr.slice(0,ui)).length-1-rr.lastIndexOf("/"),Gt=wr,Pr=0;continue}}else if(rr.length===2||rr.length===1){rr="",hr=0,Gt=wr,Pr=0;continue}}pt&&(rr.length>0?rr+="/..":rr="..",hr=2)}else rr.length>0?rr+="/"+dt.slice(Gt+1,wr):rr=dt.slice(Gt+1,wr),hr=wr-Gt-1;Gt=wr,Pr=0}else Ct===46&&Pr!==-1?++Pr:Pr=-1}return rr}s(ht,"r2"),L(ht,"r");var Tt={resolve:L(function(){for(var dt,pt="",Ct=!1,rr=arguments.length-1;rr>=-1&&!Ct;rr--){var hr;rr>=0?hr=arguments[rr]:(dt===void 0&&(dt=process.cwd()),hr=dt),yt(hr),hr.length!==0&&(pt=hr+"/"+pt,Ct=hr.charCodeAt(0)===47)}return pt=ht(pt,!Ct),Ct?pt.length>0?"/"+pt:"/":pt.length>0?pt:"."},"resolve"),normalize:L(function(dt){if(yt(dt),dt.length===0)return".";var pt=dt.charCodeAt(0)===47,Ct=dt.charCodeAt(dt.length-1)===47;return(dt=ht(dt,!pt)).length!==0||pt||(dt="."),dt.length>0&&Ct&&(dt+="/"),pt?"/"+dt:dt},"normalize"),isAbsolute:L(function(dt){return yt(dt),dt.length>0&&dt.charCodeAt(0)===47},"isAbsolute"),join:L(function(){if(arguments.length===0)return".";for(var dt,pt=0;pt0&&(dt===void 0?dt=Ct:dt+="/"+Ct)}return dt===void 0?".":Tt.normalize(dt)},"join"),relative:L(function(dt,pt){if(yt(dt),yt(pt),dt===pt||(dt=Tt.resolve(dt))===(pt=Tt.resolve(pt)))return"";for(var Ct=1;Ctwr){if(pt.charCodeAt(Gt+oi)===47)return pt.slice(Gt+oi+1);if(oi===0)return pt.slice(Gt+oi)}else hr>wr&&(dt.charCodeAt(Ct+oi)===47?ui=oi:oi===0&&(ui=0));break}var Ci=dt.charCodeAt(Ct+oi);if(Ci!==pt.charCodeAt(Gt+oi))break;Ci===47&&(ui=oi)}var ji="";for(oi=Ct+ui+1;oi<=rr;++oi)oi!==rr&&dt.charCodeAt(oi)!==47||(ji.length===0?ji+="..":ji+="/..");return ji.length>0?ji+pt.slice(Gt+ui):(Gt+=ui,pt.charCodeAt(Gt)===47&&++Gt,pt.slice(Gt))},"relative"),_makeLong:L(function(dt){return dt},"_makeLong"),dirname:L(function(dt){if(yt(dt),dt.length===0)return".";for(var pt=dt.charCodeAt(0),Ct=pt===47,rr=-1,hr=!0,Gt=dt.length-1;Gt>=1;--Gt)if((pt=dt.charCodeAt(Gt))===47){if(!hr){rr=Gt;break}}else hr=!1;return rr===-1?Ct?"/":".":Ct&&rr===1?"//":dt.slice(0,rr)},"dirname"),basename:L(function(dt,pt){if(pt!==void 0&&typeof pt!="string")throw new TypeError('"ext" argument must be a string');yt(dt);var Ct,rr=0,hr=-1,Gt=!0;if(pt!==void 0&&pt.length>0&&pt.length<=dt.length){if(pt.length===dt.length&&pt===dt)return"";var Pr=pt.length-1,wr=-1;for(Ct=dt.length-1;Ct>=0;--Ct){var ui=dt.charCodeAt(Ct);if(ui===47){if(!Gt){rr=Ct+1;break}}else wr===-1&&(Gt=!1,wr=Ct+1),Pr>=0&&(ui===pt.charCodeAt(Pr)?--Pr==-1&&(hr=Ct):(Pr=-1,hr=wr))}return rr===hr?hr=wr:hr===-1&&(hr=dt.length),dt.slice(rr,hr)}for(Ct=dt.length-1;Ct>=0;--Ct)if(dt.charCodeAt(Ct)===47){if(!Gt){rr=Ct+1;break}}else hr===-1&&(Gt=!1,hr=Ct+1);return hr===-1?"":dt.slice(rr,hr)},"basename"),extname:L(function(dt){yt(dt);for(var pt=-1,Ct=0,rr=-1,hr=!0,Gt=0,Pr=dt.length-1;Pr>=0;--Pr){var wr=dt.charCodeAt(Pr);if(wr!==47)rr===-1&&(hr=!1,rr=Pr+1),wr===46?pt===-1?pt=Pr:Gt!==1&&(Gt=1):pt!==-1&&(Gt=-1);else if(!hr){Ct=Pr+1;break}}return pt===-1||rr===-1||Gt===0||Gt===1&&pt===rr-1&&pt===Ct+1?"":dt.slice(pt,rr)},"extname"),format:L(function(dt){if(dt===null||typeof dt!="object")throw new TypeError('The "pathObject" argument must be of type Object. Received type '+typeof dt);return(function(pt,Ct){var rr=Ct.dir||Ct.root,hr=Ct.base||(Ct.name||"")+(Ct.ext||"");return rr?rr===Ct.root?rr+hr:rr+"/"+hr:hr})(0,dt)},"format"),parse:L(function(dt){yt(dt);var pt={root:"",dir:"",base:"",ext:"",name:""};if(dt.length===0)return pt;var Ct,rr=dt.charCodeAt(0),hr=rr===47;hr?(pt.root="/",Ct=1):Ct=0;for(var Gt=-1,Pr=0,wr=-1,ui=!0,oi=dt.length-1,Ci=0;oi>=Ct;--oi)if((rr=dt.charCodeAt(oi))!==47)wr===-1&&(ui=!1,wr=oi+1),rr===46?Gt===-1?Gt=oi:Ci!==1&&(Ci=1):Gt!==-1&&(Ci=-1);else if(!ui){Pr=oi+1;break}return Gt===-1||wr===-1||Ci===0||Ci===1&&Gt===wr-1&&Gt===Pr+1?wr!==-1&&(pt.base=pt.name=Pr===0&&hr?dt.slice(1,wr):dt.slice(Pr,wr)):(Pr===0&&hr?(pt.name=dt.slice(1,Gt),pt.base=dt.slice(1,wr)):(pt.name=dt.slice(Pr,Gt),pt.base=dt.slice(Pr,wr)),pt.ext=dt.slice(Gt,wr)),Pr>0?pt.dir=dt.slice(0,Pr-1):hr&&(pt.dir="/"),pt},"parse"),sep:"/",delimiter:":",win32:null,posix:null};Tt.posix=Tt,Ut.exports=Tt}},T={};function A(Ut){var yt=T[Ut];if(yt!==void 0)return yt.exports;var ht=T[Ut]={exports:{}};return q[Ut](ht,ht.exports,A),ht.exports}s(A,"r"),L(A,"r"),A.d=(Ut,yt)=>{for(var ht in yt)A.o(yt,ht)&&!A.o(Ut,ht)&&Object.defineProperty(Ut,ht,{enumerable:!0,get:yt[ht]})},A.o=(Ut,yt)=>Object.prototype.hasOwnProperty.call(Ut,yt),A.r=Ut=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(Ut,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(Ut,"__esModule",{value:!0})};var D={};let P;A.r(D),A.d(D,{URI:L(()=>je,"URI"),Utils:L(()=>Wn,"Utils")}),typeof process=="object"?P=process.platform==="win32":typeof navigator=="object"&&(P=navigator.userAgent.indexOf("Windows")>=0);let V=/^\w[\w\d+.-]*$/,Z=/^\//,re=/^\/\//;function Q(Ut,yt){if(!Ut.scheme&&yt)throw new Error(`[UriError]: Scheme is missing: {scheme: "", authority: "${Ut.authority}", path: "${Ut.path}", query: "${Ut.query}", fragment: "${Ut.fragment}"}`);if(Ut.scheme&&!V.test(Ut.scheme))throw new Error("[UriError]: Scheme contains illegal characters.");if(Ut.path){if(Ut.authority){if(!Z.test(Ut.path))throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character')}else if(re.test(Ut.path))throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")')}}s(Q,"a"),L(Q,"a");let ae="",we="/",Ue=/^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;const sn=class sn{static isUri(yt){return yt instanceof sn||!!yt&&typeof yt.authority=="string"&&typeof yt.fragment=="string"&&typeof yt.path=="string"&&typeof yt.query=="string"&&typeof yt.scheme=="string"&&typeof yt.fsPath=="string"&&typeof yt.with=="function"&&typeof yt.toString=="function"}scheme;authority;path;query;fragment;constructor(yt,ht,Tt,dt,pt,Ct=!1){typeof yt=="object"?(this.scheme=yt.scheme||ae,this.authority=yt.authority||ae,this.path=yt.path||ae,this.query=yt.query||ae,this.fragment=yt.fragment||ae):(this.scheme=(function(rr,hr){return rr||hr?rr:"file"})(yt,Ct),this.authority=ht||ae,this.path=(function(rr,hr){switch(rr){case"https":case"http":case"file":hr?hr[0]!==we&&(hr=we+hr):hr=we}return hr})(this.scheme,Tt||ae),this.query=dt||ae,this.fragment=pt||ae,Q(this,Ct))}get fsPath(){return xr(this,!1)}with(yt){if(!yt)return this;let{scheme:ht,authority:Tt,path:dt,query:pt,fragment:Ct}=yt;return ht===void 0?ht=this.scheme:ht===null&&(ht=ae),Tt===void 0?Tt=this.authority:Tt===null&&(Tt=ae),dt===void 0?dt=this.path:dt===null&&(dt=ae),pt===void 0?pt=this.query:pt===null&&(pt=ae),Ct===void 0?Ct=this.fragment:Ct===null&&(Ct=ae),ht===this.scheme&&Tt===this.authority&&dt===this.path&&pt===this.query&&Ct===this.fragment?this:new at(ht,Tt,dt,pt,Ct)}static parse(yt,ht=!1){let Tt=Ue.exec(yt);return Tt?new at(Tt[2]||ae,ia(Tt[4]||ae),ia(Tt[5]||ae),ia(Tt[7]||ae),ia(Tt[9]||ae),ht):new at(ae,ae,ae,ae,ae)}static file(yt){let ht=ae;if(P&&(yt=yt.replace(/\\/g,we)),yt[0]===we&&yt[1]===we){let Tt=yt.indexOf(we,2);Tt===-1?(ht=yt.substring(2),yt=we):(ht=yt.substring(2,Tt),yt=yt.substring(Tt)||we)}return new at("file",ht,yt,ae,ae)}static from(yt){let ht=new at(yt.scheme,yt.authority,yt.path,yt.query,yt.fragment);return Q(ht,!0),ht}toString(yt=!1){return mr(this,yt)}toJSON(){return this}static revive(yt){if(yt){if(yt instanceof sn)return yt;{let ht=new at(yt);return ht._formatted=yt.external,ht._fsPath=yt._sep===ut?yt.fsPath:null,ht}}return yt}};s(sn,"l"),L(sn,"l");let je=sn,ut=P?1:void 0;const Zs=class Zs extends je{_formatted=null;_fsPath=null;get fsPath(){return this._fsPath||(this._fsPath=xr(this,!1)),this._fsPath}toString(yt=!1){return yt?mr(this,!0):(this._formatted||(this._formatted=mr(this,!1)),this._formatted)}toJSON(){let yt={$mid:1};return this._fsPath&&(yt.fsPath=this._fsPath,yt._sep=ut),this._formatted&&(yt.external=this._formatted),this.path&&(yt.path=this.path),this.scheme&&(yt.scheme=this.scheme),this.authority&&(yt.authority=this.authority),this.query&&(yt.query=this.query),this.fragment&&(yt.fragment=this.fragment),yt}};s(Zs,"d"),L(Zs,"d");let at=Zs,Rt={58:"%3A",47:"%2F",63:"%3F",35:"%23",91:"%5B",93:"%5D",64:"%40",33:"%21",36:"%24",38:"%26",39:"%27",40:"%28",41:"%29",42:"%2A",43:"%2B",44:"%2C",59:"%3B",61:"%3D",32:"%20"};function Zt(Ut,yt,ht){let Tt,dt=-1;for(let pt=0;pt=97&&Ct<=122||Ct>=65&&Ct<=90||Ct>=48&&Ct<=57||Ct===45||Ct===46||Ct===95||Ct===126||yt&&Ct===47||ht&&Ct===91||ht&&Ct===93||ht&&Ct===58)dt!==-1&&(Tt+=encodeURIComponent(Ut.substring(dt,pt)),dt=-1),Tt!==void 0&&(Tt+=Ut.charAt(pt));else{Tt===void 0&&(Tt=Ut.substr(0,pt));let rr=Rt[Ct];rr!==void 0?(dt!==-1&&(Tt+=encodeURIComponent(Ut.substring(dt,pt)),dt=-1),Tt+=rr):dt===-1&&(dt=pt)}}return dt!==-1&&(Tt+=encodeURIComponent(Ut.substring(dt))),Tt!==void 0?Tt:Ut}s(Zt,"m"),L(Zt,"m");function Yt(Ut){let yt;for(let ht=0;ht1&&Ut.scheme==="file"?`//${Ut.authority}${Ut.path}`:Ut.path.charCodeAt(0)===47&&(Ut.path.charCodeAt(1)>=65&&Ut.path.charCodeAt(1)<=90||Ut.path.charCodeAt(1)>=97&&Ut.path.charCodeAt(1)<=122)&&Ut.path.charCodeAt(2)===58?yt?Ut.path.substr(1):Ut.path[1].toLowerCase()+Ut.path.substr(2):Ut.path,P&&(ht=ht.replace(/\//g,"\\")),ht}s(xr,"v"),L(xr,"v");function mr(Ut,yt){let ht=yt?Yt:Zt,Tt="",{scheme:dt,authority:pt,path:Ct,query:rr,fragment:hr}=Ut;if(dt&&(Tt+=dt,Tt+=":"),(pt||dt==="file")&&(Tt+=we,Tt+=we),pt){let Gt=pt.indexOf("@");if(Gt!==-1){let Pr=pt.substr(0,Gt);pt=pt.substr(Gt+1),Gt=Pr.lastIndexOf(":"),Gt===-1?Tt+=ht(Pr,!1,!1):(Tt+=ht(Pr.substr(0,Gt),!1,!1),Tt+=":",Tt+=ht(Pr.substr(Gt+1),!1,!0)),Tt+="@"}pt=pt.toLowerCase(),Gt=pt.lastIndexOf(":"),Gt===-1?Tt+=ht(pt,!1,!0):(Tt+=ht(pt.substr(0,Gt),!1,!0),Tt+=pt.substr(Gt))}if(Ct){if(Ct.length>=3&&Ct.charCodeAt(0)===47&&Ct.charCodeAt(2)===58){let Gt=Ct.charCodeAt(1);Gt>=65&&Gt<=90&&(Ct=`/${String.fromCharCode(Gt+32)}:${Ct.substr(3)}`)}else if(Ct.length>=2&&Ct.charCodeAt(1)===58){let Gt=Ct.charCodeAt(0);Gt>=65&&Gt<=90&&(Ct=`${String.fromCharCode(Gt+32)}:${Ct.substr(2)}`)}Tt+=ht(Ct,!0,!1)}return rr&&(Tt+="?",Tt+=ht(rr,!1,!1)),hr&&(Tt+="#",Tt+=yt?hr:Zt(hr,!1,!1)),Tt}s(mr,"b"),L(mr,"b");function sr(Ut){try{return decodeURIComponent(Ut)}catch{return Ut.length>3?Ut.substr(0,3)+sr(Ut.substr(3)):Ut}}s(sr,"C"),L(sr,"C");let qn=/(%[0-9A-Za-z][0-9A-Za-z])+/g;function ia(Ut){return Ut.match(qn)?Ut.replace(qn,(yt=>sr(yt))):Ut}s(ia,"w"),L(ia,"w");var Ri=A(975);let Ca=Ri.posix||Ri,Qn="/";var Wn;(function(Ut){Ut.joinPath=function(yt,...ht){return yt.with({path:Ca.join(yt.path,...ht)})},Ut.resolvePath=function(yt,...ht){let Tt=yt.path,dt=!1;Tt[0]!==Qn&&(Tt=Qn+Tt,dt=!0);let pt=Ca.resolve(Tt,...ht);return dt&&pt[0]===Qn&&!yt.authority&&(pt=pt.substring(1)),yt.with({path:pt})},Ut.dirname=function(yt){if(yt.path.length===0||yt.path===Qn)return yt;let ht=Ca.dirname(yt.path);return ht.length===1&&ht.charCodeAt(0)===46&&(ht=""),yt.with({path:ht})},Ut.basename=function(yt){return Ca.basename(yt.path)},Ut.extname=function(yt){return Ca.extname(yt.path)}})(Wn||(Wn={})),Kst=D})(),{URI:yl,Utils:Ab}=Kst,(function(q){q.basename=Ab.basename,q.dirname=Ab.dirname,q.extname=Ab.extname,q.joinPath=Ab.joinPath,q.resolvePath=Ab.resolvePath;let T=typeof process=="object"&&process?.platform==="win32";function A(Z,re){return Z?.toString()===re?.toString()}s(A,"equals"),L(A,"equals"),q.equals=A;function D(Z,re){let Q=typeof Z=="string"?yl.parse(Z).path:Z.path,ae=typeof re=="string"?yl.parse(re).path:re.path,we=Q.split("/").filter(Rt=>Rt.length>0),Ue=ae.split("/").filter(Rt=>Rt.length>0);if(T){let Rt=/^[A-Z]:$/;if(we[0]&&Rt.test(we[0])&&(we[0]=we[0].toLowerCase()),Ue[0]&&Rt.test(Ue[0])&&(Ue[0]=Ue[0].toLowerCase()),we[0]!==Ue[0])return ae.substring(1)}let je=0;for(;je({name:P.name,uri:Uo.joinPath(yl.parse(A),P.name).toString(),element:P.element})):[]}all(){return this.collectValues(this.root)}findAll(T){let A=this.getNode(Uo.normalize(T),!1);return A?this.collectValues(A):[]}getNode(T,A){let D=T.split("/");T.charAt(T.length-1)==="/"&&D.pop();let P=this.root;for(let V of D){let Z=P.children.get(V);if(!Z)if(A)Z={name:V,children:new Map,parent:P},P.children.set(V,Z);else return;P=Z}return P}collectValues(T){let A=[];T.element&&A.push(T.element);for(let D of T.children.values())A.push(...this.collectValues(D));return A}},s(ct,"UriTrie"),L(ct,"UriTrie"),ct),(function(q){q[q.Changed=0]="Changed",q[q.Parsed=1]="Parsed",q[q.IndexedContent=2]="IndexedContent",q[q.ComputedScopes=3]="ComputedScopes",q[q.Linked=4]="Linked",q[q.IndexedReferences=5]="IndexedReferences",q[q.Validated=6]="Validated"})(_i||(_i={})),Qle=(qe=class{constructor(T){this.serviceRegistry=T.ServiceRegistry,this.textDocuments=T.workspace.TextDocuments,this.fileSystemProvider=T.workspace.FileSystemProvider}async fromUri(T,A=Sn.CancellationToken.None){let D=await this.fileSystemProvider.readFile(T);return this.createAsync(T,D,A)}fromTextDocument(T,A,D){return A=A??yl.parse(T.uri),Sn.CancellationToken.is(D)?this.createAsync(A,T,D):this.create(A,T,D)}fromString(T,A,D){return Sn.CancellationToken.is(D)?this.createAsync(A,T,D):this.create(A,T,D)}fromModel(T,A){return this.create(A,{$model:T})}create(T,A,D){if(typeof A=="string"){let P=this.parse(T,A,D);return this.createLangiumDocument(P,T,void 0,A)}else if("$model"in A){let P={value:A.$model,parserErrors:[],lexerErrors:[]};return this.createLangiumDocument(P,T)}else{let P=this.parse(T,A.getText(),D);return this.createLangiumDocument(P,T,A)}}async createAsync(T,A,D){if(typeof A=="string"){let P=await this.parseAsync(T,A,D);return this.createLangiumDocument(P,T,void 0,A)}else{let P=await this.parseAsync(T,A.getText(),D);return this.createLangiumDocument(P,T,A)}}createLangiumDocument(T,A,D,P){let V;if(D)V={parseResult:T,uri:A,state:_i.Parsed,references:[],textDocument:D};else{let Z=this.createTextDocumentGetter(A,P);V={parseResult:T,uri:A,state:_i.Parsed,references:[],get textDocument(){return Z()}}}return T.value.$document=V,V}async update(T,A){let D=T.parseResult.value.$cstNode?.root.fullText,P=this.textDocuments?.get(T.uri.toString()),V=P?P.getText():await this.fileSystemProvider.readFile(T.uri);if(P)Object.defineProperty(T,"textDocument",{value:P});else{let Z=this.createTextDocumentGetter(T.uri,V);Object.defineProperty(T,"textDocument",{get:Z})}return D!==V&&(T.parseResult=await this.parseAsync(T.uri,V,A),T.parseResult.value.$document=T),T.state=_i.Parsed,T}parse(T,A,D){return this.serviceRegistry.getServices(T).parser.LangiumParser.parse(A,D)}parseAsync(T,A,D){return this.serviceRegistry.getServices(T).parser.AsyncParser.parse(A,D)}createTextDocumentGetter(T,A){let D=this.serviceRegistry,P;return()=>P??(P=c9.create(T.toString(),D.getServices(T).LanguageMetaData.languageId,0,A??""))}},s(qe,"DefaultLangiumDocumentFactory"),L(qe,"DefaultLangiumDocumentFactory"),qe),Zle=(mt=class{constructor(T){this.documentTrie=new u9,this.services=T,this.langiumDocumentFactory=T.workspace.LangiumDocumentFactory,this.documentBuilder=()=>T.workspace.DocumentBuilder}get all(){return an(this.documentTrie.all())}addDocument(T){let A=T.uri.toString();if(this.documentTrie.has(A))throw new Error(`A document with the URI '${A}' is already present.`);this.documentTrie.insert(A,T)}getDocument(T){let A=T.toString();return this.documentTrie.find(A)}getDocuments(T){let A=T.toString();return this.documentTrie.findAll(A)}async getOrCreateDocument(T,A){let D=this.getDocument(T);return D||(D=await this.langiumDocumentFactory.fromUri(T,A),this.addDocument(D),D)}createDocument(T,A,D){if(D)return this.langiumDocumentFactory.fromString(A,T,D).then(P=>(this.addDocument(P),P));{let P=this.langiumDocumentFactory.fromString(A,T);return this.addDocument(P),P}}hasDocument(T){return this.documentTrie.has(T.toString())}invalidateDocument(T){let A=T.toString(),D=this.documentTrie.find(A);return D&&this.documentBuilder().resetToState(D,_i.Changed),D}deleteDocument(T){let A=T.toString(),D=this.documentTrie.find(A);return D&&(D.state=_i.Changed,this.documentTrie.delete(A)),D}deleteDocuments(T){let A=T.toString(),D=this.documentTrie.findAll(A);for(let P of D)P.state=_i.Changed;return this.documentTrie.delete(A),D}},s(mt,"DefaultLangiumDocuments"),L(mt,"DefaultLangiumDocuments"),mt),p0=Symbol("RefResolving"),Jle=(St=class{constructor(T){this.reflection=T.shared.AstReflection,this.langiumDocuments=()=>T.shared.workspace.LangiumDocuments,this.scopeProvider=T.references.ScopeProvider,this.astNodeLocator=T.workspace.AstNodeLocator,this.profiler=T.shared.profilers.LangiumProfiler,this.languageId=T.LanguageMetaData.languageId}async link(T,A=Sn.CancellationToken.None){if(this.profiler?.isActive("linking")){let D=this.profiler.createTask("linking",this.languageId);D.start();try{for(let P of cc(T.parseResult.value))await cs(A),wm(P).forEach(V=>{let Z=`${P.$type}:${V.property}`;D.startSubTask(Z);try{this.doLink(V,T)}finally{D.stopSubTask(Z)}})}finally{D.stop()}}else for(let D of cc(T.parseResult.value))await cs(A),wm(D).forEach(P=>this.doLink(P,T))}doLink(T,A){let D=T.reference;if("_ref"in D&&D._ref===void 0){D._ref=p0;try{let P=this.getCandidate(T);if(Cf(P))D._ref=P;else{D._nodeDescription=P;let V=this.loadAstNode(P);D._ref=V??this.createLinkingError(T,P)}}catch(P){console.error(`An error occurred while resolving reference to '${D.$refText}':`,P);let V=P.message??String(P);D._ref={info:T,message:`An error occurred while resolving reference to '${D.$refText}': ${V}`}}A.references.push(D)}else if("_items"in D&&D._items===void 0){D._items=p0;try{let P=this.getCandidates(T),V=[];if(Cf(P))D._linkingError=P;else for(let Z of P){let re=this.loadAstNode(Z);re&&V.push({ref:re,$nodeDescription:Z})}D._items=V}catch(P){D._linkingError={info:T,message:`An error occurred while resolving reference to '${D.$refText}': ${P}`},D._items=[]}A.references.push(D)}}unlink(T){for(let A of T.references)"_ref"in A?(A._ref=void 0,delete A._nodeDescription):"_items"in A&&(A._items=void 0,delete A._linkingError);T.references=[]}getCandidate(T){return this.scopeProvider.getScope(T).getElement(T.reference.$refText)??this.createLinkingError(T)}getCandidates(T){let A=this.scopeProvider.getScope(T).getElements(T.reference.$refText).distinct(D=>`${D.documentUri}#${D.path}`).toArray();return A.length>0?A:this.createLinkingError(T)}buildReference(T,A,D,P){let V=this,Z={$refNode:D,$refText:P,_ref:void 0,get ref(){if(Da(this._ref))return this._ref;if(B8(this._nodeDescription)){let re=V.loadAstNode(this._nodeDescription);this._ref=re??V.createLinkingError({reference:Z,container:T,property:A},this._nodeDescription)}else if(this._ref===void 0){this._ref=p0;let re=Cm(T).$document,Q=V.getLinkedNode({reference:Z,container:T,property:A});if(Q.error&&re&&re.state<_i.ComputedScopes)return this._ref=void 0;this._ref=Q.node??Q.error,this._nodeDescription=Q.descr,re?.references.push(this)}else this._ref===p0&&V.throwCyclicReferenceError(T,A,P);return Da(this._ref)?this._ref:void 0},get $nodeDescription(){return this._nodeDescription},get error(){return Cf(this._ref)?this._ref:void 0}};return Z}buildMultiReference(T,A,D,P){let V=this,Z={$refNode:D,$refText:P,_items:void 0,get items(){if(Array.isArray(this._items))return this._items;if(this._items===void 0){this._items=p0;let re=Cm(T).$document,Q=V.getCandidates({reference:Z,container:T,property:A}),ae=[];if(Cf(Q))this._linkingError=Q;else for(let we of Q){let Ue=V.loadAstNode(we);Ue&&ae.push({ref:Ue,$nodeDescription:we})}this._items=ae,re?.references.push(this)}else this._items===p0&&V.throwCyclicReferenceError(T,A,P);return Array.isArray(this._items)?this._items:[]},get error(){if(this._linkingError)return this._linkingError;if(!(this.items.length>0))return this._linkingError=V.createLinkingError({reference:Z,container:T,property:A})}};return Z}throwCyclicReferenceError(T,A,D){throw new Error(`Cyclic reference resolution detected: ${this.astNodeLocator.getAstNodePath(T)}/${A} (symbol '${D}')`)}getLinkedNode(T){try{let A=this.getCandidate(T);if(Cf(A))return{error:A};let D=this.loadAstNode(A);return D?{node:D,descr:A}:{descr:A,error:this.createLinkingError(T,A)}}catch(A){console.error(`An error occurred while resolving reference to '${T.reference.$refText}':`,A);let D=A.message??String(A);return{error:{info:T,message:`An error occurred while resolving reference to '${T.reference.$refText}': ${D}`}}}}loadAstNode(T){if(T.node)return T.node;let A=this.langiumDocuments().getDocument(T.documentUri);if(A)return this.astNodeLocator.getAstNode(A.parseResult.value,T.path)}createLinkingError(T,A){let D=Cm(T.container).$document;D&&D.state<_i.ComputedScopes&&console.warn(`Attempted reference resolution before document reached ComputedScopes state (${D.uri}).`);let P=this.reflection.getReferenceType(T);return{info:T,message:`Could not resolve reference to ${P} named '${T.reference.$refText}'.`,targetDescription:A}}},s(St,"DefaultLinker"),L(St,"DefaultLinker"),St),s(S$,"isNamed"),L(S$,"isNamed"),ece=(Lt=class{getName(T){if(S$(T))return T.name}getNameNode(T){return Gk(T.$cstNode,"name")}},s(Lt,"DefaultNameProvider"),L(Lt,"DefaultNameProvider"),Lt),tce=(Bt=class{constructor(T){this.nameProvider=T.references.NameProvider,this.index=T.shared.workspace.IndexManager,this.nodeLocator=T.workspace.AstNodeLocator,this.documents=T.shared.workspace.LangiumDocuments,this.hasMultiReference=cc(T.Grammar).some(A=>_f(A)&&A.isMulti)}findDeclarations(T){if(T){let A=wR(T),D=T.astNode;if(A&&D){let P=D[A.feature];if(fo(P)||Zc(P))return Dk(P);if(Array.isArray(P)){for(let V of P)if((fo(V)||Zc(V))&&V.$refNode&&V.$refNode.offset<=T.offset&&V.$refNode.end>=T.end)return Dk(V)}}if(D){let P=this.nameProvider.getNameNode(D);if(P&&(P===T||nR(T,P)))return this.getSelfNodes(D)}}return[]}getSelfNodes(T){if(this.hasMultiReference){let A=this.index.findAllReferences(T,this.nodeLocator.getAstNodePath(T)),D=this.getNodeFromReferenceDescription(A.head());if(D){for(let P of wm(D))if(Zc(P.reference)&&P.reference.items.some(V=>V.ref===T))return P.reference.items.map(V=>V.ref)}return[T]}else return[T]}getNodeFromReferenceDescription(T){if(!T)return;let A=this.documents.getDocument(T.sourceUri);if(A)return this.nodeLocator.getAstNode(A.parseResult.value,T.sourcePath)}findDeclarationNodes(T){let A=this.findDeclarations(T),D=[];for(let P of A){let V=this.nameProvider.getNameNode(P)??P.$cstNode;V&&D.push(V)}return D}findReferences(T,A){let D=[];A.includeDeclaration&&D.push(...this.getSelfReferences(T));let P=this.index.findAllReferences(T,this.nodeLocator.getAstNodePath(T));return A.documentUri&&(P=P.filter(V=>Uo.equals(V.sourceUri,A.documentUri))),D.push(...P),an(D)}getSelfReferences(T){let A=this.getSelfNodes(T),D=[];for(let P of A){let V=this.nameProvider.getNameNode(P);if(V){let Z=lc(P),re=this.nodeLocator.getAstNodePath(P);D.push({sourceUri:Z.uri,sourcePath:re,targetUri:Z.uri,targetPath:re,segment:_m(V),local:!0})}}return D}},s(Bt,"DefaultReferences"),L(Bt,"DefaultReferences"),Bt),Eh=(qt=class{constructor(T){if(this.map=new Map,T)for(let[A,D]of T)this.add(A,D)}get size(){return zC.sum(an(this.map.values()).map(T=>T.length))}clear(){this.map.clear()}delete(T,A){if(A===void 0)return this.map.delete(T);{let D=this.map.get(T);if(D){let P=D.indexOf(A);if(P>=0)return D.length===1?this.map.delete(T):D.splice(P,1),!0}return!1}}get(T){return this.map.get(T)??[]}getStream(T){let A=this.map.get(T);return A?an(A):zm}has(T,A){if(A===void 0)return this.map.has(T);{let D=this.map.get(T);return D?D.indexOf(A)>=0:!1}}add(T,A){return this.map.has(T)?this.map.get(T).push(A):this.map.set(T,[A]),this}addAll(T,A){return this.map.has(T)?this.map.get(T).push(...A):this.map.set(T,Array.from(A)),this}forEach(T){this.map.forEach((A,D)=>A.forEach(P=>T(P,D,this)))}[Symbol.iterator](){return this.entries().iterator()}entries(){return an(this.map.entries()).flatMap(([T,A])=>A.map(D=>[T,D]))}keys(){return an(this.map.keys())}values(){return an(this.map.values()).flat()}entriesGroupedByKey(){return an(this.map.entries())}},s(qt,"MultiMap"),L(qt,"MultiMap"),qt),Zw=(tr=class{get size(){return this.map.size}constructor(T){if(this.map=new Map,this.inverse=new Map,T)for(let[A,D]of T)this.set(A,D)}clear(){this.map.clear(),this.inverse.clear()}set(T,A){return this.map.set(T,A),this.inverse.set(A,T),this}get(T){return this.map.get(T)}getKey(T){return this.inverse.get(T)}delete(T){let A=this.map.get(T);return A!==void 0?(this.map.delete(T),this.inverse.delete(A),!0):!1}},s(tr,"BiMap"),L(tr,"BiMap"),tr),rce=(Et=class{constructor(T){this.nameProvider=T.references.NameProvider,this.descriptions=T.workspace.AstNodeDescriptionProvider}async collectExportedSymbols(T,A=Sn.CancellationToken.None){return this.collectExportedSymbolsForNode(T.parseResult.value,T,void 0,A)}async collectExportedSymbolsForNode(T,A,D=bv,P=Sn.CancellationToken.None){let V=[];this.addExportedSymbol(T,V,A);for(let Z of D(T))await cs(P),this.addExportedSymbol(Z,V,A);return V}addExportedSymbol(T,A,D){let P=this.nameProvider.getName(T);P&&A.push(this.descriptions.createDescription(T,P,D))}async collectLocalSymbols(T,A=Sn.CancellationToken.None){let D=T.parseResult.value,P=new Eh;for(let V of oh(D))await cs(A),this.addLocalSymbol(V,T,P);return P}addLocalSymbol(T,A,D){let P=T.$container;if(P){let V=this.nameProvider.getName(T);V&&D.add(P,this.descriptions.createDescription(T,V,A))}}},s(Et,"DefaultScopeComputation"),L(Et,"DefaultScopeComputation"),Et),h9=(_t=class{constructor(T,A,D){this.elements=T,this.outerScope=A,this.caseInsensitive=D?.caseInsensitive??!1,this.concatOuterScope=D?.concatOuterScope??!0}getAllElements(){return this.outerScope?this.elements.concat(this.outerScope.getAllElements()):this.elements}getElement(T){let A=this.caseInsensitive?T.toLowerCase():T,D=this.caseInsensitive?this.elements.find(P=>P.name.toLowerCase()===A):this.elements.find(P=>P.name===T);if(D)return D;if(this.outerScope)return this.outerScope.getElement(T)}getElements(T){let A=this.caseInsensitive?T.toLowerCase():T,D=this.caseInsensitive?this.elements.filter(P=>P.name.toLowerCase()===A):this.elements.filter(P=>P.name===T);return(this.concatOuterScope||D.isEmpty())&&this.outerScope?D.concat(this.outerScope.getElements(T)):D}},s(_t,"StreamScope"),L(_t,"StreamScope"),_t),Qst=(Kt=class{constructor(T,A,D){this.elements=new Map,this.caseInsensitive=D?.caseInsensitive??!1,this.concatOuterScope=D?.concatOuterScope??!0;for(let P of T){let V=this.caseInsensitive?P.name.toLowerCase():P.name;this.elements.set(V,P)}this.outerScope=A}getElement(T){let A=this.caseInsensitive?T.toLowerCase():T,D=this.elements.get(A);if(D)return D;if(this.outerScope)return this.outerScope.getElement(T)}getElements(T){let A=this.caseInsensitive?T.toLowerCase():T,D=this.elements.get(A),P=D?[D]:[];return(this.concatOuterScope||P.length>0)&&this.outerScope?an(P).concat(this.outerScope.getElements(T)):an(P)}getAllElements(){let T=an(this.elements.values());return this.outerScope&&(T=T.concat(this.outerScope.getAllElements())),T}},s(Kt,"MapScope"),L(Kt,"MapScope"),Kt),ice=($t=class{constructor(T,A,D){this.elements=new Eh,this.caseInsensitive=D?.caseInsensitive??!1,this.concatOuterScope=D?.concatOuterScope??!0;for(let P of T){let V=this.caseInsensitive?P.name.toLowerCase():P.name;this.elements.add(V,P)}this.outerScope=A}getElement(T){let A=this.caseInsensitive?T.toLowerCase():T,D=this.elements.get(A)[0];if(D)return D;if(this.outerScope)return this.outerScope.getElement(T)}getElements(T){let A=this.caseInsensitive?T.toLowerCase():T,D=this.elements.get(A);return(this.concatOuterScope||D.length===0)&&this.outerScope?an(D).concat(this.outerScope.getElements(T)):an(D)}getAllElements(){let T=an(this.elements.values());return this.outerScope&&(T=T.concat(this.outerScope.getAllElements())),T}},s($t,"MultiMapScope"),L($t,"MultiMapScope"),$t),Zst={getElement(){},getElements(){return zm},getAllElements(){return zm}},Jw=(Ot=class{constructor(){this.toDispose=[],this.isDisposed=!1}onDispose(T){this.toDispose.push(T)}dispose(){this.throwIfDisposed(),this.clear(),this.isDisposed=!0,this.toDispose.forEach(T=>T.dispose())}throwIfDisposed(){if(this.isDisposed)throw new Error("This cache has already been disposed")}},s(Ot,"DisposableCache"),L(Ot,"DisposableCache"),Ot),d9=(Jt=class extends Jw{constructor(){super(...arguments),this.cache=new Map}has(T){return this.throwIfDisposed(),this.cache.has(T)}set(T,A){this.throwIfDisposed(),this.cache.set(T,A)}get(T,A){if(this.throwIfDisposed(),this.cache.has(T))return this.cache.get(T);if(A){let D=A();return this.cache.set(T,D),D}else return}delete(T){return this.throwIfDisposed(),this.cache.delete(T)}clear(){this.throwIfDisposed(),this.cache.clear()}},s(Jt,"SimpleCache"),L(Jt,"SimpleCache"),Jt),e5=(Br=class extends Jw{constructor(T){super(),this.cache=new Map,this.converter=T??(A=>A)}has(T,A){return this.throwIfDisposed(),this.cacheForContext(T).has(A)}set(T,A,D){this.throwIfDisposed(),this.cacheForContext(T).set(A,D)}get(T,A,D){this.throwIfDisposed();let P=this.cacheForContext(T);if(P.has(A))return P.get(A);if(D){let V=D();return P.set(A,V),V}else return}delete(T,A){return this.throwIfDisposed(),this.cacheForContext(T).delete(A)}clear(T){if(this.throwIfDisposed(),T){let A=this.converter(T);this.cache.delete(A)}else this.cache.clear()}cacheForContext(T){let A=this.converter(T),D=this.cache.get(A);return D||(D=new Map,this.cache.set(A,D)),D}},s(Br,"ContextCache"),L(Br,"ContextCache"),Br),nce=(At=class extends e5{constructor(T,A){super(D=>D.toString()),A?(this.toDispose.push(T.workspace.DocumentBuilder.onDocumentPhase(A,D=>{this.clear(D.uri.toString())})),this.toDispose.push(T.workspace.DocumentBuilder.onUpdate((D,P)=>{for(let V of P)this.clear(V)}))):this.toDispose.push(T.workspace.DocumentBuilder.onUpdate((D,P)=>{let V=D.concat(P);for(let Z of V)this.clear(Z)}))}},s(At,"DocumentCache"),L(At,"DocumentCache"),At),p9=(jr=class extends d9{constructor(T,A){super(),A?(this.toDispose.push(T.workspace.DocumentBuilder.onBuildPhase(A,()=>{this.clear()})),this.toDispose.push(T.workspace.DocumentBuilder.onUpdate((D,P)=>{P.length>0&&this.clear()}))):this.toDispose.push(T.workspace.DocumentBuilder.onUpdate(()=>{this.clear()}))}},s(jr,"WorkspaceCache"),L(jr,"WorkspaceCache"),jr),ace=(br=class{constructor(T){this.reflection=T.shared.AstReflection,this.nameProvider=T.references.NameProvider,this.descriptions=T.workspace.AstNodeDescriptionProvider,this.indexManager=T.shared.workspace.IndexManager,this.globalScopeCache=new p9(T.shared)}getScope(T){let A=[],D=this.reflection.getReferenceType(T),P=lc(T.container).localSymbols;if(P){let Z=T.container;do P.has(Z)&&A.push(P.getStream(Z).filter(re=>this.reflection.isSubtype(re.type,D))),Z=Z.$container;while(Z)}let V=this.getGlobalScope(D,T);for(let Z=A.length-1;Z>=0;Z--)V=this.createScope(A[Z],V);return V}createScope(T,A,D){return new h9(an(T),A,D)}createScopeForNodes(T,A,D){let P=an(T).map(V=>{let Z=this.nameProvider.getName(V);if(Z)return this.descriptions.createDescription(V,Z)}).nonNullable();return new h9(P,A,D)}getGlobalScope(T,A){return this.globalScopeCache.get(T,()=>new ice(this.indexManager.allElements(T)))}},s(br,"DefaultScopeProvider"),L(br,"DefaultScopeProvider"),br),s(_$,"isAstNodeWithComment"),L(_$,"isAstNodeWithComment"),s(A$,"isIntermediateReference"),L(A$,"isIntermediateReference"),sce=(Hr=class{constructor(T){this.ignoreProperties=new Set(["$container","$containerProperty","$containerIndex","$document","$cstNode"]),this.langiumDocuments=T.shared.workspace.LangiumDocuments,this.astNodeLocator=T.workspace.AstNodeLocator,this.nameProvider=T.references.NameProvider,this.commentProvider=T.documentation.CommentProvider}serialize(T,A){let D=A??{},P=A?.replacer,V=L((re,Q)=>this.replacer(re,Q,D),"defaultReplacer"),Z=P?(re,Q)=>P(re,Q,V):V;try{return this.currentDocument=lc(T),JSON.stringify(T,Z,A?.space)}finally{this.currentDocument=void 0}}deserialize(T,A){let D=A??{},P=JSON.parse(T);return this.linkNode(P,P,D),P}replacer(T,A,{refText:D,sourceText:P,textRegions:V,comments:Z,uriConverter:re}){if(!this.ignoreProperties.has(T))if(fo(A)){let Q=A.ref,ae=D?A.$refText:void 0;if(Q){let we=lc(Q),Ue="";this.currentDocument&&this.currentDocument!==we&&(re?Ue=re(we.uri,Q):Ue=we.uri.toString());let je=this.astNodeLocator.getAstNodePath(Q);return{$ref:`${Ue}#${je}`,$refText:ae}}else return{$error:A.error?.message??"Could not resolve reference",$refText:ae}}else if(Zc(A)){let Q=D?A.$refText:void 0,ae=[];for(let we of A.items){let Ue=we.ref,je=lc(we.ref),ut="";this.currentDocument&&this.currentDocument!==je&&(re?ut=re(je.uri,Ue):ut=je.uri.toString());let at=this.astNodeLocator.getAstNodePath(Ue);ae.push(`${ut}#${at}`)}return{$refs:ae,$refText:Q}}else if(Da(A)){let Q;if(V&&(Q=this.addAstNodeRegionWithAssignmentsTo({...A}),(!T||A.$document)&&Q?.$textRegion&&(Q.$textRegion.documentURI=this.currentDocument?.uri.toString())),P&&!T&&(Q??(Q={...A}),Q.$sourceText=A.$cstNode?.text),Z){Q??(Q={...A});let ae=this.commentProvider.getComment(A);ae&&(Q.$comment=ae.replace(/\r/g,""))}return Q??A}else return A}addAstNodeRegionWithAssignmentsTo(T){let A=L(D=>({offset:D.offset,end:D.end,length:D.length,range:D.range}),"createDocumentSegment");if(T.$cstNode){let D=T.$textRegion=A(T.$cstNode),P=D.assignments={};return Object.keys(T).filter(V=>!V.startsWith("$")).forEach(V=>{let Z=kR(T.$cstNode,V).map(A);Z.length!==0&&(P[V]=Z)}),T}}linkNode(T,A,D,P,V,Z){for(let[Q,ae]of Object.entries(T))if(Array.isArray(ae))for(let we=0;we{await this.handleException(()=>T.call(A,D,P,V),"An error occurred during validation",P,D)}}async handleException(T,A,D,P){try{await T()}catch(V){if(jf(V))throw V;console.error(`${A}:`,V),V instanceof Error&&V.stack&&console.error(V.stack);let Z=V instanceof Error?V.message:String(V);D("error",`${A}: ${Z}`,{node:P})}}addEntry(T,A){if(T==="AstNode"){this.entries.add("AstNode",A);return}for(let D of this.reflection.getAllSubTypes(T))this.entries.add(D,A)}getChecks(T,A){let D=an(this.entries.get(T)).concat(this.entries.get("AstNode"));return A&&(D=D.filter(P=>A.includes(P.category))),D.map(P=>P.check)}registerBeforeDocument(T,A=this){this.entriesBefore.push(this.wrapPreparationException(T,"An error occurred during set-up of the validation",A))}registerAfterDocument(T,A=this){this.entriesAfter.push(this.wrapPreparationException(T,"An error occurred during tear-down of the validation",A))}wrapPreparationException(T,A,D){return async(P,V,Z,re)=>{await this.handleException(()=>T.call(D,P,V,Z,re),A,V,P)}}get checksBefore(){return this.entriesBefore}get checksAfter(){return this.entriesAfter}getAllValidationCategories(T){return this.knownCategories}},s(Li,"ValidationRegistry"),L(Li,"ValidationRegistry"),Li),cce=Object.freeze({validateNode:!0,validateChildren:!0}),uce=(Cr=class{constructor(T){this.validationRegistry=T.validation.ValidationRegistry,this.metadata=T.LanguageMetaData,this.profiler=T.shared.profilers.LangiumProfiler,this.languageId=T.LanguageMetaData.languageId}async validateDocument(T,A={},D=Sn.CancellationToken.None){let P=T.parseResult,V=[];if(await cs(D),(!A.categories||A.categories.includes("built-in"))&&(this.processLexingErrors(P,V,A),A.stopAfterLexingErrors&&V.some(Z=>Z.data?.code===vc.LexingError)||(this.processParsingErrors(P,V,A),A.stopAfterParsingErrors&&V.some(Z=>Z.data?.code===vc.ParsingError))||(this.processLinkingErrors(T,V,A),A.stopAfterLinkingErrors&&V.some(Z=>Z.data?.code===vc.LinkingError))))return V;try{V.push(...await this.validateAst(P.value,A,D))}catch(Z){if(jf(Z))throw Z;console.error("An error occurred during validation:",Z)}return await cs(D),V}processLexingErrors(T,A,D){let P=[...T.lexerErrors,...T.lexerReport?.diagnostics??[]];for(let V of P){let Z=V.severity??"error",re={severity:Wv(Z),range:{start:{line:V.line-1,character:V.column-1},end:{line:V.line-1,character:V.column+V.length-1}},message:V.message,data:$$(Z),source:this.getSource()};A.push(re)}}processParsingErrors(T,A,D){for(let P of T.parserErrors){let V;if(isNaN(P.token.startOffset)){if("previousToken"in P){let Z=P.previousToken;if(isNaN(Z.startOffset)){let re={line:0,character:0};V={start:re,end:re}}else{let re={line:Z.endLine-1,character:Z.endColumn};V={start:re,end:re}}}}else V=Tv(P.token);if(V){let Z={severity:Wv("error"),range:V,message:P.message,data:Vf(vc.ParsingError),source:this.getSource()};A.push(Z)}}}processLinkingErrors(T,A,D){for(let P of T.references){let V=P.error;if(V){let Z={node:V.info.container,range:P.$refNode?.range,property:V.info.property,index:V.info.index,data:{code:vc.LinkingError,containerType:V.info.container.$type,property:V.info.property,refText:V.info.reference.$refText}};A.push(this.toDiagnostic("error",V.message,Z))}}}async validateAst(T,A,D=Sn.CancellationToken.None){let P=[],V=L((Z,re,Q)=>{P.push(this.toDiagnostic(Z,re,Q))},"acceptor");return await this.validateAstBefore(T,A,V,D),await this.validateAstNodes(T,A,V,D),await this.validateAstAfter(T,A,V,D),P}async validateAstBefore(T,A,D,P=Sn.CancellationToken.None){let V=this.validationRegistry.checksBefore;for(let Z of V)await cs(P),await Z(T,D,A.categories??[],P)}async validateAstNodes(T,A,D,P=Sn.CancellationToken.None){if(this.profiler?.isActive("validating")){let V=this.profiler.createTask("validating",this.languageId);V.start();try{let Z=cc(T).iterator();for(let re of Z){V.startSubTask(re.$type);let Q=this.validateSingleNodeOptions(re,A);if(Q.validateNode)try{let ae=this.validationRegistry.getChecks(re.$type,A.categories);for(let we of ae)await we(re,D,P)}finally{V.stopSubTask(re.$type)}Q.validateChildren||Z.prune()}}finally{V.stop()}}else{let V=cc(T).iterator();for(let Z of V){await cs(P);let re=this.validateSingleNodeOptions(Z,A);if(re.validateNode){let Q=this.validationRegistry.getChecks(Z.$type,A.categories);for(let ae of Q)await ae(Z,D,P)}re.validateChildren||V.prune()}}}validateSingleNodeOptions(T,A){return cce}async validateAstAfter(T,A,D,P=Sn.CancellationToken.None){let V=this.validationRegistry.checksAfter;for(let Z of V)await cs(P),await Z(T,D,A.categories??[],P)}toDiagnostic(T,A,D){return{message:A,range:R$(D),severity:Wv(T),code:D.code,codeDescription:D.codeDescription,tags:D.tags,relatedInformation:D.relatedInformation,data:D.data,source:this.getSource()}}getSource(){return this.metadata.languageId}},s(Cr,"DefaultDocumentValidator"),L(Cr,"DefaultDocumentValidator"),Cr),s(R$,"getDiagnosticRange"),L(R$,"getDiagnosticRange"),s(Wv,"toDiagnosticSeverity"),L(Wv,"toDiagnosticSeverity"),s($$,"toDiagnosticData"),L($$,"toDiagnosticData"),(function(q){q.LexingError="lexing-error",q.LexingWarning="lexing-warning",q.LexingInfo="lexing-info",q.LexingHint="lexing-hint",q.ParsingError="parsing-error",q.LinkingError="linking-error"})(vc||(vc={})),hce=(Ti=class{constructor(T){this.astNodeLocator=T.workspace.AstNodeLocator,this.nameProvider=T.references.NameProvider}createDescription(T,A,D){let P=D??lc(T);A??(A=this.nameProvider.getName(T));let V=this.astNodeLocator.getAstNodePath(T);if(!A)throw new Error(`Node at path ${V} has no name.`);let Z,re=L(()=>Z??(Z=_m(this.nameProvider.getNameNode(T)??T.$cstNode)),"nameSegmentGetter");return{node:T,name:A,get nameSegment(){return re()},selectionSegment:_m(T.$cstNode),type:T.$type,documentUri:P.uri,path:V}}},s(Ti,"DefaultAstNodeDescriptionProvider"),L(Ti,"DefaultAstNodeDescriptionProvider"),Ti),dce=(vt=class{constructor(T){this.nodeLocator=T.workspace.AstNodeLocator}async createDescriptions(T,A=Sn.CancellationToken.None){let D=[],P=T.parseResult.value;for(let V of cc(P))await cs(A),wm(V).forEach(Z=>{Z.reference.error||D.push(...this.createInfoDescriptions(Z))});return D}createInfoDescriptions(T){let A=T.reference;if(A.error||!A.$refNode)return[];let D=[];fo(A)&&A.$nodeDescription?D=[A.$nodeDescription]:Zc(A)&&(D=A.items.map(Q=>Q.$nodeDescription).filter(Q=>Q!==void 0));let P=lc(T.container).uri,V=this.nodeLocator.getAstNodePath(T.container),Z=[],re=_m(A.$refNode);for(let Q of D)Z.push({sourceUri:P,sourcePath:V,targetUri:Q.documentUri,targetPath:Q.path,segment:re,local:Uo.equals(Q.documentUri,P)});return Z}},s(vt,"DefaultReferenceDescriptionProvider"),L(vt,"DefaultReferenceDescriptionProvider"),vt),pce=(wt=class{constructor(){this.segmentSeparator="/",this.indexSeparator="@"}getAstNodePath(T){if(T.$container){let A=this.getAstNodePath(T.$container),D=this.getPathSegment(T);return A+this.segmentSeparator+D}return""}getPathSegment({$containerProperty:T,$containerIndex:A}){if(!T)throw new Error("Missing '$containerProperty' in AST node.");return A!==void 0?T+this.indexSeparator+A:T}getAstNode(T,A){return A.split(this.segmentSeparator).reduce((D,P)=>{if(!D||P.length===0)return D;let V=P.indexOf(this.indexSeparator);if(V>0){let Z=P.substring(0,V),re=parseInt(P.substring(V+1));return D[Z]?.[re]}return D[P]},T)}},s(wt,"DefaultAstNodeLocator"),L(wt,"DefaultAstNodeLocator"),wt),t5={},RC(t5,W$(Pm(),1)),fce=(Wt=class{constructor(T){this._ready=new wh,this.onConfigurationSectionUpdateEmitter=new t5.Emitter,this.settings={},this.workspaceConfig=!1,this.serviceRegistry=T.ServiceRegistry}get ready(){return this._ready.promise}initialize(T){this.workspaceConfig=T.capabilities.workspace?.configuration??!1}async initialized(T){if(this.workspaceConfig){if(T.register){let A=this.serviceRegistry.all;T.register({section:A.map(D=>this.toSectionName(D.LanguageMetaData.languageId))})}if(T.fetchConfiguration){let A=this.serviceRegistry.all.map(P=>({section:this.toSectionName(P.LanguageMetaData.languageId)})),D=await T.fetchConfiguration(A);A.forEach((P,V)=>{this.updateSectionConfiguration(P.section,D[V])})}}this._ready.resolve()}updateConfiguration(T){typeof T.settings!="object"||T.settings===null||Object.entries(T.settings).forEach(([A,D])=>{this.updateSectionConfiguration(A,D),this.onConfigurationSectionUpdateEmitter.fire({section:A,configuration:D})})}updateSectionConfiguration(T,A){this.settings[T]=A}async getConfiguration(T,A){await this.ready;let D=this.toSectionName(T);if(this.settings[D])return this.settings[D][A]}toSectionName(T){return`${T}`}get onConfigurationSectionUpdate(){return this.onConfigurationSectionUpdateEmitter.event}},s(Wt,"DefaultConfigurationProvider"),L(Wt,"DefaultConfigurationProvider"),Wt),r5=W$(wXe(),1),(function(q){function T(A){return{dispose:L(async()=>await A(),"dispose")}}s(T,"create"),L(T,"create"),q.create=T})(a1||(a1={})),gce=(kr=class{constructor(T){this.updateBuildOptions={validation:{categories:["built-in","fast"]}},this.updateListeners=[],this.buildPhaseListeners=new Eh,this.documentPhaseListeners=new Eh,this.buildState=new Map,this.documentBuildWaiters=new Map,this.currentState=_i.Changed,this.langiumDocuments=T.workspace.LangiumDocuments,this.langiumDocumentFactory=T.workspace.LangiumDocumentFactory,this.textDocuments=T.workspace.TextDocuments,this.indexManager=T.workspace.IndexManager,this.fileSystemProvider=T.workspace.FileSystemProvider,this.workspaceManager=()=>T.workspace.WorkspaceManager,this.serviceRegistry=T.ServiceRegistry}async build(T,A={},D=Sn.CancellationToken.None){for(let P of T){let V=P.uri.toString();if(P.state===_i.Validated){if(typeof A.validation=="boolean"&&A.validation)this.resetToState(P,_i.IndexedReferences);else if(typeof A.validation=="object"){let Z=this.findMissingValidationCategories(P,A);Z.length>0&&(this.buildState.set(V,{completed:!1,options:{validation:{categories:Z}},result:this.buildState.get(V)?.result}),P.state=_i.IndexedReferences)}}else this.buildState.delete(V)}this.currentState=_i.Changed,await this.emitUpdate(T.map(P=>P.uri),[]),await this.buildDocuments(T,A,D)}async update(T,A,D=Sn.CancellationToken.None){this.currentState=_i.Changed;let P=[];for(let Q of A){let ae=this.langiumDocuments.deleteDocuments(Q);for(let we of ae)P.push(we.uri),this.cleanUpDeleted(we)}let V=(await Promise.all(T.map(Q=>this.findChangedUris(Q)))).flat();for(let Q of V){let ae=this.langiumDocuments.getDocument(Q);ae===void 0&&(ae=this.langiumDocumentFactory.fromModel({$type:"INVALID"},Q),ae.state=_i.Changed,this.langiumDocuments.addDocument(ae)),this.resetToState(ae,_i.Changed)}let Z=an(V).concat(P).map(Q=>Q.toString()).toSet();this.langiumDocuments.all.filter(Q=>!Z.has(Q.uri.toString())&&this.shouldRelink(Q,Z)).forEach(Q=>this.resetToState(Q,_i.ComputedScopes)),await this.emitUpdate(V,P),await cs(D);let re=this.sortDocuments(this.langiumDocuments.all.filter(Q=>Q.state<_i.Validated||!this.buildState.get(Q.uri.toString())?.completed||this.resultsAreIncomplete(Q,this.updateBuildOptions)).toArray());await this.buildDocuments(re,this.updateBuildOptions,D)}resultsAreIncomplete(T,A){return this.findMissingValidationCategories(T,A).length>=1}findMissingValidationCategories(T,A){let D=this.buildState.get(T.uri.toString()),P=this.serviceRegistry.getServices(T.uri).validation.ValidationRegistry.getAllValidationCategories(T),V=D?.result?.validationChecks?new Set(D?.result?.validationChecks):D?.completed?P:new Set,Z=A===void 0||A.validation===!0?P:typeof A.validation=="object"?A.validation.categories??P:[];return an(Z).filter(re=>!V.has(re)).toArray()}async findChangedUris(T){if(this.langiumDocuments.getDocument(T)??this.textDocuments?.get(T))return[T];try{let A=await this.fileSystemProvider.stat(T);if(A.isDirectory)return await this.workspaceManager().searchFolder(T);if(this.workspaceManager().shouldIncludeEntry(A))return[T]}catch{}return[]}async emitUpdate(T,A){await Promise.all(this.updateListeners.map(D=>D(T,A)))}sortDocuments(T){let A=0,D=T.length-1;for(;A=0&&!this.hasTextDocument(T[D]);)D--;AD.error!==void 0)?!0:this.indexManager.isAffected(T,A)}onUpdate(T){return this.updateListeners.push(T),a1.create(()=>{let A=this.updateListeners.indexOf(T);A>=0&&this.updateListeners.splice(A,1)})}resetToState(T,A){switch(A){case _i.Changed:case _i.Parsed:this.indexManager.removeContent(T.uri);case _i.IndexedContent:T.localSymbols=void 0;case _i.ComputedScopes:this.serviceRegistry.getServices(T.uri).references.Linker.unlink(T);case _i.Linked:this.indexManager.removeReferences(T.uri);case _i.IndexedReferences:T.diagnostics=void 0,this.buildState.delete(T.uri.toString());case _i.Validated:}T.state>A&&(T.state=A)}cleanUpDeleted(T){this.buildState.delete(T.uri.toString()),this.indexManager.remove(T.uri),T.state=_i.Changed}async buildDocuments(T,A,D){this.prepareBuild(T,A),await this.runCancelable(T,_i.Parsed,D,Z=>this.langiumDocumentFactory.update(Z,D)),await this.runCancelable(T,_i.IndexedContent,D,Z=>this.indexManager.updateContent(Z,D)),await this.runCancelable(T,_i.ComputedScopes,D,async Z=>{let re=this.serviceRegistry.getServices(Z.uri).references.ScopeComputation;Z.localSymbols=await re.collectLocalSymbols(Z,D)});let P=T.filter(Z=>this.shouldLink(Z));await this.runCancelable(P,_i.Linked,D,Z=>this.serviceRegistry.getServices(Z.uri).references.Linker.link(Z,D)),await this.runCancelable(P,_i.IndexedReferences,D,Z=>this.indexManager.updateReferences(Z,D));let V=T.filter(Z=>this.shouldValidate(Z)?!0:(this.markAsCompleted(Z),!1));await this.runCancelable(V,_i.Validated,D,async Z=>{await this.validate(Z,D),this.markAsCompleted(Z)})}markAsCompleted(T){let A=this.buildState.get(T.uri.toString());A&&(A.completed=!0)}prepareBuild(T,A){for(let D of T){let P=D.uri.toString(),V=this.buildState.get(P);(!V||V.completed)&&this.buildState.set(P,{completed:!1,options:A,result:V?.result})}}async runCancelable(T,A,D,P){for(let Z of T)Z.stateZ.state===A);await this.notifyBuildPhase(V,A,D),this.currentState=A}onBuildPhase(T,A){return this.buildPhaseListeners.add(T,A),a1.create(()=>{this.buildPhaseListeners.delete(T,A)})}onDocumentPhase(T,A){return this.documentPhaseListeners.add(T,A),a1.create(()=>{this.documentPhaseListeners.delete(T,A)})}waitUntil(T,A,D){let P;return A&&"path"in A?P=A:D=A,D??(D=Sn.CancellationToken.None),P?this.awaitDocumentState(T,P,D):this.awaitBuilderState(T,D)}awaitDocumentState(T,A,D){let P=this.langiumDocuments.getDocument(A);if(P){if(P.state>=T)return Promise.resolve(A);if(D.isCancellationRequested)return Promise.reject(ou);if(this.currentState>=T&&T>P.state)return Promise.reject(new r5.ResponseError(r5.LSPErrorCodes.RequestFailed,`Document state of ${A.toString()} is ${_i[P.state]}, requiring ${_i[T]}, but workspace state is already ${_i[this.currentState]}. Returning undefined.`))}else return Promise.reject(new r5.ResponseError(r5.LSPErrorCodes.ServerCancelled,`No document found for URI: ${A.toString()}`));return new Promise((V,Z)=>{let re=this.onDocumentPhase(T,ae=>{Uo.equals(ae.uri,A)&&(re.dispose(),Q.dispose(),V(ae.uri))}),Q=D.onCancellationRequested(()=>{re.dispose(),Q.dispose(),Z(ou)})})}awaitBuilderState(T,A){return this.currentState>=T?Promise.resolve():A.isCancellationRequested?Promise.reject(ou):new Promise((D,P)=>{let V=this.onBuildPhase(T,()=>{V.dispose(),Z.dispose(),D()}),Z=A.onCancellationRequested(()=>{V.dispose(),Z.dispose(),P(ou)})})}async notifyDocumentPhase(T,A,D){let P=this.documentPhaseListeners.get(A).slice();for(let V of P)try{await cs(D),await V(T,D)}catch(Z){if(!jf(Z))throw Z}}async notifyBuildPhase(T,A,D){if(T.length===0)return;let P=this.buildPhaseListeners.get(A).slice();for(let V of P)await cs(D),await V(T,D)}shouldLink(T){return this.getBuildOptions(T).eagerLinking??!0}shouldValidate(T){return!!this.getBuildOptions(T).validation}async validate(T,A){let D=this.serviceRegistry.getServices(T.uri).validation.DocumentValidator,P=this.getBuildOptions(T),V=typeof P.validation=="object"?{...P.validation}:{};V.categories=this.findMissingValidationCategories(T,P);let Z=await D.validateDocument(T,V,A);T.diagnostics?T.diagnostics.push(...Z):T.diagnostics=Z;let re=this.buildState.get(T.uri.toString());re&&(re.result??(re.result={}),re.result.validationChecks?re.result.validationChecks=an(re.result.validationChecks).concat(V.categories).distinct().toArray():re.result.validationChecks=[...V.categories])}getBuildOptions(T){return this.buildState.get(T.uri.toString())?.options??{}}},s(kr,"DefaultDocumentBuilder"),L(kr,"DefaultDocumentBuilder"),kr),mce=(Ir=class{constructor(T){this.symbolIndex=new Map,this.symbolByTypeIndex=new e5,this.referenceIndex=new Map,this.documents=T.workspace.LangiumDocuments,this.serviceRegistry=T.ServiceRegistry,this.astReflection=T.AstReflection}findAllReferences(T,A){let D=lc(T).uri,P=[];return this.referenceIndex.forEach(V=>{V.forEach(Z=>{Uo.equals(Z.targetUri,D)&&Z.targetPath===A&&P.push(Z)})}),an(P)}allElements(T,A){let D=an(this.symbolIndex.keys());return A&&(D=D.filter(P=>!A||A.has(P))),D.map(P=>this.getFileDescriptions(P,T)).flat()}getFileDescriptions(T,A){return A?this.symbolByTypeIndex.get(T,A,()=>(this.symbolIndex.get(T)??[]).filter(D=>this.astReflection.isSubtype(D.type,A))):this.symbolIndex.get(T)??[]}remove(T){this.removeContent(T),this.removeReferences(T)}removeContent(T){let A=T.toString();this.symbolIndex.delete(A),this.symbolByTypeIndex.clear(A)}removeReferences(T){let A=T.toString();this.referenceIndex.delete(A)}async updateContent(T,A=Sn.CancellationToken.None){let D=await this.serviceRegistry.getServices(T.uri).references.ScopeComputation.collectExportedSymbols(T,A),P=T.uri.toString();this.symbolIndex.set(P,D),this.symbolByTypeIndex.clear(P)}async updateReferences(T,A=Sn.CancellationToken.None){let D=await this.serviceRegistry.getServices(T.uri).workspace.ReferenceDescriptionProvider.createDescriptions(T,A);this.referenceIndex.set(T.uri.toString(),D)}isAffected(T,A){let D=this.referenceIndex.get(T.uri.toString());return D?D.some(P=>!P.local&&A.has(P.targetUri.toString())):!1}},s(Ir,"DefaultIndexManager"),L(Ir,"DefaultIndexManager"),Ir),yce=(Lr=class{constructor(T){this.initialBuildOptions={},this._ready=new wh,this.serviceRegistry=T.ServiceRegistry,this.langiumDocuments=T.workspace.LangiumDocuments,this.documentBuilder=T.workspace.DocumentBuilder,this.fileSystemProvider=T.workspace.FileSystemProvider,this.mutex=T.workspace.WorkspaceLock}get ready(){return this._ready.promise}get workspaceFolders(){return this.folders}initialize(T){this.folders=T.workspaceFolders??void 0}initialized(T){return this.mutex.write(A=>this.initializeWorkspace(this.folders??[],A))}async initializeWorkspace(T,A=Sn.CancellationToken.None){let D=await this.performStartup(T);await cs(A),await this.documentBuilder.build(D,this.initialBuildOptions,A)}async performStartup(T){let A=[],D=L(Z=>{A.push(Z),this.langiumDocuments.hasDocument(Z.uri)||this.langiumDocuments.addDocument(Z)},"collector");await this.loadAdditionalDocuments(T,D);let P=[];await Promise.all(T.map(Z=>this.getRootFolder(Z)).map(async Z=>this.traverseFolder(Z,P)));let V=an(P).distinct(Z=>Z.toString()).filter(Z=>!this.langiumDocuments.hasDocument(Z));return await this.loadWorkspaceDocuments(V,D),this._ready.resolve(),A}async loadWorkspaceDocuments(T,A){await Promise.all(T.map(async D=>{let P=await this.langiumDocuments.getOrCreateDocument(D);A(P)}))}loadAdditionalDocuments(T,A){return Promise.resolve()}getRootFolder(T){return yl.parse(T.uri)}async traverseFolder(T,A){try{let D=await this.fileSystemProvider.readDirectory(T);await Promise.all(D.map(async P=>{this.shouldIncludeEntry(P)&&(P.isDirectory?await this.traverseFolder(P.uri,A):P.isFile&&A.push(P.uri))}))}catch(D){console.error("Failure to read directory content of "+T.toString(!0),D)}}async searchFolder(T){let A=[];return await this.traverseFolder(T,A),A}shouldIncludeEntry(T){let A=Uo.basename(T.uri);return A.startsWith(".")?!1:T.isDirectory?A!=="node_modules"&&A!=="out":T.isFile?this.serviceRegistry.hasServices(T.uri):!1}},s(Lr,"DefaultWorkspaceManager"),L(Lr,"DefaultWorkspaceManager"),Lr),vce=(di=class{buildUnexpectedCharactersMessage(T,A,D,P,V){return LL.buildUnexpectedCharactersMessage(T,A,D,P,V)}buildUnableToPopLexerModeMessage(T){return LL.buildUnableToPopLexerModeMessage(T)}},s(di,"DefaultLexerErrorMessageProvider"),L(di,"DefaultLexerErrorMessageProvider"),di),g9={mode:"full"},m9=(ki=class{constructor(T){this.errorMessageProvider=T.parser.LexerErrorMessageProvider,this.tokenBuilder=T.parser.TokenBuilder;let A=this.tokenBuilder.buildTokens(T.Grammar,{caseInsensitive:T.LanguageMetaData.caseInsensitive});this.tokenTypes=this.toTokenTypeDictionary(A);let D=wC(A)?Object.values(A):A,P=T.LanguageMetaData.mode==="production";this.chevrotainLexer=new bo(D,{positionTracking:"full",skipValidations:P,errorMessageProvider:this.errorMessageProvider})}get definition(){return this.tokenTypes}tokenize(T,A=g9){let D=this.chevrotainLexer.tokenize(T);return{tokens:D.tokens,errors:D.errors,hidden:D.groups.hidden??[],report:this.tokenBuilder.flushLexingReport?.(T)}}toTokenTypeDictionary(T){if(wC(T))return T;let A=CC(T)?Object.values(T.modes).flat():T,D={};return A.forEach(P=>D[P.name]=P),D}},s(ki,"DefaultLexer"),L(ki,"DefaultLexer"),ki),s(kC,"isTokenTypeArray"),L(kC,"isTokenTypeArray"),s(CC,"isIMultiModeLexerDefinition"),L(CC,"isIMultiModeLexerDefinition"),s(wC,"isTokenTypeDictionary"),L(wC,"isTokenTypeDictionary"),Gv(),s(L$,"parseJSDoc"),L(L$,"parseJSDoc"),s(I$,"isJSDoc"),L(I$,"isJSDoc"),s(M$,"getLines"),L(M$,"getLines"),bce=/\s*(@([\p{L}][\p{L}\p{N}]*)?)/uy,Jst=/\{(@[\p{L}][\p{L}\p{N}]*)(\s*)([^\r\n}]+)?\}/gu,s(Nne,"tokenize"),L(Nne,"tokenize"),s(One,"buildInlineTokens"),L(One,"buildInlineTokens"),eot=/\S/,tot=/\s*$/,s(EC,"skipWhitespace"),L(EC,"skipWhitespace"),s(Bne,"lastCharacter"),L(Bne,"lastCharacter"),s(Pne,"parseJSDocComment"),L(Pne,"parseJSDocComment"),s(Fne,"parseJSDocElement"),L(Fne,"parseJSDocElement"),s(zne,"appendEmptyLine"),L(zne,"appendEmptyLine"),s(D$,"parseJSDocText"),L(D$,"parseJSDocText"),s(qne,"parseJSDocInline"),L(qne,"parseJSDocInline"),s(N$,"parseJSDocTag"),L(N$,"parseJSDocTag"),s(O$,"parseJSDocLine"),L(O$,"parseJSDocLine"),s(SC,"normalizeOptions"),L(SC,"normalizeOptions"),s(_C,"normalizeOption"),L(_C,"normalizeOption"),xce=(qr=class{constructor(T,A){this.elements=T,this.range=A}getTag(T){return this.getAllTags().find(A=>A.name===T)}getTags(T){return this.getAllTags().filter(A=>A.name===T)}getAllTags(){return this.elements.filter(T=>"name"in T)}toString(){let T="";for(let A of this.elements)if(T.length===0)T=A.toString();else{let D=A.toString();T+=B$(T)+D}return T.trim()}toMarkdown(T){let A="";for(let D of this.elements)if(A.length===0)A=D.toMarkdown(T);else{let P=D.toMarkdown(T);A+=B$(A)+P}return A.trim()}},s(qr,"JSDocCommentImpl"),L(qr,"JSDocCommentImpl"),qr),y9=(fi=class{constructor(T,A,D,P){this.name=T,this.content=A,this.inline=D,this.range=P}toString(){let T=`@${this.name}`,A=this.content.toString();return this.content.inlines.length===1?T=`${T} ${A}`:this.content.inlines.length>1&&(T=`${T} +${A}`),this.inline?`{${T}}`:T}toMarkdown(T){return T?.renderTag?.(this)??this.toMarkdownDefault(T)}toMarkdownDefault(T){let A=this.content.toMarkdown(T);if(this.inline){let V=Wne(this.name,A,T??{});if(typeof V=="string")return V}let D="";T?.tag==="italic"||T?.tag===void 0?D="*":T?.tag==="bold"?D="**":T?.tag==="bold-italic"&&(D="***");let P=`${D}@${this.name}${D}`;return this.content.inlines.length===1?P=`${P} — ${A}`:this.content.inlines.length>1&&(P=`${P} +${A}`),this.inline?`{${P}}`:P}},s(fi,"JSDocTagImpl"),L(fi,"JSDocTagImpl"),fi),s(Wne,"renderInlineTag"),L(Wne,"renderInlineTag"),s(jne,"renderLinkDefault"),L(jne,"renderLinkDefault"),v9=(gt=class{constructor(T,A){this.inlines=T,this.range=A}toString(){let T="";for(let A=0;AD.range.start.line&&(T+=` +`)}return T}toMarkdown(T){let A="";for(let D=0;DP.range.start.line&&(A+=` +`)}return A}},s(gt,"JSDocTextImpl"),L(gt,"JSDocTextImpl"),gt),Tce=(Mt=class{constructor(T,A){this.text=T,this.range=A}toString(){return this.text}toMarkdown(){return this.text}},s(Mt,"JSDocLineImpl"),L(Mt,"JSDocLineImpl"),Mt),s(B$,"fillNewlines"),L(B$,"fillNewlines"),kce=(kt=class{constructor(T){this.indexManager=T.shared.workspace.IndexManager,this.commentProvider=T.documentation.CommentProvider}getDocumentation(T){let A=this.commentProvider.getComment(T);if(A&&I$(A))return L$(A).toMarkdown({renderLink:L((D,P)=>this.documentationLinkRenderer(T,D,P),"renderLink"),renderTag:L(D=>this.documentationTagRenderer(T,D),"renderTag")})}documentationLinkRenderer(T,A,D){let P=this.findNameInLocalSymbols(T,A)??this.findNameInGlobalScope(T,A);if(P&&P.nameSegment){let V=P.nameSegment.range.start.line+1,Z=P.nameSegment.range.start.character+1,re=P.documentUri.with({fragment:`L${V},${Z}`});return`[${D}](${re.toString()})`}else return}documentationTagRenderer(T,A){}findNameInLocalSymbols(T,A){let D=lc(T).localSymbols;if(!D)return;let P=T;do{let V=D.getStream(P).find(Z=>Z.name===A);if(V)return V;P=P.$container}while(P)}findNameInGlobalScope(T,A){return this.indexManager.allElements().find(D=>D.name===A)}},s(kt,"JSDocDocumentationProvider"),L(kt,"JSDocDocumentationProvider"),kt),Cce=(Qt=class{constructor(T){this.grammarConfig=()=>T.parser.GrammarConfig}getComment(T){return _$(T)?T.$comment:oR(T.$cstNode,this.grammarConfig().multilineCommentRules)?.text}},s(Qt,"DefaultCommentProvider"),L(Qt,"DefaultCommentProvider"),Qt),wce=(Pe=class{constructor(T){this.syncParser=T.parser.LangiumParser}parse(T,A){return Promise.resolve(this.syncParser.parse(T))}},s(Pe,"DefaultAsyncParser"),L(Pe,"DefaultAsyncParser"),Pe),rot=(ii=class{constructor(T){this.threadCount=8,this.terminationDelay=200,this.workerPool=[],this.queue=[],this.hydrator=T.serializer.Hydrator}initializeWorkers(){for(;this.workerPool.length{if(this.queue.length>0){let A=this.queue.shift();A&&(T.lock(),A.resolve(T))}}),this.workerPool.push(T)}}async parse(T,A){let D=await this.acquireParserWorker(A),P=new wh,V,Z=A.onCancellationRequested(()=>{V=setTimeout(()=>{this.terminateWorker(D)},this.terminationDelay)});return D.parse(T).then(re=>{let Q=this.hydrator.hydrate(re);P.resolve(Q)}).catch(re=>{P.reject(re)}).finally(()=>{Z.dispose(),clearTimeout(V)}),P.promise}terminateWorker(T){T.terminate();let A=this.workerPool.indexOf(T);A>=0&&this.workerPool.splice(A,1)}async acquireParserWorker(T){this.initializeWorkers();for(let D of this.workerPool)if(D.ready)return D.lock(),D;let A=new wh;return T.onCancellationRequested(()=>{let D=this.queue.indexOf(A);D>=0&&this.queue.splice(D,1),A.reject(ou)}),this.queue.push(A),A.promise}},s(ii,"AbstractThreadedAsyncParser"),L(ii,"AbstractThreadedAsyncParser"),ii),iot=(Ye=class{get ready(){return this._ready}get onReady(){return this.onReadyEmitter.event}constructor(T,A,D,P){this.onReadyEmitter=new t5.Emitter,this.deferred=new wh,this._ready=!0,this._parsing=!1,this.sendMessage=T,this._terminate=P,A(V=>{let Z=V;this.deferred.resolve(Z),this.unlock()}),D(V=>{this.deferred.reject(V),this.unlock()})}terminate(){this.deferred.reject(ou),this._terminate()}lock(){this._ready=!1}unlock(){this._parsing=!1,this._ready=!0,this.onReadyEmitter.fire()}parse(T){if(this._parsing)throw new Error("Parser worker is busy");return this._parsing=!0,this.deferred=new wh,this.sendMessage(T),this.deferred.promise}},s(Ye,"ParserWorker"),L(Ye,"ParserWorker"),Ye),Ece=(ti=class{constructor(){this.previousTokenSource=new Sn.CancellationTokenSource,this.writeQueue=[],this.readQueue=[],this.done=!0}write(T){this.cancelWrite();let A=xC();return this.previousTokenSource=A,this.enqueue(this.writeQueue,T,A.token)}read(T){return this.enqueue(this.readQueue,T)}enqueue(T,A,D=Sn.CancellationToken.None){let P=new wh,V={action:A,deferred:P,cancellationToken:D};return T.push(V),this.performNextOperation(),P.promise}async performNextOperation(){if(!this.done)return;let T=[];if(this.writeQueue.length>0)T.push(this.writeQueue.shift());else if(this.readQueue.length>0)T.push(...this.readQueue.splice(0,this.readQueue.length));else return;this.done=!1,await Promise.all(T.map(async({action:A,deferred:D,cancellationToken:P})=>{try{let V=await Promise.resolve().then(()=>A(P));D.resolve(V)}catch(V){jf(V)?D.resolve(void 0):D.reject(V)}})),this.done=!0,this.performNextOperation()}cancelWrite(){this.previousTokenSource.cancel()}},s(ti,"DefaultWorkspaceLock"),L(ti,"DefaultWorkspaceLock"),ti),Sce=(Na=class{constructor(T){this.grammarElementIdMap=new Zw,this.tokenTypeIdMap=new Zw,this.grammar=T.Grammar,this.lexer=T.parser.Lexer,this.linker=T.references.Linker}dehydrate(T){return{lexerErrors:T.lexerErrors,lexerReport:T.lexerReport?this.dehydrateLexerReport(T.lexerReport):void 0,parserErrors:T.parserErrors.map(A=>({...A,message:A.message})),value:this.dehydrateAstNode(T.value,this.createDehyrationContext(T.value))}}dehydrateLexerReport(T){return T}createDehyrationContext(T){let A=new Map,D=new Map;for(let P of cc(T))A.set(P,{});if(T.$cstNode)for(let P of Sm(T.$cstNode))D.set(P,{});return{astNodes:A,cstNodes:D}}dehydrateAstNode(T,A){let D=A.astNodes.get(T);D.$type=T.$type,D.$containerIndex=T.$containerIndex,D.$containerProperty=T.$containerProperty,T.$cstNode!==void 0&&(D.$cstNode=this.dehydrateCstNode(T.$cstNode,A));for(let[P,V]of Object.entries(T))if(!P.startsWith("$"))if(Array.isArray(V)){let Z=[];D[P]=Z;for(let re of V)Da(re)?Z.push(this.dehydrateAstNode(re,A)):fo(re)?Z.push(this.dehydrateReference(re,A)):Z.push(re)}else Da(V)?D[P]=this.dehydrateAstNode(V,A):fo(V)?D[P]=this.dehydrateReference(V,A):V!==void 0&&(D[P]=V);return D}dehydrateReference(T,A){let D={};return D.$refText=T.$refText,T.$refNode&&(D.$refNode=A.cstNodes.get(T.$refNode)),D}dehydrateCstNode(T,A){let D=A.cstNodes.get(T);return Mk(T)?D.fullText=T.fullText:D.grammarSource=this.getGrammarElementId(T.grammarSource),D.hidden=T.hidden,D.astNode=A.astNodes.get(T.astNode),sh(T)?D.content=T.content.map(P=>this.dehydrateCstNode(P,A)):wf(T)&&(D.tokenType=T.tokenType.name,D.offset=T.offset,D.length=T.length,D.startLine=T.range.start.line,D.startColumn=T.range.start.character,D.endLine=T.range.end.line,D.endColumn=T.range.end.character),D}hydrate(T){let A=T.value,D=this.createHydrationContext(A);return"$cstNode"in A&&this.hydrateCstNode(A.$cstNode,D),{lexerErrors:T.lexerErrors,lexerReport:T.lexerReport,parserErrors:T.parserErrors,value:this.hydrateAstNode(A,D)}}createHydrationContext(T){let A=new Map,D=new Map;for(let V of cc(T))A.set(V,{});let P;if(T.$cstNode)for(let V of Sm(T.$cstNode)){let Z;"fullText"in V?(Z=new a9(V.fullText),P=Z):"content"in V?Z=new Gw:"tokenType"in V&&(Z=this.hydrateCstLeafNode(V)),Z&&(D.set(V,Z),Z.root=P)}return{astNodes:A,cstNodes:D}}hydrateAstNode(T,A){let D=A.astNodes.get(T);D.$type=T.$type,D.$containerIndex=T.$containerIndex,D.$containerProperty=T.$containerProperty,T.$cstNode&&(D.$cstNode=A.cstNodes.get(T.$cstNode));for(let[P,V]of Object.entries(T))if(!P.startsWith("$"))if(Array.isArray(V)){let Z=[];D[P]=Z;for(let re of V)Da(re)?Z.push(this.setParent(this.hydrateAstNode(re,A),D)):fo(re)?Z.push(this.hydrateReference(re,D,P,A)):Z.push(re)}else Da(V)?D[P]=this.setParent(this.hydrateAstNode(V,A),D):fo(V)?D[P]=this.hydrateReference(V,D,P,A):V!==void 0&&(D[P]=V);return D}setParent(T,A){return T.$container=A,T}hydrateReference(T,A,D,P){return this.linker.buildReference(A,D,P.cstNodes.get(T.$refNode),T.$refText)}hydrateCstNode(T,A,D=0){let P=A.cstNodes.get(T);if(typeof T.grammarSource=="number"&&(P.grammarSource=this.getGrammarElement(T.grammarSource)),P.astNode=A.astNodes.get(T.astNode),sh(P))for(let V of T.content){let Z=this.hydrateCstNode(V,A,D++);P.content.push(Z)}return P}hydrateCstLeafNode(T){let A=this.getTokenType(T.tokenType),D=T.offset,P=T.length,V=T.startLine,Z=T.startColumn,re=T.endLine,Q=T.endColumn,ae=T.hidden;return new Hw(D,P,{start:{line:V,character:Z},end:{line:re,character:Q}},A,ae)}getTokenType(T){return this.lexer.definition[T]}getGrammarElementId(T){if(T)return this.grammarElementIdMap.size===0&&this.createGrammarElementIdMap(),this.grammarElementIdMap.get(T)}getGrammarElement(T){return this.grammarElementIdMap.size===0&&this.createGrammarElementIdMap(),this.grammarElementIdMap.getKey(T)}createGrammarElementIdMap(){let T=0;for(let A of cc(this.grammar))Bk(A)&&this.grammarElementIdMap.set(A,T++)}},s(Na,"DefaultHydrator"),L(Na,"DefaultHydrator"),Na),s(us,"createDefaultCoreModule"),L(us,"createDefaultCoreModule"),s(hs,"createDefaultSharedCoreModule"),L(hs,"createDefaultSharedCoreModule"),(function(q){q.merge=(T,A)=>Nm(Nm({},T),A)})(_ce||(_ce={})),s(Bi,"inject"),L(Bi,"inject"),Ace=Symbol("isProxy"),s(P$,"eagerLoad"),L(P$,"eagerLoad"),s(F$,"_inject"),L(F$,"_inject"),Rce=Symbol(),s(z$,"_resolve"),L(z$,"_resolve"),s(Nm,"_merge"),L(Nm,"_merge"),b9={indentTokenName:"INDENT",dedentTokenName:"DEDENT",whitespaceTokenName:"WS",ignoreIndentationDelimiters:[]},(function(q){q.REGULAR="indentation-sensitive",q.IGNORE_INDENTATION="ignore-indentation"})(s1||(s1={})),$ce=(ka=class extends Kw{constructor(T=b9){super(),this.indentationStack=[0],this.whitespaceRegExp=/[ \t]+/y,this.options={...b9,...T},this.indentTokenType=Mm({name:this.options.indentTokenName,pattern:this.indentMatcher.bind(this),line_breaks:!1}),this.dedentTokenType=Mm({name:this.options.dedentTokenName,pattern:this.dedentMatcher.bind(this),line_breaks:!1})}buildTokens(T,A){let D=super.buildTokens(T,A);if(!kC(D))throw new Error("Invalid tokens built by default builder");let{indentTokenName:P,dedentTokenName:V,whitespaceTokenName:Z,ignoreIndentationDelimiters:re}=this.options,Q,ae,we,Ue=[];for(let je of D){for(let[ut,at]of re)je.name===ut?je.PUSH_MODE=s1.IGNORE_INDENTATION:je.name===at&&(je.POP_MODE=!0);je.name===V?Q=je:je.name===P?ae=je:je.name===Z?we=je:Ue.push(je)}if(!Q||!ae||!we)throw new Error("Some indentation/whitespace tokens not found!");return re.length>0?{modes:{[s1.REGULAR]:[Q,ae,...Ue,we],[s1.IGNORE_INDENTATION]:[...Ue,we]},defaultMode:s1.REGULAR}:[Q,ae,we,...Ue]}flushLexingReport(T){return{...super.flushLexingReport(T),remainingDedents:this.flushRemainingDedents(T)}}isStartOfLine(T,A){return A===0||`\r +`.includes(T[A-1])}matchWhitespace(T,A,D,P){this.whitespaceRegExp.lastIndex=A;let V=this.whitespaceRegExp.exec(T);return{currIndentLevel:V?.[0].length??0,prevIndentLevel:this.indentationStack.at(-1),match:V}}createIndentationTokenInstance(T,A,D,P){let V=this.getLineNumber(A,P);return Mv(T,D,P,P+D.length,V,V,1,D.length)}getLineNumber(T,A){return T.substring(0,A).split(/\r\n|\r|\n/).length}indentMatcher(T,A,D,P){if(!this.isStartOfLine(T,A))return null;let{currIndentLevel:V,prevIndentLevel:Z,match:re}=this.matchWhitespace(T,A,D,P);return V<=Z?null:(this.indentationStack.push(V),re)}dedentMatcher(T,A,D,P){if(!this.isStartOfLine(T,A))return null;let{currIndentLevel:V,prevIndentLevel:Z,match:re}=this.matchWhitespace(T,A,D,P);if(V>=Z)return null;let Q=this.indentationStack.lastIndexOf(V);if(Q===-1)return this.diagnostics.push({severity:"error",message:`Invalid dedent level ${V} at offset: ${A}. Current indentation stack: ${this.indentationStack}`,offset:A,length:re?.[0]?.length??0,line:this.getLineNumber(T,A),column:1}),null;let ae=this.indentationStack.length-Q-1,we=T.substring(0,A).match(/[\r\n]+$/)?.[0].length??1;for(let Ue=0;Ue1;)A.push(this.createIndentationTokenInstance(this.dedentTokenType,T,"",T.length)),this.indentationStack.pop();return this.indentationStack=[0],A}},s(ka,"IndentationAwareTokenBuilder"),L(ka,"IndentationAwareTokenBuilder"),ka),not=(Ao=class extends m9{constructor(T){if(super(T),T.parser.TokenBuilder instanceof $ce)this.indentationTokenBuilder=T.parser.TokenBuilder;else throw new Error("IndentationAwareLexer requires an accompanying IndentationAwareTokenBuilder")}tokenize(T,A=g9){let D=super.tokenize(T),P=D.report;A?.mode==="full"&&D.tokens.push(...P.remainingDedents),P.remainingDedents=[];let{indentTokenType:V,dedentTokenType:Z}=this.indentationTokenBuilder,re=V.tokenTypeIdx,Q=Z.tokenTypeIdx,ae=[],we=D.tokens.length-1;for(let Ue=0;Ue=0&&ae.push(D.tokens[we]),D.tokens=ae,D}},s(Ao,"IndentationAwareLexer"),L(Ao,"IndentationAwareLexer"),Ao),x9={},Vd(x9,{AstUtils:s(()=>tL,"AstUtils"),BiMap:s(()=>Zw,"BiMap"),Cancellation:s(()=>Sn,"Cancellation"),ContextCache:s(()=>e5,"ContextCache"),CstUtils:s(()=>J$,"CstUtils"),DONE_RESULT:s(()=>mo,"DONE_RESULT"),Deferred:s(()=>wh,"Deferred"),Disposable:s(()=>a1,"Disposable"),DisposableCache:s(()=>Jw,"DisposableCache"),DocumentCache:s(()=>nce,"DocumentCache"),EMPTY_STREAM:s(()=>zm,"EMPTY_STREAM"),ErrorWithLocation:s(()=>ew,"ErrorWithLocation"),GrammarUtils:s(()=>nL,"GrammarUtils"),MultiMap:s(()=>Eh,"MultiMap"),OperationCancelled:s(()=>ou,"OperationCancelled"),Reduction:s(()=>zC,"Reduction"),RegExpUtils:s(()=>aL,"RegExpUtils"),SimpleCache:s(()=>d9,"SimpleCache"),StreamImpl:s(()=>eu,"StreamImpl"),TreeStreamImpl:s(()=>qm,"TreeStreamImpl"),URI:s(()=>yl,"URI"),UriTrie:s(()=>u9,"UriTrie"),UriUtils:s(()=>Uo,"UriUtils"),WorkspaceCache:s(()=>p9,"WorkspaceCache"),assertCondition:s(()=>dR,"assertCondition"),assertUnreachable:s(()=>qd,"assertUnreachable"),delayNextTick:s(()=>bC,"delayNextTick"),interruptAndCheck:s(()=>cs,"interruptAndCheck"),isOperationCancelled:s(()=>jf,"isOperationCancelled"),loadGrammarFromJson:s(()=>ds,"loadGrammarFromJson"),setInterruptionPeriod:s(()=>k$,"setInterruptionPeriod"),startCancelableOperation:s(()=>xC,"startCancelableOperation"),stream:s(()=>an,"stream")}),RC(x9,t5),Lce=(Qo=class{stat(T){throw new Error("No file system is available.")}statSync(T){throw new Error("No file system is available.")}async exists(){return!1}existsSync(){return!1}readBinary(){throw new Error("No file system is available.")}readBinarySync(){throw new Error("No file system is available.")}readFile(){throw new Error("No file system is available.")}readFileSync(){throw new Error("No file system is available.")}async readDirectory(){return[]}readDirectorySync(){return[]}},s(Qo,"EmptyFileSystemProvider"),L(Qo,"EmptyFileSystemProvider"),Qo),$s={fileSystemProvider:L(()=>new Lce,"fileSystemProvider")},aot={Grammar:L(()=>{},"Grammar"),LanguageMetaData:L(()=>({caseInsensitive:!1,fileExtensions:[".langium"],languageId:"langium"}),"LanguageMetaData")},sot={AstReflection:L(()=>new rL,"AstReflection")},s(Vne,"createMinimalGrammarServices"),L(Vne,"createMinimalGrammarServices"),s(ds,"loadGrammarFromJson"),L(ds,"loadGrammarFromJson"),RC(Qae,x9),oot=(Zo=class{constructor(T){this.activeCategories=new Set,this.allCategories=new Set(["validating","parsing","linking"]),this.activeCategories=T??new Set(this.allCategories),this.records=new Eh}isActive(T){return this.activeCategories.has(T)}start(...T){T?T.forEach(A=>this.activeCategories.add(A)):this.activeCategories=new Set(this.allCategories)}stop(...T){T?T.forEach(A=>this.activeCategories.delete(A)):this.activeCategories.clear()}createTask(T,A){if(!this.isActive(T))throw new Error(`Category "${T}" is not active.`);return console.log(`Creating profiling task for '${T}.${A}'.`),new Ice(D=>this.records.add(T,this.dumpRecord(T,D)),A)}dumpRecord(T,A){console.info(`Task ${T}.${A.identifier} executed in ${A.duration.toFixed(2)}ms and ended at ${A.date.toISOString()}`);let D=[];for(let Z of A.entries.keys()){let re=A.entries.get(Z),Q=re.reduce((ae,we)=>ae+we);D.push({name:`${A.identifier}.${Z}`,count:re.length,duration:Q})}let P=A.duration-D.map(Z=>Z.duration).reduce((Z,re)=>Z+re,0);D.push({name:A.identifier,count:1,duration:P}),D.sort((Z,re)=>re.duration-Z.duration);function V(Z){return Math.round(100*Z)/100}return s(V,"Round"),L(V,"Round"),console.table(D.map(Z=>({Element:Z.name,Count:Z.count,"Self %":V(100*Z.duration/A.duration),"Time (ms)":V(Z.duration)}))),A}getRecords(...T){return T.length===0?this.records.values():this.records.entries().filter(A=>T.some(D=>D===A[0])).flatMap(A=>A[1])}},s(Zo,"DefaultLangiumProfiler"),L(Zo,"DefaultLangiumProfiler"),Zo),Ice=(nn=class{constructor(T,A){this.stack=[],this.entries=new Eh,this.addRecord=T,this.identifier=A}start(){if(this.startTime!==void 0)throw new Error(`Task "${this.identifier}" is already started.`);this.startTime=performance.now()}stop(){if(this.startTime===void 0)throw new Error(`Task "${this.identifier}" was not started.`);if(this.stack.length!==0)throw new Error(`Task "${this.identifier}" cannot be stopped before sub-task(s): ${this.stack.map(A=>A.id).join(", ")}.`);let T={identifier:this.identifier,date:new Date,duration:performance.now()-this.startTime,entries:this.entries};this.addRecord(T),this.startTime=void 0,this.entries.clear()}startSubTask(T){this.stack.push({id:T,start:performance.now(),content:0})}stopSubTask(T){let A=this.stack.pop();if(!A)throw new Error(`Task "${this.identifier}.${T}" was not started.`);if(A.id!==T)throw new Error(`Sub-Task "${A.id}" is not already stopped.`);let D=performance.now()-A.start;this.stack.at(-1)!==void 0&&(this.stack[this.stack.length-1].content+=D);let P=D-A.content;this.entries.add(T,P)}},s(nn,"ProfilingTask"),L(nn,"ProfilingTask"),nn),(q=>{q.Terminals={ARROW_DIRECTION:/L|R|T|B/,ARROW_GROUP:/\{group\}/,ARROW_INTO:/<|>/,ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,STRING:/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/,ID:/[\w]([-\w]*\w)?/,NEWLINE:/\r?\n/,WHITESPACE:/[\t ]+/,YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/,ARCH_ICON:/\([\w-:]+\)/,ARCH_TITLE:/\[(?:"([^"\\]|\\.)*"|'([^'\\]|\\.)*'|[^\[\]\r\n]+)\]/}})(Mce||(Mce={})),(q=>{q.Terminals={DOMAIN_NAME:/complex|complicated|clear|chaotic|confusion/,ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,STRING:/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/,NEWLINE:/\r?\n/,WHITESPACE:/[\t ]+/,YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/}})(Dce||(Dce={})),(q=>{q.Terminals={EM_ID:/[_a-zA-Z][\w_]*/,EM_FID:/\d{1,3}/,EM_DATA_INLINE:/\{(.*)\}|"(.*)"|'(.*)'/,EM_DATA_BLOCK:/\{[\t ]*\r?\n(?:[\S\s]*?\r?\n)?\}(?:\r?\n|(?!\S))/,EM_ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,EM_ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,EM_TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,EM_WS:/\s+/,EM_YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,EM_DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,EM_SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/,EM_ML_COMMENT:/\/\*[\s\S]*?\*\//,EM_SL_COMMENT:/\/\/[^\n\r]*/}})(Nce||(Nce={})),(q=>{q.Terminals={ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,INT:/0|[1-9][0-9]*(?!\.)/,STRING:/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/,NEWLINE:/\r?\n/,WHITESPACE:/[\t ]+/,YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/,REFERENCE:/\w([-\./\w]*[-\w])?/}})(Oce||(Oce={})),(q=>{q.Terminals={ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,NEWLINE:/\r?\n/,WHITESPACE:/[\t ]+/,YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/}})(Bce||(Bce={})),(q=>{q.Terminals={ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,INT:/0|[1-9][0-9]*(?!\.)/,STRING:/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/,NEWLINE:/\r?\n/,WHITESPACE:/[\t ]+/,YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/}})(Pce||(Pce={})),(q=>{q.Terminals={NUMBER_PIE:/(?:-?[0-9]+\.[0-9]+(?!\.))|(?:-?(0|[1-9][0-9]*)(?!\.))/,ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,STRING:/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/,NEWLINE:/\r?\n/,WHITESPACE:/[\t ]+/,YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/}})(Fce||(Fce={})),(q=>{q.Terminals={GRATICULE:/circle|polygon/,BOOLEAN:/true|false/,ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,NUMBER:/(?:[0-9]+\.[0-9]+(?!\.))|(?:0|[1-9][0-9]*(?!\.))/,STRING:/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/,ID:/[\w]([-\w]*\w)?/,NEWLINE:/\r?\n/,WHITESPACE:/[\t ]+/,YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/}})(zce||(zce={})),(q=>{q.Terminals={TITLE:/title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,ACC_TITLE:/accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,ACC_DESCR:/accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ABNF_RULENAME:/[A-Za-z][A-Za-z0-9-]*/,ABNF_STRING:/"[^"]*"/,ABNF_NUMVAL:/%[xXdDbB][0-9A-Fa-f]+(?:-[0-9A-Fa-f]+|\.[0-9A-Fa-f]+)*/,ABNF_REPEAT:/[0-9]*\*[0-9]*/,ABNF_EXACT_REPEAT:/[0-9]+/,ABNF_WHITESPACE:/[\t \r\n]+/,ABNF_YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,ABNF_DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,ABNF_SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/,ABNF_COMMENT:/;[^\n\r]*/}})(qce||(qce={})),(q=>{q.Terminals={TITLE:/title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,ACC_TITLE:/accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,ACC_DESCR:/accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,EBNF_ID:/[A-Z_a-z][\w-]*/,EBNF_STRING:/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/,EBNF_SPECIAL_SEQUENCE:/\?(?=[^?;]*[^?\s;][^?;]*\?)[^?;]*\?/,EBNF_WHITESPACE:/[\t \r\n]+/,EBNF_YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,EBNF_DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,EBNF_SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/,EBNF_BLOCK_COMMENT:/\/\*[\s\S]*?\*\//,EBNF_ISO_COMMENT:/\(\*[\s\S]*?\*\)/}})(Wce||(Wce={})),(q=>{q.Terminals={TITLE:/title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,ACC_TITLE:/accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,ACC_DESCR:/accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,RR_ID:/[A-Z_a-z][\w-]*/,RR_STRING:/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/,RR_WHITESPACE:/[\t \r\n]+/,RR_YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,RR_DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,RR_SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/,RR_BLOCK_COMMENT:/\/\*[\s\S]*?\*\//}})(jce||(jce={})),(q=>{q.Terminals={TITLE:/title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,ACC_TITLE:/accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,ACC_DESCR:/accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,PEG_ID:/[A-Z_a-z][\w-]*/,PEG_STRING:/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/,PEG_WHITESPACE:/[\t \r\n]+/,PEG_YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,PEG_DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,PEG_SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/,PEG_LINE_COMMENT:/#[^\n\r]*/}})(Vce||(Vce={})),(q=>{q.Terminals={ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,TREEMAP_KEYWORD:/treemap-beta|treemap/,CLASS_DEF:/classDef\s+([a-zA-Z_][a-zA-Z0-9_]+)(?:\s+([^;\r\n]*))?(?:;)?/,STYLE_SEPARATOR:/:::/,SEPARATOR:/:/,COMMA:/,/,INDENTATION:/[ \t]{1,}/,WS:/[ \t]+/,ML_COMMENT:/\%\%[^\n]*/,NL:/\r?\n/,ID2:/[a-zA-Z_][a-zA-Z0-9_]*/,NUMBER2:/[0-9_\.\,]+/,STRING2:/"[^"]*"|'[^']*'/}})(Uce||(Uce={})),(q=>{q.Terminals={ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,CLASS_ANNOTATION:/[ \t]+:::[ \t]*[A-Za-z_][\w-]*/,ICON_ANNOTATION:/[ \t]+icon\([\w-]*(?::[\w-]+)?\)/,DESC_ANNOTATION:/[ \t]+##[^\n\r]*/,INDENTATION:/[ \t]{1,}/,QUOTED_NAME:/"[^"]*"|'[^']*'/,WS:/[ \t]+/,ML_COMMENT:/\%\%[^\n]*/,NL:/\r?\n/,BARE_NAME:/(?!:::|icon\(|##)[^ \t\n\r"'](?:(?![ \t]+:::[ \t]*[A-Za-z_]|[ \t]+icon\(|[ \t]+##)[^\n\r])*/}})(Yce||(Yce={})),(q=>{q.Terminals={WARDLEY_NUMBER:/[0-9]+\.[0-9]+/,ARROW:/->/,LINK_PORT:/\+<>|\+>|\+|-\.->|>|\+'[^']*'<>|\+'[^']*'<|\+'[^']*'>/,LINK_LABEL:/;[^\n\r]+/,STRATEGY:/build|buy|outsource|market/,KW_WARDLEY:/wardley-beta/,KW_SIZE:/size/,KW_EVOLUTION:/evolution/,KW_ANCHOR:/anchor/,KW_COMPONENT:/component/,KW_LABEL:/label/,KW_INERTIA:/inertia/,KW_EVOLVE:/evolve/,KW_PIPELINE:/pipeline/,KW_NOTE:/note/,KW_ANNOTATIONS:/annotations/,KW_ANNOTATION:/annotation/,KW_ACCELERATOR:/accelerator/,KW_DEACCELERATOR:/deaccelerator/,NAME_WITH_SPACES:/(?!title\s|accTitle|accDescr)[A-Za-z](?:[A-Za-z0-9_()&]|-(?!>))*(?:[ \t]+[A-Za-z(](?:[A-Za-z0-9_()&]|-(?!>))*)*/,WS:/[ \t]+/,ACC_DESCR:/[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/,ACC_TITLE:/[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/,TITLE:/[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/,INT:/0|[1-9][0-9]*(?!\.)/,STRING:/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/,ID:/[\w]([-\w]*\w)?/,NEWLINE:/\r?\n/,WHITESPACE:/[\t ]+/,YAML:/---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/,DIRECTIVE:/[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/,SINGLE_LINE_COMMENT:/[\t ]*%%[^\n\r]*/}})(Hce||(Hce={})),m8t={...Mce.Terminals,...Dce.Terminals,...Nce.Terminals,...Oce.Terminals,...Bce.Terminals,...Pce.Terminals,...Fce.Terminals,...zce.Terminals,...qce.Terminals,...Wce.Terminals,...jce.Terminals,...Vce.Terminals,...Yce.Terminals,...Uce.Terminals,...Hce.Terminals},Gce={$type:"AbnfAlternation",alternatives:"alternatives"},Xce={$type:"AbnfConcatenation",elements:"elements"},T9={$type:"AbnfElement",primary:"primary",repeat:"repeat"},Kce={$type:"AbnfGroup",element:"element"},Qce={$type:"AbnfNumVal",value:"value"},Zce={$type:"AbnfOptionalGroup",element:"element"},o1={$type:"AbnfPrimary"},k9={$type:"AbnfRule",definition:"definition",name:"name"},Jce={$type:"AbnfRuleName",name:"name"},eue={$type:"AbnfStringLiteral",value:"value"},i5={$type:"Accelerator",name:"name",x:"x",y:"y"},C9={$type:"Alignment",direction:"direction",members:"members"},n5={$type:"Anchor",evolution:"evolution",name:"name",visibility:"visibility"},Rb={$type:"Annotation",number:"number",text:"text",x:"x",y:"y"},w9={$type:"Annotations",x:"x",y:"y"},lu={$type:"Architecture",accDescr:"accDescr",accTitle:"accTitle",alignments:"alignments",edges:"edges",groups:"groups",junctions:"junctions",services:"services",title:"title"},s(vGe,"isArchitecture"),L(vGe,"isArchitecture"),a5={$type:"Axis",label:"label",name:"name"},s5={$type:"Branch",name:"name",order:"order"},s(bGe,"isBranch"),L(bGe,"isBranch"),tue={$type:"Checkout",branch:"branch"},o5={$type:"CherryPicking",id:"id",parent:"parent",tags:"tags"},E9={$type:"ClassDefStatement",className:"className",styleText:"styleText"},l1={$type:"Commit",id:"id",message:"message",tags:"tags",type:"type"},s(xGe,"isCommit"),L(xGe,"isCommit"),l5={$type:"Common",accDescr:"accDescr",accTitle:"accTitle",title:"title"},f0={$type:"Component",decorator:"decorator",evolution:"evolution",inertia:"inertia",label:"label",name:"name",visibility:"visibility"},c5={$type:"Curve",entries:"entries",label:"label",name:"name"},g0={$type:"Cynefin",accDescr:"accDescr",accTitle:"accTitle",domains:"domains",title:"title",transitions:"transitions"},s(TGe,"isCynefin"),L(TGe,"isCynefin"),u5={$type:"Deaccelerator",name:"name",x:"x",y:"y"},rue={$type:"Decorator",strategy:"strategy"},c1={$type:"Direction",accDescr:"accDescr",accTitle:"accTitle",dir:"dir",statements:"statements",title:"title"},h5={$type:"DomainBlock",domain:"domain",items:"items"},s(kGe,"isDomainBlock"),L(kGe,"isDomainBlock"),S9={$type:"DomainItem",label:"label"},s(CGe,"isDomainItem"),L(CGe,"isDomainItem"),iue={$type:"EbnfChoice",alternatives:"alternatives"},nue={$type:"EbnfExceptionPostfix",except:"except"},aue={$type:"EbnfGroup",element:"element"},sue={$type:"EbnfNonTerminal",name:"name"},oue={$type:"EbnfOneOrMorePostfix",operator:"operator"},lue={$type:"EbnfOptional",element:"element"},cue={$type:"EbnfOptionalPostfix",operator:"operator"},$b={$type:"EbnfPostfix"},m0={$type:"EbnfPrimary"},uue={$type:"EbnfRepetition",element:"element"},_9={$type:"EbnfRule",definition:"definition",name:"name"},hue={$type:"EbnfSequence",elements:"elements"},due={$type:"EbnfSpecial",text:"text"},A9={$type:"EbnfTerm",base:"base",postfixes:"postfixes"},pue={$type:"EbnfTerminal",value:"value"},fue={$type:"EbnfZeroOrMorePostfix",operator:"operator"},cu={$type:"Edge",lhsDir:"lhsDir",lhsGroup:"lhsGroup",lhsId:"lhsId",lhsInto:"lhsInto",rhsDir:"rhsDir",rhsGroup:"rhsGroup",rhsId:"rhsId",rhsInto:"rhsInto",title:"title"},u1={$type:"EmDataEntity",dataBlockValue:"dataBlockValue",dataType:"dataType",name:"name"},y0={$type:"EmFrame"},Lb={$type:"EmGwt",givenStatements:"givenStatements",sourceFrame:"sourceFrame",thenStatements:"thenStatements",whenStatements:"whenStatements"},gue={$type:"EmGwtStatement",entityIdentifier:"entityIdentifier"},R9={$type:"EmModelEntity",name:"name"},s(wGe,"isEmModelEntityType"),L(wGe,"isEmModelEntityType"),d5={$type:"EmNoteEntity",dataBlockValue:"dataBlockValue",dataType:"dataType",sourceFrame:"sourceFrame"},Sh={$type:"EmResetFrame",dataInlineValue:"dataInlineValue",dataReference:"dataReference",dataType:"dataType",entityIdentifier:"entityIdentifier",modelEntityType:"modelEntityType",name:"name",sourceFrames:"sourceFrames"},s(Une,"isEmResetFrame"),L(Une,"isEmResetFrame"),rp={$type:"EmTimeFrame",dataInlineValue:"dataInlineValue",dataReference:"dataReference",dataType:"dataType",entityIdentifier:"entityIdentifier",modelEntityType:"modelEntityType",name:"name",sourceFrames:"sourceFrames"},$9={$type:"Entry",axis:"axis",value:"value"},_h={$type:"EventModel",accDescr:"accDescr",accTitle:"accTitle",dataEntities:"dataEntities",frames:"frames",gwtEntities:"gwtEntities",modelEntities:"modelEntities",noteEntities:"noteEntities",title:"title"},mue={$type:"Evolution",stages:"stages"},p5={$type:"EvolutionStage",boundary:"boundary",name:"name",secondName:"secondName"},L9={$type:"Evolve",component:"component",target:"target"},v0={$type:"GitGraph",accDescr:"accDescr",accTitle:"accTitle",statements:"statements",title:"title"},s(EGe,"isGitGraph"),L(EGe,"isGitGraph"),Ib={$type:"Group",icon:"icon",id:"id",in:"in",title:"title"},Mb={$type:"Info",accDescr:"accDescr",accTitle:"accTitle",title:"title"},s(SGe,"isInfo"),L(SGe,"isInfo"),Db={$type:"Item",classSelector:"classSelector",name:"name"},I9={$type:"Junction",id:"id",in:"in"},Nb={$type:"Label",negX:"negX",negY:"negY",offsetX:"offsetX",offsetY:"offsetY"},f5={$type:"Leaf",classSelector:"classSelector",name:"name",value:"value"},b0={$type:"Link",arrow:"arrow",from:"from",fromPort:"fromPort",linkLabel:"linkLabel",to:"to",toPort:"toPort"},h1={$type:"Merge",branch:"branch",id:"id",tags:"tags",type:"type"},s(_Ge,"isMerge"),L(_Ge,"isMerge"),g5={$type:"Note",evolution:"evolution",text:"text",visibility:"visibility"},M9={$type:"Option",name:"name",value:"value"},d1={$type:"Packet",accDescr:"accDescr",accTitle:"accTitle",blocks:"blocks",title:"title"},s(AGe,"isPacket"),L(AGe,"isPacket"),p1={$type:"PacketBlock",bits:"bits",end:"end",label:"label",start:"start"},s(RGe,"isPacketBlock"),L(RGe,"isPacketBlock"),yue={$type:"PegAny",dot:"dot"},vue={$type:"PegGroup",element:"element"},bue={$type:"PegIdentifier",name:"name"},xue={$type:"PegLiteral",value:"value"},Tue={$type:"PegOrderedChoice",alternatives:"alternatives"},D9={$type:"PegPrefix",operator:"operator",suffix:"suffix"},Ob={$type:"PegPrimary"},N9={$type:"PegRule",definition:"definition",name:"name"},kue={$type:"PegSequence",elements:"elements"},O9={$type:"PegSuffix",operator:"operator",primary:"primary"},x0={$type:"Pie",accDescr:"accDescr",accTitle:"accTitle",sections:"sections",showData:"showData",title:"title"},s($Ge,"isPie"),L($Ge,"isPie"),m5={$type:"PieSection",label:"label",value:"value"},s(LGe,"isPieSection"),L(LGe,"isPieSection"),B9={$type:"Pipeline",components:"components",parent:"parent"},y5={$type:"PipelineComponent",evolution:"evolution",label:"label",name:"name"},T0={$type:"Radar",accDescr:"accDescr",accTitle:"accTitle",axes:"axes",curves:"curves",options:"options",title:"title"},f1={$type:"Railroad",accDescr:"accDescr",accTitle:"accTitle",rules:"rules",title:"title"},s(IGe,"isRailroad"),L(IGe,"isRailroad"),g1={$type:"RailroadAbnf",accDescr:"accDescr",accTitle:"accTitle",rules:"rules",title:"title"},s(MGe,"isRailroadAbnf"),L(MGe,"isRailroadAbnf"),Cue={$type:"RailroadChoiceExpr",alternatives:"alternatives"},m1={$type:"RailroadEbnf",accDescr:"accDescr",accTitle:"accTitle",rules:"rules",title:"title"},s(DGe,"isRailroadEbnf"),L(DGe,"isRailroadEbnf"),Ah={$type:"RailroadExpression"},wue={$type:"RailroadNonTerminalExpr",name:"name"},Eue={$type:"RailroadOneOrMoreExpr",element:"element"},Sue={$type:"RailroadOptionalExpr",element:"element"},y1={$type:"RailroadPeg",accDescr:"accDescr",accTitle:"accTitle",rules:"rules",title:"title"},s(NGe,"isRailroadPeg"),L(NGe,"isRailroadPeg"),P9={$type:"RailroadRule",definition:"definition",name:"name"},_ue={$type:"RailroadSequenceExpr",elements:"elements"},Aue={$type:"RailroadSpecialExpr",text:"text"},Rue={$type:"RailroadTerminalExpr",value:"value"},$ue={$type:"RailroadZeroOrMoreExpr",element:"element"},F9={$type:"Section",classSelector:"classSelector",name:"name"},v1={$type:"Service",icon:"icon",iconText:"iconText",id:"id",in:"in",title:"title"},z9={$type:"Size",height:"height",width:"width"},b1={$type:"Statement"},Bb={$type:"Transition",from:"from",label:"label",to:"to"},s(OGe,"isTransition"),L(OGe,"isTransition"),x1={$type:"Treemap",accDescr:"accDescr",accTitle:"accTitle",title:"title",TreemapRows:"TreemapRows"},s(BGe,"isTreemap"),L(BGe,"isTreemap"),q9={$type:"TreemapRow",indent:"indent",item:"item"},T1={$type:"TreeNode",classAnnotation:"classAnnotation",descAnnotation:"descAnnotation",iconAnnotation:"iconAnnotation",indent:"indent",name:"name"},Pb={$type:"TreeView",accDescr:"accDescr",accTitle:"accTitle",nodes:"nodes",title:"title"},Ls={$type:"Wardley",accDescr:"accDescr",accelerators:"accelerators",accTitle:"accTitle",anchors:"anchors",annotation:"annotation",annotations:"annotations",components:"components",deaccelerators:"deaccelerators",evolution:"evolution",evolves:"evolves",links:"links",notes:"notes",pipelines:"pipelines",size:"size",title:"title"},s(PGe,"isWardley"),L(PGe,"isWardley"),Lue=(fs=class extends eL{constructor(){super(...arguments),this.types={AbnfAlternation:{name:Gce.$type,properties:{alternatives:{name:Gce.alternatives,defaultValue:[]}},superTypes:[]},AbnfConcatenation:{name:Xce.$type,properties:{elements:{name:Xce.elements,defaultValue:[]}},superTypes:[]},AbnfElement:{name:T9.$type,properties:{primary:{name:T9.primary},repeat:{name:T9.repeat}},superTypes:[]},AbnfGroup:{name:Kce.$type,properties:{element:{name:Kce.element}},superTypes:[o1.$type]},AbnfNumVal:{name:Qce.$type,properties:{value:{name:Qce.value}},superTypes:[o1.$type]},AbnfOptionalGroup:{name:Zce.$type,properties:{element:{name:Zce.element}},superTypes:[o1.$type]},AbnfPrimary:{name:o1.$type,properties:{},superTypes:[]},AbnfRule:{name:k9.$type,properties:{definition:{name:k9.definition},name:{name:k9.name}},superTypes:[]},AbnfRuleName:{name:Jce.$type,properties:{name:{name:Jce.name}},superTypes:[o1.$type]},AbnfStringLiteral:{name:eue.$type,properties:{value:{name:eue.value}},superTypes:[o1.$type]},Accelerator:{name:i5.$type,properties:{name:{name:i5.name},x:{name:i5.x},y:{name:i5.y}},superTypes:[]},Alignment:{name:C9.$type,properties:{direction:{name:C9.direction},members:{name:C9.members,defaultValue:[]}},superTypes:[]},Anchor:{name:n5.$type,properties:{evolution:{name:n5.evolution},name:{name:n5.name},visibility:{name:n5.visibility}},superTypes:[]},Annotation:{name:Rb.$type,properties:{number:{name:Rb.number},text:{name:Rb.text},x:{name:Rb.x},y:{name:Rb.y}},superTypes:[]},Annotations:{name:w9.$type,properties:{x:{name:w9.x},y:{name:w9.y}},superTypes:[]},Architecture:{name:lu.$type,properties:{accDescr:{name:lu.accDescr},accTitle:{name:lu.accTitle},alignments:{name:lu.alignments,defaultValue:[]},edges:{name:lu.edges,defaultValue:[]},groups:{name:lu.groups,defaultValue:[]},junctions:{name:lu.junctions,defaultValue:[]},services:{name:lu.services,defaultValue:[]},title:{name:lu.title}},superTypes:[]},Axis:{name:a5.$type,properties:{label:{name:a5.label},name:{name:a5.name}},superTypes:[]},Branch:{name:s5.$type,properties:{name:{name:s5.name},order:{name:s5.order}},superTypes:[b1.$type]},Checkout:{name:tue.$type,properties:{branch:{name:tue.branch}},superTypes:[b1.$type]},CherryPicking:{name:o5.$type,properties:{id:{name:o5.id},parent:{name:o5.parent},tags:{name:o5.tags,defaultValue:[]}},superTypes:[b1.$type]},ClassDefStatement:{name:E9.$type,properties:{className:{name:E9.className},styleText:{name:E9.styleText}},superTypes:[]},Commit:{name:l1.$type,properties:{id:{name:l1.id},message:{name:l1.message},tags:{name:l1.tags,defaultValue:[]},type:{name:l1.type}},superTypes:[b1.$type]},Common:{name:l5.$type,properties:{accDescr:{name:l5.accDescr},accTitle:{name:l5.accTitle},title:{name:l5.title}},superTypes:[]},Component:{name:f0.$type,properties:{decorator:{name:f0.decorator},evolution:{name:f0.evolution},inertia:{name:f0.inertia,defaultValue:!1},label:{name:f0.label},name:{name:f0.name},visibility:{name:f0.visibility}},superTypes:[]},Curve:{name:c5.$type,properties:{entries:{name:c5.entries,defaultValue:[]},label:{name:c5.label},name:{name:c5.name}},superTypes:[]},Cynefin:{name:g0.$type,properties:{accDescr:{name:g0.accDescr},accTitle:{name:g0.accTitle},domains:{name:g0.domains,defaultValue:[]},title:{name:g0.title},transitions:{name:g0.transitions,defaultValue:[]}},superTypes:[]},Deaccelerator:{name:u5.$type,properties:{name:{name:u5.name},x:{name:u5.x},y:{name:u5.y}},superTypes:[]},Decorator:{name:rue.$type,properties:{strategy:{name:rue.strategy}},superTypes:[]},Direction:{name:c1.$type,properties:{accDescr:{name:c1.accDescr},accTitle:{name:c1.accTitle},dir:{name:c1.dir},statements:{name:c1.statements,defaultValue:[]},title:{name:c1.title}},superTypes:[v0.$type]},DomainBlock:{name:h5.$type,properties:{domain:{name:h5.domain},items:{name:h5.items,defaultValue:[]}},superTypes:[]},DomainItem:{name:S9.$type,properties:{label:{name:S9.label}},superTypes:[]},EbnfChoice:{name:iue.$type,properties:{alternatives:{name:iue.alternatives,defaultValue:[]}},superTypes:[]},EbnfExceptionPostfix:{name:nue.$type,properties:{except:{name:nue.except}},superTypes:[$b.$type]},EbnfGroup:{name:aue.$type,properties:{element:{name:aue.element}},superTypes:[m0.$type]},EbnfNonTerminal:{name:sue.$type,properties:{name:{name:sue.name}},superTypes:[m0.$type]},EbnfOneOrMorePostfix:{name:oue.$type,properties:{operator:{name:oue.operator}},superTypes:[$b.$type]},EbnfOptional:{name:lue.$type,properties:{element:{name:lue.element}},superTypes:[m0.$type]},EbnfOptionalPostfix:{name:cue.$type,properties:{operator:{name:cue.operator}},superTypes:[$b.$type]},EbnfPostfix:{name:$b.$type,properties:{},superTypes:[]},EbnfPrimary:{name:m0.$type,properties:{},superTypes:[]},EbnfRepetition:{name:uue.$type,properties:{element:{name:uue.element}},superTypes:[m0.$type]},EbnfRule:{name:_9.$type,properties:{definition:{name:_9.definition},name:{name:_9.name}},superTypes:[]},EbnfSequence:{name:hue.$type,properties:{elements:{name:hue.elements,defaultValue:[]}},superTypes:[]},EbnfSpecial:{name:due.$type,properties:{text:{name:due.text}},superTypes:[m0.$type]},EbnfTerm:{name:A9.$type,properties:{base:{name:A9.base},postfixes:{name:A9.postfixes,defaultValue:[]}},superTypes:[]},EbnfTerminal:{name:pue.$type,properties:{value:{name:pue.value}},superTypes:[m0.$type]},EbnfZeroOrMorePostfix:{name:fue.$type,properties:{operator:{name:fue.operator}},superTypes:[$b.$type]},Edge:{name:cu.$type,properties:{lhsDir:{name:cu.lhsDir},lhsGroup:{name:cu.lhsGroup,defaultValue:!1},lhsId:{name:cu.lhsId},lhsInto:{name:cu.lhsInto,defaultValue:!1},rhsDir:{name:cu.rhsDir},rhsGroup:{name:cu.rhsGroup,defaultValue:!1},rhsId:{name:cu.rhsId},rhsInto:{name:cu.rhsInto,defaultValue:!1},title:{name:cu.title}},superTypes:[]},EmDataEntity:{name:u1.$type,properties:{dataBlockValue:{name:u1.dataBlockValue},dataType:{name:u1.dataType},name:{name:u1.name}},superTypes:[]},EmFrame:{name:y0.$type,properties:{},superTypes:[]},EmGwt:{name:Lb.$type,properties:{givenStatements:{name:Lb.givenStatements,defaultValue:[]},sourceFrame:{name:Lb.sourceFrame,referenceType:y0.$type},thenStatements:{name:Lb.thenStatements,defaultValue:[]},whenStatements:{name:Lb.whenStatements,defaultValue:[]}},superTypes:[]},EmGwtStatement:{name:gue.$type,properties:{entityIdentifier:{name:gue.entityIdentifier,referenceType:R9.$type}},superTypes:[]},EmModelEntity:{name:R9.$type,properties:{name:{name:R9.name}},superTypes:[]},EmNoteEntity:{name:d5.$type,properties:{dataBlockValue:{name:d5.dataBlockValue},dataType:{name:d5.dataType},sourceFrame:{name:d5.sourceFrame,referenceType:y0.$type}},superTypes:[]},EmResetFrame:{name:Sh.$type,properties:{dataInlineValue:{name:Sh.dataInlineValue},dataReference:{name:Sh.dataReference,referenceType:u1.$type},dataType:{name:Sh.dataType},entityIdentifier:{name:Sh.entityIdentifier},modelEntityType:{name:Sh.modelEntityType},name:{name:Sh.name},sourceFrames:{name:Sh.sourceFrames,defaultValue:[],referenceType:y0.$type}},superTypes:[y0.$type]},EmTimeFrame:{name:rp.$type,properties:{dataInlineValue:{name:rp.dataInlineValue},dataReference:{name:rp.dataReference,referenceType:u1.$type},dataType:{name:rp.dataType},entityIdentifier:{name:rp.entityIdentifier},modelEntityType:{name:rp.modelEntityType},name:{name:rp.name},sourceFrames:{name:rp.sourceFrames,defaultValue:[],referenceType:y0.$type}},superTypes:[y0.$type]},Entry:{name:$9.$type,properties:{axis:{name:$9.axis,referenceType:a5.$type},value:{name:$9.value}},superTypes:[]},EventModel:{name:_h.$type,properties:{accDescr:{name:_h.accDescr},accTitle:{name:_h.accTitle},dataEntities:{name:_h.dataEntities,defaultValue:[]},frames:{name:_h.frames,defaultValue:[]},gwtEntities:{name:_h.gwtEntities,defaultValue:[]},modelEntities:{name:_h.modelEntities,defaultValue:[]},noteEntities:{name:_h.noteEntities,defaultValue:[]},title:{name:_h.title}},superTypes:[]},Evolution:{name:mue.$type,properties:{stages:{name:mue.stages,defaultValue:[]}},superTypes:[]},EvolutionStage:{name:p5.$type,properties:{boundary:{name:p5.boundary},name:{name:p5.name},secondName:{name:p5.secondName}},superTypes:[]},Evolve:{name:L9.$type,properties:{component:{name:L9.component},target:{name:L9.target}},superTypes:[]},GitGraph:{name:v0.$type,properties:{accDescr:{name:v0.accDescr},accTitle:{name:v0.accTitle},statements:{name:v0.statements,defaultValue:[]},title:{name:v0.title}},superTypes:[]},Group:{name:Ib.$type,properties:{icon:{name:Ib.icon},id:{name:Ib.id},in:{name:Ib.in},title:{name:Ib.title}},superTypes:[]},Info:{name:Mb.$type,properties:{accDescr:{name:Mb.accDescr},accTitle:{name:Mb.accTitle},title:{name:Mb.title}},superTypes:[]},Item:{name:Db.$type,properties:{classSelector:{name:Db.classSelector},name:{name:Db.name}},superTypes:[]},Junction:{name:I9.$type,properties:{id:{name:I9.id},in:{name:I9.in}},superTypes:[]},Label:{name:Nb.$type,properties:{negX:{name:Nb.negX,defaultValue:!1},negY:{name:Nb.negY,defaultValue:!1},offsetX:{name:Nb.offsetX},offsetY:{name:Nb.offsetY}},superTypes:[]},Leaf:{name:f5.$type,properties:{classSelector:{name:f5.classSelector},name:{name:f5.name},value:{name:f5.value}},superTypes:[Db.$type]},Link:{name:b0.$type,properties:{arrow:{name:b0.arrow},from:{name:b0.from},fromPort:{name:b0.fromPort},linkLabel:{name:b0.linkLabel},to:{name:b0.to},toPort:{name:b0.toPort}},superTypes:[]},Merge:{name:h1.$type,properties:{branch:{name:h1.branch},id:{name:h1.id},tags:{name:h1.tags,defaultValue:[]},type:{name:h1.type}},superTypes:[b1.$type]},Note:{name:g5.$type,properties:{evolution:{name:g5.evolution},text:{name:g5.text},visibility:{name:g5.visibility}},superTypes:[]},Option:{name:M9.$type,properties:{name:{name:M9.name},value:{name:M9.value,defaultValue:!1}},superTypes:[]},Packet:{name:d1.$type,properties:{accDescr:{name:d1.accDescr},accTitle:{name:d1.accTitle},blocks:{name:d1.blocks,defaultValue:[]},title:{name:d1.title}},superTypes:[]},PacketBlock:{name:p1.$type,properties:{bits:{name:p1.bits},end:{name:p1.end},label:{name:p1.label},start:{name:p1.start}},superTypes:[]},PegAny:{name:yue.$type,properties:{dot:{name:yue.dot}},superTypes:[Ob.$type]},PegGroup:{name:vue.$type,properties:{element:{name:vue.element}},superTypes:[Ob.$type]},PegIdentifier:{name:bue.$type,properties:{name:{name:bue.name}},superTypes:[Ob.$type]},PegLiteral:{name:xue.$type,properties:{value:{name:xue.value}},superTypes:[Ob.$type]},PegOrderedChoice:{name:Tue.$type,properties:{alternatives:{name:Tue.alternatives,defaultValue:[]}},superTypes:[]},PegPrefix:{name:D9.$type,properties:{operator:{name:D9.operator},suffix:{name:D9.suffix}},superTypes:[]},PegPrimary:{name:Ob.$type,properties:{},superTypes:[]},PegRule:{name:N9.$type,properties:{definition:{name:N9.definition},name:{name:N9.name}},superTypes:[]},PegSequence:{name:kue.$type,properties:{elements:{name:kue.elements,defaultValue:[]}},superTypes:[]},PegSuffix:{name:O9.$type,properties:{operator:{name:O9.operator},primary:{name:O9.primary}},superTypes:[]},Pie:{name:x0.$type,properties:{accDescr:{name:x0.accDescr},accTitle:{name:x0.accTitle},sections:{name:x0.sections,defaultValue:[]},showData:{name:x0.showData,defaultValue:!1},title:{name:x0.title}},superTypes:[]},PieSection:{name:m5.$type,properties:{label:{name:m5.label},value:{name:m5.value}},superTypes:[]},Pipeline:{name:B9.$type,properties:{components:{name:B9.components,defaultValue:[]},parent:{name:B9.parent}},superTypes:[]},PipelineComponent:{name:y5.$type,properties:{evolution:{name:y5.evolution},label:{name:y5.label},name:{name:y5.name}},superTypes:[]},Radar:{name:T0.$type,properties:{accDescr:{name:T0.accDescr},accTitle:{name:T0.accTitle},axes:{name:T0.axes,defaultValue:[]},curves:{name:T0.curves,defaultValue:[]},options:{name:T0.options,defaultValue:[]},title:{name:T0.title}},superTypes:[]},Railroad:{name:f1.$type,properties:{accDescr:{name:f1.accDescr},accTitle:{name:f1.accTitle},rules:{name:f1.rules,defaultValue:[]},title:{name:f1.title}},superTypes:[]},RailroadAbnf:{name:g1.$type,properties:{accDescr:{name:g1.accDescr},accTitle:{name:g1.accTitle},rules:{name:g1.rules,defaultValue:[]},title:{name:g1.title}},superTypes:[]},RailroadChoiceExpr:{name:Cue.$type,properties:{alternatives:{name:Cue.alternatives,defaultValue:[]}},superTypes:[Ah.$type]},RailroadEbnf:{name:m1.$type,properties:{accDescr:{name:m1.accDescr},accTitle:{name:m1.accTitle},rules:{name:m1.rules,defaultValue:[]},title:{name:m1.title}},superTypes:[]},RailroadExpression:{name:Ah.$type,properties:{},superTypes:[]},RailroadNonTerminalExpr:{name:wue.$type,properties:{name:{name:wue.name}},superTypes:[Ah.$type]},RailroadOneOrMoreExpr:{name:Eue.$type,properties:{element:{name:Eue.element}},superTypes:[Ah.$type]},RailroadOptionalExpr:{name:Sue.$type,properties:{element:{name:Sue.element}},superTypes:[Ah.$type]},RailroadPeg:{name:y1.$type,properties:{accDescr:{name:y1.accDescr},accTitle:{name:y1.accTitle},rules:{name:y1.rules,defaultValue:[]},title:{name:y1.title}},superTypes:[]},RailroadRule:{name:P9.$type,properties:{definition:{name:P9.definition},name:{name:P9.name}},superTypes:[]},RailroadSequenceExpr:{name:_ue.$type,properties:{elements:{name:_ue.elements,defaultValue:[]}},superTypes:[Ah.$type]},RailroadSpecialExpr:{name:Aue.$type,properties:{text:{name:Aue.text}},superTypes:[Ah.$type]},RailroadTerminalExpr:{name:Rue.$type,properties:{value:{name:Rue.value}},superTypes:[Ah.$type]},RailroadZeroOrMoreExpr:{name:$ue.$type,properties:{element:{name:$ue.element}},superTypes:[Ah.$type]},Section:{name:F9.$type,properties:{classSelector:{name:F9.classSelector},name:{name:F9.name}},superTypes:[Db.$type]},Service:{name:v1.$type,properties:{icon:{name:v1.icon},iconText:{name:v1.iconText},id:{name:v1.id},in:{name:v1.in},title:{name:v1.title}},superTypes:[]},Size:{name:z9.$type,properties:{height:{name:z9.height},width:{name:z9.width}},superTypes:[]},Statement:{name:b1.$type,properties:{},superTypes:[]},Transition:{name:Bb.$type,properties:{from:{name:Bb.from},label:{name:Bb.label},to:{name:Bb.to}},superTypes:[]},TreeNode:{name:T1.$type,properties:{classAnnotation:{name:T1.classAnnotation},descAnnotation:{name:T1.descAnnotation},iconAnnotation:{name:T1.iconAnnotation},indent:{name:T1.indent},name:{name:T1.name}},superTypes:[]},TreeView:{name:Pb.$type,properties:{accDescr:{name:Pb.accDescr},accTitle:{name:Pb.accTitle},nodes:{name:Pb.nodes,defaultValue:[]},title:{name:Pb.title}},superTypes:[]},Treemap:{name:x1.$type,properties:{accDescr:{name:x1.accDescr},accTitle:{name:x1.accTitle},title:{name:x1.title},TreemapRows:{name:x1.TreemapRows,defaultValue:[]}},superTypes:[]},TreemapRow:{name:q9.$type,properties:{indent:{name:q9.indent},item:{name:q9.item}},superTypes:[]},Wardley:{name:Ls.$type,properties:{accDescr:{name:Ls.accDescr},accelerators:{name:Ls.accelerators,defaultValue:[]},accTitle:{name:Ls.accTitle},anchors:{name:Ls.anchors,defaultValue:[]},annotation:{name:Ls.annotation,defaultValue:[]},annotations:{name:Ls.annotations,defaultValue:[]},components:{name:Ls.components,defaultValue:[]},deaccelerators:{name:Ls.deaccelerators,defaultValue:[]},evolution:{name:Ls.evolution},evolves:{name:Ls.evolves,defaultValue:[]},links:{name:Ls.links,defaultValue:[]},notes:{name:Ls.notes,defaultValue:[]},pipelines:{name:Ls.pipelines,defaultValue:[]},size:{name:Ls.size},title:{name:Ls.title}},superTypes:[]}}}},s(fs,"MermaidAstReflection"),L(fs,"MermaidAstReflection"),fs),Ta=new Lue,cot=L(()=>lot??(lot=ds(`{"$type":"Grammar","isDeclared":true,"name":"ArchitectureGrammar","imports":[],"rules":[{"$type":"ParserRule","entry":true,"name":"Architecture","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@24"},"arguments":[],"cardinality":"*"},{"$type":"Keyword","value":"architecture-beta"},{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@24"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}],"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"Statement","definition":{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"groups","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},{"$type":"Assignment","feature":"services","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]}},{"$type":"Assignment","feature":"junctions","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}},{"$type":"Assignment","feature":"edges","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}},{"$type":"Assignment","feature":"alignments","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}}]},"entry":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"LeftPort","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":":"},{"$type":"Assignment","feature":"lhsDir","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}}]},"entry":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"RightPort","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"rhsDir","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}},{"$type":"Keyword","value":":"}]},"entry":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"Arrow","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]},{"$type":"Assignment","feature":"lhsInto","operator":"?=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]},"cardinality":"?"},{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"--"},{"$type":"Group","elements":[{"$type":"Keyword","value":"-"},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@30"},"arguments":[]}},{"$type":"Keyword","value":"-"}]}]},{"$type":"Assignment","feature":"rhsInto","operator":"?=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]},"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}]},"entry":false,"parameters":[]},{"$type":"ParserRule","name":"Group","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"group"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}},{"$type":"Assignment","feature":"icon","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@29"},"arguments":[]},"cardinality":"?"},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@30"},"arguments":[]},"cardinality":"?"},{"$type":"Group","elements":[{"$type":"Keyword","value":"in"},{"$type":"Assignment","feature":"in","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Service","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"service"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"iconText","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@22"},"arguments":[]}},{"$type":"Assignment","feature":"icon","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@29"},"arguments":[]}}],"cardinality":"?"},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@30"},"arguments":[]},"cardinality":"?"},{"$type":"Group","elements":[{"$type":"Keyword","value":"in"},{"$type":"Assignment","feature":"in","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Junction","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"junction"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"in"},{"$type":"Assignment","feature":"in","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Edge","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"lhsId","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}},{"$type":"Assignment","feature":"lhsGroup","operator":"?=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]},"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]},{"$type":"Assignment","feature":"rhsId","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}},{"$type":"Assignment","feature":"rhsGroup","operator":"?=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]},"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Alignment","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"align"},{"$type":"Assignment","feature":"direction","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"row"},{"$type":"Keyword","value":"column"}]}},{"$type":"Assignment","feature":"members","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}},{"$type":"Assignment","feature":"members","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]},"cardinality":"+"},{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"TerminalRule","name":"ARROW_DIRECTION","definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"L"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"R"},"parenthesized":false}],"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"T"},"parenthesized":false}],"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"B"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ARROW_GROUP","definition":{"$type":"RegexToken","regex":"/\\\\{group\\\\}/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ARROW_INTO","definition":{"$type":"RegexToken","regex":"/<|>/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"ParserRule","name":"EOL","dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@24"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"TitleAndAccessibilities","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}],"cardinality":"+"},"entry":false,"parameters":[]},{"$type":"TerminalRule","name":"BOOLEAN","type":{"$type":"ReturnType","name":"boolean"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"true"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"false"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"FLOAT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/[0-9]+\\\\.[0-9]+(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/0|[1-9][0-9]*(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NUMBER","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@19"},"parenthesized":false},{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@20"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[\\\\w]([-\\\\w]*\\\\w)?/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","name":"ARCH_ICON","definition":{"$type":"RegexToken","regex":"/\\\\([\\\\w-:]+\\\\)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ARCH_TITLE","definition":{"$type":"RegexToken","regex":"/\\\\[(?:\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'|[^\\\\[\\\\]\\\\r\\\\n]+)\\\\]/","parenthesized":false},"fragment":false,"hidden":false}],"interfaces":[],"types":[]}`)),"ArchitectureGrammarGrammar"),hot=L(()=>uot??(uot=ds(`{"$type":"Grammar","isDeclared":true,"name":"CynefinGrammar","imports":[],"rules":[{"$type":"ParserRule","entry":true,"name":"Cynefin","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[],"cardinality":"*"},{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"cynefin-beta"},{"$type":"Keyword","value":"cynefin-beta:"}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[],"cardinality":"*"},{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]},{"$type":"Assignment","feature":"domains","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}},{"$type":"Assignment","feature":"transitions","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}],"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"DomainBlock","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"domain","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[],"cardinality":"*"},{"$type":"Group","elements":[{"$type":"Assignment","feature":"items","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[],"cardinality":"*"}],"cardinality":"*"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"DomainItem","definition":{"$type":"Assignment","feature":"label","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Transition","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"from","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"Keyword","value":"-->"},{"$type":"Assignment","feature":"to","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":":"},{"$type":"Assignment","feature":"label","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"TerminalRule","name":"DOMAIN_NAME","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"complex"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"complicated"},"parenthesized":false}],"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"clear"},"parenthesized":false}],"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"chaotic"},"parenthesized":false}],"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"confusion"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"ParserRule","name":"EOL","dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"TitleAndAccessibilities","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}],"cardinality":"+"},"entry":false,"parameters":[]},{"$type":"TerminalRule","name":"BOOLEAN","type":{"$type":"ReturnType","name":"boolean"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"true"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"false"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"FLOAT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/[0-9]+\\\\.[0-9]+(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/0|[1-9][0-9]*(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NUMBER","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@11"},"parenthesized":false},{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@12"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[\\\\w]([-\\\\w]*\\\\w)?/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false}],"interfaces":[],"types":[]}`)),"CynefinGrammarGrammar"),pot=L(()=>dot??(dot=ds('{"$type":"Grammar","isDeclared":true,"name":"EventModeling","interfaces":[{"$type":"Interface","name":"Common","attributes":[{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}}],"superTypes":[]}],"rules":[{"$type":"ParserRule","entry":true,"name":"EventModel","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"eventmodeling"},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@21"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@22"},"arguments":[]}},{"$type":"Assignment","feature":"modelEntities","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}},{"$type":"Assignment","feature":"frames","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}},{"$type":"Assignment","feature":"dataEntities","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}},{"$type":"Assignment","feature":"noteEntities","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]}},{"$type":"Assignment","feature":"gwtEntities","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}}],"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EmModelEntityType","dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"rmo"},{"$type":"Keyword","value":"readmodel"},{"$type":"Keyword","value":"ui"},{"$type":"Keyword","value":"cmd"},{"$type":"Keyword","value":"command"},{"$type":"Keyword","value":"evt"},{"$type":"Keyword","value":"event"},{"$type":"Keyword","value":"pcr"},{"$type":"Keyword","value":"processor"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EmDataType","dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"json"},{"$type":"Keyword","value":"jsobj"},{"$type":"Keyword","value":"figma"},{"$type":"Keyword","value":"salt"},{"$type":"Keyword","value":"uri"},{"$type":"Keyword","value":"md"},{"$type":"Keyword","value":"html"},{"$type":"Keyword","value":"text"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"EmDataInline","definition":{"$type":"Group","elements":[{"$type":"Group","elements":[{"$type":"Keyword","value":"`"},{"$type":"Assignment","feature":"dataType","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}},{"$type":"Keyword","value":"`"}],"cardinality":"?"},{"$type":"Assignment","feature":"dataInlineValue","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[]}}]},"entry":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"EmDataBlock","definition":{"$type":"Group","elements":[{"$type":"Group","elements":[{"$type":"Keyword","value":"`"},{"$type":"Assignment","feature":"dataType","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}},{"$type":"Keyword","value":"`"}],"cardinality":"?"},{"$type":"Assignment","feature":"dataBlockValue","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}}]},"entry":false,"parameters":[]},{"$type":"ParserRule","name":"QualifiedName","dataType":"string","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},{"$type":"Group","elements":[{"$type":"Keyword","value":"."},{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}],"cardinality":"*"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EmTimeFrame","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"tf"},{"$type":"Keyword","value":"timeframe"}]},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}},{"$type":"Assignment","feature":"modelEntityType","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}},{"$type":"Assignment","feature":"entityIdentifier","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"->>"},{"$type":"Assignment","feature":"sourceFrames","operator":"+=","terminal":{"$type":"CrossReference","type":{"$ref":"#/rules@8"},"terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]},"deprecatedSyntax":false,"isMulti":false}}],"cardinality":"*"},{"$type":"Group","elements":[{"$type":"Keyword","value":"[["},{"$type":"Assignment","feature":"dataReference","operator":"=","terminal":{"$type":"CrossReference","type":{"$ref":"#/rules@10"},"terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},"deprecatedSyntax":false,"isMulti":false}},{"$type":"Keyword","value":"]]"}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[],"cardinality":"?"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EmResetFrame","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"rf"},{"$type":"Keyword","value":"resetframe"}]},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}},{"$type":"Assignment","feature":"modelEntityType","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}},{"$type":"Assignment","feature":"entityIdentifier","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"->>"},{"$type":"Assignment","feature":"sourceFrames","operator":"+=","terminal":{"$type":"CrossReference","type":{"$ref":"#/rules@8"},"terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]},"deprecatedSyntax":false,"isMulti":false}}],"cardinality":"*"},{"$type":"Group","elements":[{"$type":"Keyword","value":"[["},{"$type":"Assignment","feature":"dataReference","operator":"=","terminal":{"$type":"CrossReference","type":{"$ref":"#/rules@10"},"terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},"deprecatedSyntax":false,"isMulti":false}},{"$type":"Keyword","value":"]]"}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[],"cardinality":"?"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EmFrame","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EmModelEntity","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"entity"},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EmDataEntity","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"data"},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EmNoteEntity","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"note"},{"$type":"Assignment","feature":"sourceFrame","operator":"=","terminal":{"$type":"CrossReference","type":{"$ref":"#/rules@8"},"terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]},"deprecatedSyntax":false,"isMulti":false}},{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EmGwt","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"gwt"},{"$type":"Assignment","feature":"sourceFrame","operator":"=","terminal":{"$type":"CrossReference","type":{"$ref":"#/rules@8"},"terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]},"deprecatedSyntax":false,"isMulti":false}},{"$type":"Keyword","value":"given"},{"$type":"Assignment","feature":"givenStatements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]},"cardinality":"+"},{"$type":"Group","elements":[{"$type":"Keyword","value":"when"},{"$type":"Assignment","feature":"whenStatements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]},"cardinality":"+"}],"cardinality":"?"},{"$type":"Keyword","value":"then"},{"$type":"Assignment","feature":"thenStatements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]},"cardinality":"+"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EmGwtStatement","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]},{"$type":"Assignment","feature":"entityIdentifier","operator":"=","terminal":{"$type":"CrossReference","type":{"$ref":"#/rules@9"},"terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},"deprecatedSyntax":false,"isMulti":false}}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EM_EID","dataType":"string","definition":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EM_FI","dataType":"string","definition":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]},"entry":false,"fragment":false,"parameters":[]},{"$type":"TerminalRule","name":"EM_ID","definition":{"$type":"RegexToken","regex":"/[_a-zA-Z][\\\\w_]*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"EM_FID","definition":{"$type":"RegexToken","regex":"/\\\\d{1,3}/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"EM_DATA_INLINE","definition":{"$type":"RegexToken","regex":"/\\\\{(.*)\\\\}|\\"(.*)\\"|\'(.*)\'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"EM_DATA_BLOCK","definition":{"$type":"RegexToken","regex":"/\\\\{[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?\\\\}(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"EM_ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"EM_ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"EM_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"EM_WS","definition":{"$type":"RegexToken","regex":"/\\\\s+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"EM_YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"EM_DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"EM_SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"EM_ML_COMMENT","definition":{"$type":"RegexToken","regex":"/\\\\/\\\\*[\\\\s\\\\S]*?\\\\*\\\\//","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"EM_SL_COMMENT","definition":{"$type":"RegexToken","regex":"/\\\\/\\\\/[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false}],"imports":[],"types":[]}')),"EventModelingGrammar"),got=L(()=>fot??(fot=ds(`{"$type":"Grammar","isDeclared":true,"name":"GitGraphGrammar","imports":[],"rules":[{"$type":"ParserRule","entry":true,"name":"GitGraph","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[],"cardinality":"*"},{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"gitGraph"},{"$type":"Group","elements":[{"$type":"Keyword","value":"gitGraph"},{"$type":"Keyword","value":":"}]},{"$type":"Keyword","value":"gitGraph:"},{"$type":"Group","elements":[{"$type":"Keyword","value":"gitGraph"},{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]},{"$type":"Keyword","value":":"}]}]},{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]},{"$type":"Assignment","feature":"statements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}}],"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Statement","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Direction","definition":{"$type":"Assignment","feature":"dir","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"LR"},{"$type":"Keyword","value":"TB"},{"$type":"Keyword","value":"BT"}]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Commit","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"commit"},{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"Keyword","value":"id:"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"msg:","cardinality":"?"},{"$type":"Assignment","feature":"message","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"tag:"},{"$type":"Assignment","feature":"tags","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"type:"},{"$type":"Assignment","feature":"type","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"NORMAL"},{"$type":"Keyword","value":"REVERSE"},{"$type":"Keyword","value":"HIGHLIGHT"}]}}]}],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Branch","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"branch"},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@24"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"order:"},{"$type":"Assignment","feature":"order","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Merge","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"merge"},{"$type":"Assignment","feature":"branch","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@24"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}]}},{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"Keyword","value":"id:"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"tag:"},{"$type":"Assignment","feature":"tags","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"type:"},{"$type":"Assignment","feature":"type","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"NORMAL"},{"$type":"Keyword","value":"REVERSE"},{"$type":"Keyword","value":"HIGHLIGHT"}]}}]}],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Checkout","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"checkout"},{"$type":"Keyword","value":"switch"}]},{"$type":"Assignment","feature":"branch","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@24"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"CherryPicking","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"cherry-pick"},{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"Keyword","value":"id:"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"tag:"},{"$type":"Assignment","feature":"tags","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"parent:"},{"$type":"Assignment","feature":"parent","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]}],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EOL","dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"TitleAndAccessibilities","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}],"cardinality":"+"},"entry":false,"parameters":[]},{"$type":"TerminalRule","name":"BOOLEAN","type":{"$type":"ReturnType","name":"boolean"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"true"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"false"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"FLOAT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/[0-9]+\\\\.[0-9]+(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/0|[1-9][0-9]*(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NUMBER","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@14"},"parenthesized":false},{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@15"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[\\\\w]([-\\\\w]*\\\\w)?/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","name":"REFERENCE","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\\\w([-\\\\./\\\\w]*[-\\\\w])?/","parenthesized":false},"fragment":false,"hidden":false}],"interfaces":[],"types":[]}`)),"GitGraphGrammarGrammar"),yot=L(()=>mot??(mot=ds(`{"$type":"Grammar","isDeclared":true,"name":"InfoGrammar","imports":[],"rules":[{"$type":"ParserRule","entry":true,"name":"Info","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[],"cardinality":"*"},{"$type":"Keyword","value":"info"},{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[],"cardinality":"*"},{"$type":"Group","elements":[{"$type":"Keyword","value":"showInfo"},{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[],"cardinality":"*"}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[],"cardinality":"?"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EOL","dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"TitleAndAccessibilities","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}],"cardinality":"+"},"entry":false,"parameters":[]},{"$type":"TerminalRule","name":"BOOLEAN","type":{"$type":"ReturnType","name":"boolean"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"true"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"false"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"FLOAT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/[0-9]+\\\\.[0-9]+(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/0|[1-9][0-9]*(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NUMBER","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@7"},"parenthesized":false},{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@8"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[\\\\w]([-\\\\w]*\\\\w)?/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false}],"interfaces":[],"types":[]}`)),"InfoGrammarGrammar"),bot=L(()=>vot??(vot=ds(`{"$type":"Grammar","isDeclared":true,"name":"PacketGrammar","imports":[],"rules":[{"$type":"ParserRule","entry":true,"name":"Packet","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[],"cardinality":"*"},{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"packet"},{"$type":"Keyword","value":"packet-beta"}]},{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]},{"$type":"Assignment","feature":"blocks","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}],"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PacketBlock","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"Assignment","feature":"start","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"-"},{"$type":"Assignment","feature":"end","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}}],"cardinality":"?"}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"+"},{"$type":"Assignment","feature":"bits","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}}]}]},{"$type":"Keyword","value":":"},{"$type":"Assignment","feature":"label","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EOL","dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"TitleAndAccessibilities","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}],"cardinality":"+"},"entry":false,"parameters":[]},{"$type":"TerminalRule","name":"BOOLEAN","type":{"$type":"ReturnType","name":"boolean"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"true"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"false"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"FLOAT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/[0-9]+\\\\.[0-9]+(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/0|[1-9][0-9]*(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NUMBER","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@8"},"parenthesized":false},{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@9"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[\\\\w]([-\\\\w]*\\\\w)?/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false}],"interfaces":[],"types":[]}`)),"PacketGrammarGrammar"),Tot=L(()=>xot??(xot=ds(`{"$type":"Grammar","isDeclared":true,"name":"PieGrammar","imports":[],"rules":[{"$type":"ParserRule","entry":true,"name":"Pie","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[],"cardinality":"*"},{"$type":"Keyword","value":"pie"},{"$type":"Assignment","feature":"showData","operator":"?=","terminal":{"$type":"Keyword","value":"showData"},"cardinality":"?"},{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]},{"$type":"Assignment","feature":"sections","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}],"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PieSection","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"label","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}},{"$type":"Keyword","value":":"},{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"TerminalRule","name":"FLOAT_PIE","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/-?[0-9]+\\\\.[0-9]+(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INT_PIE","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/-?(0|[1-9][0-9]*)(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NUMBER_PIE","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@2"},"parenthesized":false},{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@3"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"ParserRule","name":"EOL","dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"TitleAndAccessibilities","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}],"cardinality":"+"},"entry":false,"parameters":[]},{"$type":"TerminalRule","name":"BOOLEAN","type":{"$type":"ReturnType","name":"boolean"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"true"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"false"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"FLOAT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/[0-9]+\\\\.[0-9]+(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/0|[1-9][0-9]*(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NUMBER","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@11"},"parenthesized":false},{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@12"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[\\\\w]([-\\\\w]*\\\\w)?/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false}],"interfaces":[],"types":[]}`)),"PieGrammarGrammar"),Cot=L(()=>kot??(kot=ds(`{"$type":"Grammar","isDeclared":true,"name":"RadarGrammar","imports":[],"rules":[{"$type":"ParserRule","entry":true,"name":"Radar","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[],"cardinality":"*"},{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"radar-beta"},{"$type":"Keyword","value":"radar-beta:"},{"$type":"Group","elements":[{"$type":"Keyword","value":"radar-beta"},{"$type":"Keyword","value":":"}]}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[],"cardinality":"*"},{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]},{"$type":"Group","elements":[{"$type":"Keyword","value":"axis"},{"$type":"Assignment","feature":"axes","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"axes","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}}],"cardinality":"*"}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"curve"},{"$type":"Assignment","feature":"curves","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"curves","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}}],"cardinality":"*"}]},{"$type":"Group","elements":[{"$type":"Assignment","feature":"options","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"options","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}}],"cardinality":"*"}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}],"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"Label","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"label","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[]}},{"$type":"Keyword","value":"]"}]},"entry":false,"parameters":[]},{"$type":"ParserRule","name":"Axis","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[],"cardinality":"?"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Curve","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[],"cardinality":"?"},{"$type":"Keyword","value":"{"},{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]},{"$type":"Keyword","value":"}"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"Entries","definition":{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[],"cardinality":"*"},{"$type":"Assignment","feature":"entries","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":","},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[],"cardinality":"*"},{"$type":"Assignment","feature":"entries","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]}}],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[],"cardinality":"*"}]},{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[],"cardinality":"*"},{"$type":"Assignment","feature":"entries","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":","},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[],"cardinality":"*"},{"$type":"Assignment","feature":"entries","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}}],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[],"cardinality":"*"}]}]},"entry":false,"parameters":[]},{"$type":"ParserRule","name":"DetailedEntry","returnType":{"$ref":"#/interfaces@0"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"axis","operator":"=","terminal":{"$type":"CrossReference","type":{"$ref":"#/rules@2"},"terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},"deprecatedSyntax":false,"isMulti":false}},{"$type":"Keyword","value":":","cardinality":"?"},{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"NumberEntry","returnType":{"$ref":"#/interfaces@0"},"definition":{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Option","definition":{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Keyword","value":"showLegend"}},{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Keyword","value":"ticks"}},{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Keyword","value":"max"}},{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Keyword","value":"min"}},{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Keyword","value":"graticule"}},{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}}]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"TerminalRule","name":"GRATICULE","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"circle"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"polygon"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"ParserRule","name":"EOL","dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"TitleAndAccessibilities","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}],"cardinality":"+"},"entry":false,"parameters":[]},{"$type":"TerminalRule","name":"BOOLEAN","type":{"$type":"ReturnType","name":"boolean"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"true"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"false"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"FLOAT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/[0-9]+\\\\.[0-9]+(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/0|[1-9][0-9]*(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NUMBER","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@15"},"parenthesized":false},{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@16"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[\\\\w]([-\\\\w]*\\\\w)?/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false}],"interfaces":[{"$type":"Interface","name":"Entry","attributes":[{"$type":"TypeAttribute","name":"axis","isOptional":true,"type":{"$type":"ReferenceType","referenceType":{"$type":"SimpleType","typeRef":{"$ref":"#/rules@2"}},"isMulti":false}},{"$type":"TypeAttribute","name":"value","type":{"$type":"SimpleType","primitiveType":"number"},"isOptional":false}],"superTypes":[]}],"types":[]}`)),"RadarGrammarGrammar"),Eot=L(()=>wot??(wot=ds('{"$type":"Grammar","isDeclared":true,"name":"RailroadAbnfGrammar","rules":[{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ABNF_RULENAME","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[A-Za-z][A-Za-z0-9-]*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ABNF_STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"[^\\"]*\\"/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ABNF_NUMVAL","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/%[xXdDbB][0-9A-Fa-f]+(?:-[0-9A-Fa-f]+|\\\\.[0-9A-Fa-f]+)*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ABNF_REPEAT","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[0-9]*\\\\*[0-9]*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ABNF_EXACT_REPEAT","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[0-9]+/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"ABNF_WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t \\\\r\\\\n]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"ABNF_YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"ABNF_DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"ABNF_SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"ABNF_COMMENT","definition":{"$type":"RegexToken","regex":"/;[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false},{"$type":"ParserRule","entry":true,"name":"RailroadAbnf","returnType":{"$ref":"#/interfaces@0"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"railroad-abnf-beta"},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@0"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}},{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}}],"cardinality":"*"},{"$type":"Assignment","feature":"rules","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"AbnfRule","returnType":{"$ref":"#/interfaces@1"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"Keyword","value":"="},{"$type":"Assignment","feature":"definition","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}},{"$type":"Keyword","value":";"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"AbnfAlternation","returnType":{"$ref":"#/interfaces@2"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"alternatives","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"/"},{"$type":"Assignment","feature":"alternatives","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}}],"cardinality":"*"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"AbnfConcatenation","returnType":{"$ref":"#/interfaces@3"},"definition":{"$type":"Assignment","feature":"elements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]},"cardinality":"+"},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"AbnfElement","returnType":{"$ref":"#/interfaces@4"},"definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"repeat","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]}},{"$type":"Assignment","feature":"repeat","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}}],"cardinality":"?"},{"$type":"Assignment","feature":"primary","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[]}}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"AbnfPrimary","returnType":{"$ref":"#/interfaces@5"},"definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@21"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@22"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"AbnfStringLiteral","returnType":{"$ref":"#/interfaces@6"},"definition":{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"AbnfNumVal","returnType":{"$ref":"#/interfaces@7"},"definition":{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"AbnfRuleName","returnType":{"$ref":"#/interfaces@8"},"definition":{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"AbnfGroup","returnType":{"$ref":"#/interfaces@9"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"element","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"AbnfOptionalGroup","returnType":{"$ref":"#/interfaces@10"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"element","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}},{"$type":"Keyword","value":"]"}]},"entry":false,"fragment":false,"parameters":[]}],"interfaces":[{"$type":"Interface","name":"RailroadAbnf","attributes":[{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"rules","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@1"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"AbnfRule","attributes":[{"$type":"TypeAttribute","name":"name","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false},{"$type":"TypeAttribute","name":"definition","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"AbnfAlternation","attributes":[{"$type":"TypeAttribute","name":"alternatives","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@3"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"AbnfConcatenation","attributes":[{"$type":"TypeAttribute","name":"elements","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@4"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"AbnfElement","attributes":[{"$type":"TypeAttribute","name":"repeat","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"primary","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@5"}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"AbnfPrimary","attributes":[],"superTypes":[]},{"$type":"Interface","name":"AbnfStringLiteral","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"value","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"AbnfNumVal","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"value","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"AbnfRuleName","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"name","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"AbnfGroup","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"element","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}]},{"$type":"Interface","name":"AbnfOptionalGroup","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"element","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}]}],"imports":[],"types":[]}')),"RailroadAbnfGrammarGrammar"),_ot=L(()=>Sot??(Sot=ds(`{"$type":"Grammar","isDeclared":true,"name":"RailroadEbnfGrammar","rules":[{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"EBNF_ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[A-Z_a-z][\\\\w-]*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"EBNF_STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"EBNF_SPECIAL_SEQUENCE","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\\\?(?=[^?;]*[^?\\\\s;][^?;]*\\\\?)[^?;]*\\\\?/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"EBNF_WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t \\\\r\\\\n]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"EBNF_YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"EBNF_DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"EBNF_SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"EBNF_BLOCK_COMMENT","definition":{"$type":"RegexToken","regex":"/\\\\/\\\\*[\\\\s\\\\S]*?\\\\*\\\\//","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"EBNF_ISO_COMMENT","definition":{"$type":"RegexToken","regex":"/\\\\(\\\\*[\\\\s\\\\S]*?\\\\*\\\\)/","parenthesized":false},"fragment":false},{"$type":"ParserRule","entry":true,"name":"RailroadEbnf","returnType":{"$ref":"#/interfaces@0"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"railroad-ebnf-beta"},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@0"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}},{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}}],"cardinality":"*"},{"$type":"Assignment","feature":"rules","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]},"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfRule","returnType":{"$ref":"#/interfaces@1"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"="},{"$type":"Keyword","value":"::="}]},{"$type":"Assignment","feature":"definition","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}},{"$type":"Keyword","value":";"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfChoice","returnType":{"$ref":"#/interfaces@2"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"alternatives","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"|"},{"$type":"Assignment","feature":"alternatives","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}}],"cardinality":"*"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfSequence","returnType":{"$ref":"#/interfaces@3"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"elements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":",","cardinality":"?"},{"$type":"Assignment","feature":"elements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}}],"cardinality":"*"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfTerm","returnType":{"$ref":"#/interfaces@4"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"base","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}},{"$type":"Assignment","feature":"postfixes","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@24"},"arguments":[]},"cardinality":"*"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfPrimary","returnType":{"$ref":"#/interfaces@5"},"definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@21"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@22"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfTerminal","returnType":{"$ref":"#/interfaces@7"},"definition":{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfNonTerminal","returnType":{"$ref":"#/interfaces@8"},"definition":{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfSpecial","returnType":{"$ref":"#/interfaces@9"},"definition":{"$type":"Assignment","feature":"text","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfGroup","returnType":{"$ref":"#/interfaces@10"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"element","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfOptional","returnType":{"$ref":"#/interfaces@11"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"element","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}},{"$type":"Keyword","value":"]"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfRepetition","returnType":{"$ref":"#/interfaces@12"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"{"},{"$type":"Assignment","feature":"element","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}},{"$type":"Keyword","value":"}"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfPostfix","returnType":{"$ref":"#/interfaces@6"},"definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@25"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@26"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@27"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@28"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfOptionalPostfix","returnType":{"$ref":"#/interfaces@13"},"definition":{"$type":"Assignment","feature":"operator","operator":"=","terminal":{"$type":"Keyword","value":"?"}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfZeroOrMorePostfix","returnType":{"$ref":"#/interfaces@14"},"definition":{"$type":"Assignment","feature":"operator","operator":"=","terminal":{"$type":"Keyword","value":"*"}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfOneOrMorePostfix","returnType":{"$ref":"#/interfaces@15"},"definition":{"$type":"Assignment","feature":"operator","operator":"=","terminal":{"$type":"Keyword","value":"+"}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EbnfExceptionPostfix","returnType":{"$ref":"#/interfaces@16"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"-"},{"$type":"Assignment","feature":"except","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}}]},"entry":false,"fragment":false,"parameters":[]}],"interfaces":[{"$type":"Interface","name":"RailroadEbnf","attributes":[{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"rules","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@1"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"EbnfRule","attributes":[{"$type":"TypeAttribute","name":"name","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false},{"$type":"TypeAttribute","name":"definition","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"EbnfChoice","attributes":[{"$type":"TypeAttribute","name":"alternatives","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@3"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"EbnfSequence","attributes":[{"$type":"TypeAttribute","name":"elements","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@4"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"EbnfTerm","attributes":[{"$type":"TypeAttribute","name":"base","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@5"}},"isOptional":false},{"$type":"TypeAttribute","name":"postfixes","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@6"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"EbnfPrimary","attributes":[],"superTypes":[]},{"$type":"Interface","name":"EbnfPostfix","attributes":[],"superTypes":[]},{"$type":"Interface","name":"EbnfTerminal","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"value","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"EbnfNonTerminal","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"name","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"EbnfSpecial","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"text","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"EbnfGroup","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"element","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}]},{"$type":"Interface","name":"EbnfOptional","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"element","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}]},{"$type":"Interface","name":"EbnfRepetition","superTypes":[{"$ref":"#/interfaces@5"}],"attributes":[{"$type":"TypeAttribute","name":"element","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}]},{"$type":"Interface","name":"EbnfOptionalPostfix","superTypes":[{"$ref":"#/interfaces@6"}],"attributes":[{"$type":"TypeAttribute","name":"operator","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"EbnfZeroOrMorePostfix","superTypes":[{"$ref":"#/interfaces@6"}],"attributes":[{"$type":"TypeAttribute","name":"operator","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"EbnfOneOrMorePostfix","superTypes":[{"$ref":"#/interfaces@6"}],"attributes":[{"$type":"TypeAttribute","name":"operator","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"EbnfExceptionPostfix","superTypes":[{"$ref":"#/interfaces@6"}],"attributes":[{"$type":"TypeAttribute","name":"except","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@5"}},"isOptional":false}]}],"imports":[],"types":[]}`)),"RailroadEbnfGrammarGrammar"),Rot=L(()=>Aot??(Aot=ds(`{"$type":"Grammar","isDeclared":true,"name":"RailroadGrammar","rules":[{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"RR_ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[A-Z_a-z][\\\\w-]*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"RR_STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"RR_WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t \\\\r\\\\n]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"RR_YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"RR_DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"RR_SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"RR_BLOCK_COMMENT","definition":{"$type":"RegexToken","regex":"/\\\\/\\\\*[\\\\s\\\\S]*?\\\\*\\\\//","parenthesized":false},"fragment":false},{"$type":"ParserRule","entry":true,"name":"Railroad","returnType":{"$ref":"#/interfaces@0"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"railroad-beta"},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@0"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}},{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}}],"cardinality":"*"},{"$type":"Assignment","feature":"rules","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]},"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"RailroadRule","returnType":{"$ref":"#/interfaces@1"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"Keyword","value":"="},{"$type":"Assignment","feature":"definition","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}},{"$type":"Keyword","value":";"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"RailroadExpression","returnType":{"$ref":"#/interfaces@2"},"definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"RailroadSequenceExpr","returnType":{"$ref":"#/interfaces@3"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"sequence"},{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"elements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"elements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}}],"cardinality":"*"},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"RailroadChoiceExpr","returnType":{"$ref":"#/interfaces@4"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"choice"},{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"alternatives","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"alternatives","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}}],"cardinality":"*"},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"RailroadOptionalExpr","returnType":{"$ref":"#/interfaces@5"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"optional"},{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"element","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"RailroadOneOrMoreExpr","returnType":{"$ref":"#/interfaces@6"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"oneOrMore"},{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"element","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"RailroadZeroOrMoreExpr","returnType":{"$ref":"#/interfaces@7"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"zeroOrMore"},{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"element","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"RailroadTerminalExpr","returnType":{"$ref":"#/interfaces@8"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"terminal"},{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"RailroadNonTerminalExpr","returnType":{"$ref":"#/interfaces@9"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"nonterminal"},{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"RailroadSpecialExpr","returnType":{"$ref":"#/interfaces@10"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"special"},{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"text","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]}],"interfaces":[{"$type":"Interface","name":"Railroad","attributes":[{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"rules","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@1"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"RailroadRule","attributes":[{"$type":"TypeAttribute","name":"name","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false},{"$type":"TypeAttribute","name":"definition","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"RailroadExpression","attributes":[],"superTypes":[]},{"$type":"Interface","name":"RailroadSequenceExpr","superTypes":[{"$ref":"#/interfaces@2"}],"attributes":[{"$type":"TypeAttribute","name":"elements","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}}},"isOptional":false}]},{"$type":"Interface","name":"RailroadChoiceExpr","superTypes":[{"$ref":"#/interfaces@2"}],"attributes":[{"$type":"TypeAttribute","name":"alternatives","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}}},"isOptional":false}]},{"$type":"Interface","name":"RailroadOptionalExpr","superTypes":[{"$ref":"#/interfaces@2"}],"attributes":[{"$type":"TypeAttribute","name":"element","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}]},{"$type":"Interface","name":"RailroadOneOrMoreExpr","superTypes":[{"$ref":"#/interfaces@2"}],"attributes":[{"$type":"TypeAttribute","name":"element","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}]},{"$type":"Interface","name":"RailroadZeroOrMoreExpr","superTypes":[{"$ref":"#/interfaces@2"}],"attributes":[{"$type":"TypeAttribute","name":"element","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}]},{"$type":"Interface","name":"RailroadTerminalExpr","superTypes":[{"$ref":"#/interfaces@2"}],"attributes":[{"$type":"TypeAttribute","name":"value","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"RailroadNonTerminalExpr","superTypes":[{"$ref":"#/interfaces@2"}],"attributes":[{"$type":"TypeAttribute","name":"name","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"RailroadSpecialExpr","superTypes":[{"$ref":"#/interfaces@2"}],"attributes":[{"$type":"TypeAttribute","name":"text","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]}],"imports":[],"types":[]}`)),"RailroadGrammarGrammar"),Lot=L(()=>$ot??($ot=ds(`{"$type":"Grammar","isDeclared":true,"name":"RailroadPegGrammar","rules":[{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"PEG_ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[A-Z_a-z][\\\\w-]*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"PEG_STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"PEG_WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t \\\\r\\\\n]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"PEG_YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"PEG_DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"PEG_SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"PEG_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/#[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false},{"$type":"ParserRule","entry":true,"name":"RailroadPeg","returnType":{"$ref":"#/interfaces@0"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"railroad-peg-beta"},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@0"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}},{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}}],"cardinality":"*"},{"$type":"Assignment","feature":"rules","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]},"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PegRule","returnType":{"$ref":"#/interfaces@1"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"Keyword","value":"<-"},{"$type":"Assignment","feature":"definition","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}},{"$type":"Keyword","value":";"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PegOrderedChoice","returnType":{"$ref":"#/interfaces@2"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"alternatives","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"/"},{"$type":"Assignment","feature":"alternatives","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}}],"cardinality":"*"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PegSequence","returnType":{"$ref":"#/interfaces@3"},"definition":{"$type":"Assignment","feature":"elements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},"cardinality":"+"},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PegPrefix","returnType":{"$ref":"#/interfaces@4"},"definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"operator","operator":"=","terminal":{"$type":"Keyword","value":"&"}},{"$type":"Assignment","feature":"operator","operator":"=","terminal":{"$type":"Keyword","value":"!"}}],"cardinality":"?"},{"$type":"Assignment","feature":"suffix","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PegSuffix","returnType":{"$ref":"#/interfaces@5"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"primary","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"operator","operator":"=","terminal":{"$type":"Keyword","value":"?"}},{"$type":"Assignment","feature":"operator","operator":"=","terminal":{"$type":"Keyword","value":"*"}},{"$type":"Assignment","feature":"operator","operator":"=","terminal":{"$type":"Keyword","value":"+"}}],"cardinality":"?"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PegPrimary","returnType":{"$ref":"#/interfaces@6"},"definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PegLiteral","returnType":{"$ref":"#/interfaces@7"},"definition":{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PegIdentifier","returnType":{"$ref":"#/interfaces@8"},"definition":{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PegGroup","returnType":{"$ref":"#/interfaces@9"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"element","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PegAny","returnType":{"$ref":"#/interfaces@10"},"definition":{"$type":"Assignment","feature":"dot","operator":"=","terminal":{"$type":"Keyword","value":"."}},"entry":false,"fragment":false,"parameters":[]}],"interfaces":[{"$type":"Interface","name":"RailroadPeg","attributes":[{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"rules","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@1"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"PegRule","attributes":[{"$type":"TypeAttribute","name":"name","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false},{"$type":"TypeAttribute","name":"definition","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"PegOrderedChoice","attributes":[{"$type":"TypeAttribute","name":"alternatives","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@3"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"PegSequence","attributes":[{"$type":"TypeAttribute","name":"elements","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@4"}}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"PegPrefix","attributes":[{"$type":"TypeAttribute","name":"operator","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"suffix","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@5"}},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"PegSuffix","attributes":[{"$type":"TypeAttribute","name":"primary","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@6"}},"isOptional":false},{"$type":"TypeAttribute","name":"operator","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}}],"superTypes":[]},{"$type":"Interface","name":"PegPrimary","attributes":[],"superTypes":[]},{"$type":"Interface","name":"PegLiteral","superTypes":[{"$ref":"#/interfaces@6"}],"attributes":[{"$type":"TypeAttribute","name":"value","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"PegIdentifier","superTypes":[{"$ref":"#/interfaces@6"}],"attributes":[{"$type":"TypeAttribute","name":"name","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]},{"$type":"Interface","name":"PegGroup","superTypes":[{"$ref":"#/interfaces@6"}],"attributes":[{"$type":"TypeAttribute","name":"element","type":{"$type":"SimpleType","typeRef":{"$ref":"#/interfaces@2"}},"isOptional":false}]},{"$type":"Interface","name":"PegAny","superTypes":[{"$ref":"#/interfaces@6"}],"attributes":[{"$type":"TypeAttribute","name":"dot","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}]}],"imports":[],"types":[]}`)),"RailroadPegGrammarGrammar"),Mot=L(()=>Iot??(Iot=ds(`{"$type":"Grammar","isDeclared":true,"name":"TreemapGrammar","rules":[{"$type":"ParserRule","fragment":true,"name":"TitleAndAccessibilities","definition":{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}}],"cardinality":"+"},"entry":false,"parameters":[]},{"$type":"TerminalRule","name":"BOOLEAN","type":{"$type":"ReturnType","name":"boolean"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"true"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"false"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"ParserRule","entry":true,"name":"Treemap","returnType":{"$ref":"#/interfaces@4"},"definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]},{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@0"},"arguments":[]},{"$type":"Assignment","feature":"TreemapRows","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}}],"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"TerminalRule","name":"TREEMAP_KEYWORD","definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"treemap-beta"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"treemap"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"CLASS_DEF","definition":{"$type":"RegexToken","regex":"/classDef\\\\s+([a-zA-Z_][a-zA-Z0-9_]+)(?:\\\\s+([^;\\\\r\\\\n]*))?(?:;)?/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STYLE_SEPARATOR","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":":::"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"SEPARATOR","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":":"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"COMMA","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":","},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INDENTATION","definition":{"$type":"RegexToken","regex":"/[ \\\\t]{1,}/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WS","definition":{"$type":"RegexToken","regex":"/[ \\\\t]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"ML_COMMENT","definition":{"$type":"RegexToken","regex":"/\\\\%\\\\%[^\\\\n]*/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"NL","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/","parenthesized":false},"fragment":false},{"$type":"ParserRule","name":"TreemapRow","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"indent","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]},"cardinality":"?"},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"item","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"ClassDef","dataType":"string","definition":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Item","returnType":{"$ref":"#/interfaces@0"},"definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Section","returnType":{"$ref":"#/interfaces@1"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]},{"$type":"Assignment","feature":"classSelector","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}}],"cardinality":"?"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Leaf","returnType":{"$ref":"#/interfaces@2"},"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[],"cardinality":"?"},{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[],"cardinality":"?"},{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@22"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]},{"$type":"Assignment","feature":"classSelector","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}}],"cardinality":"?"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"TerminalRule","name":"ID2","definition":{"$type":"RegexToken","regex":"/[a-zA-Z_][a-zA-Z0-9_]*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NUMBER2","definition":{"$type":"RegexToken","regex":"/[0-9_\\\\.\\\\,]+/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"ParserRule","name":"MyNumber","dataType":"number","definition":{"$type":"RuleCall","rule":{"$ref":"#/rules@21"},"arguments":[]},"entry":false,"fragment":false,"parameters":[]},{"$type":"TerminalRule","name":"STRING2","definition":{"$type":"RegexToken","regex":"/\\"[^\\"]*\\"|'[^']*'/","parenthesized":false},"fragment":false,"hidden":false}],"interfaces":[{"$type":"Interface","name":"Item","attributes":[{"$type":"TypeAttribute","name":"name","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false},{"$type":"TypeAttribute","name":"classSelector","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}}],"superTypes":[]},{"$type":"Interface","name":"Section","superTypes":[{"$ref":"#/interfaces@0"}],"attributes":[]},{"$type":"Interface","name":"Leaf","superTypes":[{"$ref":"#/interfaces@0"}],"attributes":[{"$type":"TypeAttribute","name":"value","type":{"$type":"SimpleType","primitiveType":"number"},"isOptional":false}]},{"$type":"Interface","name":"ClassDefStatement","attributes":[{"$type":"TypeAttribute","name":"className","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false},{"$type":"TypeAttribute","name":"styleText","type":{"$type":"SimpleType","primitiveType":"string"},"isOptional":false}],"superTypes":[]},{"$type":"Interface","name":"Treemap","attributes":[{"$type":"TypeAttribute","name":"TreemapRows","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/rules@15"}}},"isOptional":false},{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}}],"superTypes":[]}],"imports":[],"types":[],"$comment":"/**\\n * Treemap grammar for Langium\\n * Converted from mindmap grammar\\n *\\n * The ML_COMMENT and NL hidden terminals handle whitespace, comments, and newlines\\n * before the treemap keyword, allowing for empty lines and comments before the\\n * treemap declaration.\\n */"}`)),"TreemapGrammarGrammar"),Not=L(()=>Dot??(Dot=ds(`{"$type":"Grammar","isDeclared":true,"name":"TreeViewGrammar","rules":[{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"ParserRule","entry":true,"name":"TreeView","returnType":{"$ref":"#/interfaces@0"},"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"treeView-beta"},{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[],"cardinality":"?"},{"$type":"Assignment","feature":"nodes","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"TitleAndAccessibilities","definition":{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@0"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}}],"cardinality":"+"},"entry":false,"parameters":[]},{"$type":"TerminalRule","name":"CLASS_ANNOTATION","definition":{"$type":"RegexToken","regex":"/[ \\\\t]+:::[ \\\\t]*[A-Za-z_][\\\\w-]*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ICON_ANNOTATION","definition":{"$type":"RegexToken","regex":"/[ \\\\t]+icon\\\\([\\\\w-]*(?::[\\\\w-]+)?\\\\)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"DESC_ANNOTATION","definition":{"$type":"RegexToken","regex":"/[ \\\\t]+##[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INDENTATION","definition":{"$type":"RegexToken","regex":"/[ \\\\t]{1,}/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"QUOTED_NAME","definition":{"$type":"RegexToken","regex":"/\\"[^\\"]*\\"|'[^']*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WS","definition":{"$type":"RegexToken","regex":"/[ \\\\t]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"ML_COMMENT","definition":{"$type":"RegexToken","regex":"/\\\\%\\\\%[^\\\\n]*/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"NL","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","name":"BARE_NAME","definition":{"$type":"RegexToken","regex":"/(?!:::|icon\\\\(|##)[^ \\\\t\\\\n\\\\r\\"'](?:(?![ \\\\t]+:::[ \\\\t]*[A-Za-z_]|[ \\\\t]+icon\\\\(|[ \\\\t]+##)[^\\\\n\\\\r])*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"ParserRule","name":"TreeNode","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"indent","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]},"cardinality":"?"},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}}]},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"classAnnotation","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},{"$type":"Assignment","feature":"iconAnnotation","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]}},{"$type":"Assignment","feature":"descAnnotation","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}}],"cardinality":"*"}]},"entry":false,"fragment":false,"parameters":[]}],"interfaces":[{"$type":"Interface","name":"TreeView","attributes":[{"$type":"TypeAttribute","name":"nodes","type":{"$type":"ArrayType","elementType":{"$type":"SimpleType","typeRef":{"$ref":"#/rules@14"}}},"isOptional":false},{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}}],"superTypes":[]}],"imports":[],"types":[],"$comment":"/**\\n * TreeView grammar for Langium\\n *\\n * Supports both quoted labels (\\"my file\\") and bare labels (index.js).\\n * Annotations (:::class, icon(), ## description) are parsed directly into\\n * AST fields by the grammar. Value conversion for stripping quotes, extracting\\n * class names, icon names, and description text happens in valueConverter.ts.\\n *\\n * The ML_COMMENT and NL hidden terminals handle whitespace, comments, and newlines\\n * before the treeView keyword, allowing for empty lines and comments before the\\n * treeView declaration.\\n */"}`)),"TreeViewGrammarGrammar"),Bot=L(()=>Oot??(Oot=ds(`{"$type":"Grammar","isDeclared":true,"name":"WardleyGrammar","imports":[],"rules":[{"$type":"ParserRule","entry":true,"name":"Wardley","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@52"},"arguments":[],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@25"},"arguments":[]},{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@52"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@42"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}],"cardinality":"*"}]},"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"Statement","definition":{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"size","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}},{"$type":"Assignment","feature":"evolution","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"Assignment","feature":"anchors","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},{"$type":"Assignment","feature":"components","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]}},{"$type":"Assignment","feature":"links","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}},{"$type":"Assignment","feature":"evolves","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}},{"$type":"Assignment","feature":"pipelines","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]}},{"$type":"Assignment","feature":"notes","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}},{"$type":"Assignment","feature":"annotations","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]}},{"$type":"Assignment","feature":"annotation","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]}},{"$type":"Assignment","feature":"accelerators","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}},{"$type":"Assignment","feature":"deaccelerators","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[]}}]},"entry":false,"parameters":[]},{"$type":"ParserRule","name":"Size","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@26"},"arguments":[]},{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"width","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@48"},"arguments":[]}},{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"height","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@48"},"arguments":[]}},{"$type":"Keyword","value":"]"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Evolution","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@27"},"arguments":[]},{"$type":"Assignment","feature":"stages","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]},{"$type":"Assignment","feature":"stages","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}}],"cardinality":"+"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"EvolutionStage","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"@"},{"$type":"Assignment","feature":"boundary","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}}],"cardinality":"?"},{"$type":"Group","elements":[{"$type":"Keyword","value":"/"},{"$type":"Assignment","feature":"secondName","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}}],"cardinality":"?"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Anchor","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@28"},"arguments":[]},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}},{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"visibility","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"evolution","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":"]"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Component","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@29"},"arguments":[]},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}},{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"visibility","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"evolution","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":"]"},{"$type":"Assignment","feature":"label","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]},"cardinality":"?"},{"$type":"Assignment","feature":"decorator","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]},"cardinality":"?"},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"inertia","operator":"?=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@31"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"inertia","operator":"?=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@31"},"arguments":[]}},{"$type":"Keyword","value":")"}]}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Label","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@30"},"arguments":[]},{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"negX","operator":"?=","terminal":{"$type":"Keyword","value":"-"},"cardinality":"?"},{"$type":"Assignment","feature":"offsetX","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@48"},"arguments":[]}},{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"negY","operator":"?=","terminal":{"$type":"Keyword","value":"-"},"cardinality":"?"},{"$type":"Assignment","feature":"offsetY","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@48"},"arguments":[]}},{"$type":"Keyword","value":"]"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Decorator","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"("},{"$type":"Assignment","feature":"strategy","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@24"},"arguments":[]}},{"$type":"Keyword","value":")"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Link","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"from","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}},{"$type":"Assignment","feature":"fromPort","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@21"},"arguments":[]},"cardinality":"?"},{"$type":"Assignment","feature":"arrow","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@22"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}]},"cardinality":"?"},{"$type":"Assignment","feature":"to","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}},{"$type":"Assignment","feature":"toPort","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@21"},"arguments":[]},"cardinality":"?"},{"$type":"Assignment","feature":"linkLabel","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@23"},"arguments":[]},"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Evolve","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@32"},"arguments":[]},{"$type":"Assignment","feature":"component","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}},{"$type":"Assignment","feature":"target","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Pipeline","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@33"},"arguments":[]},{"$type":"Assignment","feature":"parent","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}},{"$type":"Keyword","value":"{"},{"$type":"RuleCall","rule":{"$ref":"#/rules@52"},"arguments":[],"cardinality":"+"},{"$type":"Assignment","feature":"components","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]},"cardinality":"+"},{"$type":"Keyword","value":"}"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"PipelineComponent","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@29"},"arguments":[]},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}},{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"evolution","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":"]"},{"$type":"Assignment","feature":"label","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]},"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Note","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@34"},"arguments":[]},{"$type":"Assignment","feature":"text","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]}},{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"visibility","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"evolution","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":"]"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Annotations","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@35"},"arguments":[]},{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"x","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}},{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"y","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}},{"$type":"Keyword","value":"]"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Annotation","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@36"},"arguments":[]},{"$type":"Assignment","feature":"number","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@48"},"arguments":[]}},{"$type":"Keyword","value":","},{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"x","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}},{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"y","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}},{"$type":"Keyword","value":"]"},{"$type":"Assignment","feature":"text","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"CoordinateValue","dataType":"number","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@48"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Accelerator","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@37"},"arguments":[]},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}},{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"x","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"y","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":"]"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","name":"Deaccelerator","definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@38"},"arguments":[]},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@50"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@51"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@39"},"arguments":[]}]}},{"$type":"Keyword","value":"["},{"$type":"Assignment","feature":"x","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":","},{"$type":"Assignment","feature":"y","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Keyword","value":"]"},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"TerminalRule","name":"WARDLEY_NUMBER","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/[0-9]+\\\\.[0-9]+/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ARROW","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"->"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"LINK_PORT","definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"+<>"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"+>"},"parenthesized":false}],"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"+<"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"LINK_ARROW","definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"-->"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"-.->"},"parenthesized":false}],"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":">"},"parenthesized":false}],"parenthesized":false},{"$type":"RegexToken","regex":"/\\\\+'[^']*'<>/","parenthesized":false}],"parenthesized":false},{"$type":"RegexToken","regex":"/\\\\+'[^']*'/","parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"LINK_LABEL","definition":{"$type":"RegexToken","regex":"/;[^\\\\n\\\\r]+/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRATEGY","definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"build"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"buy"},"parenthesized":false}],"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"outsource"},"parenthesized":false}],"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"market"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_WARDLEY","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"wardley-beta"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_SIZE","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"size"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_EVOLUTION","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"evolution"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_ANCHOR","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"anchor"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_COMPONENT","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"component"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_LABEL","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"label"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_INERTIA","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"inertia"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_EVOLVE","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"evolve"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_PIPELINE","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"pipeline"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_NOTE","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"note"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_ANNOTATIONS","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"annotations"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_ANNOTATION","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"annotation"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_ACCELERATOR","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"accelerator"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"KW_DEACCELERATOR","definition":{"$type":"CharacterRange","left":{"$type":"Keyword","value":"deaccelerator"},"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NAME_WITH_SPACES","definition":{"$type":"RegexToken","regex":"/(?!title\\\\s|accTitle|accDescr)[A-Za-z](?:[A-Za-z0-9_()&]|-(?!>))*(?:[ \\\\t]+[A-Za-z(](?:[A-Za-z0-9_()&]|-(?!>))*)*/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WS","definition":{"$type":"RegexToken","regex":"/[ \\\\t]+/","parenthesized":false},"fragment":false},{"$type":"ParserRule","name":"EOL","dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@52"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"entry":false,"fragment":false,"parameters":[]},{"$type":"ParserRule","fragment":true,"name":"TitleAndAccessibilities","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@44"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@45"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@46"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@41"},"arguments":[]}],"cardinality":"+"},"entry":false,"parameters":[]},{"$type":"TerminalRule","name":"BOOLEAN","type":{"$type":"ReturnType","name":"boolean"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"true"},"parenthesized":false},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"false"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"FLOAT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/[0-9]+\\\\.[0-9]+(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"INT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/0|[1-9][0-9]*(?!\\\\.)/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NUMBER","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@47"},"parenthesized":false},{"$type":"TerminalRuleCall","rule":{"$ref":"#/rules@48"},"parenthesized":false}],"parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRING","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\"|'([^'\\\\\\\\]|\\\\\\\\.)*'/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/[\\\\w]([-\\\\w]*\\\\w)?/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/","parenthesized":false},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/","parenthesized":false},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/","parenthesized":false},"fragment":false}],"interfaces":[],"types":[]}`)),"WardleyGrammarGrammar"),Pot={languageId:"architecture",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Fot={languageId:"cynefin",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},zot={languageId:"eventmodeling",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},qot={languageId:"gitGraph",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Wot={languageId:"info",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},jot={languageId:"packet",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Vot={languageId:"pie",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Uot={languageId:"radar",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Yot={languageId:"railroadAbnf",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Hot={languageId:"railroadEbnf",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Got={languageId:"railroad",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Xot={languageId:"railroadPeg",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Kot={languageId:"treemap",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Qot={languageId:"treeView",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},Zot={languageId:"wardley",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1,mode:"production"},To={AstReflection:L(()=>new Lue,"AstReflection")},Jot={Grammar:L(()=>cot(),"Grammar"),LanguageMetaData:L(()=>Pot,"LanguageMetaData"),parser:{}},elt={Grammar:L(()=>hot(),"Grammar"),LanguageMetaData:L(()=>Fot,"LanguageMetaData"),parser:{}},tlt={Grammar:L(()=>pot(),"Grammar"),LanguageMetaData:L(()=>zot,"LanguageMetaData"),parser:{}},rlt={Grammar:L(()=>got(),"Grammar"),LanguageMetaData:L(()=>qot,"LanguageMetaData"),parser:{}},ilt={Grammar:L(()=>yot(),"Grammar"),LanguageMetaData:L(()=>Wot,"LanguageMetaData"),parser:{}},nlt={Grammar:L(()=>bot(),"Grammar"),LanguageMetaData:L(()=>jot,"LanguageMetaData"),parser:{}},alt={Grammar:L(()=>Tot(),"Grammar"),LanguageMetaData:L(()=>Vot,"LanguageMetaData"),parser:{}},slt={Grammar:L(()=>Cot(),"Grammar"),LanguageMetaData:L(()=>Uot,"LanguageMetaData"),parser:{}},olt={Grammar:L(()=>Eot(),"Grammar"),LanguageMetaData:L(()=>Yot,"LanguageMetaData"),parser:{}},llt={Grammar:L(()=>_ot(),"Grammar"),LanguageMetaData:L(()=>Hot,"LanguageMetaData"),parser:{}},clt={Grammar:L(()=>Rot(),"Grammar"),LanguageMetaData:L(()=>Got,"LanguageMetaData"),parser:{}},ult={Grammar:L(()=>Lot(),"Grammar"),LanguageMetaData:L(()=>Xot,"LanguageMetaData"),parser:{}},hlt={Grammar:L(()=>Mot(),"Grammar"),LanguageMetaData:L(()=>Kot,"LanguageMetaData"),parser:{}},dlt={Grammar:L(()=>Not(),"Grammar"),LanguageMetaData:L(()=>Qot,"LanguageMetaData"),parser:{}},plt={Grammar:L(()=>Bot(),"Grammar"),LanguageMetaData:L(()=>Zot,"LanguageMetaData"),parser:{}},flt=/accDescr(?:[\t ]*:([^\n\r]*)|\s*{([^}]*)})/,glt=/accTitle[\t ]*:([^\n\r]*)/,mlt=/title([\t ][^\n\r]*|)/,ylt={ACC_DESCR:flt,ACC_TITLE:glt,TITLE:mlt},uu=(gs=class extends l9{runConverter(T,A,D){let P=this.runCommonConverter(T,A,D);return P===void 0&&(P=this.runCustomConverter(T,A,D)),P===void 0?super.runConverter(T,A,D):P}runCommonConverter(T,A,D){let P=ylt[T.name];if(P===void 0)return;let V=P.exec(A);if(V!==null){if(V[1]!==void 0)return V[1].trim().replace(/[\t ]{2,}/gm," ");if(V[2]!==void 0)return V[2].replace(/^\s*/gm,"").replace(/\s+$/gm,"").replace(/[\t ]{2,}/gm," ").replace(/[\n\r]{2,}/gm,` +`)}}},s(gs,"AbstractMermaidValueConverter"),L(gs,"AbstractMermaidValueConverter"),gs),k1=(ms=class extends uu{runCustomConverter(T,A,D){}},s(ms,"CommonValueConverter"),L(ms,"CommonValueConverter"),ms),ko=(An=class extends Kw{constructor(T){super(),this.keywords=new Set(T)}buildKeywordTokens(T,A,D){let P=super.buildKeywordTokens(T,A,D);return P.forEach(V=>{this.keywords.has(V.name)&&V.PATTERN!==void 0&&(V.PATTERN=new RegExp(V.PATTERN.toString()+"(?:(?=%%)|(?!\\S))"))}),P}},s(An,"AbstractMermaidTokenBuilder"),L(An,"AbstractMermaidTokenBuilder"),An),y8t=(We=class extends ko{},s(We,"CommonTokenBuilder"),L(We,"CommonTokenBuilder"),We)});function Iue(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),slt,Mue);return t.ServiceRegistry.register(r),{shared:t,Radar:r}}var vlt,Mue,blt=G(()=>{"use strict";var e;rn(),vlt=(e=class extends ko{constructor(){super(["radar-beta"])}},s(e,"RadarTokenBuilder"),L(e,"RadarTokenBuilder"),e),Mue={parser:{TokenBuilder:L(()=>new vlt,"TokenBuilder"),ValueConverter:L(()=>new k1,"ValueConverter")}},s(Iue,"createRadarServices"),L(Iue,"createRadarServices")});function W9(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),clt,Nue);return t.ServiceRegistry.register(r),{shared:t,Railroad:r}}var xlt,Due,Tlt,Nue,klt=G(()=>{"use strict";var e,t;rn(),xlt=(e=class extends ko{constructor(){super(["railroad-beta"])}},s(e,"RailroadTokenBuilder"),L(e,"RailroadTokenBuilder"),e),Due=L(r=>{let i=r.slice(1,-1),n="";for(let a=0;anew xlt,"TokenBuilder"),ValueConverter:L(()=>new Tlt,"ValueConverter")}},s(W9,"createRailroadServices"),L(W9,"createRailroadServices")});function j9(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),llt,Bue);return t.ServiceRegistry.register(r),{shared:t,RailroadEbnf:r}}var Clt,Oue,wlt,Bue,Elt=G(()=>{"use strict";var e,t;rn(),Clt=(e=class extends ko{constructor(){super(["railroad-ebnf-beta"])}},s(e,"RailroadEbnfTokenBuilder"),L(e,"RailroadEbnfTokenBuilder"),e),Oue=L(r=>{let i=r.slice(1,-1),n="";for(let a=0;anew Clt,"TokenBuilder"),ValueConverter:L(()=>new wlt,"ValueConverter")}},s(j9,"createRailroadEbnfServices"),L(j9,"createRailroadEbnfServices")});function V9(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),olt,Pue);return t.ServiceRegistry.register(r),{shared:t,RailroadAbnf:r}}var Slt,_lt,Pue,Alt=G(()=>{"use strict";var e,t;rn(),Slt=(e=class extends ko{constructor(){super(["railroad-abnf-beta"])}},s(e,"RailroadAbnfTokenBuilder"),L(e,"RailroadAbnfTokenBuilder"),e),_lt=(t=class extends uu{runConverter(i,n,a){let o=super.runConverter(i,n,a);if(i.name==="TITLE"&&typeof o=="string"){let l=o.trim();if(l.startsWith('"')&&l.endsWith('"')||l.startsWith("'")&&l.endsWith("'"))return l.slice(1,-1)}return o}runCustomConverter(i,n,a){if(i.name==="ABNF_STRING")return n.slice(1,-1)}},s(t,"RailroadAbnfValueConverter"),L(t,"RailroadAbnfValueConverter"),t),Pue={parser:{TokenBuilder:L(()=>new Slt,"TokenBuilder"),ValueConverter:L(()=>new _lt,"ValueConverter")}},s(V9,"createRailroadAbnfServices"),L(V9,"createRailroadAbnfServices")});function U9(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),ult,zue);return t.ServiceRegistry.register(r),{shared:t,RailroadPeg:r}}var Rlt,Fue,$lt,zue,Llt=G(()=>{"use strict";var e,t;rn(),Rlt=(e=class extends ko{constructor(){super(["railroad-peg-beta"])}},s(e,"RailroadPegTokenBuilder"),L(e,"RailroadPegTokenBuilder"),e),Fue=L(r=>{let i=r.slice(1,-1),n="";for(let a=0;anew Rlt,"TokenBuilder"),ValueConverter:L(()=>new $lt,"ValueConverter")}},s(U9,"createRailroadPegServices"),L(U9,"createRailroadPegServices")});function que(e){let t=e.validation.TreemapValidator,r=e.validation.ValidationRegistry;if(r){let i={Treemap:t.checkSingleRoot.bind(t)};r.register(i,t)}}function Wue(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),hlt,jue);return t.ServiceRegistry.register(r),que(r),{shared:t,Treemap:r}}var Ilt,Mlt,Dlt,Nlt,jue,Olt=G(()=>{"use strict";var e,t,r;rn(),Ilt=(e=class extends ko{constructor(){super(["treemap"])}},s(e,"TreemapTokenBuilder"),L(e,"TreemapTokenBuilder"),e),Mlt=/classDef\s+([A-Z_a-z]\w+)(?:\s+([^\n\r;]*))?;?/,Dlt=(t=class extends uu{runCustomConverter(n,a,o){if(n.name==="NUMBER2")return parseFloat(a.replace(/,/g,""));if(n.name==="SEPARATOR"||n.name==="STRING2")return a.substring(1,a.length-1);if(n.name==="INDENTATION")return a.length;if(n.name==="ClassDef"){if(typeof a!="string")return a;let l=Mlt.exec(a);if(l)return{$type:"ClassDefStatement",className:l[1],styleText:l[2]||void 0}}}},s(t,"TreemapValueConverter"),L(t,"TreemapValueConverter"),t),s(que,"registerValidationChecks"),L(que,"registerValidationChecks"),Nlt=(r=class{checkSingleRoot(n,a){let o;for(let l of n.TreemapRows)l.item&&(o===void 0&&l.indent===void 0?o=0:l.indent===void 0?a("error","Multiple root nodes are not allowed in a treemap.",{node:l,property:"item"}):o!==void 0&&o>=parseInt(l.indent,10)&&a("error","Multiple root nodes are not allowed in a treemap.",{node:l,property:"item"}))}},s(r,"TreemapValidator"),L(r,"TreemapValidator"),r),jue={parser:{TokenBuilder:L(()=>new Ilt,"TokenBuilder"),ValueConverter:L(()=>new Dlt,"ValueConverter")},validation:{TreemapValidator:L(()=>new Nlt,"TreemapValidator")}},s(Wue,"createTreemapServices"),L(Wue,"createTreemapServices")});function Vue(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),plt,Uue);return t.ServiceRegistry.register(r),{shared:t,Wardley:r}}var Blt,Uue,Plt=G(()=>{"use strict";var e;rn(),Blt=(e=class extends uu{runCustomConverter(r,i,n){if(r.name.toUpperCase()==="LINK_LABEL")return i.substring(1).trim()}},s(e,"WardleyValueConverter"),L(e,"WardleyValueConverter"),e),Uue={parser:{ValueConverter:L(()=>new Blt,"ValueConverter")}},s(Vue,"createWardleyServices"),L(Vue,"createWardleyServices")});function Yue(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),elt,Hue);return t.ServiceRegistry.register(r),{shared:t,Cynefin:r}}var Flt,Hue,zlt=G(()=>{"use strict";var e;rn(),Flt=(e=class extends ko{constructor(){super(["cynefin-beta"])}},s(e,"CynefinTokenBuilder"),L(e,"CynefinTokenBuilder"),e),Hue={parser:{TokenBuilder:L(()=>new Flt,"TokenBuilder"),ValueConverter:L(()=>new k1,"ValueConverter")}},s(Yue,"createCynefinServices"),L(Yue,"createCynefinServices")});function Gue(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),rlt,Xue);return t.ServiceRegistry.register(r),{shared:t,GitGraph:r}}var qlt,Xue,Wlt=G(()=>{"use strict";var e;rn(),qlt=(e=class extends ko{constructor(){super(["gitGraph"])}},s(e,"GitGraphTokenBuilder"),L(e,"GitGraphTokenBuilder"),e),Xue={parser:{TokenBuilder:L(()=>new qlt,"TokenBuilder"),ValueConverter:L(()=>new k1,"ValueConverter")}},s(Gue,"createGitGraphServices"),L(Gue,"createGitGraphServices")});function Kue(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),ilt,Que);return t.ServiceRegistry.register(r),{shared:t,Info:r}}var jlt,Que,Vlt=G(()=>{"use strict";var e;rn(),jlt=(e=class extends ko{constructor(){super(["info","showInfo"])}},s(e,"InfoTokenBuilder"),L(e,"InfoTokenBuilder"),e),Que={parser:{TokenBuilder:L(()=>new jlt,"TokenBuilder"),ValueConverter:L(()=>new k1,"ValueConverter")}},s(Kue,"createInfoServices"),L(Kue,"createInfoServices")});function Zue(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),nlt,Jue);return t.ServiceRegistry.register(r),{shared:t,Packet:r}}var Ult,Jue,Ylt=G(()=>{"use strict";var e;rn(),Ult=(e=class extends ko{constructor(){super(["packet"])}},s(e,"PacketTokenBuilder"),L(e,"PacketTokenBuilder"),e),Jue={parser:{TokenBuilder:L(()=>new Ult,"TokenBuilder"),ValueConverter:L(()=>new k1,"ValueConverter")}},s(Zue,"createPacketServices"),L(Zue,"createPacketServices")});function ehe(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),alt,the);return t.ServiceRegistry.register(r),{shared:t,Pie:r}}var Hlt,Glt,the,Xlt=G(()=>{"use strict";var e,t;rn(),Hlt=(e=class extends ko{constructor(){super(["pie","showData"])}},s(e,"PieTokenBuilder"),L(e,"PieTokenBuilder"),e),Glt=(t=class extends uu{runCustomConverter(i,n,a){if(i.name==="PIE_SECTION_LABEL")return n.replace(/"/g,"").trim()}},s(t,"PieValueConverter"),L(t,"PieValueConverter"),t),the={parser:{TokenBuilder:L(()=>new Hlt,"TokenBuilder"),ValueConverter:L(()=>new Glt,"ValueConverter")}},s(ehe,"createPieServices"),L(ehe,"createPieServices")});function rhe(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),dlt,ihe);return t.ServiceRegistry.register(r),{shared:t,TreeView:r}}var Klt,Qlt,ihe,Zlt=G(()=>{"use strict";var e,t;rn(),Klt=(e=class extends uu{runCustomConverter(i,n,a){if(i.name==="INDENTATION")return n?.length||0;if(i.name==="QUOTED_NAME")return n.substring(1,n.length-1);if(i.name==="BARE_NAME")return n.replace(/[\t ]+$/,"");if(i.name==="CLASS_ANNOTATION")return n.trim().substring(3).trim();if(i.name==="ICON_ANNOTATION"){let o=n.trim();return o.substring(5,o.length-1)}if(i.name==="DESC_ANNOTATION")return n.trim().substring(2).trim()}},s(e,"TreeViewValueConverter"),L(e,"TreeViewValueConverter"),e),Qlt=(t=class extends ko{constructor(){super(["treeView-beta"])}},s(t,"TreeViewTokenBuilder"),L(t,"TreeViewTokenBuilder"),t),ihe={parser:{TokenBuilder:L(()=>new Qlt,"TokenBuilder"),ValueConverter:L(()=>new Klt,"ValueConverter")}},s(rhe,"createTreeViewServices"),L(rhe,"createTreeViewServices")});function nhe(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),Jot,ahe);return t.ServiceRegistry.register(r),{shared:t,Architecture:r}}var Jlt,ect,ahe,tct=G(()=>{"use strict";var e,t;rn(),Jlt=(e=class extends ko{constructor(){super(["architecture"])}},s(e,"ArchitectureTokenBuilder"),L(e,"ArchitectureTokenBuilder"),e),ect=(t=class extends uu{runCustomConverter(i,n,a){if(i.name==="ARCH_ICON")return n.replace(/[()]/g,"").trim();if(i.name==="ARCH_TEXT_ICON")return n.replace(/["()]/g,"");if(i.name==="ARCH_TITLE"){let o=n.replace(/^\[|]$/g,"").trim();return(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))&&(o=o.slice(1,-1),o=o.replace(/\\"/g,'"').replace(/\\'/g,"'")),o.trim()}}},s(t,"ArchitectureValueConverter"),L(t,"ArchitectureValueConverter"),t),ahe={parser:{TokenBuilder:L(()=>new Jlt,"TokenBuilder"),ValueConverter:L(()=>new ect,"ValueConverter")}},s(nhe,"createArchitectureServices"),L(nhe,"createArchitectureServices")});function she(e){let t=e.validation.EventModelingValidator,r=e.validation.ValidationRegistry;if(r){let i={EmTimeFrame:t.checkSourceFrameTypes.bind(t),EmResetFrame:t.checkSourceFrameTypes.bind(t)};r.register(i,t)}}function ohe(e=$s){let t=Bi(hs(e),To),r=Bi(us({shared:t}),tlt,dhe);return t.ServiceRegistry.register(r),she(r),{shared:t,EventModel:r}}var rct,lhe,che,Y9,uhe,hhe,ict,dhe,nct=G(()=>{"use strict";var e,t;rn(),rct=(e=class extends ko{constructor(){super(["eventmodeling"])}},s(e,"EventModelingTokenBuilder"),L(e,"EventModelingTokenBuilder"),e),lhe=new Set(["cmd","command"]),che=new Set(["evt","event"]),Y9=new Set(["rmo","readmodel"]),uhe=new Set(["pcr","processor"]),hhe=new Set(["ui"]),s(she,"registerValidationChecks"),L(she,"registerValidationChecks"),ict=(t=class{checkSourceFrameTypes(i,n){i.sourceFrames.length!==0&&(lhe.has(i.modelEntityType)?this.validateSources(i,new Set([...hhe,...uhe]),"command","ui or processor",n):che.has(i.modelEntityType)?this.validateSources(i,lhe,"event","command",n):Y9.has(i.modelEntityType)?this.validateSources(i,che,"read model","event",n):uhe.has(i.modelEntityType)?this.validateSources(i,Y9,"processor","read model",n):hhe.has(i.modelEntityType)&&this.validateSources(i,Y9,"ui","read model",n))}validateSources(i,n,a,o,l){for(let u of i.sourceFrames){let h=u.ref;h!==void 0&&!n.has(h.modelEntityType)&&l("error",`A ${a} can only receive input from a ${o}, not from '${h.modelEntityType}'.`,{node:i,property:"sourceFrames"})}}},s(t,"EventModelingValidator"),L(t,"EventModelingValidator"),t),dhe={parser:{TokenBuilder:L(()=>new rct,"TokenBuilder"),ValueConverter:L(()=>new k1,"ValueConverter")},validation:{EventModelingValidator:L(()=>new ict,"EventModelingValidator")}},s(ohe,"createEventModelingServices"),L(ohe,"createEventModelingServices")}),act={};Mr(act,{InfoModule:()=>Que,createInfoServices:()=>Kue});var v8t=G(()=>{"use strict";Vlt(),rn()}),sct={};Mr(sct,{PacketModule:()=>Jue,createPacketServices:()=>Zue});var b8t=G(()=>{"use strict";Ylt(),rn()}),oct={};Mr(oct,{PieModule:()=>the,createPieServices:()=>ehe});var x8t=G(()=>{"use strict";Xlt(),rn()}),lct={};Mr(lct,{TreeViewModule:()=>ihe,createTreeViewServices:()=>rhe});var T8t=G(()=>{"use strict";Zlt(),rn()}),cct={};Mr(cct,{ArchitectureModule:()=>ahe,createArchitectureServices:()=>nhe});var k8t=G(()=>{"use strict";tct(),rn()}),uct={};Mr(uct,{GitGraphModule:()=>Xue,createGitGraphServices:()=>Gue});var C8t=G(()=>{"use strict";Wlt(),rn()}),hct={};Mr(hct,{EventModelingModule:()=>dhe,createEventModelingServices:()=>ohe});var w8t=G(()=>{"use strict";nct(),rn()}),dct={};Mr(dct,{RadarModule:()=>Mue,createRadarServices:()=>Iue});var E8t=G(()=>{"use strict";blt(),rn()}),pct={};Mr(pct,{RailroadModule:()=>Nue,createRailroadServices:()=>W9});var S8t=G(()=>{"use strict";klt(),rn()}),fct={};Mr(fct,{RailroadEbnfModule:()=>Bue,createRailroadEbnfServices:()=>j9});var _8t=G(()=>{"use strict";Elt(),rn()}),gct={};Mr(gct,{RailroadAbnfModule:()=>Pue,createRailroadAbnfServices:()=>V9});var A8t=G(()=>{"use strict";Alt(),rn()}),mct={};Mr(mct,{RailroadPegModule:()=>zue,createRailroadPegServices:()=>U9});var R8t=G(()=>{"use strict";Llt(),rn()}),yct={};Mr(yct,{TreemapModule:()=>jue,createTreemapServices:()=>Wue});var $8t=G(()=>{"use strict";Olt(),rn()}),vct={};Mr(vct,{WardleyModule:()=>Uue,createWardleyServices:()=>Vue});var L8t=G(()=>{"use strict";Plt(),rn()}),bct={};Mr(bct,{CynefinModule:()=>Hue,createCynefinServices:()=>Yue});var I8t=G(()=>{"use strict";zlt(),rn()});async function Pl(e,t){let r=xct[e];if(!r)throw new Error(`Unknown diagram type: ${e}`);Is[e]||await r();let i=Is[e].parse(t);if(i.lexerErrors.length>0||i.parserErrors.length>0)throw new Fb(i);return i.value}var Is,xct,Fb,Co=G(()=>{"use strict";var e;blt(),klt(),Elt(),Alt(),Llt(),Olt(),Plt(),zlt(),Wlt(),Vlt(),Ylt(),Xlt(),Zlt(),tct(),nct(),rn(),Is={},xct={info:L(async()=>{let{createInfoServices:t}=await Promise.resolve().then(()=>(v8t(),act)),r=t().Info.parser.LangiumParser;Is.info=r},"info"),packet:L(async()=>{let{createPacketServices:t}=await Promise.resolve().then(()=>(b8t(),sct)),r=t().Packet.parser.LangiumParser;Is.packet=r},"packet"),pie:L(async()=>{let{createPieServices:t}=await Promise.resolve().then(()=>(x8t(),oct)),r=t().Pie.parser.LangiumParser;Is.pie=r},"pie"),treeView:L(async()=>{let{createTreeViewServices:t}=await Promise.resolve().then(()=>(T8t(),lct)),r=t().TreeView.parser.LangiumParser;Is.treeView=r},"treeView"),architecture:L(async()=>{let{createArchitectureServices:t}=await Promise.resolve().then(()=>(k8t(),cct)),r=t().Architecture.parser.LangiumParser;Is.architecture=r},"architecture"),gitGraph:L(async()=>{let{createGitGraphServices:t}=await Promise.resolve().then(()=>(C8t(),uct)),r=t().GitGraph.parser.LangiumParser;Is.gitGraph=r},"gitGraph"),eventmodeling:L(async()=>{let{createEventModelingServices:t}=await Promise.resolve().then(()=>(w8t(),hct)),r=t().EventModel.parser.LangiumParser;Is.eventmodeling=r},"eventmodeling"),radar:L(async()=>{let{createRadarServices:t}=await Promise.resolve().then(()=>(E8t(),dct)),r=t().Radar.parser.LangiumParser;Is.radar=r},"radar"),railroad:L(async()=>{let{createRailroadServices:t}=await Promise.resolve().then(()=>(S8t(),pct)),r=t().Railroad.parser.LangiumParser;Is.railroad=r},"railroad"),railroadEbnf:L(async()=>{let{createRailroadEbnfServices:t}=await Promise.resolve().then(()=>(_8t(),fct)),r=t().RailroadEbnf.parser.LangiumParser;Is.railroadEbnf=r},"railroadEbnf"),railroadAbnf:L(async()=>{let{createRailroadAbnfServices:t}=await Promise.resolve().then(()=>(A8t(),gct)),r=t().RailroadAbnf.parser.LangiumParser;Is.railroadAbnf=r},"railroadAbnf"),railroadPeg:L(async()=>{let{createRailroadPegServices:t}=await Promise.resolve().then(()=>(R8t(),mct)),r=t().RailroadPeg.parser.LangiumParser;Is.railroadPeg=r},"railroadPeg"),treemap:L(async()=>{let{createTreemapServices:t}=await Promise.resolve().then(()=>($8t(),yct)),r=t().Treemap.parser.LangiumParser;Is.treemap=r},"treemap"),wardley:L(async()=>{let{createWardleyServices:t}=await Promise.resolve().then(()=>(L8t(),vct)),r=t().Wardley.parser.LangiumParser;Is.wardley=r},"wardley"),cynefin:L(async()=>{let{createCynefinServices:t}=await Promise.resolve().then(()=>(I8t(),bct)),r=t().Cynefin.parser.LangiumParser;Is.cynefin=r},"cynefin")},s(Pl,"parse"),L(Pl,"parse"),Fb=(e=class extends Error{constructor(r){let i=r.lexerErrors.map(a=>{let o=a.line!==void 0&&!isNaN(a.line)?a.line:"?",l=a.column!==void 0&&!isNaN(a.column)?a.column:"?";return`Lexer error on line ${o}, column ${l}: ${a.message}`}).join(` +`),n=r.parserErrors.map(a=>{let o=a.token.startLine!==void 0&&!isNaN(a.token.startLine)?a.token.startLine:"?",l=a.token.startColumn!==void 0&&!isNaN(a.token.startColumn)?a.token.startColumn:"?";return`Parse error on line ${o}, column ${l}: ${a.message}`}).join(` +`);super(`Parsing failed: ${i} ${n}`),this.result=r}},s(e,"MermaidParseError"),L(e,"MermaidParseError"),e)});function Yo(e,t){e.accDescr&&t.setAccDescription?.(e.accDescr),e.accTitle&&t.setAccTitle?.(e.accTitle),e.title&&t.setDiagramTitle?.(e.title)}var vl=G(()=>{"use strict";s(Yo,"populateCommonDb")}),cn,phe=G(()=>{"use strict";cn={NORMAL:0,REVERSE:1,HIGHLIGHT:2,MERGE:3,CHERRY_PICK:4}}),H9,fhe=G(()=>{"use strict";var e;H9=(e=class{constructor(r){this.init=r,this.records=this.init()}reset(){this.records=this.init()}},s(e,"ImperativeState"),e)});function G9(){return UF({length:7})}function Tct(e,t){let r=Object.create(null);return e.reduce((i,n)=>{let a=t(n);return r[a]||(r[a]=!0,i.push(n)),i},[])}function ghe(e,t,r){let i=e.indexOf(t);i===-1?e.push(r):e.splice(i,1,r)}function mhe(e){let t=e.reduce((n,a)=>n.seq>a.seq?n:a,e[0]),r="";e.forEach(function(n){n===t?r+=" *":r+=" |"});let i=[r,t.id,t.seq];for(let n in ar.records.branches)ar.records.branches.get(n)===t.id&&i.push(n);if(Ee.debug(i.join(" ")),t.parents&&t.parents.length==2&&t.parents[0]&&t.parents[1]){let n=ar.records.commits.get(t.parents[0]);ghe(e,t,n),t.parents[1]&&e.push(ar.records.commits.get(t.parents[1]))}else{if(t.parents.length==0)return;if(t.parents[0]){let n=ar.records.commits.get(t.parents[0]);ghe(e,t,n)}}e=Tct(e,n=>n.id),mhe(e)}var kct,k0,ar,Cct,wct,Ect,Sct,_ct,Act,Rct,yhe,$ct,Lct,Ict,Mct,Dct,vhe,Nct,Oct,Bct,bhe,Pct=G(()=>{"use strict";Nt(),Ar(),Gr(),mi(),fn(),phe(),fhe(),Za(),kct=vi.gitGraph,k0=s(()=>Gn({...kct,...Rr().gitGraph}),"getConfig"),ar=new H9(()=>{let e=k0(),t=e.mainBranchName,r=e.mainBranchOrder;return{mainBranchName:t,commits:new Map,head:null,branchConfig:new Map([[t,{name:t,order:r}]]),branches:new Map([[t,null]]),currBranch:t,direction:"LR",seq:0,options:{}}}),s(G9,"getID"),s(Tct,"uniqBy"),Cct=s(function(e){ar.records.direction=e},"setDirection"),wct=s(function(e){Ee.debug("options str",e),e=e?.trim(),e=e||"{}";try{ar.records.options=JSON.parse(e)}catch(t){Ee.error("error while parsing gitGraph options",t.message)}},"setOptions"),Ect=s(function(){return ar.records.options},"getOptions"),Sct=s(function(e){let t=e.msg,r=e.id,i=e.type,n=e.tags;Ee.info("commit",t,r,i,n),Ee.debug("Entering commit:",t,r,i,n);let a=k0();r=Vt.sanitizeText(r,a),t=Vt.sanitizeText(t,a),n=n?.map(l=>Vt.sanitizeText(l,a));let o={id:r||ar.records.seq+"-"+G9(),message:t,seq:ar.records.seq++,type:i??cn.NORMAL,tags:n??[],parents:ar.records.head==null?[]:[ar.records.head.id],branch:ar.records.currBranch};ar.records.head=o,Ee.info("main branch",a.mainBranchName),ar.records.commits.has(o.id)&&Ee.warn(`Commit ID ${o.id} already exists`),ar.records.commits.set(o.id,o),ar.records.branches.set(ar.records.currBranch,o.id),Ee.debug("in pushCommit "+o.id)},"commit"),_ct=s(function(e){let t=e.name,r=e.order;if(t=Vt.sanitizeText(t,k0()),ar.records.branches.has(t))throw new Error(`Trying to create an existing branch. (Help: Either use a new name if you want create a new branch or try using "checkout ${t}")`);ar.records.branches.set(t,ar.records.head!=null?ar.records.head.id:null),ar.records.branchConfig.set(t,{name:t,order:r}),yhe(t),Ee.debug("in createBranch")},"branch"),Act=s(e=>{let t=e.branch,r=e.id,i=e.type,n=e.tags,a=k0();t=Vt.sanitizeText(t,a),r&&(r=Vt.sanitizeText(r,a));let o=ar.records.branches.get(ar.records.currBranch),l=ar.records.branches.get(t),u=o?ar.records.commits.get(o):void 0,h=l?ar.records.commits.get(l):void 0;if(u&&h&&u.branch===t)throw new Error(`Cannot merge branch '${t}' into itself.`);if(ar.records.currBranch===t){let f=new Error('Incorrect usage of "merge". Cannot merge a branch to itself');throw f.hash={text:`merge ${t}`,token:`merge ${t}`,expected:["branch abc"]},f}if(u===void 0||!u){let f=new Error(`Incorrect usage of "merge". Current branch (${ar.records.currBranch})has no commits`);throw f.hash={text:`merge ${t}`,token:`merge ${t}`,expected:["commit"]},f}if(!ar.records.branches.has(t)){let f=new Error('Incorrect usage of "merge". Branch to be merged ('+t+") does not exist");throw f.hash={text:`merge ${t}`,token:`merge ${t}`,expected:[`branch ${t}`]},f}if(h===void 0||!h){let f=new Error('Incorrect usage of "merge". Branch to be merged ('+t+") has no commits");throw f.hash={text:`merge ${t}`,token:`merge ${t}`,expected:['"commit"']},f}if(u===h){let f=new Error('Incorrect usage of "merge". Both branches have same head');throw f.hash={text:`merge ${t}`,token:`merge ${t}`,expected:["branch abc"]},f}if(r&&ar.records.commits.has(r)){let f=new Error('Incorrect usage of "merge". Commit with id:'+r+" already exists, use different custom id");throw f.hash={text:`merge ${t} ${r} ${i} ${n?.join(" ")}`,token:`merge ${t} ${r} ${i} ${n?.join(" ")}`,expected:[`merge ${t} ${r}_UNIQUE ${i} ${n?.join(" ")}`]},f}let d=l||"",p={id:r||`${ar.records.seq}-${G9()}`,message:`merged branch ${t} into ${ar.records.currBranch}`,seq:ar.records.seq++,parents:ar.records.head==null?[]:[ar.records.head.id,d],branch:ar.records.currBranch,type:cn.MERGE,customType:i,customId:!!r,tags:n??[]};ar.records.head=p,ar.records.commits.set(p.id,p),ar.records.branches.set(ar.records.currBranch,p.id),Ee.debug(ar.records.branches),Ee.debug("in mergeBranch")},"merge"),Rct=s(function(e){let t=e.id,r=e.targetId,i=e.tags,n=e.parent;Ee.debug("Entering cherryPick:",t,r,i);let a=k0();if(t=Vt.sanitizeText(t,a),r=Vt.sanitizeText(r,a),i=i?.map(u=>Vt.sanitizeText(u,a)),n=Vt.sanitizeText(n,a),!t||!ar.records.commits.has(t)){let u=new Error('Incorrect usage of "cherryPick". Source commit id should exist and provided');throw u.hash={text:`cherryPick ${t} ${r}`,token:`cherryPick ${t} ${r}`,expected:["cherry-pick abc"]},u}let o=ar.records.commits.get(t);if(o===void 0||!o)throw new Error('Incorrect usage of "cherryPick". Source commit id should exist and provided');if(n&&!(Array.isArray(o.parents)&&o.parents.includes(n)))throw new Error("Invalid operation: The specified parent commit is not an immediate parent of the cherry-picked commit.");let l=o.branch;if(o.type===cn.MERGE&&!n)throw new Error("Incorrect usage of cherry-pick: If the source commit is a merge commit, an immediate parent commit must be specified.");if(!r||!ar.records.commits.has(r)){if(l===ar.records.currBranch){let p=new Error('Incorrect usage of "cherryPick". Source commit is already on current branch');throw p.hash={text:`cherryPick ${t} ${r}`,token:`cherryPick ${t} ${r}`,expected:["cherry-pick abc"]},p}let u=ar.records.branches.get(ar.records.currBranch);if(u===void 0||!u){let p=new Error(`Incorrect usage of "cherry-pick". Current branch (${ar.records.currBranch})has no commits`);throw p.hash={text:`cherryPick ${t} ${r}`,token:`cherryPick ${t} ${r}`,expected:["cherry-pick abc"]},p}let h=ar.records.commits.get(u);if(h===void 0||!h){let p=new Error(`Incorrect usage of "cherry-pick". Current branch (${ar.records.currBranch})has no commits`);throw p.hash={text:`cherryPick ${t} ${r}`,token:`cherryPick ${t} ${r}`,expected:["cherry-pick abc"]},p}let d={id:ar.records.seq+"-"+G9(),message:`cherry-picked ${o?.message} into ${ar.records.currBranch}`,seq:ar.records.seq++,parents:ar.records.head==null?[]:[ar.records.head.id,o.id],branch:ar.records.currBranch,type:cn.CHERRY_PICK,tags:i?i.filter(Boolean):[`cherry-pick:${o.id}${o.type===cn.MERGE?`|parent:${n}`:""}`]};ar.records.head=d,ar.records.commits.set(d.id,d),ar.records.branches.set(ar.records.currBranch,d.id),Ee.debug(ar.records.branches),Ee.debug("in cherryPick")}},"cherryPick"),yhe=s(function(e){if(e=Vt.sanitizeText(e,k0()),ar.records.branches.has(e)){ar.records.currBranch=e;let t=ar.records.branches.get(ar.records.currBranch);t===void 0||!t?ar.records.head=null:ar.records.head=ar.records.commits.get(t)??null}else{let t=new Error(`Trying to checkout branch which is not yet created. (Help try using "branch ${e}")`);throw t.hash={text:`checkout ${e}`,token:`checkout ${e}`,expected:[`branch ${e}`]},t}},"checkout"),s(ghe,"upsert"),s(mhe,"prettyPrintCommitHistory"),$ct=s(function(){Ee.debug(ar.records.commits);let e=vhe()[0];mhe([e])},"prettyPrint"),Lct=s(function(){ar.reset(),pn()},"clear"),Ict=s(function(){return[...ar.records.branchConfig.values()].map((e,t)=>e.order!==null&&e.order!==void 0?e:{...e,order:parseFloat(`0.${t}`)}).sort((e,t)=>(e.order??0)-(t.order??0)).map(({name:e})=>({name:e}))},"getBranchesAsObjArray"),Mct=s(function(){return ar.records.branches},"getBranches"),Dct=s(function(){return ar.records.commits},"getCommits"),vhe=s(function(){let e=[...ar.records.commits.values()];return e.forEach(function(t){Ee.debug(t.id)}),e.sort((t,r)=>t.seq-r.seq),e},"getCommitsArray"),Nct=s(function(){return ar.records.currBranch},"getCurrentBranch"),Oct=s(function(){return ar.records.direction},"getDirection"),Bct=s(function(){return ar.records.head},"getHead"),bhe={commitType:cn,getConfig:k0,setDirection:Cct,setOptions:wct,getOptions:Ect,commit:Sct,branch:_ct,merge:Act,cherryPick:Rct,checkout:yhe,prettyPrint:$ct,clear:Lct,getBranchesAsObjArray:Ict,getBranches:Mct,getCommits:Dct,getCommitsArray:vhe,getCurrentBranch:Nct,getDirection:Oct,getHead:Bct,setAccTitle:bn,getAccTitle:Ln,getAccDescription:Mn,setAccDescription:In,setDiagramTitle:Dn,getDiagramTitle:xn}}),Fct,zct,qct,Wct,jct,Vct,Uct,Yct,M8t=G(()=>{"use strict";Co(),Nt(),vl(),Pct(),phe(),Fct=s((e,t)=>{Yo(e,t),e.dir&&t.setDirection(e.dir);for(let r of e.statements)zct(r,t)},"populate"),zct=s((e,t)=>{let r={Commit:s(i=>t.commit(qct(i)),"Commit"),Branch:s(i=>t.branch(Wct(i)),"Branch"),Merge:s(i=>t.merge(jct(i)),"Merge"),Checkout:s(i=>t.checkout(Vct(i)),"Checkout"),CherryPicking:s(i=>t.cherryPick(Uct(i)),"CherryPicking")}[e.$type];r?r(e):Ee.error(`Unknown statement type: ${e.$type}`)},"parseStatement"),qct=s(e=>({id:e.id,msg:e.message??"",type:e.type!==void 0?cn[e.type]:cn.NORMAL,tags:e.tags??void 0}),"parseCommit"),Wct=s(e=>({name:e.name,order:e.order??0}),"parseBranch"),jct=s(e=>({branch:e.branch,id:e.id??"",type:e.type!==void 0?cn[e.type]:void 0,tags:e.tags??void 0}),"parseMerge"),Vct=s(e=>e.branch,"parseCheckout"),Uct=s(e=>({id:e.id,targetId:"",tags:e.tags?.length===0?void 0:e.tags,parent:e.parent}),"parseCherryPicking"),Yct={parse:s(async e=>{let t=await Pl("gitGraph",e);Ee.debug(t),Fct(t,bhe)},"parse")}}),ip,np,hu,Rh,C0,v5,X9,K9,Hct,w0,Ho,Go,b5,zb,x5,$h,Ai,Gct,xhe,The,Xct,Kct,Qct,Zct,Jct,eut,tut,rut,iut,nut,aut,sut,khe,out,qb,lut,cut,uut,hut,dut,put,D8t=G(()=>{"use strict";pi(),Er(),Nt(),Ar(),phe(),ip=10,np=40,hu=4,Rh=2,C0=8,v5=new Set(["redux","redux-dark","redux-color","redux-dark-color"]),X9=12,K9=new Set(["redux-color","redux-dark-color"]),Hct=new Set(["dark","redux-dark","redux-dark-color","neo-dark"]),w0=s((e,t,r=!1)=>r&&e>0?(e-1)%(t-1)+1:e%t,"calcColorIndex"),Ho=new Map,Go=new Map,b5=30,zb=new Map,x5=[],$h=0,Ai="LR",Gct=s(()=>{Ho.clear(),Go.clear(),zb.clear(),$h=0,x5=[],Ai="LR"},"clear"),xhe=s(e=>{let t=document.createElementNS("http://www.w3.org/2000/svg","text");return(typeof e=="string"?e.split(/\\n|\n|/gi):e).forEach(r=>{let i=document.createElementNS("http://www.w3.org/2000/svg","tspan");i.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),i.setAttribute("dy","1em"),i.setAttribute("x","0"),i.setAttribute("class","row"),i.textContent=r.trim(),t.appendChild(i)}),t},"drawText"),The=s(e=>{let t,r,i;return Ai==="BT"?(r=s((n,a)=>n<=a,"comparisonFunc"),i=1/0):(r=s((n,a)=>n>=a,"comparisonFunc"),i=0),e.forEach(n=>{let a=Ai==="TB"||Ai=="BT"?Go.get(n)?.y:Go.get(n)?.x;a!==void 0&&r(a,i)&&(t=n,i=a)}),t},"findClosestParent"),Xct=s(e=>{let t="",r=1/0;return e.forEach(i=>{let n=Go.get(i).y;n<=r&&(t=i,r=n)}),t||void 0},"findClosestParentBT"),Kct=s((e,t,r)=>{let i=r,n=r,a=[];e.forEach(o=>{let l=t.get(o);if(!l)throw new Error(`Commit not found for key ${o}`);l.parents.length?(i=Zct(l),n=Math.max(i,n)):a.push(l),Jct(l,i)}),i=n,a.forEach(o=>{eut(o,i,r)}),e.forEach(o=>{let l=t.get(o);if(l?.parents.length){let u=Xct(l.parents);i=Go.get(u).y-np,i<=n&&(n=i);let h=Ho.get(l.branch).pos,d=i-ip;Go.set(l.id,{x:h,y:d})}})},"setParallelBTPos"),Qct=s(e=>{let t=The(e.parents.filter(i=>i!==null));if(!t)throw new Error(`Closest parent not found for commit ${e.id}`);let r=Go.get(t)?.y;if(r===void 0)throw new Error(`Closest parent position not found for commit ${e.id}`);return r},"findClosestParentPos"),Zct=s(e=>Qct(e)+np,"calculateCommitPosition"),Jct=s((e,t)=>{let r=Ho.get(e.branch);if(!r)throw new Error(`Branch not found for commit ${e.id}`);let i=r.pos,n=t+ip;return Go.set(e.id,{x:i,y:n}),{x:i,y:n}},"setCommitPosition"),eut=s((e,t,r)=>{let i=Ho.get(e.branch);if(!i)throw new Error(`Branch not found for commit ${e.id}`);let n=t+r,a=i.pos;Go.set(e.id,{x:a,y:n})},"setRootPosition"),tut=s((e,t,r,i,n,a)=>{let{theme:o}=it(),l=v5.has(o??""),u=K9.has(o??""),h=Hct.has(o??"");if(a===cn.HIGHLIGHT)e.append("rect").attr("x",r.x-10+(l?3:0)).attr("y",r.y-10+(l?3:0)).attr("width",l?14:20).attr("height",l?14:20).attr("class",`commit ${t.id} commit-highlight${w0(n,C0,u)} ${i}-outer`),e.append("rect").attr("x",r.x-6+(l?2:0)).attr("y",r.y-6+(l?2:0)).attr("width",l?8:12).attr("height",l?8:12).attr("class",`commit ${t.id} commit${w0(n,C0,u)} ${i}-inner`);else if(a===cn.CHERRY_PICK)e.append("circle").attr("cx",r.x).attr("cy",r.y).attr("r",l?7:10).attr("class",`commit ${t.id} ${i}`),e.append("circle").attr("cx",r.x-3).attr("cy",r.y+2).attr("r",l?2.5:2.75).attr("fill",h?"#000000":"#fff").attr("class",`commit ${t.id} ${i}`),e.append("circle").attr("cx",r.x+3).attr("cy",r.y+2).attr("r",l?2.5:2.75).attr("fill",h?"#000000":"#fff").attr("class",`commit ${t.id} ${i}`),e.append("line").attr("x1",r.x+3).attr("y1",r.y+1).attr("x2",r.x).attr("y2",r.y-5).attr("stroke",h?"#000000":"#fff").attr("class",`commit ${t.id} ${i}`),e.append("line").attr("x1",r.x-3).attr("y1",r.y+1).attr("x2",r.x).attr("y2",r.y-5).attr("stroke",h?"#000000":"#fff").attr("class",`commit ${t.id} ${i}`);else{let d=e.append("circle");if(d.attr("cx",r.x),d.attr("cy",r.y),d.attr("r",l?7:10),d.attr("class",`commit ${t.id} commit${w0(n,C0,u)}`),a===cn.MERGE){let p=e.append("circle");p.attr("cx",r.x),p.attr("cy",r.y),p.attr("r",l?5:6),p.attr("class",`commit ${i} ${t.id} commit${w0(n,C0,u)}`)}if(a===cn.REVERSE){let p=e.append("path"),f=l?4:5;p.attr("d",`M ${r.x-f},${r.y-f}L${r.x+f},${r.y+f}M${r.x-f},${r.y+f}L${r.x+f},${r.y-f}`).attr("class",`commit ${i} ${t.id} commit${w0(n,C0,u)}`)}}},"drawCommitBullet"),rut=s((e,t,r,i,n)=>{if(t.type!==cn.CHERRY_PICK&&(t.customId&&t.type===cn.MERGE||t.type!==cn.MERGE)&&n.showCommitLabel){let a=e.append("g"),o=a.insert("rect").attr("class","commit-label-bkg"),l=a.append("text").attr("x",i).attr("y",r.y+25).attr("class","commit-label").text(t.id),u=l.node()?.getBBox();if(u&&(o.attr("x",r.posWithOffset-u.width/2-Rh).attr("y",r.y+13.5).attr("width",u.width+2*Rh).attr("height",u.height+2*Rh),Ai==="TB"||Ai==="BT"?(o.attr("x",r.x-(u.width+4*hu+5)).attr("y",r.y-12),l.attr("x",r.x-(u.width+4*hu)).attr("y",r.y+u.height-12)):l.attr("x",r.posWithOffset-u.width/2),n.rotateCommitLabel))if(Ai==="TB"||Ai==="BT")l.attr("transform","rotate(-45, "+r.x+", "+r.y+")"),o.attr("transform","rotate(-45, "+r.x+", "+r.y+")");else{let h=-7.5-(u.width+10)/25*9.5,d=10+u.width/25*8.5;a.attr("transform","translate("+h+", "+d+") rotate(-45, "+i+", "+r.y+")")}}},"drawCommitLabel"),iut=s((e,t,r,i)=>{if(t.tags.length>0){let n=0,a=0,o=0,l=[];for(let u of t.tags.reverse()){let h=e.insert("polygon"),d=e.append("circle"),p=e.append("text").attr("y",r.y-16-n).attr("class","tag-label").text(u),f=p.node()?.getBBox();if(!f)throw new Error("Tag bbox not found");a=Math.max(a,f.width),o=Math.max(o,f.height),p.attr("x",r.posWithOffset-f.width/2),l.push({tag:p,hole:d,rect:h,yOffset:n}),n+=20}for(let{tag:u,hole:h,rect:d,yOffset:p}of l){let f=o/2,g=r.y-19.2-p;if(d.attr("class","tag-label-bkg").attr("points",` + ${i-a/2-hu/2},${g+Rh} + ${i-a/2-hu/2},${g-Rh} + ${r.posWithOffset-a/2-hu},${g-f-Rh} + ${r.posWithOffset+a/2+hu},${g-f-Rh} + ${r.posWithOffset+a/2+hu},${g+f+Rh} + ${r.posWithOffset-a/2-hu},${g+f+Rh}`),h.attr("cy",g).attr("cx",i-a/2+hu/2).attr("r",1.5).attr("class","tag-hole"),Ai==="TB"||Ai==="BT"){let m=i+p;d.attr("class","tag-label-bkg").attr("points",` + ${r.x},${m+2} + ${r.x},${m-2} + ${r.x+ip},${m-f-2} + ${r.x+ip+a+4},${m-f-2} + ${r.x+ip+a+4},${m+f+2} + ${r.x+ip},${m+f+2}`).attr("transform","translate(12,12) rotate(45, "+r.x+","+i+")"),h.attr("cx",r.x+hu/2).attr("cy",m).attr("transform","translate(12,12) rotate(45, "+r.x+","+i+")"),u.attr("x",r.x+5).attr("y",m+3).attr("transform","translate(14,14) rotate(45, "+r.x+","+i+")")}}}},"drawCommitTags"),nut=s(e=>{switch(e.customType??e.type){case cn.NORMAL:return"commit-normal";case cn.REVERSE:return"commit-reverse";case cn.HIGHLIGHT:return"commit-highlight";case cn.MERGE:return"commit-merge";case cn.CHERRY_PICK:return"commit-cherry-pick";default:return"commit-normal"}},"getCommitClassType"),aut=s((e,t,r,i)=>{let n={x:0,y:0};if(e.parents.length>0){let a=The(e.parents);if(a){let o=i.get(a)??n;return t==="TB"?o.y+np:t==="BT"?(i.get(e.id)??n).y-np:o.x+np}}else return t==="TB"?b5:t==="BT"?(i.get(e.id)??n).y-np:0;return 0},"calculatePosition"),sut=s((e,t,r)=>{let i=Ai==="BT"&&r?t:t+ip,n=Ho.get(e.branch)?.pos,a=Ai==="TB"||Ai==="BT"?Ho.get(e.branch)?.pos:i;if(a===void 0||n===void 0)throw new Error(`Position were undefined for commit ${e.id}`);let o=v5.has(it().theme??""),l=Ai==="TB"||Ai==="BT"?i:n+(o?X9/2+1:-2);return{x:a,y:l,posWithOffset:i}},"getCommitPosition"),khe=s((e,t,r,i)=>{let n=e.append("g").attr("class","commit-bullets"),a=e.append("g").attr("class","commit-labels"),o=Ai==="TB"||Ai==="BT"?b5:0,l=[...t.keys()],u=i.parallelCommits??!1,h=s((p,f)=>{let g=t.get(p)?.seq,m=t.get(f)?.seq;return g!==void 0&&m!==void 0?g-m:0},"sortKeys"),d=l.sort(h);Ai==="BT"&&(u&&Kct(d,t,o),d=d.reverse()),d.forEach(p=>{let f=t.get(p);if(!f)throw new Error(`Commit not found for key ${p}`);u&&(o=aut(f,Ai,o,Go));let g=sut(f,o,u);if(r){let m=nut(f),y=f.customType??f.type,k=Ho.get(f.branch)?.index??0;tut(n,f,g,m,k,y),rut(a,f,g,o,i),iut(a,f,g,o)}Ai==="TB"||Ai==="BT"?Go.set(f.id,{x:g.x,y:g.posWithOffset}):Go.set(f.id,{x:g.posWithOffset,y:g.y}),o=Ai==="BT"&&u?o+np:o+np+ip,o>$h&&($h=o)})},"drawCommits"),out=s((e,t,r,i,n)=>{let a=(Ai==="TB"||Ai==="BT"?r.xu.branch===a,"isOnBranchToGetCurve"),l=s(u=>u.seq>e.seq&&u.seql(u)&&o(u))},"shouldRerouteArrow"),qb=s((e,t,r=0)=>{let i=e+Math.abs(e-t)/2;if(r>5)return i;if(x5.every(a=>Math.abs(a-i)>=10))return x5.push(i),i;let n=Math.abs(e-t);return qb(e,t-n/5,r+1)},"findLane"),lut=s((e,t,r,i)=>{let{theme:n}=it(),a=K9.has(n??""),o=Go.get(t.id),l=Go.get(r.id);if(o===void 0||l===void 0)throw new Error(`Commit positions not found for commits ${t.id} and ${r.id}`);let u=out(t,r,o,l,i),h="",d="",p=0,f=0,g=Ho.get(r.branch)?.index;r.type===cn.MERGE&&t.id!==r.parents[0]&&(g=Ho.get(t.branch)?.index);let m;if(u){h="A 10 10, 0, 0, 0,",d="A 10 10, 0, 0, 1,",p=10,f=10;let y=o.yl.x&&(h="A 20 20, 0, 0, 0,",d="A 20 20, 0, 0, 1,",p=20,f=20,r.type===cn.MERGE&&t.id!==r.parents[0]?m=`M ${o.x} ${o.y} L ${o.x} ${l.y-p} ${d} ${o.x-f} ${l.y} L ${l.x} ${l.y}`:m=`M ${o.x} ${o.y} L ${l.x+p} ${o.y} ${h} ${l.x} ${o.y+f} L ${l.x} ${l.y}`),o.x===l.x&&(m=`M ${o.x} ${o.y} L ${l.x} ${l.y}`)):Ai==="BT"?(o.xl.x&&(h="A 20 20, 0, 0, 0,",d="A 20 20, 0, 0, 1,",p=20,f=20,r.type===cn.MERGE&&t.id!==r.parents[0]?m=`M ${o.x} ${o.y} L ${o.x} ${l.y+p} ${h} ${o.x-f} ${l.y} L ${l.x} ${l.y}`:m=`M ${o.x} ${o.y} L ${l.x+p} ${o.y} ${d} ${l.x} ${o.y-f} L ${l.x} ${l.y}`),o.x===l.x&&(m=`M ${o.x} ${o.y} L ${l.x} ${l.y}`)):(o.yl.y&&(r.type===cn.MERGE&&t.id!==r.parents[0]?m=`M ${o.x} ${o.y} L ${l.x-p} ${o.y} ${h} ${l.x} ${o.y-f} L ${l.x} ${l.y}`:m=`M ${o.x} ${o.y} L ${o.x} ${l.y+p} ${d} ${o.x+f} ${l.y} L ${l.x} ${l.y}`),o.y===l.y&&(m=`M ${o.x} ${o.y} L ${l.x} ${l.y}`));if(m===void 0)throw new Error("Line definition not found");e.append("path").attr("d",m).attr("class","arrow arrow"+w0(g,C0,a))},"drawArrow"),cut=s((e,t)=>{let r=e.append("g").attr("class","commit-arrows");[...t.keys()].forEach(i=>{let n=t.get(i);n.parents&&n.parents.length>0&&n.parents.forEach(a=>{lut(r,t.get(a),n,t)})})},"drawArrows"),uut=s((e,t,r,i)=>{let{look:n,theme:a,themeVariables:o}=it(),{dropShadow:l,THEME_COLOR_LIMIT:u}=o,h=v5.has(a??""),d=K9.has(a??""),p=e.append("g");t.forEach((f,g)=>{let m=w0(g,h?u:C0,d),y=Ho.get(f.name)?.pos;if(y===void 0)throw new Error(`Position not found for branch ${f.name}`);let k=Ai==="TB"||Ai==="BT"?y:h?y+X9/2+1:y-2,C=p.append("line");C.attr("x1",0),C.attr("y1",k),C.attr("x2",$h),C.attr("y2",k),C.attr("class","branch branch"+m),Ai==="TB"?(C.attr("y1",b5),C.attr("x1",y),C.attr("y2",$h),C.attr("x2",y)):Ai==="BT"&&(C.attr("y1",$h),C.attr("x1",y),C.attr("y2",b5),C.attr("x2",y)),x5.push(k);let S=f.name,E=xhe(S),_=p.insert("rect"),b=p.insert("g").attr("class","branchLabel").insert("g").attr("class","label branch-label"+m);b.node().appendChild(E);let v=E.getBBox(),x=h?0:4,w=h?16:0,R=h?X9:0;n==="neo"&&_.attr("data-look","neo"),_.attr("class","branchLabelBkg label"+m).attr("style",n==="neo"?`filter:${h?`url(#${i}-drop-shadow)`:l}`:"").attr("rx",x).attr("ry",x).attr("x",-v.width-4-(r.rotateCommitLabel===!0?30:0)).attr("y",-v.height/2+10).attr("width",v.width+18+w).attr("height",v.height+4+R),b.attr("transform","translate("+(-v.width-14-(r.rotateCommitLabel===!0?30:0)+w/2)+", "+(k-v.height/2-2)+")"),Ai==="TB"?(_.attr("x",y-v.width/2-10).attr("y",0),b.attr("transform","translate("+(y-v.width/2-5)+", 0)"),h&&(_.attr("transform",`translate(${-w/2-3}, ${-R-10})`),b.attr("transform","translate("+(y-v.width/2-5)+", "+(-R*2+7)+")"))):Ai==="BT"?(_.attr("x",y-v.width/2-10).attr("y",$h),b.attr("transform","translate("+(y-v.width/2-5)+", "+$h+")"),h&&(_.attr("transform",`translate(${-w/2-3}, ${R+10})`),b.attr("transform","translate("+(y-v.width/2-5)+", "+($h+R*2+4)+")"))):_.attr("transform","translate(-19, "+(k-12-R/2)+")")})},"drawBranches"),hut=s(function(e,t,r,i,n){return Ho.set(e,{pos:t,index:r}),t+=50+(n?40:0)+(Ai==="TB"||Ai==="BT"?i.width/2:0),t},"setBranchPosition"),dut=s(function(e,t,r,i){Gct(),Ee.debug("in gitgraph renderer",e+` +`,"id:",t,r);let n=i.db;if(!n.getConfig){Ee.error("getConfig method is not available on db");return}let a=n.getConfig(),o=a.rotateCommitLabel??!1;zb=n.getCommits();let l=n.getBranchesAsObjArray();Ai=n.getDirection();let u=zt(`[id="${t}"]`),{look:h,theme:d,themeVariables:p}=it(),{useGradient:f,gradientStart:g,gradientStop:m,filterColor:y}=p;if(f){let C=u.append("defs").append("linearGradient").attr("id",t+"-gradient").attr("gradientUnits","objectBoundingBox").attr("x1","0%").attr("y1","0%").attr("x2","100%").attr("y2","0%");C.append("stop").attr("offset","0%").attr("stop-color",g).attr("stop-opacity",1),C.append("stop").attr("offset","100%").attr("stop-color",m).attr("stop-opacity",1)}h==="neo"&&v5.has(d??"")&&u.append("defs").append("filter").attr("id",t+"-drop-shadow").attr("height","130%").attr("width","130%").append("feDropShadow").attr("dx","4").attr("dy","4").attr("stdDeviation",0).attr("flood-opacity","0.06").attr("flood-color",y);let k=0;l.forEach((C,S)=>{let E=xhe(C.name),_=u.append("g"),b=_.insert("g").attr("class","branchLabel"),v=b.insert("g").attr("class","label branch-label");v.node()?.appendChild(E);let x=E.getBBox();k=hut(C.name,k,S,x,o),v.remove(),b.remove(),_.remove()}),khe(u,zb,!1,a),a.showBranches&&uut(u,l,a,t),cut(u,zb),khe(u,zb,!0,a),ai.insertTitle(u,"gitTitleText",a.titleTopMargin??0,n.getDiagramTitle()),NS(void 0,u,a.diagramPadding,a.useMaxWidth)},"draw"),put={draw:dut}}),Che,whe,fut,gut,mut,yut,vut,but,xut,Tut,kut,N8t=G(()=>{"use strict";Gr(),Che=8,whe=new Set(["redux","redux-dark","redux-color","redux-dark-color"]),fut=new Set(["redux-color","redux-dark-color"]),gut=new Set(["neo","neo-dark"]),mut=new Set(["dark","redux-dark","redux-dark-color","neo-dark"]),yut=new Set(["redux","redux-dark","redux-color","redux-dark-color","neo","neo-dark"]),vut=s(e=>{let{svgId:t}=e,r="";if(e.useGradient&&t)for(let i=0;i{let t=Rr(),{theme:r,themeVariables:i}=t,{borderColorArray:n}=i,a=whe.has(r);if(gut.has(r)){let o="";for(let l=0;l`${Array.from({length:e.THEME_COLOR_LIMIT},(t,r)=>r).map(t=>{let r=t%Che;return` + .branch-label${t} { fill: ${e["gitBranchLabel"+r]}; } + .commit${t} { stroke: ${e["git"+r]}; fill: ${e["git"+r]}; } + .commit-highlight${t} { stroke: ${e["gitInv"+r]}; fill: ${e["gitInv"+r]}; } + .label${t} { fill: ${e["git"+r]}; } + .arrow${t} { stroke: ${e["git"+r]}; } + `}).join(` +`)}`,"normalTheme"),Tut=s(e=>{let t=Rr(),{theme:r}=t,i=yut.has(r);return` + .commit-id, + .commit-msg, + .branch-label { + fill: lightgrey; + color: lightgrey; + font-family: 'trebuchet ms', verdana, arial, sans-serif; + font-family: var(--mermaid-font-family); + } + + ${i?but(e):xut(e)} + + .branch { + stroke-width: ${e.strokeWidth}; + stroke: ${e.commitLineColor??e.lineColor}; + stroke-dasharray: ${i?"4 2":"2"}; + } + .commit-label { font-size: ${e.commitLabelFontSize}; fill: ${i?e.nodeBorder:e.commitLabelColor}; ${i?`font-weight:${e.noteFontWeight};`:""}} + .commit-label-bkg { font-size: ${e.commitLabelFontSize}; fill: ${i?"transparent":e.commitLabelBackground}; opacity: ${i?"":.5}; } + .tag-label { font-size: ${e.tagLabelFontSize}; fill: ${e.tagLabelColor};} + .tag-label-bkg { fill: ${i?e.mainBkg:e.tagLabelBackground}; stroke: ${i?e.nodeBorder:e.tagLabelBorder}; ${i?`filter:${e.dropShadow}`:""} } + .tag-hole { fill: ${e.textColor}; } + + .commit-merge { + stroke: ${i?e.mainBkg:e.primaryColor}; + fill: ${i?e.mainBkg:e.primaryColor}; + } + .commit-reverse { + stroke: ${i?e.mainBkg:e.primaryColor}; + fill: ${i?e.mainBkg:e.primaryColor}; + stroke-width: ${i?e.strokeWidth:3}; + } + .commit-highlight-outer { + } + .commit-highlight-inner { + stroke: ${i?e.mainBkg:e.primaryColor}; + fill: ${i?e.mainBkg:e.primaryColor}; + } + + .arrow { + /* Intentional: neo themes keep the bold 8px arrow (like classic themes); only redux-geometry themes use the thinner options.strokeWidth. */ + stroke-width: ${whe.has(r)?e.strokeWidth:8}; + stroke-linecap: round; + fill: none + } + .gitTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${e.textColor}; + } +`},"getStyles"),kut=Tut}),Cut={};Mr(Cut,{diagram:()=>wut});var wut,O8t=G(()=>{"use strict";M8t(),Pct(),D8t(),N8t(),wut={parser:Yct,db:bhe,renderer:put,styles:kut}}),Q9,Eut,B8t=G(()=>{"use strict";Q9=(function(){var e=s(function(j,I,z,F){for(z=z||{},F=j.length;F--;z[j[F]]=I);return z},"o"),t=[6,8,10,12,13,14,15,16,17,18,20,21,22,23,24,25,26,27,28,29,30,31,33,35,36,38,40],r=[1,26],i=[1,27],n=[1,28],a=[1,29],o=[1,30],l=[1,31],u=[1,32],h=[1,33],d=[1,34],p=[1,9],f=[1,10],g=[1,11],m=[1,12],y=[1,13],k=[1,14],C=[1,15],S=[1,16],E=[1,19],_=[1,20],b=[1,21],v=[1,22],x=[1,23],w=[1,25],R=[1,35],$={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,gantt:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NL:10,weekday:11,weekday_monday:12,weekday_tuesday:13,weekday_wednesday:14,weekday_thursday:15,weekday_friday:16,weekday_saturday:17,weekday_sunday:18,weekend:19,weekend_friday:20,weekend_saturday:21,dateFormat:22,inclusiveEndDates:23,topAxis:24,axisFormat:25,tickInterval:26,excludes:27,includes:28,todayMarker:29,title:30,acc_title:31,acc_title_value:32,acc_descr:33,acc_descr_value:34,acc_descr_multiline_value:35,section:36,clickStatement:37,taskTxt:38,taskData:39,click:40,callbackname:41,callbackargs:42,href:43,clickStatementDebug:44,$accept:0,$end:1},terminals_:{2:"error",4:"gantt",6:"EOF",8:"SPACE",10:"NL",12:"weekday_monday",13:"weekday_tuesday",14:"weekday_wednesday",15:"weekday_thursday",16:"weekday_friday",17:"weekday_saturday",18:"weekday_sunday",20:"weekend_friday",21:"weekend_saturday",22:"dateFormat",23:"inclusiveEndDates",24:"topAxis",25:"axisFormat",26:"tickInterval",27:"excludes",28:"includes",29:"todayMarker",30:"title",31:"acc_title",32:"acc_title_value",33:"acc_descr",34:"acc_descr_value",35:"acc_descr_multiline_value",36:"section",38:"taskTxt",39:"taskData",40:"click",41:"callbackname",42:"callbackargs",43:"href"},productions_:[0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[11,1],[11,1],[11,1],[11,1],[11,1],[11,1],[11,1],[19,1],[19,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,2],[9,2],[9,1],[9,1],[9,1],[9,2],[37,2],[37,3],[37,3],[37,4],[37,3],[37,4],[37,2],[44,2],[44,3],[44,3],[44,4],[44,3],[44,4],[44,2]],performAction:s(function(j,I,z,F,O,B,Y){var U=B.length-1;switch(O){case 1:return B[U-1];case 2:this.$=[];break;case 3:B[U-1].push(B[U]),this.$=B[U-1];break;case 4:case 5:this.$=B[U];break;case 6:case 7:this.$=[];break;case 8:F.setWeekday("monday");break;case 9:F.setWeekday("tuesday");break;case 10:F.setWeekday("wednesday");break;case 11:F.setWeekday("thursday");break;case 12:F.setWeekday("friday");break;case 13:F.setWeekday("saturday");break;case 14:F.setWeekday("sunday");break;case 15:F.setWeekend("friday");break;case 16:F.setWeekend("saturday");break;case 17:F.setDateFormat(B[U].substr(11)),this.$=B[U].substr(11);break;case 18:F.enableInclusiveEndDates(),this.$=B[U].substr(18);break;case 19:F.TopAxis(),this.$=B[U].substr(8);break;case 20:F.setAxisFormat(B[U].substr(11)),this.$=B[U].substr(11);break;case 21:F.setTickInterval(B[U].substr(13)),this.$=B[U].substr(13);break;case 22:F.setExcludes(B[U].substr(9)),this.$=B[U].substr(9);break;case 23:F.setIncludes(B[U].substr(9)),this.$=B[U].substr(9);break;case 24:F.setTodayMarker(B[U].substr(12)),this.$=B[U].substr(12);break;case 27:F.setDiagramTitle(B[U].substr(6)),this.$=B[U].substr(6);break;case 28:this.$=B[U].trim(),F.setAccTitle(this.$);break;case 29:case 30:this.$=B[U].trim(),F.setAccDescription(this.$);break;case 31:F.addSection(B[U].substr(8)),this.$=B[U].substr(8);break;case 33:F.addTask(B[U-1],B[U]),this.$="task";break;case 34:this.$=B[U-1],F.setClickEvent(B[U-1],B[U],null);break;case 35:this.$=B[U-2],F.setClickEvent(B[U-2],B[U-1],B[U]);break;case 36:this.$=B[U-2],F.setClickEvent(B[U-2],B[U-1],null),F.setLink(B[U-2],B[U]);break;case 37:this.$=B[U-3],F.setClickEvent(B[U-3],B[U-2],B[U-1]),F.setLink(B[U-3],B[U]);break;case 38:this.$=B[U-2],F.setClickEvent(B[U-2],B[U],null),F.setLink(B[U-2],B[U-1]);break;case 39:this.$=B[U-3],F.setClickEvent(B[U-3],B[U-1],B[U]),F.setLink(B[U-3],B[U-2]);break;case 40:this.$=B[U-1],F.setLink(B[U-1],B[U]);break;case 41:case 47:this.$=B[U-1]+" "+B[U];break;case 42:case 43:case 45:this.$=B[U-2]+" "+B[U-1]+" "+B[U];break;case 44:case 46:this.$=B[U-3]+" "+B[U-2]+" "+B[U-1]+" "+B[U];break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},e(t,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:17,12:r,13:i,14:n,15:a,16:o,17:l,18:u,19:18,20:h,21:d,22:p,23:f,24:g,25:m,26:y,27:k,28:C,29:S,30:E,31:_,33:b,35:v,36:x,37:24,38:w,40:R},e(t,[2,7],{1:[2,1]}),e(t,[2,3]),{9:36,11:17,12:r,13:i,14:n,15:a,16:o,17:l,18:u,19:18,20:h,21:d,22:p,23:f,24:g,25:m,26:y,27:k,28:C,29:S,30:E,31:_,33:b,35:v,36:x,37:24,38:w,40:R},e(t,[2,5]),e(t,[2,6]),e(t,[2,17]),e(t,[2,18]),e(t,[2,19]),e(t,[2,20]),e(t,[2,21]),e(t,[2,22]),e(t,[2,23]),e(t,[2,24]),e(t,[2,25]),e(t,[2,26]),e(t,[2,27]),{32:[1,37]},{34:[1,38]},e(t,[2,30]),e(t,[2,31]),e(t,[2,32]),{39:[1,39]},e(t,[2,8]),e(t,[2,9]),e(t,[2,10]),e(t,[2,11]),e(t,[2,12]),e(t,[2,13]),e(t,[2,14]),e(t,[2,15]),e(t,[2,16]),{41:[1,40],43:[1,41]},e(t,[2,4]),e(t,[2,28]),e(t,[2,29]),e(t,[2,33]),e(t,[2,34],{42:[1,42],43:[1,43]}),e(t,[2,40],{41:[1,44]}),e(t,[2,35],{43:[1,45]}),e(t,[2,36]),e(t,[2,38],{42:[1,46]}),e(t,[2,37]),e(t,[2,39])],defaultActions:{},parseError:s(function(j,I){if(I.recoverable)this.trace(j);else{var z=new Error(j);throw z.hash=I,z}},"parseError"),parse:s(function(j){var I=this,z=[0],F=[],O=[null],B=[],Y=this.table,U="",H=0,J=0,te=0,ie=2,ue=1,se=B.slice.call(arguments,1),le=Object.create(this.lexer),Te={yy:{}};for(var oe in this.yy)Object.prototype.hasOwnProperty.call(this.yy,oe)&&(Te.yy[oe]=this.yy[oe]);le.setInput(j,Te.yy),Te.yy.lexer=le,Te.yy.parser=this,typeof le.yylloc>"u"&&(le.yylloc={});var fe=le.yylloc;B.push(fe);var ge=le.options&&le.options.ranges;typeof Te.yy.parseError=="function"?this.parseError=Te.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ke(pe){z.length=z.length-2*pe,O.length=O.length-pe,B.length=B.length-pe}s(ke,"popStack");function Ne(){var pe;return pe=F.pop()||le.lex()||ue,typeof pe!="number"&&(pe instanceof Array&&(F=pe,pe=F.pop()),pe=I.symbols_[pe]||pe),pe}s(Ne,"lex");for(var xe,$e,Me,He,Ge,ze,Ve={},et,he,ye,be;;){if(Me=z[z.length-1],this.defaultActions[Me]?He=this.defaultActions[Me]:((xe===null||typeof xe>"u")&&(xe=Ne()),He=Y[Me]&&Y[Me][xe]),typeof He>"u"||!He.length||!He[0]){var Ie="";be=[];for(et in Y[Me])this.terminals_[et]&&et>ie&&be.push("'"+this.terminals_[et]+"'");le.showPosition?Ie="Parse error on line "+(H+1)+`: +`+le.showPosition()+` +Expecting `+be.join(", ")+", got '"+(this.terminals_[xe]||xe)+"'":Ie="Parse error on line "+(H+1)+": Unexpected "+(xe==ue?"end of input":"'"+(this.terminals_[xe]||xe)+"'"),this.parseError(Ie,{text:le.match,token:this.terminals_[xe]||xe,line:le.yylineno,loc:fe,expected:be})}if(He[0]instanceof Array&&He.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Me+", token: "+xe);switch(He[0]){case 1:z.push(xe),O.push(le.yytext),B.push(le.yylloc),z.push(He[1]),xe=null,$e?(xe=$e,$e=null):(J=le.yyleng,U=le.yytext,H=le.yylineno,fe=le.yylloc,te>0&&te--);break;case 2:if(he=this.productions_[He[1]][1],Ve.$=O[O.length-he],Ve._$={first_line:B[B.length-(he||1)].first_line,last_line:B[B.length-1].last_line,first_column:B[B.length-(he||1)].first_column,last_column:B[B.length-1].last_column},ge&&(Ve._$.range=[B[B.length-(he||1)].range[0],B[B.length-1].range[1]]),ze=this.performAction.apply(Ve,[U,J,H,Te.yy,He[1],O,B].concat(se)),typeof ze<"u")return ze;he&&(z=z.slice(0,-1*he*2),O=O.slice(0,-1*he),B=B.slice(0,-1*he)),z.push(this.productions_[He[1]][0]),O.push(Ve.$),B.push(Ve._$),ye=Y[z[z.length-2]][z[z.length-1]],z.push(ye);break;case 3:return!0}}return!0},"parse")},M=(function(){var j={EOF:1,parseError:s(function(I,z){if(this.yy.parser)this.yy.parser.parseError(I,z);else throw new Error(I)},"parseError"),setInput:s(function(I,z){return this.yy=z||this.yy||{},this._input=I,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var I=this._input[0];this.yytext+=I,this.yyleng++,this.offset++,this.match+=I,this.matched+=I;var z=I.match(/(?:\r\n?|\n).*/g);return z?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),I},"input"),unput:s(function(I){var z=I.length,F=I.split(/(?:\r\n?|\n)/g);this._input=I+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-z),this.offset-=z;var O=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),F.length-1&&(this.yylineno-=F.length-1);var B=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:F?(F.length===O.length?this.yylloc.first_column:0)+O[O.length-F.length].length-F[0].length:this.yylloc.first_column-z},this.options.ranges&&(this.yylloc.range=[B[0],B[0]+this.yyleng-z]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(I){this.unput(this.match.slice(I))},"less"),pastInput:s(function(){var I=this.matched.substr(0,this.matched.length-this.match.length);return(I.length>20?"...":"")+I.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var I=this.match;return I.length<20&&(I+=this._input.substr(0,20-I.length)),(I.substr(0,20)+(I.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var I=this.pastInput(),z=new Array(I.length+1).join("-");return I+this.upcomingInput()+` +`+z+"^"},"showPosition"),test_match:s(function(I,z){var F,O,B;if(this.options.backtrack_lexer&&(B={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(B.yylloc.range=this.yylloc.range.slice(0))),O=I[0].match(/(?:\r\n?|\n).*/g),O&&(this.yylineno+=O.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:O?O[O.length-1].length-O[O.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+I[0].length},this.yytext+=I[0],this.match+=I[0],this.matches=I,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(I[0].length),this.matched+=I[0],F=this.performAction.call(this,this.yy,this,z,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),F)return F;if(this._backtrack){for(var Y in B)this[Y]=B[Y];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var I,z,F,O;this._more||(this.yytext="",this.match="");for(var B=this._currentRules(),Y=0;Yz[0].length)){if(z=F,O=Y,this.options.backtrack_lexer){if(I=this.test_match(F,B[Y]),I!==!1)return I;if(this._backtrack){z=!1;continue}else return!1}else if(!this.options.flex)break}return z?(I=this.test_match(z,B[O]),I!==!1?I:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var I=this.next();return I||this.lex()},"lex"),begin:s(function(I){this.conditionStack.push(I)},"begin"),popState:s(function(){var I=this.conditionStack.length-1;return I>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(I){return I=this.conditionStack.length-1-Math.abs(I||0),I>=0?this.conditionStack[I]:"INITIAL"},"topState"),pushState:s(function(I){this.begin(I)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(I,z,F,O){var B=O;switch(F){case 0:return this.begin("open_directive"),"open_directive";case 1:return this.begin("acc_title"),31;case 2:return this.popState(),"acc_title_value";case 3:return this.begin("acc_descr"),33;case 4:return this.popState(),"acc_descr_value";case 5:this.begin("acc_descr_multiline");break;case 6:this.popState();break;case 7:return"acc_descr_multiline_value";case 8:break;case 9:break;case 10:break;case 11:return 10;case 12:break;case 13:break;case 14:this.begin("href");break;case 15:this.popState();break;case 16:return 43;case 17:this.begin("callbackname");break;case 18:this.popState();break;case 19:this.popState(),this.begin("callbackargs");break;case 20:return 41;case 21:this.popState();break;case 22:return 42;case 23:this.begin("click");break;case 24:this.popState();break;case 25:return 40;case 26:return 4;case 27:return 22;case 28:return 23;case 29:return 24;case 30:return 25;case 31:return 26;case 32:return 28;case 33:return 27;case 34:return 29;case 35:return 12;case 36:return 13;case 37:return 14;case 38:return 15;case 39:return 16;case 40:return 17;case 41:return 18;case 42:return 20;case 43:return 21;case 44:return"date";case 45:return 30;case 46:return"accDescription";case 47:return 36;case 48:return 38;case 49:return 39;case 50:return":";case 51:return 6;case 52:return"INVALID"}},"anonymous"),rules:[/^(?:%%\{)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:%%(?!\{)*[^\n]*)/i,/^(?:[^\}]%%*[^\n]*)/i,/^(?:%%*[^\n]*[\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:%[^\n]*)/i,/^(?:href[\s]+["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:call[\s]+)/i,/^(?:\([\s]*\))/i,/^(?:\()/i,/^(?:[^(]*)/i,/^(?:\))/i,/^(?:[^)]*)/i,/^(?:click[\s]+)/i,/^(?:[\s\n])/i,/^(?:[^\s\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:inclusiveEndDates\b)/i,/^(?:topAxis\b)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:tickInterval\s[^#\n;]+)/i,/^(?:includes\s[^#\n;]+)/i,/^(?:excludes\s[^#\n;]+)/i,/^(?:todayMarker\s[^\n;]+)/i,/^(?:weekday\s+monday\b)/i,/^(?:weekday\s+tuesday\b)/i,/^(?:weekday\s+wednesday\b)/i,/^(?:weekday\s+thursday\b)/i,/^(?:weekday\s+friday\b)/i,/^(?:weekday\s+saturday\b)/i,/^(?:weekday\s+sunday\b)/i,/^(?:weekend\s+friday\b)/i,/^(?:weekend\s+saturday\b)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^\n]+)/i,/^(?:accDescription\s[^#\n;]+)/i,/^(?:section\s[^\n]+)/i,/^(?:[^:\n]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{acc_descr_multiline:{rules:[6,7],inclusive:!1},acc_descr:{rules:[4],inclusive:!1},acc_title:{rules:[2],inclusive:!1},callbackargs:{rules:[21,22],inclusive:!1},callbackname:{rules:[18,19,20],inclusive:!1},href:{rules:[15,16],inclusive:!1},click:{rules:[24,25],inclusive:!1},INITIAL:{rules:[0,1,3,5,8,9,10,11,12,13,14,17,23,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52],inclusive:!0}}};return j})();$.lexer=M;function N(){this.yy={}}return s(N,"Parser"),N.prototype=$,$.Parser=N,new N})(),Q9.parser=Q9,Eut=Q9}),P8t=Ro((e,t)=>{"use strict";(function(r,i){typeof e=="object"&&typeof t<"u"?t.exports=i():typeof define=="function"&&define.amd?define(i):(r=typeof globalThis<"u"?globalThis:r||self).dayjs_plugin_isoWeek=i()})(e,(function(){"use strict";var r="day";return function(i,n,a){var o=s(function(h){return h.add(4-h.isoWeekday(),r)},"a"),l=n.prototype;l.isoWeekYear=function(){return o(this).year()},l.isoWeek=function(h){if(!this.$utils().u(h))return this.add(7*(h-this.isoWeek()),r);var d,p,f,g,m=o(this),y=(d=this.isoWeekYear(),p=this.$u,f=(p?a.utc:a)().year(d).startOf("year"),g=4-f.isoWeekday(),f.isoWeekday()>4&&(g+=7),f.add(g,r));return m.diff(y,"week")+1},l.isoWeekday=function(h){return this.$utils().u(h)?this.day()||7:this.day(this.day()%7?h:h-7)};var u=l.startOf;l.startOf=function(h,d){var p=this.$utils(),f=!!p.u(d)||d;return p.p(h)==="isoweek"?f?this.date(this.date()-(this.isoWeekday()-1)).startOf("day"):this.date(this.date()-1-(this.isoWeekday()-1)+7).endOf("day"):u.bind(this)(h,d)}}}))}),F8t=Ro((e,t)=>{"use strict";(function(r,i){typeof e=="object"&&typeof t<"u"?t.exports=i():typeof define=="function"&&define.amd?define(i):(r=typeof globalThis<"u"?globalThis:r||self).dayjs_plugin_customParseFormat=i()})(e,(function(){"use strict";var r={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},i=/(\[[^[]*\])|([-_:/.,()\s]+)|(A|a|Q|YYYY|YY?|ww?|MM?M?M?|Do|DD?|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g,n=/\d/,a=/\d\d/,o=/\d\d?/,l=/\d*[^-_:/,()\s\d]+/,u={},h=s(function(k){return(k=+k)+(k>68?1900:2e3)},"a"),d=s(function(k){return function(C){this[k]=+C}},"f"),p=[/[+-]\d\d:?(\d\d)?|Z/,function(k){(this.zone||(this.zone={})).offset=(function(C){if(!C||C==="Z")return 0;var S=C.match(/([+-]|\d\d)/g),E=60*S[1]+(+S[2]||0);return E===0?0:S[0]==="+"?-E:E})(k)}],f=s(function(k){var C=u[k];return C&&(C.indexOf?C:C.s.concat(C.f))},"u"),g=s(function(k,C){var S,E=u.meridiem;if(E){for(var _=1;_<=24;_+=1)if(k.indexOf(E(_,0,C))>-1){S=_>12;break}}else S=k===(C?"pm":"PM");return S},"d"),m={A:[l,function(k){this.afternoon=g(k,!1)}],a:[l,function(k){this.afternoon=g(k,!0)}],Q:[n,function(k){this.month=3*(k-1)+1}],S:[n,function(k){this.milliseconds=100*+k}],SS:[a,function(k){this.milliseconds=10*+k}],SSS:[/\d{3}/,function(k){this.milliseconds=+k}],s:[o,d("seconds")],ss:[o,d("seconds")],m:[o,d("minutes")],mm:[o,d("minutes")],H:[o,d("hours")],h:[o,d("hours")],HH:[o,d("hours")],hh:[o,d("hours")],D:[o,d("day")],DD:[a,d("day")],Do:[l,function(k){var C=u.ordinal,S=k.match(/\d+/);if(this.day=S[0],C)for(var E=1;E<=31;E+=1)C(E).replace(/\[|\]/g,"")===k&&(this.day=E)}],w:[o,d("week")],ww:[a,d("week")],M:[o,d("month")],MM:[a,d("month")],MMM:[l,function(k){var C=f("months"),S=(f("monthsShort")||C.map((function(E){return E.slice(0,3)}))).indexOf(k)+1;if(S<1)throw new Error;this.month=S%12||S}],MMMM:[l,function(k){var C=f("months").indexOf(k)+1;if(C<1)throw new Error;this.month=C%12||C}],Y:[/[+-]?\d+/,d("year")],YY:[a,function(k){this.year=h(k)}],YYYY:[/\d{4}/,d("year")],Z:p,ZZ:p};function y(k){var C,S;C=k,S=u&&u.formats;for(var E=(k=C.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,(function($,M,N){var j=N&&N.toUpperCase();return M||S[N]||r[N]||S[j].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,(function(I,z,F){return z||F.slice(1)}))}))).match(i),_=E.length,b=0;b<_;b+=1){var v=E[b],x=m[v],w=x&&x[0],R=x&&x[1];E[b]=R?{regex:w,parser:R}:v.replace(/^\[|\]$/g,"")}return function($){for(var M={},N=0,j=0;N<_;N+=1){var I=E[N];if(typeof I=="string")j+=I.length;else{var z=I.regex,F=I.parser,O=$.slice(j),B=z.exec(O)[0];F.call(M,B),$=$.replace(B,"")}}return(function(Y){var U=Y.afternoon;if(U!==void 0){var H=Y.hours;U?H<12&&(Y.hours+=12):H===12&&(Y.hours=0),delete Y.afternoon}})(M),M}}return s(y,"l"),function(k,C,S){S.p.customParseFormat=!0,k&&k.parseTwoDigitYear&&(h=k.parseTwoDigitYear);var E=C.prototype,_=E.parse;E.parse=function(b){var v=b.date,x=b.utc,w=b.args;this.$u=x;var R=w[1];if(typeof R=="string"){var $=w[2]===!0,M=w[3]===!0,N=$||M,j=w[2];M&&(j=w[2]),u=this.$locale(),!$&&j&&(u=S.Ls[j]),this.$d=(function(O,B,Y,U){try{if(["x","X"].indexOf(B)>-1)return new Date((B==="X"?1e3:1)*O);var H=y(B)(O),J=H.year,te=H.month,ie=H.day,ue=H.hours,se=H.minutes,le=H.seconds,Te=H.milliseconds,oe=H.zone,fe=H.week,ge=new Date,ke=ie||(J||te?1:ge.getDate()),Ne=J||ge.getFullYear(),xe=0;J&&!te||(xe=te>0?te-1:ge.getMonth());var $e,Me=ue||0,He=se||0,Ge=le||0,ze=Te||0;return oe?new Date(Date.UTC(Ne,xe,ke,Me,He,Ge,ze+60*oe.offset*1e3)):Y?new Date(Date.UTC(Ne,xe,ke,Me,He,Ge,ze)):($e=new Date(Ne,xe,ke,Me,He,Ge,ze),fe&&($e=U($e).week(fe).toDate()),$e)}catch{return new Date("")}})(v,R,x,S),this.init(),j&&j!==!0&&(this.$L=this.locale(j).$L),N&&v!=this.format(R)&&(this.$d=new Date("")),u={}}else if(R instanceof Array)for(var I=R.length,z=1;z<=I;z+=1){w[1]=R[z-1];var F=S.apply(this,w);if(F.isValid()){this.$d=F.$d,this.$L=F.$L,this.init();break}z===I&&(this.$d=new Date(""))}else _.call(this,b)}}}))}),z8t=Ro((e,t)=>{"use strict";(function(r,i){typeof e=="object"&&typeof t<"u"?t.exports=i():typeof define=="function"&&define.amd?define(i):(r=typeof globalThis<"u"?globalThis:r||self).dayjs_plugin_advancedFormat=i()})(e,(function(){"use strict";return function(r,i){var n=i.prototype,a=n.format;n.format=function(o){var l=this,u=this.$locale();if(!this.isValid())return a.bind(this)(o);var h=this.$utils(),d=(o||"YYYY-MM-DDTHH:mm:ssZ").replace(/\[([^\]]+)]|Q|wo|ww|w|WW|W|zzz|z|gggg|GGGG|Do|X|x|k{1,2}|S/g,(function(p){switch(p){case"Q":return Math.ceil((l.$M+1)/3);case"Do":return u.ordinal(l.$D);case"gggg":return l.weekYear();case"GGGG":return l.isoWeekYear();case"wo":return u.ordinal(l.week(),"W");case"w":case"ww":return h.s(l.week(),p==="w"?1:2,"0");case"W":case"WW":return h.s(l.isoWeek(),p==="W"?1:2,"0");case"k":case"kk":return h.s(String(l.$H===0?24:l.$H),p==="k"?1:2,"0");case"X":return Math.floor(l.$d.getTime()/1e3);case"x":return l.$d.getTime();case"z":return"["+l.offsetName()+"]";case"zzz":return"["+l.offsetName("long")+"]";default:return p}}));return a.bind(this)(d)}}}))});function Ehe(e,t,r){let i=!0;for(;i;)i=!1,r.forEach(function(n){let a="^\\s*"+n+"\\s*$",o=new RegExp(a);e[0].match(o)&&(t[n]=!0,e.shift(1),i=!0)})}var Sut,Fl,_ut,Aut,Rut,She,du,Z9,_he,J9,C1,w1,eI,tI,T5,E1,rI,Ahe,iI,S1,Wb,nI,aI,k5,sI,$ut,Lut,Iut,Mut,Dut,Nut,Out,But,Put,Fut,zut,qut,Wut,jut,Vut,Uut,Rhe,Yut,Hut,Gut,Xut,Kut,Qut,Zut,Jut,$he,eht,tht,rht,Lhe,iht,oI,Ihe,Mhe,C5,_1,nht,aht,Dhe,lI,Ga,Nhe,sht,E0,oht,Ohe,lht,Bhe,cht,Phe,uht,hht,dht,q8t=G(()=>{"use strict";Sut=$o(zS(),1),Fl=$o(iD(),1),_ut=$o(P8t(),1),Aut=$o(F8t(),1),Rut=$o(z8t(),1),Nt(),Er(),Ar(),fn(),Fl.default.extend(_ut.default),Fl.default.extend(Aut.default),Fl.default.extend(Rut.default),She={friday:5,saturday:6},du="",Z9="",J9="",C1=[],w1=[],eI=new Map,tI=[],T5=[],E1="",rI="",Ahe=["active","done","crit","milestone","vert"],iI=[],S1="",Wb=!1,nI=!1,aI="sunday",k5="saturday",sI=0,$ut=s(function(){tI=[],T5=[],E1="",iI=[],C5=0,Dhe=void 0,lI=void 0,Ga=[],du="",Z9="",rI="",_he=void 0,J9="",C1=[],w1=[],Wb=!1,nI=!1,sI=0,eI=new Map,S1="",pn(),aI="sunday",k5="saturday"},"clear"),Lut=s(function(e){S1=e},"setDiagramId"),Iut=s(function(e){Z9=e},"setAxisFormat"),Mut=s(function(){return Z9},"getAxisFormat"),Dut=s(function(e){_he=e},"setTickInterval"),Nut=s(function(){return _he},"getTickInterval"),Out=s(function(e){J9=e},"setTodayMarker"),But=s(function(){return J9},"getTodayMarker"),Put=s(function(e){du=e},"setDateFormat"),Fut=s(function(){Wb=!0},"enableInclusiveEndDates"),zut=s(function(){return Wb},"endDatesAreInclusive"),qut=s(function(){nI=!0},"enableTopAxis"),Wut=s(function(){return nI},"topAxisEnabled"),jut=s(function(e){rI=e},"setDisplayMode"),Vut=s(function(){return rI},"getDisplayMode"),Uut=s(function(){return du},"getDateFormat"),Rhe=s((e,t)=>{let r=t.toLowerCase().split(/[\s,]+/).filter(i=>i!=="");return[...new Set([...e,...r])]},"mergeTokens"),Yut=s(function(e){C1=Rhe(C1,e)},"setIncludes"),Hut=s(function(){return C1},"getIncludes"),Gut=s(function(e){w1=Rhe(w1,e)},"setExcludes"),Xut=s(function(){return w1},"getExcludes"),Kut=s(function(){return eI},"getLinks"),Qut=s(function(e){E1=e,tI.push(e)},"addSection"),Zut=s(function(){return tI},"getSections"),Jut=s(function(){let e=Ohe(),t=10,r=0;for(;!e&&rl))throw new Error("Failed to find a valid date that was not excluded by `excludes` after 10,000 iterations.");e=e.add(1,"d")}return[t,o]},"fixTaskDates"),oI=s(function(e,t,r){if(r=r.trim(),s(a=>{let o=a.trim();return o==="x"||o==="X"},"isTimestampFormat")(t)&&/^\d+$/.test(r))return new Date(Number(r));let i=/^after\s+(?[\d\w- ]+)/.exec(r);if(i!==null){let a=null;for(let l of i.groups.ids.split(" ")){let u=E0(l);u!==void 0&&(!a||u.endTime>a.endTime)&&(a=u)}if(a)return a.endTime;let o=new Date;return o.setHours(0,0,0,0),o}let n=(0,Fl.default)(r,t.trim(),!0);if(n.isValid())return n.toDate();{Ee.debug("Invalid date:"+r),Ee.debug("With date format:"+t.trim());let a=new Date(r);if(a===void 0||isNaN(a.getTime())||a.getFullYear()<-1e4||a.getFullYear()>1e4)throw new Error("Invalid date:"+r);return a}},"getStartDate"),Ihe=s(function(e){let t=/^(\d+(?:\.\d+)?)([Mdhmswy]|ms)$/.exec(e.trim());return t!==null?[Number.parseFloat(t[1]),t[2]]:[NaN,"ms"]},"parseDuration"),Mhe=s(function(e,t,r,i=!1){r=r.trim();let n=/^until\s+(?[\d\w- ]+)/.exec(r);if(n!==null){let h=null;for(let p of n.groups.ids.split(" ")){let f=E0(p);f!==void 0&&(!h||f.startTime{window.open(r,"_self")}),eI.set(i,r))}),Bhe(e,"clickable")},"setLink"),Bhe=s(function(e,t){e.split(",").forEach(function(r){let i=E0(r);i!==void 0&&i.classes.push(t)})},"setClass"),cht=s(function(e,t,r){if(it().securityLevel!=="loose"||t===void 0)return;let i=[];if(typeof r=="string"){i=r.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(let n=0;n{ai.runFunc(t,...i)})},"setClickFun"),Phe=s(function(e,t){iI.push(function(){let r=S1?`${S1}-${e}`:e,i=document.querySelector(`[id="${r}"]`);i!==null&&i.addEventListener("click",function(){t()})},function(){let r=S1?`${S1}-${e}`:e,i=document.querySelector(`[id="${r}-text"]`);i!==null&&i.addEventListener("click",function(){t()})})},"pushFun"),uht=s(function(e,t,r){e.split(",").forEach(function(i){cht(i,t,r)}),Bhe(e,"clickable")},"setClickEvent"),hht=s(function(e){iI.forEach(function(t){t(e)})},"bindFunctions"),dht={getConfig:s(()=>it().gantt,"getConfig"),clear:$ut,setDateFormat:Put,getDateFormat:Uut,enableInclusiveEndDates:Fut,endDatesAreInclusive:zut,enableTopAxis:qut,topAxisEnabled:Wut,setAxisFormat:Iut,getAxisFormat:Mut,setTickInterval:Dut,getTickInterval:Nut,setTodayMarker:Out,getTodayMarker:But,setAccTitle:bn,getAccTitle:Ln,setDiagramTitle:Dn,getDiagramTitle:xn,setDiagramId:Lut,setDisplayMode:jut,getDisplayMode:Vut,setAccDescription:In,getAccDescription:Mn,addSection:Qut,getSections:Zut,getTasks:Jut,addTask:sht,findTaskById:E0,addTaskOrg:oht,setIncludes:Yut,getIncludes:Hut,setExcludes:Gut,getExcludes:Xut,setClickEvent:uht,setLink:lht,getLinks:Kut,bindFunctions:hht,parseDuration:Ihe,isInvalidDate:$he,setWeekday:eht,getWeekday:tht,setWeekend:rht},s(Ehe,"getTaskTags")}),W8t=Ro((e,t)=>{"use strict";(function(r,i){typeof e=="object"&&typeof t<"u"?t.exports=i():typeof define=="function"&&define.amd?define(i):(r=typeof globalThis<"u"?globalThis:r||self).dayjs_plugin_duration=i()})(e,(function(){"use strict";var r,i,n=1e3,a=6e4,o=36e5,l=864e5,u=31536e6,h=2628e6,d=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/,p=/\[([^\]]+)]|YYYY|YY|Y|M{1,2}|D{1,2}|H{1,2}|m{1,2}|s{1,2}|SSS/g,f={years:u,months:h,days:l,hours:o,minutes:a,seconds:n,milliseconds:1,weeks:6048e5},g=s(function(v){return v instanceof _},"c"),m=s(function(v,x,w){return new _(v,w,x.$l)},"f"),y=s(function(v){return i.p(v)+"s"},"m"),k=s(function(v){return v<0},"l"),C=s(function(v){return k(v)?Math.ceil(v):Math.floor(v)},"$"),S=s(function(v){return Math.abs(v)},"y"),E=s(function(v,x){return v?k(v)?{negative:!0,format:""+S(v)+x}:{negative:!1,format:""+v+x}:{negative:!1,format:""}},"v"),_=(function(){function v(w,R,$){var M=this;if(this.$d={},this.$l=$,w===void 0&&(this.$ms=0,this.parseFromMilliseconds()),R)return m(w*f[y(R)],this);if(typeof w=="number")return this.$ms=w,this.parseFromMilliseconds(),this;if(typeof w=="object")return Object.keys(w).forEach((function(I){M.$d[y(I)]=w[I]})),this.calMilliseconds(),this;if(typeof w=="string"){var N=w.match(d);if(N){var j=N.slice(2).map((function(I){return I!=null?Number(I):0}));return this.$d.years=j[0],this.$d.months=j[1],this.$d.weeks=j[2],this.$d.days=j[3],this.$d.hours=j[4],this.$d.minutes=j[5],this.$d.seconds=j[6],this.calMilliseconds(),this}}return this}s(v,"l");var x=v.prototype;return x.calMilliseconds=function(){var w=this;this.$ms=Object.keys(this.$d).reduce((function(R,$){return R+(w.$d[$]||0)*f[$]}),0)},x.parseFromMilliseconds=function(){var w=this.$ms;this.$d.years=C(w/u),w%=u,this.$d.months=C(w/h),w%=h,this.$d.days=C(w/l),w%=l,this.$d.hours=C(w/o),w%=o,this.$d.minutes=C(w/a),w%=a,this.$d.seconds=C(w/n),w%=n,this.$d.milliseconds=w},x.toISOString=function(){var w=E(this.$d.years,"Y"),R=E(this.$d.months,"M"),$=+this.$d.days||0;this.$d.weeks&&($+=7*this.$d.weeks);var M=E($,"D"),N=E(this.$d.hours,"H"),j=E(this.$d.minutes,"M"),I=this.$d.seconds||0;this.$d.milliseconds&&(I+=this.$d.milliseconds/1e3,I=Math.round(1e3*I)/1e3);var z=E(I,"S"),F=w.negative||R.negative||M.negative||N.negative||j.negative||z.negative,O=N.format||j.format||z.format?"T":"",B=(F?"-":"")+"P"+w.format+R.format+M.format+O+N.format+j.format+z.format;return B==="P"||B==="-P"?"P0D":B},x.toJSON=function(){return this.toISOString()},x.format=function(w){var R=w||"YYYY-MM-DDTHH:mm:ss",$={Y:this.$d.years,YY:i.s(this.$d.years,2,"0"),YYYY:i.s(this.$d.years,4,"0"),M:this.$d.months,MM:i.s(this.$d.months,2,"0"),D:this.$d.days,DD:i.s(this.$d.days,2,"0"),H:this.$d.hours,HH:i.s(this.$d.hours,2,"0"),m:this.$d.minutes,mm:i.s(this.$d.minutes,2,"0"),s:this.$d.seconds,ss:i.s(this.$d.seconds,2,"0"),SSS:i.s(this.$d.milliseconds,3,"0")};return R.replace(p,(function(M,N){return N||String($[M])}))},x.as=function(w){return this.$ms/f[y(w)]},x.get=function(w){var R=this.$ms,$=y(w);return $==="milliseconds"?R%=1e3:R=$==="weeks"?C(R/f[$]):this.$d[$],R||0},x.add=function(w,R,$){var M;return M=R?w*f[y(R)]:g(w)?w.$ms:m(w,this).$ms,m(this.$ms+M*($?-1:1),this)},x.subtract=function(w,R){return this.add(w,R,!0)},x.locale=function(w){var R=this.clone();return R.$l=w,R},x.clone=function(){return m(this.$ms,this)},x.humanize=function(w){return r().add(this.$ms,"ms").locale(this.$l).fromNow(!w)},x.valueOf=function(){return this.asMilliseconds()},x.milliseconds=function(){return this.get("milliseconds")},x.asMilliseconds=function(){return this.as("milliseconds")},x.seconds=function(){return this.get("seconds")},x.asSeconds=function(){return this.as("seconds")},x.minutes=function(){return this.get("minutes")},x.asMinutes=function(){return this.as("minutes")},x.hours=function(){return this.get("hours")},x.asHours=function(){return this.as("hours")},x.days=function(){return this.get("days")},x.asDays=function(){return this.as("days")},x.weeks=function(){return this.get("weeks")},x.asWeeks=function(){return this.as("weeks")},x.months=function(){return this.get("months")},x.asMonths=function(){return this.as("months")},x.years=function(){return this.get("years")},x.asYears=function(){return this.as("years")},v})(),b=s(function(v,x,w){return v.add(x.years()*w,"y").add(x.months()*w,"M").add(x.days()*w,"d").add(x.hours()*w,"h").add(x.minutes()*w,"m").add(x.seconds()*w,"s").add(x.milliseconds()*w,"ms")},"p");return function(v,x,w){r=w,i=w().$utils(),w.duration=function(M,N){var j=w.locale();return m(M,{$l:j},N)},w.isDuration=g;var R=x.prototype.add,$=x.prototype.subtract;x.prototype.add=function(M,N){return g(M)?b(this,M,1):R.bind(this)(M,N)},x.prototype.subtract=function(M,N){return g(M)?b(this,M,-1):$.bind(this)(M,N)}}}))}),jb,pht,fht,Fhe,ght,ap,cI,mht,yht,j8t=G(()=>{"use strict";jb=$o(iD(),1),pht=$o(W8t(),1),Nt(),pi(),mi(),Er(),vn(),jb.default.extend(pht.default),fht=s(function(){Ee.debug("Something is calling, setConf, remove the call")},"setConf"),Fhe={monday:Iy,tuesday:fP,wednesday:gP,thursday:Op,friday:mP,saturday:yP,sunday:Ly},ght=s((e,t)=>{let r=[...e].map(()=>-1/0),i=[...e].sort((a,o)=>a.startTime-o.startTime||a.order-o.order),n=0;for(let a of i)for(let o=0;o=r[o]){r[o]=a.endTime,a.order=o+t,o>n&&(n=o);break}return n},"getMaxIntersections"),cI=1e4,mht=s(function(e,t,r,i){let n=it().gantt;i.db.setDiagramId(t);let a=it().securityLevel,o;a==="sandbox"&&(o=zt("#i"+t));let l=zt(a==="sandbox"?o.nodes()[0].contentDocument.body:"body"),u=a==="sandbox"?o.nodes()[0].contentDocument:document,h=u.getElementById(t);ap=h.parentElement.offsetWidth,ap===void 0&&(ap=1200),n.useWidth!==void 0&&(ap=n.useWidth);let d=i.db.getTasks(),p=d.filter($=>!$.vert),f=[];for(let $ of p)f.push($.type);f=R(f);let g={},m=2*n.topPadding;if(i.db.getDisplayMode()==="compact"||n.displayMode==="compact"){let $={};for(let N of p)$[N.section]===void 0?$[N.section]=[N]:$[N.section].push(N);let M=0;for(let N of Object.keys($)){let j=ght($[N],M)+1;M+=j,m+=j*(n.barHeight+n.barGap),g[N]=j}}else{m+=p.length*(n.barHeight+n.barGap);for(let $ of f)g[$]=p.filter(M=>M.type===$).length}h.setAttribute("viewBox","0 0 "+ap+" "+m);let y=l.select(`[id="${t}"]`),k=Z3e().domain([E2e(d,function($){return $.startTime}),w2e(d,function($){return $.endTime})]).rangeRound([0,ap-n.leftPadding-n.rightPadding]);function C($,M){let N=$.startTime,j=M.startTime,I=0;return N>j?I=1:NH.vert===J.vert?0:H.vert?1:-1);let O=$.filter(H=>!H.vert),B=[...new Set(O.map(H=>H.order))].map(H=>O.find(J=>J.order===H));y.append("g").selectAll("rect").data(B).enter().append("rect").attr("x",0).attr("y",function(H,J){return J=H.order,J*M+N-2}).attr("width",function(){return F-n.rightPadding/2}).attr("height",M).attr("class",function(H){for(let[J,te]of f.entries())if(H.type===te)return"section section"+J%n.numberSectionStyles;return"section section0"}).enter();let Y=y.append("g").selectAll("rect").data($).enter(),U=i.db.getLinks();if(Y.append("rect").attr("id",function(H){return t+"-"+H.id}).attr("rx",3).attr("ry",3).attr("x",function(H){return H.milestone?k(H.startTime)+j+.5*(k(H.endTime)-k(H.startTime))-.5*I:k(H.startTime)+j}).attr("y",function(H,J){return J=H.order,H.vert?n.gridLineStartPadding:J*M+N}).attr("width",function(H){return H.milestone?I:H.vert?.08*I:k(H.renderEndTime||H.endTime)-k(H.startTime)}).attr("height",function(H){return H.vert?O.length*(n.barHeight+n.barGap)+n.barHeight*2:I}).attr("transform-origin",function(H,J){return J=H.order,(k(H.startTime)+j+.5*(k(H.endTime)-k(H.startTime))).toString()+"px "+(J*M+N+.5*I).toString()+"px"}).attr("class",function(H){let J="task",te="";H.classes.length>0&&(te=H.classes.join(" "));let ie=0;for(let[se,le]of f.entries())H.type===le&&(ie=se%n.numberSectionStyles);let ue="";return H.active?H.crit?ue+=" activeCrit":ue=" active":H.done?H.crit?ue=" doneCrit":ue=" done":H.crit&&(ue+=" crit"),ue.length===0&&(ue=" task"),H.milestone&&(ue=" milestone "+ue),H.vert&&(ue=" vert "+ue),ue+=ie,ue+=" "+te,J+ue}),Y.append("text").attr("id",function(H){return t+"-"+H.id+"-text"}).text(function(H){return H.task}).attr("font-size",n.fontSize).attr("x",function(H){let J=k(H.startTime),te=k(H.renderEndTime||H.endTime);if(H.milestone&&(J+=.5*(k(H.endTime)-k(H.startTime))-.5*I,te=J+I),H.vert)return k(H.startTime)+j;let ie=this.getBBox().width;return ie>te-J?te+ie+1.5*n.leftPadding>F?J+j-5:te+j+5:(te-J)/2+J+j}).attr("y",function(H,J){return H.vert?n.gridLineStartPadding+O.length*(n.barHeight+n.barGap)+60:(J=H.order,J*M+n.barHeight/2+(n.fontSize/2-2)+N)}).attr("text-height",I).attr("class",function(H){let J=k(H.startTime),te=k(H.endTime);H.milestone&&(te=J+I);let ie=this.getBBox().width,ue="";H.classes.length>0&&(ue=H.classes.join(" "));let se=0;for(let[Te,oe]of f.entries())H.type===oe&&(se=Te%n.numberSectionStyles);let le="";return H.active&&(H.crit?le="activeCritText"+se:le="activeText"+se),H.done?H.crit?le=le+" doneCritText"+se:le=le+" doneText"+se:H.crit&&(le=le+" critText"+se),H.milestone&&(le+=" milestoneText"),H.vert&&(le+=" vertText"),ie>te-J?te+ie+1.5*n.leftPadding>F?ue+" taskTextOutsideLeft taskTextOutside"+se+" "+le:ue+" taskTextOutsideRight taskTextOutside"+se+" "+le+" width-"+ie:ue+" taskText taskText"+se+" "+le+" width-"+ie}),it().securityLevel==="sandbox"){let H;H=zt("#i"+t);let J=H.nodes()[0].contentDocument;Y.filter(function(te){return U.has(te.id)}).each(function(te){var ie=J.querySelector("#"+CSS.escape(t+"-"+te.id)),ue=J.querySelector("#"+CSS.escape(t+"-"+te.id+"-text"));let se=ie.parentNode;var le=J.createElement("a");le.setAttribute("xlink:href",U.get(te.id)),le.setAttribute("target","_top"),se.appendChild(le),le.appendChild(ie),le.appendChild(ue)})}}s(E,"drawRects");function _($,M,N,j,I,z,F,O){if(F.length===0&&O.length===0)return;let B,Y;for(let{startTime:ie,endTime:ue}of z)(B===void 0||ieY)&&(Y=ue);if(!B||!Y)return;if((0,jb.default)(Y).diff((0,jb.default)(B),"year")>5){Ee.warn("The difference between the min and max time is more than 5 years. This will cause performance issues. Skipping drawing exclude days.");return}let U=i.db.getDateFormat(),H=[],J=null,te=(0,jb.default)(B);for(;te.valueOf()<=Y;)i.db.isInvalidDate(te,U,F,O)?J?J.end=te:J={start:te,end:te}:J&&(H.push(J),J=null),te=te.add(1,"d");y.append("g").selectAll("rect").data(H).enter().append("rect").attr("id",ie=>t+"-exclude-"+ie.start.format("YYYY-MM-DD")).attr("x",ie=>k(ie.start.startOf("day"))+N).attr("y",n.gridLineStartPadding).attr("width",ie=>k(ie.end.endOf("day"))-k(ie.start.startOf("day"))).attr("height",I-M-n.gridLineStartPadding).attr("transform-origin",function(ie,ue){return(k(ie.start)+N+.5*(k(ie.end)-k(ie.start))).toString()+"px "+(ue*$+.5*I).toString()+"px"}).attr("class","exclude-range")}s(_,"drawExcludeDays");function b($,M,N,j){if(N<=0||$>M)return 1/0;let I=M-$,z=jb.default.duration({[j??"day"]:N}).asMilliseconds();return z<=0?1/0:Math.ceil(I/z)}s(b,"getEstimatedTickCount");function v($,M,N,j){let I=i.db.getDateFormat(),z=i.db.getAxisFormat(),F;z?F=z:I==="D"?F="%d":F=n.axisFormat??"%Y-%m-%d";let O=D2e(k).tickSize(-j+M+n.gridLineStartPadding).tickFormat(D6(F)),B=/^([1-9]\d*)(millisecond|second|minute|hour|day|week|month)$/.exec(i.db.getTickInterval()||n.tickInterval);if(B!==null){let Y=parseInt(B[1],10);if(isNaN(Y)||Y<=0)Ee.warn(`Invalid tick interval value: "${B[1]}". Skipping custom tick interval.`);else{let U=B[2],H=i.db.getWeekday()||n.weekday,J=k.domain(),te=J[0],ie=J[1],ue=b(te,ie,Y,U);if(ue>cI)Ee.warn(`The tick interval "${Y}${U}" would generate ${ue} ticks, which exceeds the maximum allowed (${cI}). This may indicate an invalid date or time range. Skipping custom tick interval.`);else switch(U){case"millisecond":O.ticks(lg.every(Y));break;case"second":O.ticks(rd.every(Y));break;case"minute":O.ticks(Ry.every(Y));break;case"hour":O.ticks($y.every(Y));break;case"day":O.ticks(Mp.every(Y));break;case"week":O.ticks(Fhe[H].every(Y));break;case"month":O.ticks(My.every(Y));break}}}if(y.append("g").attr("class","grid").attr("transform","translate("+$+", "+(j-50)+")").call(O).selectAll("text").style("text-anchor","middle").attr("fill","#000").attr("stroke","none").attr("font-size",10).attr("dy","1em"),i.db.topAxisEnabled()||n.topAxis){let Y=M2e(k).tickSize(-j+M+n.gridLineStartPadding).tickFormat(D6(F));if(B!==null){let U=parseInt(B[1],10);if(isNaN(U)||U<=0)Ee.warn(`Invalid tick interval value: "${B[1]}". Skipping custom tick interval.`);else{let H=B[2],J=i.db.getWeekday()||n.weekday,te=k.domain(),ie=te[0],ue=te[1];if(b(ie,ue,U,H)<=cI)switch(H){case"millisecond":Y.ticks(lg.every(U));break;case"second":Y.ticks(rd.every(U));break;case"minute":Y.ticks(Ry.every(U));break;case"hour":Y.ticks($y.every(U));break;case"day":Y.ticks(Mp.every(U));break;case"week":Y.ticks(Fhe[J].every(U));break;case"month":Y.ticks(My.every(U));break}}}y.append("g").attr("class","grid").attr("transform","translate("+$+", "+M+")").call(Y).selectAll("text").style("text-anchor","middle").attr("fill","#000").attr("stroke","none").attr("font-size",10)}}s(v,"makeGrid");function x($,M){let N=0,j=Object.keys(g).map(I=>[I,g[I]]);y.append("g").selectAll("text").data(j).enter().append(function(I){let z=I[0].split(Vt.lineBreakRegex),F=-(z.length-1)/2,O=u.createElementNS("http://www.w3.org/2000/svg","text");O.setAttribute("dy",F+"em");for(let[B,Y]of z.entries()){let U=u.createElementNS("http://www.w3.org/2000/svg","tspan");U.setAttribute("alignment-baseline","central"),U.setAttribute("x","10"),B>0&&U.setAttribute("dy","1em"),U.textContent=Y,O.appendChild(U)}return O}).attr("x",10).attr("y",function(I,z){if(z>0)for(let F=0;F{"use strict";vht=s(e=>` + .mermaid-main-font { + font-family: ${e.fontFamily}; + } + + .exclude-range { + fill: ${e.excludeBkgColor}; + } + + .section { + stroke: none; + opacity: 0.2; + } + + .section0 { + fill: ${e.sectionBkgColor}; + } + + .section2 { + fill: ${e.sectionBkgColor2}; + } + + .section1, + .section3 { + fill: ${e.altSectionBkgColor}; + opacity: 0.2; + } + + .sectionTitle0 { + fill: ${e.titleColor}; + } + + .sectionTitle1 { + fill: ${e.titleColor}; + } + + .sectionTitle2 { + fill: ${e.titleColor}; + } + + .sectionTitle3 { + fill: ${e.titleColor}; + } + + .sectionTitle { + text-anchor: start; + font-family: ${e.fontFamily}; + } + + + /* Grid and axis */ + + .grid .tick { + stroke: ${e.gridColor}; + opacity: 0.8; + shape-rendering: crispEdges; + } + + .grid .tick text { + font-family: ${e.fontFamily}; + fill: ${e.textColor}; + } + + .grid path { + stroke-width: 0; + } + + + /* Today line */ + + .today { + fill: none; + stroke: ${e.todayLineColor}; + stroke-width: 2px; + } + + + /* Task styling */ + + /* Default task */ + + .task { + stroke-width: 2; + } + + .taskText { + text-anchor: middle; + font-family: ${e.fontFamily}; + } + + .taskTextOutsideRight { + fill: ${e.taskTextDarkColor}; + text-anchor: start; + font-family: ${e.fontFamily}; + } + + .taskTextOutsideLeft { + fill: ${e.taskTextDarkColor}; + text-anchor: end; + } + + + /* Special case clickable */ + + .task.clickable { + cursor: pointer; + } + + .taskText.clickable { + cursor: pointer; + fill: ${e.taskTextClickableColor} !important; + font-weight: bold; + } + + .taskTextOutsideLeft.clickable { + cursor: pointer; + fill: ${e.taskTextClickableColor} !important; + font-weight: bold; + } + + .taskTextOutsideRight.clickable { + cursor: pointer; + fill: ${e.taskTextClickableColor} !important; + font-weight: bold; + } + + + /* Specific task settings for the sections*/ + + .taskText0, + .taskText1, + .taskText2, + .taskText3 { + fill: ${e.taskTextColor}; + } + + .task0, + .task1, + .task2, + .task3 { + fill: ${e.taskBkgColor}; + stroke: ${e.taskBorderColor}; + } + + .taskTextOutside0, + .taskTextOutside2 + { + fill: ${e.taskTextOutsideColor}; + } + + .taskTextOutside1, + .taskTextOutside3 { + fill: ${e.taskTextOutsideColor}; + } + + + /* Active task */ + + .active0, + .active1, + .active2, + .active3 { + fill: ${e.activeTaskBkgColor}; + stroke: ${e.activeTaskBorderColor}; + } + + .activeText0, + .activeText1, + .activeText2, + .activeText3 { + fill: ${e.taskTextDarkColor} !important; + } + + + /* Completed task */ + + .done0, + .done1, + .done2, + .done3 { + stroke: ${e.doneTaskBorderColor}; + fill: ${e.doneTaskBkgColor}; + stroke-width: 2; + } + + .doneText0, + .doneText1, + .doneText2, + .doneText3 { + fill: ${e.taskTextDarkColor} !important; + } + + /* Done task text displayed outside the bar sits against the diagram background, + not against the done-task bar, so it must use the outside/contrast color. */ + .doneText0.taskTextOutsideLeft, + .doneText0.taskTextOutsideRight, + .doneText1.taskTextOutsideLeft, + .doneText1.taskTextOutsideRight, + .doneText2.taskTextOutsideLeft, + .doneText2.taskTextOutsideRight, + .doneText3.taskTextOutsideLeft, + .doneText3.taskTextOutsideRight { + fill: ${e.taskTextOutsideColor} !important; + } + + + /* Tasks on the critical line */ + + .crit0, + .crit1, + .crit2, + .crit3 { + stroke: ${e.critBorderColor}; + fill: ${e.critBkgColor}; + stroke-width: 2; + } + + .activeCrit0, + .activeCrit1, + .activeCrit2, + .activeCrit3 { + stroke: ${e.critBorderColor}; + fill: ${e.activeTaskBkgColor}; + stroke-width: 2; + } + + .doneCrit0, + .doneCrit1, + .doneCrit2, + .doneCrit3 { + stroke: ${e.critBorderColor}; + fill: ${e.doneTaskBkgColor}; + stroke-width: 2; + cursor: pointer; + shape-rendering: crispEdges; + } + + .milestone { + transform: rotate(45deg) scale(0.8,0.8); + } + + .milestoneText { + font-style: italic; + } + .doneCritText0, + .doneCritText1, + .doneCritText2, + .doneCritText3 { + fill: ${e.taskTextDarkColor} !important; + } + + /* Done-crit task text outside the bar — same reasoning as doneText above. */ + .doneCritText0.taskTextOutsideLeft, + .doneCritText0.taskTextOutsideRight, + .doneCritText1.taskTextOutsideLeft, + .doneCritText1.taskTextOutsideRight, + .doneCritText2.taskTextOutsideLeft, + .doneCritText2.taskTextOutsideRight, + .doneCritText3.taskTextOutsideLeft, + .doneCritText3.taskTextOutsideRight { + fill: ${e.taskTextOutsideColor} !important; + } + + .vert { + stroke: ${e.vertLineColor}; + } + + .vertText { + font-size: 15px; + text-anchor: middle; + fill: ${e.vertLineColor} !important; + } + + .activeCritText0, + .activeCritText1, + .activeCritText2, + .activeCritText3 { + fill: ${e.taskTextDarkColor} !important; + } + + .titleText { + text-anchor: middle; + font-size: 18px; + fill: ${e.titleColor||e.textColor}; + font-family: ${e.fontFamily}; + } +`,"getStyles"),bht=vht}),xht={};Mr(xht,{diagram:()=>Tht});var Tht,U8t=G(()=>{"use strict";B8t(),q8t(),j8t(),V8t(),Tht={parser:Eut,db:dht,renderer:yht,styles:bht}}),kht,Y8t=G(()=>{"use strict";Co(),Nt(),kht={parse:s(async e=>{let t=await Pl("info",e);Ee.debug(t)},"parse")}}),Cht,wht,Eht,H8t=G(()=>{"use strict";Cht={version:"11.17.2"},wht=s(()=>Cht.version,"getVersion"),Eht={getVersion:wht}}),Ks,wo=G(()=>{"use strict";pi(),Er(),Ks=s(e=>{let{securityLevel:t}=it(),r=zt("body");if(t==="sandbox"){let i=zt(`#i${e}`).node()?.contentDocument??document;r=zt(i.body)}return r.select(`#${e}`)},"selectSvgElement")}),Sht,_ht,G8t=G(()=>{"use strict";Nt(),wo(),vn(),Sht=s((e,t,r)=>{Ee.debug(`rendering info diagram +`+e);let i=Ks(t);aa(i,100,400,!0),i.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size",32).style("text-anchor","middle").text(`v${r}`)},"draw"),_ht={draw:Sht}}),Aht={};Mr(Aht,{diagram:()=>Rht});var Rht,X8t=G(()=>{"use strict";Y8t(),H8t(),G8t(),Rht={parser:kht,db:Eht,renderer:_ht}}),zhe,uI,w5,hI,$ht,Lht,Iht,Mht,Dht,Nht,Oht,qhe,Bht=G(()=>{"use strict";Nt(),fn(),Za(),zhe=vi.pie,uI={sections:new Map,showData:!1,config:zhe},w5=uI.sections,hI=uI.showData,$ht=structuredClone(zhe),Lht=s(()=>structuredClone($ht),"getConfig"),Iht=s(()=>{w5=new Map,hI=uI.showData,pn()},"clear"),Mht=s(({label:e,value:t})=>{if(t<0)throw new Error(`"${e}" has invalid value: ${t}. Negative values are not allowed in pie charts. All slice values must be >= 0.`);w5.has(e)||(w5.set(e,t),Ee.debug(`added new section: ${e}, with value: ${t}`))},"addSection"),Dht=s(()=>w5,"getSections"),Nht=s(e=>{hI=e},"setShowData"),Oht=s(()=>hI,"getShowData"),qhe={getConfig:Lht,clear:Iht,setDiagramTitle:Dn,getDiagramTitle:xn,setAccTitle:bn,getAccTitle:Ln,setAccDescription:In,getAccDescription:Mn,addSection:Mht,getSections:Dht,setShowData:Nht,getShowData:Oht}}),Pht,Fht,K8t=G(()=>{"use strict";Co(),Nt(),vl(),Bht(),Pht=s((e,t)=>{Yo(e,t),t.setShowData(e.showData),e.sections.map(t.addSection)},"populateDb"),Fht={parse:s(async e=>{let t=await Pl("pie",e);Ee.debug(t),Pht(t,qhe)},"parse")}}),zht,qht,Q8t=G(()=>{"use strict";zht=s(e=>` + .pieCircle{ + stroke: ${e.pieStrokeColor}; + stroke-width : ${e.pieStrokeWidth}; + opacity : ${e.pieOpacity}; + } + .pieCircle.highlighted{ + scale: 1.05; + opacity: 1; + } + .pieCircle.highlightedOnHover:hover{ + transition-duration: 250ms; + scale: 1.05; + opacity: 1; + } + .pieOuterCircle{ + stroke: ${e.pieOuterStrokeColor}; + stroke-width: ${e.pieOuterStrokeWidth}; + fill: none; + } + .pieTitleText { + text-anchor: middle; + font-size: ${e.pieTitleTextSize}; + fill: ${e.pieTitleTextColor}; + font-family: ${e.fontFamily}; + } + .slice { + font-family: ${e.fontFamily}; + fill: ${e.pieSectionTextColor}; + font-size:${e.pieSectionTextSize}; + // fill: white; + } + .legend text { + fill: ${e.pieLegendTextColor}; + font-family: ${e.fontFamily}; + font-size: ${e.pieLegendTextSize}; + } +`,"getStyles"),qht=zht}),Wht,jht,Vht,Z8t=G(()=>{"use strict";pi(),Er(),Nt(),wo(),vn(),Ar(),Wht=s(e=>{let t=[...e.values()].reduce((i,n)=>i+n,0),r=[...e.entries()].map(([i,n])=>({label:i,value:n})).filter(i=>i.value/t*100>=1);return mke().value(i=>i.value).sort(null)(r)},"createPieArcs"),jht=s((e,t,r,i)=>{Ee.debug(`rendering pie chart +`+e);let n=i.db,a=it(),o=Gn(n.getConfig(),a.pie),l=40,u=18,h=4,d=450,p=d,f=Ks(t),g=f.append("g");g.attr("transform","translate("+p/2+","+d/2+")");let{themeVariables:m}=a,[y]=zu(m.pieOuterStrokeWidth);y??=2;let k=o.legendPosition,C=o.textPosition,S=o.donutHole>0&&o.donutHole<=.9?o.donutHole:0,E=Math.min(p,d)/2-l,_=hg().innerRadius(S*E).outerRadius(E),b=hg().innerRadius(E*C).outerRadius(E*C),v=g.append("g");v.append("circle").attr("cx",0).attr("cy",0).attr("r",E+y/2).attr("class","pieOuterCircle");let x=n.getSections(),w=Wht(x),R=[m.pie1,m.pie2,m.pie3,m.pie4,m.pie5,m.pie6,m.pie7,m.pie8,m.pie9,m.pie10,m.pie11,m.pie12],$=0;x.forEach(se=>{$+=se});let M=w.filter(se=>(se.data.value/$*100).toFixed(0)!=="0"),N=Lp(R).domain([...x.keys()]);v.selectAll("mySlices").data(M).enter().append("path").attr("d",_).attr("fill",se=>N(se.data.label)).attr("class",se=>{let le="pieCircle";return o.highlightSlice==="hover"?le+=" highlightedOnHover":o.highlightSlice===se.data.label&&(le+=" highlighted"),le}),v.selectAll("mySlices").data(M).enter().append("text").text(se=>(se.data.value/$*100).toFixed(0)+"%").attr("transform",se=>"translate("+b.centroid(se)+")").style("text-anchor","middle").attr("class","slice");let j=g.append("text").text(n.getDiagramTitle()).attr("x",0).attr("y",-(d-50)/2).attr("class","pieTitleText"),I=[...x.entries()].map(([se,le])=>({label:se,value:le})),z=g.selectAll(".legend").data(I).enter().append("g").attr("class","legend");z.append("rect").attr("width",u).attr("height",u).style("fill",se=>N(se.label)).style("stroke",se=>N(se.label)),z.append("text").attr("x",u+h).attr("y",u-h).text(se=>n.getShowData()?`${se.label} [${se.value}]`:se.label);let F=Math.max(...z.selectAll("text").nodes().map(se=>se?.getBoundingClientRect().width??0)),O=d,B=p+l,Y=u+h,U=I.length*Y;switch(k){case"center":z.attr("transform",(se,le)=>{let Te=Y*I.length/2,oe=-F/2-(u+h),fe=le*Y-Te;return"translate("+oe+","+fe+")"});break;case"top":O+=U,z.attr("transform",(se,le)=>{let Te=E,oe=-F/2-(u+h),fe=le*Y-Te;return`translate(${oe}, ${fe})`}),v.attr("transform",()=>`translate(0, ${U+Y})`);break;case"bottom":O+=U,z.attr("transform",(se,le)=>{let Te=-E-Y,oe=-F/2-(u+h),fe=le*Y-Te;return"translate("+oe+","+fe+")"});break;case"left":B+=u+h+F,z.attr("transform",(se,le)=>{let Te=Y*I.length/2,oe=-E-(u+h),fe=le*Y-Te;return"translate("+oe+","+fe+")"}),v.attr("transform",()=>`translate(${F+u+h}, 0)`);break;default:B+=u+h+F,z.attr("transform",(se,le)=>{let Te=Y*I.length/2,oe=12*u,fe=le*Y-Te;return"translate("+oe+","+fe+")"});break}let H=j.node()?.getBoundingClientRect().width??0,J=p/2-H/2,te=p/2+H/2,ie=Math.min(0,J),ue=Math.max(B,te)-ie;f.attr("viewBox",`${ie} 0 ${ue} ${O}`),aa(f,O,ue,o.useMaxWidth)},"draw"),Vht={draw:jht}}),Uht={};Mr(Uht,{diagram:()=>Yht});var Yht,J8t=G(()=>{"use strict";K8t(),Bht(),Q8t(),Z8t(),Yht={parser:Fht,db:qhe,renderer:Vht,styles:qht}}),dI,Hht,eRt=G(()=>{"use strict";dI=(function(){var e=s(function(ve,X,me,Ce){for(me=me||{},Ce=ve.length;Ce--;me[ve[Ce]]=X);return me},"o"),t=[1,3],r=[1,4],i=[1,5],n=[1,6],a=[1,7],o=[1,4,5,10,12,13,14,15,18,25,35,37,39,41,42,48,50,51,52,53,54,55,56,57,60,61,63,64,65,66,67],l=[1,4,5,10,12,13,14,15,18,25,28,35,37,39,41,42,48,50,51,52,53,54,55,56,57,60,61,63,64,65,66,67],u=[55,56,57],h=[2,36],d=[1,37],p=[1,36],f=[1,38],g=[1,35],m=[1,43],y=[1,41],k=[1,45],C=[1,14],S=[1,23],E=[1,18],_=[1,19],b=[1,20],v=[1,21],x=[1,22],w=[1,24],R=[1,25],$=[1,26],M=[1,27],N=[1,28],j=[1,29],I=[1,32],z=[1,33],F=[1,34],O=[1,39],B=[1,40],Y=[1,42],U=[1,44],H=[1,63],J=[1,62],te=[4,5,8,10,12,13,14,15,18,44,47,49,55,56,57,63,64,65,66,67],ie=[1,66],ue=[1,67],se=[1,68],le=[1,69],Te=[1,70],oe=[1,71],fe=[1,72],ge=[1,73],ke=[1,74],Ne=[1,75],xe=[1,76],$e=[1,77],Me=[4,5,6,7,8,9,10,11,12,13,14,15,18],He=[1,91],Ge=[1,92],ze=[1,93],Ve=[1,100],et=[1,94],he=[1,97],ye=[1,95],be=[1,96],Ie=[1,98],pe=[1,99],Qe=[1,103],W=[10,55,56,57],Ae=[4,5,6,8,10,11,13,17,18,19,20,55,56,57],K={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,idStringToken:3,ALPHA:4,NUM:5,NODE_STRING:6,DOWN:7,MINUS:8,DEFAULT:9,COMMA:10,COLON:11,AMP:12,BRKT:13,MULT:14,UNICODE_TEXT:15,styleComponent:16,UNIT:17,SPACE:18,STYLE:19,PCT:20,idString:21,style:22,stylesOpt:23,classDefStatement:24,CLASSDEF:25,start:26,eol:27,QUADRANT:28,document:29,line:30,statement:31,axisDetails:32,quadrantDetails:33,points:34,title:35,title_value:36,acc_title:37,acc_title_value:38,acc_descr:39,acc_descr_value:40,acc_descr_multiline_value:41,section:42,text:43,point_start:44,point_x:45,point_y:46,class_name:47,"X-AXIS":48,"AXIS-TEXT-DELIMITER":49,"Y-AXIS":50,QUADRANT_1:51,QUADRANT_2:52,QUADRANT_3:53,QUADRANT_4:54,NEWLINE:55,SEMI:56,EOF:57,alphaNumToken:58,textNoTagsToken:59,STR:60,MD_STR:61,alphaNum:62,PUNCTUATION:63,PLUS:64,EQUALS:65,DOT:66,UNDERSCORE:67,$accept:0,$end:1},terminals_:{2:"error",4:"ALPHA",5:"NUM",6:"NODE_STRING",7:"DOWN",8:"MINUS",9:"DEFAULT",10:"COMMA",11:"COLON",12:"AMP",13:"BRKT",14:"MULT",15:"UNICODE_TEXT",17:"UNIT",18:"SPACE",19:"STYLE",20:"PCT",25:"CLASSDEF",28:"QUADRANT",35:"title",36:"title_value",37:"acc_title",38:"acc_title_value",39:"acc_descr",40:"acc_descr_value",41:"acc_descr_multiline_value",42:"section",44:"point_start",45:"point_x",46:"point_y",47:"class_name",48:"X-AXIS",49:"AXIS-TEXT-DELIMITER",50:"Y-AXIS",51:"QUADRANT_1",52:"QUADRANT_2",53:"QUADRANT_3",54:"QUADRANT_4",55:"NEWLINE",56:"SEMI",57:"EOF",60:"STR",61:"MD_STR",63:"PUNCTUATION",64:"PLUS",65:"EQUALS",66:"DOT",67:"UNDERSCORE"},productions_:[0,[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[21,1],[21,2],[22,1],[22,2],[23,1],[23,3],[24,5],[26,2],[26,2],[26,2],[29,0],[29,2],[30,2],[31,0],[31,1],[31,2],[31,1],[31,1],[31,1],[31,2],[31,2],[31,2],[31,1],[31,1],[34,4],[34,5],[34,5],[34,6],[32,4],[32,3],[32,2],[32,4],[32,3],[32,2],[33,2],[33,2],[33,2],[33,2],[27,1],[27,1],[27,1],[43,1],[43,2],[43,1],[43,1],[62,1],[62,2],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[59,1],[59,1],[59,1]],performAction:s(function(ve,X,me,Ce,Be,Re,Ke){var De=Re.length-1;switch(Be){case 23:this.$=Re[De];break;case 24:this.$=Re[De-1]+""+Re[De];break;case 26:this.$=Re[De-1]+Re[De];break;case 27:this.$=[Re[De].trim()];break;case 28:Re[De-2].push(Re[De].trim()),this.$=Re[De-2];break;case 29:this.$=Re[De-4],Ce.addClass(Re[De-2],Re[De]);break;case 37:this.$=[];break;case 42:this.$=Re[De].trim(),Ce.setDiagramTitle(this.$);break;case 43:this.$=Re[De].trim(),Ce.setAccTitle(this.$);break;case 44:case 45:this.$=Re[De].trim(),Ce.setAccDescription(this.$);break;case 46:Ce.addSection(Re[De].substr(8)),this.$=Re[De].substr(8);break;case 47:Ce.addPoint(Re[De-3],"",Re[De-1],Re[De],[]);break;case 48:Ce.addPoint(Re[De-4],Re[De-3],Re[De-1],Re[De],[]);break;case 49:Ce.addPoint(Re[De-4],"",Re[De-2],Re[De-1],Re[De]);break;case 50:Ce.addPoint(Re[De-5],Re[De-4],Re[De-2],Re[De-1],Re[De]);break;case 51:Ce.setXAxisLeftText(Re[De-2]),Ce.setXAxisRightText(Re[De]);break;case 52:Re[De-1].text+=" ⟶ ",Ce.setXAxisLeftText(Re[De-1]);break;case 53:Ce.setXAxisLeftText(Re[De]);break;case 54:Ce.setYAxisBottomText(Re[De-2]),Ce.setYAxisTopText(Re[De]);break;case 55:Re[De-1].text+=" ⟶ ",Ce.setYAxisBottomText(Re[De-1]);break;case 56:Ce.setYAxisBottomText(Re[De]);break;case 57:Ce.setQuadrant1Text(Re[De]);break;case 58:Ce.setQuadrant2Text(Re[De]);break;case 59:Ce.setQuadrant3Text(Re[De]);break;case 60:Ce.setQuadrant4Text(Re[De]);break;case 64:this.$={text:Re[De],type:"text"};break;case 65:this.$={text:Re[De-1].text+""+Re[De],type:Re[De-1].type};break;case 66:this.$={text:Re[De],type:"text"};break;case 67:this.$={text:Re[De],type:"markdown"};break;case 68:this.$=Re[De];break;case 69:this.$=Re[De-1]+""+Re[De];break}},"anonymous"),table:[{18:t,26:1,27:2,28:r,55:i,56:n,57:a},{1:[3]},{18:t,26:8,27:2,28:r,55:i,56:n,57:a},{18:t,26:9,27:2,28:r,55:i,56:n,57:a},e(o,[2,33],{29:10}),e(l,[2,61]),e(l,[2,62]),e(l,[2,63]),{1:[2,30]},{1:[2,31]},e(u,h,{30:11,31:12,24:13,32:15,33:16,34:17,43:30,58:31,1:[2,32],4:d,5:p,10:f,12:g,13:m,14:y,15:k,18:C,25:S,35:E,37:_,39:b,41:v,42:x,48:w,50:R,51:$,52:M,53:N,54:j,60:I,61:z,63:F,64:O,65:B,66:Y,67:U}),e(o,[2,34]),{27:46,55:i,56:n,57:a},e(u,[2,37]),e(u,h,{24:13,32:15,33:16,34:17,43:30,58:31,31:47,4:d,5:p,10:f,12:g,13:m,14:y,15:k,18:C,25:S,35:E,37:_,39:b,41:v,42:x,48:w,50:R,51:$,52:M,53:N,54:j,60:I,61:z,63:F,64:O,65:B,66:Y,67:U}),e(u,[2,39]),e(u,[2,40]),e(u,[2,41]),{36:[1,48]},{38:[1,49]},{40:[1,50]},e(u,[2,45]),e(u,[2,46]),{18:[1,51]},{4:d,5:p,10:f,12:g,13:m,14:y,15:k,43:52,58:31,60:I,61:z,63:F,64:O,65:B,66:Y,67:U},{4:d,5:p,10:f,12:g,13:m,14:y,15:k,43:53,58:31,60:I,61:z,63:F,64:O,65:B,66:Y,67:U},{4:d,5:p,10:f,12:g,13:m,14:y,15:k,43:54,58:31,60:I,61:z,63:F,64:O,65:B,66:Y,67:U},{4:d,5:p,10:f,12:g,13:m,14:y,15:k,43:55,58:31,60:I,61:z,63:F,64:O,65:B,66:Y,67:U},{4:d,5:p,10:f,12:g,13:m,14:y,15:k,43:56,58:31,60:I,61:z,63:F,64:O,65:B,66:Y,67:U},{4:d,5:p,10:f,12:g,13:m,14:y,15:k,43:57,58:31,60:I,61:z,63:F,64:O,65:B,66:Y,67:U},{4:d,5:p,8:H,10:f,12:g,13:m,14:y,15:k,18:J,44:[1,58],47:[1,59],58:61,59:60,63:F,64:O,65:B,66:Y,67:U},e(te,[2,64]),e(te,[2,66]),e(te,[2,67]),e(te,[2,70]),e(te,[2,71]),e(te,[2,72]),e(te,[2,73]),e(te,[2,74]),e(te,[2,75]),e(te,[2,76]),e(te,[2,77]),e(te,[2,78]),e(te,[2,79]),e(te,[2,80]),e(te,[2,81]),e(o,[2,35]),e(u,[2,38]),e(u,[2,42]),e(u,[2,43]),e(u,[2,44]),{3:65,4:ie,5:ue,6:se,7:le,8:Te,9:oe,10:fe,11:ge,12:ke,13:Ne,14:xe,15:$e,21:64},e(u,[2,53],{59:60,58:61,4:d,5:p,8:H,10:f,12:g,13:m,14:y,15:k,18:J,49:[1,78],63:F,64:O,65:B,66:Y,67:U}),e(u,[2,56],{59:60,58:61,4:d,5:p,8:H,10:f,12:g,13:m,14:y,15:k,18:J,49:[1,79],63:F,64:O,65:B,66:Y,67:U}),e(u,[2,57],{59:60,58:61,4:d,5:p,8:H,10:f,12:g,13:m,14:y,15:k,18:J,63:F,64:O,65:B,66:Y,67:U}),e(u,[2,58],{59:60,58:61,4:d,5:p,8:H,10:f,12:g,13:m,14:y,15:k,18:J,63:F,64:O,65:B,66:Y,67:U}),e(u,[2,59],{59:60,58:61,4:d,5:p,8:H,10:f,12:g,13:m,14:y,15:k,18:J,63:F,64:O,65:B,66:Y,67:U}),e(u,[2,60],{59:60,58:61,4:d,5:p,8:H,10:f,12:g,13:m,14:y,15:k,18:J,63:F,64:O,65:B,66:Y,67:U}),{45:[1,80]},{44:[1,81]},e(te,[2,65]),e(te,[2,82]),e(te,[2,83]),e(te,[2,84]),{3:83,4:ie,5:ue,6:se,7:le,8:Te,9:oe,10:fe,11:ge,12:ke,13:Ne,14:xe,15:$e,18:[1,82]},e(Me,[2,23]),e(Me,[2,1]),e(Me,[2,2]),e(Me,[2,3]),e(Me,[2,4]),e(Me,[2,5]),e(Me,[2,6]),e(Me,[2,7]),e(Me,[2,8]),e(Me,[2,9]),e(Me,[2,10]),e(Me,[2,11]),e(Me,[2,12]),e(u,[2,52],{58:31,43:84,4:d,5:p,10:f,12:g,13:m,14:y,15:k,60:I,61:z,63:F,64:O,65:B,66:Y,67:U}),e(u,[2,55],{58:31,43:85,4:d,5:p,10:f,12:g,13:m,14:y,15:k,60:I,61:z,63:F,64:O,65:B,66:Y,67:U}),{46:[1,86]},{45:[1,87]},{4:He,5:Ge,6:ze,8:Ve,11:et,13:he,16:90,17:ye,18:be,19:Ie,20:pe,22:89,23:88},e(Me,[2,24]),e(u,[2,51],{59:60,58:61,4:d,5:p,8:H,10:f,12:g,13:m,14:y,15:k,18:J,63:F,64:O,65:B,66:Y,67:U}),e(u,[2,54],{59:60,58:61,4:d,5:p,8:H,10:f,12:g,13:m,14:y,15:k,18:J,63:F,64:O,65:B,66:Y,67:U}),e(u,[2,47],{22:89,16:90,23:101,4:He,5:Ge,6:ze,8:Ve,11:et,13:he,17:ye,18:be,19:Ie,20:pe}),{46:[1,102]},e(u,[2,29],{10:Qe}),e(W,[2,27],{16:104,4:He,5:Ge,6:ze,8:Ve,11:et,13:he,17:ye,18:be,19:Ie,20:pe}),e(Ae,[2,25]),e(Ae,[2,13]),e(Ae,[2,14]),e(Ae,[2,15]),e(Ae,[2,16]),e(Ae,[2,17]),e(Ae,[2,18]),e(Ae,[2,19]),e(Ae,[2,20]),e(Ae,[2,21]),e(Ae,[2,22]),e(u,[2,49],{10:Qe}),e(u,[2,48],{22:89,16:90,23:105,4:He,5:Ge,6:ze,8:Ve,11:et,13:he,17:ye,18:be,19:Ie,20:pe}),{4:He,5:Ge,6:ze,8:Ve,11:et,13:he,16:90,17:ye,18:be,19:Ie,20:pe,22:106},e(Ae,[2,26]),e(u,[2,50],{10:Qe}),e(W,[2,28],{16:104,4:He,5:Ge,6:ze,8:Ve,11:et,13:he,17:ye,18:be,19:Ie,20:pe})],defaultActions:{8:[2,30],9:[2,31]},parseError:s(function(ve,X){if(X.recoverable)this.trace(ve);else{var me=new Error(ve);throw me.hash=X,me}},"parseError"),parse:s(function(ve){var X=this,me=[0],Ce=[],Be=[null],Re=[],Ke=this.table,De="",Ze=0,Fe=0,Je=0,Le=2,ct=1,qe=Re.slice.call(arguments,1),mt=Object.create(this.lexer),St={yy:{}};for(var Lt in this.yy)Object.prototype.hasOwnProperty.call(this.yy,Lt)&&(St.yy[Lt]=this.yy[Lt]);mt.setInput(ve,St.yy),St.yy.lexer=mt,St.yy.parser=this,typeof mt.yylloc>"u"&&(mt.yylloc={});var Bt=mt.yylloc;Re.push(Bt);var qt=mt.options&&mt.options.ranges;typeof St.yy.parseError=="function"?this.parseError=St.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function tr(Cr){me.length=me.length-2*Cr,Be.length=Be.length-Cr,Re.length=Re.length-Cr}s(tr,"popStack");function Et(){var Cr;return Cr=Ce.pop()||mt.lex()||ct,typeof Cr!="number"&&(Cr instanceof Array&&(Ce=Cr,Cr=Ce.pop()),Cr=X.symbols_[Cr]||Cr),Cr}s(Et,"lex");for(var _t,Kt,$t,Ot,Jt,Br,At={},jr,br,Hr,Ei;;){if($t=me[me.length-1],this.defaultActions[$t]?Ot=this.defaultActions[$t]:((_t===null||typeof _t>"u")&&(_t=Et()),Ot=Ke[$t]&&Ke[$t][_t]),typeof Ot>"u"||!Ot.length||!Ot[0]){var Li="";Ei=[];for(jr in Ke[$t])this.terminals_[jr]&&jr>Le&&Ei.push("'"+this.terminals_[jr]+"'");mt.showPosition?Li="Parse error on line "+(Ze+1)+`: +`+mt.showPosition()+` +Expecting `+Ei.join(", ")+", got '"+(this.terminals_[_t]||_t)+"'":Li="Parse error on line "+(Ze+1)+": Unexpected "+(_t==ct?"end of input":"'"+(this.terminals_[_t]||_t)+"'"),this.parseError(Li,{text:mt.match,token:this.terminals_[_t]||_t,line:mt.yylineno,loc:Bt,expected:Ei})}if(Ot[0]instanceof Array&&Ot.length>1)throw new Error("Parse Error: multiple actions possible at state: "+$t+", token: "+_t);switch(Ot[0]){case 1:me.push(_t),Be.push(mt.yytext),Re.push(mt.yylloc),me.push(Ot[1]),_t=null,Kt?(_t=Kt,Kt=null):(Fe=mt.yyleng,De=mt.yytext,Ze=mt.yylineno,Bt=mt.yylloc,Je>0&&Je--);break;case 2:if(br=this.productions_[Ot[1]][1],At.$=Be[Be.length-br],At._$={first_line:Re[Re.length-(br||1)].first_line,last_line:Re[Re.length-1].last_line,first_column:Re[Re.length-(br||1)].first_column,last_column:Re[Re.length-1].last_column},qt&&(At._$.range=[Re[Re.length-(br||1)].range[0],Re[Re.length-1].range[1]]),Br=this.performAction.apply(At,[De,Fe,Ze,St.yy,Ot[1],Be,Re].concat(qe)),typeof Br<"u")return Br;br&&(me=me.slice(0,-1*br*2),Be=Be.slice(0,-1*br),Re=Re.slice(0,-1*br)),me.push(this.productions_[Ot[1]][0]),Be.push(At.$),Re.push(At._$),Hr=Ke[me[me.length-2]][me[me.length-1]],me.push(Hr);break;case 3:return!0}}return!0},"parse")},de=(function(){var ve={EOF:1,parseError:s(function(X,me){if(this.yy.parser)this.yy.parser.parseError(X,me);else throw new Error(X)},"parseError"),setInput:s(function(X,me){return this.yy=me||this.yy||{},this._input=X,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var X=this._input[0];this.yytext+=X,this.yyleng++,this.offset++,this.match+=X,this.matched+=X;var me=X.match(/(?:\r\n?|\n).*/g);return me?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),X},"input"),unput:s(function(X){var me=X.length,Ce=X.split(/(?:\r\n?|\n)/g);this._input=X+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-me),this.offset-=me;var Be=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),Ce.length-1&&(this.yylineno-=Ce.length-1);var Re=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:Ce?(Ce.length===Be.length?this.yylloc.first_column:0)+Be[Be.length-Ce.length].length-Ce[0].length:this.yylloc.first_column-me},this.options.ranges&&(this.yylloc.range=[Re[0],Re[0]+this.yyleng-me]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(X){this.unput(this.match.slice(X))},"less"),pastInput:s(function(){var X=this.matched.substr(0,this.matched.length-this.match.length);return(X.length>20?"...":"")+X.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var X=this.match;return X.length<20&&(X+=this._input.substr(0,20-X.length)),(X.substr(0,20)+(X.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var X=this.pastInput(),me=new Array(X.length+1).join("-");return X+this.upcomingInput()+` +`+me+"^"},"showPosition"),test_match:s(function(X,me){var Ce,Be,Re;if(this.options.backtrack_lexer&&(Re={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(Re.yylloc.range=this.yylloc.range.slice(0))),Be=X[0].match(/(?:\r\n?|\n).*/g),Be&&(this.yylineno+=Be.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:Be?Be[Be.length-1].length-Be[Be.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+X[0].length},this.yytext+=X[0],this.match+=X[0],this.matches=X,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(X[0].length),this.matched+=X[0],Ce=this.performAction.call(this,this.yy,this,me,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),Ce)return Ce;if(this._backtrack){for(var Ke in Re)this[Ke]=Re[Ke];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var X,me,Ce,Be;this._more||(this.yytext="",this.match="");for(var Re=this._currentRules(),Ke=0;Keme[0].length)){if(me=Ce,Be=Ke,this.options.backtrack_lexer){if(X=this.test_match(Ce,Re[Ke]),X!==!1)return X;if(this._backtrack){me=!1;continue}else return!1}else if(!this.options.flex)break}return me?(X=this.test_match(me,Re[Be]),X!==!1?X:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var X=this.next();return X||this.lex()},"lex"),begin:s(function(X){this.conditionStack.push(X)},"begin"),popState:s(function(){var X=this.conditionStack.length-1;return X>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(X){return X=this.conditionStack.length-1-Math.abs(X||0),X>=0?this.conditionStack[X]:"INITIAL"},"topState"),pushState:s(function(X){this.begin(X)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(X,me,Ce,Be){var Re=Be;switch(Ce){case 0:break;case 1:break;case 2:return 55;case 3:break;case 4:return this.begin("title"),35;case 5:return this.popState(),"title_value";case 6:return this.begin("acc_title"),37;case 7:return this.popState(),"acc_title_value";case 8:return this.begin("acc_descr"),39;case 9:return this.popState(),"acc_descr_value";case 10:this.begin("acc_descr_multiline");break;case 11:this.popState();break;case 12:return"acc_descr_multiline_value";case 13:return 48;case 14:return 50;case 15:return 49;case 16:return 51;case 17:return 52;case 18:return 53;case 19:return 54;case 20:return 25;case 21:this.begin("md_string");break;case 22:return"MD_STR";case 23:this.popState();break;case 24:this.begin("string");break;case 25:this.popState();break;case 26:return"STR";case 27:this.begin("class_name");break;case 28:return this.popState(),47;case 29:return this.begin("point_start"),44;case 30:return this.begin("point_x"),45;case 31:this.popState();break;case 32:this.popState(),this.begin("point_y");break;case 33:return this.popState(),46;case 34:return 28;case 35:return 4;case 36:return 15;case 37:return 11;case 38:return 64;case 39:return 10;case 40:return 65;case 41:return 65;case 42:return 14;case 43:return 13;case 44:return 67;case 45:return 66;case 46:return 12;case 47:return 8;case 48:return 5;case 49:return 18;case 50:return 56;case 51:return 63;case 52:return 57}},"anonymous"),rules:[/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:title\b)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?: *x-axis *)/i,/^(?: *y-axis *)/i,/^(?: *--+> *)/i,/^(?: *quadrant-1 *)/i,/^(?: *quadrant-2 *)/i,/^(?: *quadrant-3 *)/i,/^(?: *quadrant-4 *)/i,/^(?:classDef\b)/i,/^(?:["][`])/i,/^(?:[^`"]+)/i,/^(?:[`]["])/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?::::)/i,/^(?:^\w+)/i,/^(?:\s*:\s*\[\s*)/i,/^(?:(1)|(0(.\d+)?))/i,/^(?:\s*\] *)/i,/^(?:\s*,\s*)/i,/^(?:(1)|(0(.\d+)?))/i,/^(?: *quadrantChart *)/i,/^(?:[A-Za-z]+)/i,/^(?:[^\x00-\x7F]+)/i,/^(?::)/i,/^(?:\+)/i,/^(?:,)/i,/^(?:=)/i,/^(?:=)/i,/^(?:\*)/i,/^(?:#)/i,/^(?:[\_])/i,/^(?:\.)/i,/^(?:&)/i,/^(?:-)/i,/^(?:[0-9]+)/i,/^(?:\s)/i,/^(?:;)/i,/^(?:[!"#$%&'*+,-.`?\\_/])/i,/^(?:$)/i],conditions:{class_name:{rules:[28],inclusive:!1},point_y:{rules:[33],inclusive:!1},point_x:{rules:[32],inclusive:!1},point_start:{rules:[30,31],inclusive:!1},acc_descr_multiline:{rules:[11,12],inclusive:!1},acc_descr:{rules:[9],inclusive:!1},acc_title:{rules:[7],inclusive:!1},title:{rules:[5],inclusive:!1},md_string:{rules:[22,23],inclusive:!1},string:{rules:[25,26],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,6,8,10,13,14,15,16,17,18,19,20,21,24,27,29,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52],inclusive:!0}}};return ve})();K.lexer=de;function ee(){this.yy={}}return s(ee,"Parser"),ee.prototype=K,K.Parser=ee,new ee})(),dI.parser=dI,Hht=dI}),Eo,Ght,tRt=G(()=>{"use strict";var e;pi(),Za(),Nt(),Wh(),Eo=ku(),Ght=(e=class{constructor(){this.classes=new Map,this.config=this.getDefaultConfig(),this.themeConfig=this.getDefaultThemeConfig(),this.data=this.getDefaultData()}getDefaultData(){return{titleText:"",quadrant1Text:"",quadrant2Text:"",quadrant3Text:"",quadrant4Text:"",xAxisLeftText:"",xAxisRightText:"",yAxisBottomText:"",yAxisTopText:"",points:[]}}getDefaultConfig(){return{showXAxis:!0,showYAxis:!0,showTitle:!0,chartHeight:vi.quadrantChart?.chartWidth||500,chartWidth:vi.quadrantChart?.chartHeight||500,titlePadding:vi.quadrantChart?.titlePadding||10,titleFontSize:vi.quadrantChart?.titleFontSize||20,quadrantPadding:vi.quadrantChart?.quadrantPadding||5,xAxisLabelPadding:vi.quadrantChart?.xAxisLabelPadding||5,yAxisLabelPadding:vi.quadrantChart?.yAxisLabelPadding||5,xAxisLabelFontSize:vi.quadrantChart?.xAxisLabelFontSize||16,yAxisLabelFontSize:vi.quadrantChart?.yAxisLabelFontSize||16,quadrantLabelFontSize:vi.quadrantChart?.quadrantLabelFontSize||16,quadrantTextTopPadding:vi.quadrantChart?.quadrantTextTopPadding||5,pointTextPadding:vi.quadrantChart?.pointTextPadding||5,pointLabelFontSize:vi.quadrantChart?.pointLabelFontSize||12,pointRadius:vi.quadrantChart?.pointRadius||5,xAxisPosition:vi.quadrantChart?.xAxisPosition||"top",yAxisPosition:vi.quadrantChart?.yAxisPosition||"left",quadrantInternalBorderStrokeWidth:vi.quadrantChart?.quadrantInternalBorderStrokeWidth||1,quadrantExternalBorderStrokeWidth:vi.quadrantChart?.quadrantExternalBorderStrokeWidth||2}}getDefaultThemeConfig(){return{quadrant1Fill:Eo.quadrant1Fill,quadrant2Fill:Eo.quadrant2Fill,quadrant3Fill:Eo.quadrant3Fill,quadrant4Fill:Eo.quadrant4Fill,quadrant1TextFill:Eo.quadrant1TextFill,quadrant2TextFill:Eo.quadrant2TextFill,quadrant3TextFill:Eo.quadrant3TextFill,quadrant4TextFill:Eo.quadrant4TextFill,quadrantPointFill:Eo.quadrantPointFill,quadrantPointTextFill:Eo.quadrantPointTextFill,quadrantXAxisTextFill:Eo.quadrantXAxisTextFill,quadrantYAxisTextFill:Eo.quadrantYAxisTextFill,quadrantTitleFill:Eo.quadrantTitleFill,quadrantInternalBorderStrokeFill:Eo.quadrantInternalBorderStrokeFill,quadrantExternalBorderStrokeFill:Eo.quadrantExternalBorderStrokeFill}}clear(){this.config=this.getDefaultConfig(),this.themeConfig=this.getDefaultThemeConfig(),this.data=this.getDefaultData(),this.classes=new Map,Ee.info("clear called")}setData(r){this.data={...this.data,...r}}addPoints(r){this.data.points=[...r,...this.data.points]}addClass(r,i){this.classes.set(r,i)}setConfig(r){Ee.trace("setConfig called with: ",r),this.config={...this.config,...r}}setThemeConfig(r){Ee.trace("setThemeConfig called with: ",r),this.themeConfig={...this.themeConfig,...r}}calculateSpace(r,i,n,a){let o=this.config.xAxisLabelPadding*2+this.config.xAxisLabelFontSize,l={top:r==="top"&&i?o:0,bottom:r==="bottom"&&i?o:0},u=this.config.yAxisLabelPadding*2+this.config.yAxisLabelFontSize,h={left:this.config.yAxisPosition==="left"&&n?u:0,right:this.config.yAxisPosition==="right"&&n?u:0},d=this.config.titleFontSize+this.config.titlePadding*2,p={top:a?d:0},f=this.config.quadrantPadding+h.left,g=this.config.quadrantPadding+l.top+p.top,m=this.config.chartWidth-this.config.quadrantPadding*2-h.left-h.right,y=this.config.chartHeight-this.config.quadrantPadding*2-l.top-l.bottom-p.top,k=m/2,C=y/2;return{xAxisSpace:l,yAxisSpace:h,titleSpace:p,quadrantSpace:{quadrantLeft:f,quadrantTop:g,quadrantWidth:m,quadrantHalfWidth:k,quadrantHeight:y,quadrantHalfHeight:C}}}getAxisLabels(r,i,n,a){let{quadrantSpace:o,titleSpace:l}=a,{quadrantHalfHeight:u,quadrantHeight:h,quadrantLeft:d,quadrantHalfWidth:p,quadrantTop:f,quadrantWidth:g}=o,m=!!this.data.xAxisRightText,y=!!this.data.yAxisTopText,k=[];return this.data.xAxisLeftText&&i&&k.push({text:this.data.xAxisLeftText,fill:this.themeConfig.quadrantXAxisTextFill,x:d+(m?p/2:0),y:r==="top"?this.config.xAxisLabelPadding+l.top:this.config.xAxisLabelPadding+f+h+this.config.quadrantPadding,fontSize:this.config.xAxisLabelFontSize,verticalPos:m?"center":"left",horizontalPos:"top",rotation:0}),this.data.xAxisRightText&&i&&k.push({text:this.data.xAxisRightText,fill:this.themeConfig.quadrantXAxisTextFill,x:d+p+(m?p/2:0),y:r==="top"?this.config.xAxisLabelPadding+l.top:this.config.xAxisLabelPadding+f+h+this.config.quadrantPadding,fontSize:this.config.xAxisLabelFontSize,verticalPos:m?"center":"left",horizontalPos:"top",rotation:0}),this.data.yAxisBottomText&&n&&k.push({text:this.data.yAxisBottomText,fill:this.themeConfig.quadrantYAxisTextFill,x:this.config.yAxisPosition==="left"?this.config.yAxisLabelPadding:this.config.yAxisLabelPadding+d+g+this.config.quadrantPadding,y:f+h-(y?u/2:0),fontSize:this.config.yAxisLabelFontSize,verticalPos:y?"center":"left",horizontalPos:"top",rotation:-90}),this.data.yAxisTopText&&n&&k.push({text:this.data.yAxisTopText,fill:this.themeConfig.quadrantYAxisTextFill,x:this.config.yAxisPosition==="left"?this.config.yAxisLabelPadding:this.config.yAxisLabelPadding+d+g+this.config.quadrantPadding,y:f+u-(y?u/2:0),fontSize:this.config.yAxisLabelFontSize,verticalPos:y?"center":"left",horizontalPos:"top",rotation:-90}),k}getQuadrants(r){let{quadrantSpace:i}=r,{quadrantHalfHeight:n,quadrantLeft:a,quadrantHalfWidth:o,quadrantTop:l}=i,u=[{text:{text:this.data.quadrant1Text,fill:this.themeConfig.quadrant1TextFill,x:0,y:0,fontSize:this.config.quadrantLabelFontSize,verticalPos:"center",horizontalPos:"middle",rotation:0},x:a+o,y:l,width:o,height:n,fill:this.themeConfig.quadrant1Fill},{text:{text:this.data.quadrant2Text,fill:this.themeConfig.quadrant2TextFill,x:0,y:0,fontSize:this.config.quadrantLabelFontSize,verticalPos:"center",horizontalPos:"middle",rotation:0},x:a,y:l,width:o,height:n,fill:this.themeConfig.quadrant2Fill},{text:{text:this.data.quadrant3Text,fill:this.themeConfig.quadrant3TextFill,x:0,y:0,fontSize:this.config.quadrantLabelFontSize,verticalPos:"center",horizontalPos:"middle",rotation:0},x:a,y:l+n,width:o,height:n,fill:this.themeConfig.quadrant3Fill},{text:{text:this.data.quadrant4Text,fill:this.themeConfig.quadrant4TextFill,x:0,y:0,fontSize:this.config.quadrantLabelFontSize,verticalPos:"center",horizontalPos:"middle",rotation:0},x:a+o,y:l+n,width:o,height:n,fill:this.themeConfig.quadrant4Fill}];for(let h of u)h.text.x=h.x+h.width/2,this.data.points.length===0?(h.text.y=h.y+h.height/2,h.text.horizontalPos="middle"):(h.text.y=h.y+this.config.quadrantTextTopPadding,h.text.horizontalPos="top");return u}getQuadrantPoints(r){let{quadrantSpace:i}=r,{quadrantHeight:n,quadrantLeft:a,quadrantTop:o,quadrantWidth:l}=i,u=og().domain([0,1]).range([a,l+a]),h=og().domain([0,1]).range([n+o,o]);return this.data.points.map(d=>{let p=this.classes.get(d.className);return p&&(d={...p,...d}),{x:u(d.x),y:h(d.y),fill:d.color??this.themeConfig.quadrantPointFill,radius:d.radius??this.config.pointRadius,text:{text:d.text,fill:this.themeConfig.quadrantPointTextFill,x:u(d.x),y:h(d.y)+this.config.pointTextPadding,verticalPos:"center",horizontalPos:"top",fontSize:this.config.pointLabelFontSize,rotation:0},strokeColor:d.strokeColor??this.themeConfig.quadrantPointFill,strokeWidth:d.strokeWidth??"0px"}})}getBorders(r){let i=this.config.quadrantExternalBorderStrokeWidth/2,{quadrantSpace:n}=r,{quadrantHalfHeight:a,quadrantHeight:o,quadrantLeft:l,quadrantHalfWidth:u,quadrantTop:h,quadrantWidth:d}=n;return[{strokeFill:this.themeConfig.quadrantExternalBorderStrokeFill,strokeWidth:this.config.quadrantExternalBorderStrokeWidth,x1:l-i,y1:h,x2:l+d+i,y2:h},{strokeFill:this.themeConfig.quadrantExternalBorderStrokeFill,strokeWidth:this.config.quadrantExternalBorderStrokeWidth,x1:l+d,y1:h+i,x2:l+d,y2:h+o-i},{strokeFill:this.themeConfig.quadrantExternalBorderStrokeFill,strokeWidth:this.config.quadrantExternalBorderStrokeWidth,x1:l-i,y1:h+o,x2:l+d+i,y2:h+o},{strokeFill:this.themeConfig.quadrantExternalBorderStrokeFill,strokeWidth:this.config.quadrantExternalBorderStrokeWidth,x1:l,y1:h+i,x2:l,y2:h+o-i},{strokeFill:this.themeConfig.quadrantInternalBorderStrokeFill,strokeWidth:this.config.quadrantInternalBorderStrokeWidth,x1:l+u,y1:h+i,x2:l+u,y2:h+o-i},{strokeFill:this.themeConfig.quadrantInternalBorderStrokeFill,strokeWidth:this.config.quadrantInternalBorderStrokeWidth,x1:l+i,y1:h+a,x2:l+d-i,y2:h+a}]}getTitle(r){if(r)return{text:this.data.titleText,fill:this.themeConfig.quadrantTitleFill,fontSize:this.config.titleFontSize,horizontalPos:"top",verticalPos:"center",rotation:0,y:this.config.titlePadding,x:this.config.chartWidth/2}}build(){let r=this.config.showXAxis&&!!(this.data.xAxisLeftText||this.data.xAxisRightText),i=this.config.showYAxis&&!!(this.data.yAxisTopText||this.data.yAxisBottomText),n=this.config.showTitle&&!!this.data.titleText,a=this.data.points.length>0?"bottom":this.config.xAxisPosition,o=this.calculateSpace(a,r,i,n);return{points:this.getQuadrantPoints(o),quadrants:this.getQuadrants(o),axisLabels:this.getAxisLabels(a,r,i,o),borderLines:this.getBorders(o),title:this.getTitle(n)}}},s(e,"QuadrantBuilder"),e)});function Whe(e){return!/^#?([\dA-Fa-f]{6}|[\dA-Fa-f]{3})$/.test(e)}function Xht(e){return!/^\d+$/.test(e)}function Kht(e){return!/^\d+px$/.test(e)}var E5,rRt=G(()=>{"use strict";var e;E5=(e=class extends Error{constructor(r,i,n){super(`value for ${r} ${i} is invalid, please use a valid ${n}`),this.name="InvalidStyleError"}},s(e,"InvalidStyleError"),e),s(Whe,"validateHexCode"),s(Xht,"validateNumber"),s(Kht,"validateSizeInPixels")});function Lh(e){return Si(e.trim(),it())}function Qht(e){Ms.setData({quadrant1Text:Lh(e.text)})}function Zht(e){Ms.setData({quadrant2Text:Lh(e.text)})}function Jht(e){Ms.setData({quadrant3Text:Lh(e.text)})}function edt(e){Ms.setData({quadrant4Text:Lh(e.text)})}function tdt(e){Ms.setData({xAxisLeftText:Lh(e.text)})}function rdt(e){Ms.setData({xAxisRightText:Lh(e.text)})}function idt(e){Ms.setData({yAxisTopText:Lh(e.text)})}function ndt(e){Ms.setData({yAxisBottomText:Lh(e.text)})}function pI(e){let t={};for(let r of e){let[i,n]=r.trim().split(/\s*:\s*/);if(i==="radius"){if(Xht(n))throw new E5(i,n,"number");t.radius=parseInt(n)}else if(i==="color"){if(Whe(n))throw new E5(i,n,"hex code");t.color=n}else if(i==="stroke-color"){if(Whe(n))throw new E5(i,n,"hex code");t.strokeColor=n}else if(i==="stroke-width"){if(Kht(n))throw new E5(i,n,"number of pixels (eg. 10px)");t.strokeWidth=n}else throw new Error(`style named ${i} is not supported.`)}return t}function adt(e,t,r,i,n){let a=pI(n);Ms.addPoints([{x:r,y:i,text:Lh(e.text),className:t,...a}])}function sdt(e,t){Ms.addClass(e,pI(t))}function odt(e){Ms.setConfig({chartWidth:e})}function ldt(e){Ms.setConfig({chartHeight:e})}function cdt(){let e=it(),{themeVariables:t,quadrantChart:r}=e;return r&&Ms.setConfig(r),Ms.setThemeConfig({quadrant1Fill:t.quadrant1Fill,quadrant2Fill:t.quadrant2Fill,quadrant3Fill:t.quadrant3Fill,quadrant4Fill:t.quadrant4Fill,quadrant1TextFill:t.quadrant1TextFill,quadrant2TextFill:t.quadrant2TextFill,quadrant3TextFill:t.quadrant3TextFill,quadrant4TextFill:t.quadrant4TextFill,quadrantPointFill:t.quadrantPointFill,quadrantPointTextFill:t.quadrantPointTextFill,quadrantXAxisTextFill:t.quadrantXAxisTextFill,quadrantYAxisTextFill:t.quadrantYAxisTextFill,quadrantExternalBorderStrokeFill:t.quadrantExternalBorderStrokeFill,quadrantInternalBorderStrokeFill:t.quadrantInternalBorderStrokeFill,quadrantTitleFill:t.quadrantTitleFill}),Ms.setData({titleText:xn()}),Ms.build()}var Ms,udt,hdt,iRt=G(()=>{"use strict";Er(),mi(),fn(),tRt(),rRt(),s(Lh,"textSanitizer"),Ms=new Ght,s(Qht,"setQuadrant1Text"),s(Zht,"setQuadrant2Text"),s(Jht,"setQuadrant3Text"),s(edt,"setQuadrant4Text"),s(tdt,"setXAxisLeftText"),s(rdt,"setXAxisRightText"),s(idt,"setYAxisTopText"),s(ndt,"setYAxisBottomText"),s(pI,"parseStyles"),s(adt,"addPoint"),s(sdt,"addClass"),s(odt,"setWidth"),s(ldt,"setHeight"),s(cdt,"getQuadrantData"),udt=s(function(){Ms.clear(),pn()},"clear"),hdt={setWidth:odt,setHeight:ldt,setQuadrant1Text:Qht,setQuadrant2Text:Zht,setQuadrant3Text:Jht,setQuadrant4Text:edt,setXAxisLeftText:tdt,setXAxisRightText:rdt,setYAxisTopText:idt,setYAxisBottomText:ndt,parseStyles:pI,addPoint:adt,addClass:sdt,getQuadrantData:cdt,clear:udt,setAccTitle:bn,getAccTitle:Ln,setDiagramTitle:Dn,getDiagramTitle:xn,getAccDescription:Mn,setAccDescription:In}}),ddt,pdt,nRt=G(()=>{"use strict";pi(),Er(),Nt(),vn(),ddt=s((e,t,r,i)=>{function n(v){return v==="top"?"hanging":"middle"}s(n,"getDominantBaseLine");function a(v){return v==="left"?"start":"middle"}s(a,"getTextAnchor");function o(v){return`translate(${v.x}, ${v.y}) rotate(${v.rotation||0})`}s(o,"getTransformation");let l=it();Ee.debug(`Rendering quadrant chart +`+e);let u=l.securityLevel,h;u==="sandbox"&&(h=zt("#i"+t));let d=zt(u==="sandbox"?h.nodes()[0].contentDocument.body:"body").select(`[id="${t}"]`),p=d.append("g").attr("class","main"),f=l.quadrantChart?.chartWidth??500,g=l.quadrantChart?.chartHeight??500;aa(d,g,f,l.quadrantChart?.useMaxWidth??!0),d.attr("viewBox","0 0 "+f+" "+g),i.db.setHeight(g),i.db.setWidth(f);let m=i.db.getQuadrantData(),y=p.append("g").attr("class","quadrants"),k=p.append("g").attr("class","border"),C=p.append("g").attr("class","data-points"),S=p.append("g").attr("class","labels"),E=p.append("g").attr("class","title");m.title&&E.append("text").attr("x",0).attr("y",0).attr("fill",m.title.fill).attr("font-size",m.title.fontSize).attr("dominant-baseline",n(m.title.horizontalPos)).attr("text-anchor",a(m.title.verticalPos)).attr("transform",o(m.title)).text(m.title.text),m.borderLines&&k.selectAll("line").data(m.borderLines).enter().append("line").attr("x1",v=>v.x1).attr("y1",v=>v.y1).attr("x2",v=>v.x2).attr("y2",v=>v.y2).style("stroke",v=>v.strokeFill).style("stroke-width",v=>v.strokeWidth);let _=y.selectAll("g.quadrant").data(m.quadrants).enter().append("g").attr("class","quadrant");_.append("rect").attr("x",v=>v.x).attr("y",v=>v.y).attr("width",v=>v.width).attr("height",v=>v.height).attr("fill",v=>v.fill),_.append("text").attr("x",0).attr("y",0).attr("fill",v=>v.text.fill).attr("font-size",v=>v.text.fontSize).attr("dominant-baseline",v=>n(v.text.horizontalPos)).attr("text-anchor",v=>a(v.text.verticalPos)).attr("transform",v=>o(v.text)).text(v=>v.text.text),S.selectAll("g.label").data(m.axisLabels).enter().append("g").attr("class","label").append("text").attr("x",0).attr("y",0).text(v=>v.text).attr("fill",v=>v.fill).attr("font-size",v=>v.fontSize).attr("dominant-baseline",v=>n(v.horizontalPos)).attr("text-anchor",v=>a(v.verticalPos)).attr("transform",v=>o(v));let b=C.selectAll("g.data-point").data(m.points).enter().append("g").attr("class","data-point");b.append("circle").attr("cx",v=>v.x).attr("cy",v=>v.y).attr("r",v=>v.radius).attr("fill",v=>v.fill).attr("stroke",v=>v.strokeColor).attr("stroke-width",v=>v.strokeWidth),b.append("text").attr("x",0).attr("y",0).text(v=>v.text.text).attr("fill",v=>v.text.fill).attr("font-size",v=>v.text.fontSize).attr("dominant-baseline",v=>n(v.text.horizontalPos)).attr("text-anchor",v=>a(v.text.verticalPos)).attr("transform",v=>o(v.text))},"draw"),pdt={draw:ddt}}),fdt={};Mr(fdt,{diagram:()=>gdt});var gdt,aRt=G(()=>{"use strict";eRt(),iRt(),nRt(),gdt={parser:Hht,db:hdt,renderer:pdt,styles:s(()=>"","styles")}}),fI,mdt,sRt=G(()=>{"use strict";fI=(function(){var e=s(function(F,O,B,Y){for(B=B||{},Y=F.length;Y--;B[F[Y]]=O);return B},"o"),t=[1,10,12,14,16,18,19,21,23],r=[2,6],i=[1,3],n=[1,5],a=[1,6],o=[1,7],l=[1,5,10,12,14,16,18,19,21,23,36,37,38],u=[1,25],h=[1,26],d=[1,28],p=[1,29],f=[1,30],g=[1,31],m=[1,32],y=[1,33],k=[1,34],C=[1,35],S=[1,36],E=[1,37],_=[1,43],b=[1,42],v=[1,47],x=[1,50],w=[1,10,12,14,16,18,19,21,23,36,37,38],R=[1,10,12,14,16,18,19,21,23,24,26,28,29,36,37,38],$=[1,10,12,14,16,18,19,21,23,24,26,28,29,36,37,38,42,43,44,45,46,47,48,49,50,51],M=[1,65],N=[26,28],j={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,dataPoints:25,SQUARE_BRACES_END:26,dataPoint:27,COMMA:28,NUMBER_WITH_DECIMAL:29,STR:30,xAxisData:31,bandData:32,ARROW_DELIMITER:33,commaSeparatedTexts:34,yAxisData:35,NEWLINE:36,SEMI:37,EOF:38,alphaNum:39,MD_STR:40,alphaNumToken:41,AMP:42,NUM:43,ALPHA:44,PLUS:45,EQUALS:46,MULT:47,DOT:48,BRKT:49,MINUS:50,UNDERSCORE:51,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",28:"COMMA",29:"NUMBER_WITH_DECIMAL",30:"STR",33:"ARROW_DELIMITER",36:"NEWLINE",37:"SEMI",38:"EOF",40:"MD_STR",42:"AMP",43:"NUM",44:"ALPHA",45:"PLUS",46:"EQUALS",47:"MULT",48:"DOT",49:"BRKT",50:"MINUS",51:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[27,2],[27,1],[13,1],[13,2],[13,1],[31,1],[31,3],[32,3],[34,3],[34,1],[15,1],[15,2],[15,1],[35,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[39,1],[39,2],[41,1],[41,1],[41,1],[41,1],[41,1],[41,1],[41,1],[41,1],[41,1],[41,1]],performAction:s(function(F,O,B,Y,U,H,J){var te=H.length-1;switch(U){case 5:Y.setOrientation(H[te]);break;case 9:Y.setDiagramTitle(H[te].text.trim());break;case 12:Y.setLineData({text:"",type:"text"},H[te]);break;case 13:Y.setLineData(H[te-1],H[te]);break;case 14:Y.setBarData({text:"",type:"text"},H[te]);break;case 15:Y.setBarData(H[te-1],H[te]);break;case 16:this.$=H[te].trim(),Y.setAccTitle(this.$);break;case 17:case 18:this.$=H[te].trim(),Y.setAccDescription(this.$);break;case 19:this.$=H[te-1];break;case 20:case 30:this.$=[H[te-2],...H[te]];break;case 21:case 31:this.$=[H[te]];break;case 22:this.$={value:Number(H[te-1]),label:H[te]};break;case 23:this.$={value:Number(H[te]),label:""};break;case 24:Y.setXAxisTitle(H[te]);break;case 25:Y.setXAxisTitle(H[te-1]);break;case 26:Y.setXAxisTitle({type:"text",text:""});break;case 27:Y.setXAxisBand(H[te]);break;case 28:Y.setXAxisRangeData(Number(H[te-2]),Number(H[te]));break;case 29:this.$=H[te-1];break;case 32:Y.setYAxisTitle(H[te]);break;case 33:Y.setYAxisTitle(H[te-1]);break;case 34:Y.setYAxisTitle({type:"text",text:""});break;case 35:Y.setYAxisRangeData(Number(H[te-2]),Number(H[te]));break;case 39:this.$={text:H[te],type:"text"};break;case 40:this.$={text:H[te],type:"text"};break;case 41:this.$={text:H[te],type:"markdown"};break;case 42:this.$=H[te];break;case 43:this.$=H[te-1]+""+H[te];break}},"anonymous"),table:[e(t,r,{3:1,4:2,7:4,5:i,36:n,37:a,38:o}),{1:[3]},e(t,r,{4:2,7:4,3:8,5:i,36:n,37:a,38:o}),e(t,r,{4:2,7:4,6:9,3:10,5:i,8:[1,11],36:n,37:a,38:o}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},e(l,[2,36]),e(l,[2,37]),e(l,[2,38]),{1:[2,1]},e(t,r,{4:2,7:4,3:21,5:i,36:n,37:a,38:o}),{1:[2,3]},e(l,[2,5]),e(t,[2,7],{4:22,36:n,37:a,38:o}),{11:23,30:u,39:24,40:h,41:27,42:d,43:p,44:f,45:g,46:m,47:y,48:k,49:C,50:S,51:E},{11:39,13:38,24:_,29:b,30:u,31:40,32:41,39:24,40:h,41:27,42:d,43:p,44:f,45:g,46:m,47:y,48:k,49:C,50:S,51:E},{11:45,15:44,29:v,30:u,35:46,39:24,40:h,41:27,42:d,43:p,44:f,45:g,46:m,47:y,48:k,49:C,50:S,51:E},{11:49,17:48,24:x,30:u,39:24,40:h,41:27,42:d,43:p,44:f,45:g,46:m,47:y,48:k,49:C,50:S,51:E},{11:52,17:51,24:x,30:u,39:24,40:h,41:27,42:d,43:p,44:f,45:g,46:m,47:y,48:k,49:C,50:S,51:E},{20:[1,53]},{22:[1,54]},e(w,[2,18]),{1:[2,2]},e(w,[2,8]),e(w,[2,9]),e(R,[2,39],{41:55,42:d,43:p,44:f,45:g,46:m,47:y,48:k,49:C,50:S,51:E}),e(R,[2,40]),e(R,[2,41]),e($,[2,42]),e($,[2,44]),e($,[2,45]),e($,[2,46]),e($,[2,47]),e($,[2,48]),e($,[2,49]),e($,[2,50]),e($,[2,51]),e($,[2,52]),e($,[2,53]),e(w,[2,10]),e(w,[2,24],{32:41,31:56,24:_,29:b}),e(w,[2,26]),e(w,[2,27]),{33:[1,57]},{11:59,30:u,34:58,39:24,40:h,41:27,42:d,43:p,44:f,45:g,46:m,47:y,48:k,49:C,50:S,51:E},e(w,[2,11]),e(w,[2,32],{35:60,29:v}),e(w,[2,34]),{33:[1,61]},e(w,[2,12]),{17:62,24:x},{25:63,27:64,29:M},e(w,[2,14]),{17:66,24:x},e(w,[2,16]),e(w,[2,17]),e($,[2,43]),e(w,[2,25]),{29:[1,67]},{26:[1,68]},{26:[2,31],28:[1,69]},e(w,[2,33]),{29:[1,70]},e(w,[2,13]),{26:[1,71]},{26:[2,21],28:[1,72]},e(N,[2,23],{30:[1,73]}),e(w,[2,15]),e(w,[2,28]),e(w,[2,29]),{11:59,30:u,34:74,39:24,40:h,41:27,42:d,43:p,44:f,45:g,46:m,47:y,48:k,49:C,50:S,51:E},e(w,[2,35]),e(w,[2,19]),{25:75,27:64,29:M},e(N,[2,22]),{26:[2,30]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],74:[2,30],75:[2,20]},parseError:s(function(F,O){if(O.recoverable)this.trace(F);else{var B=new Error(F);throw B.hash=O,B}},"parseError"),parse:s(function(F){var O=this,B=[0],Y=[],U=[null],H=[],J=this.table,te="",ie=0,ue=0,se=0,le=2,Te=1,oe=H.slice.call(arguments,1),fe=Object.create(this.lexer),ge={yy:{}};for(var ke in this.yy)Object.prototype.hasOwnProperty.call(this.yy,ke)&&(ge.yy[ke]=this.yy[ke]);fe.setInput(F,ge.yy),ge.yy.lexer=fe,ge.yy.parser=this,typeof fe.yylloc>"u"&&(fe.yylloc={});var Ne=fe.yylloc;H.push(Ne);var xe=fe.options&&fe.options.ranges;typeof ge.yy.parseError=="function"?this.parseError=ge.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function $e(Ae){B.length=B.length-2*Ae,U.length=U.length-Ae,H.length=H.length-Ae}s($e,"popStack");function Me(){var Ae;return Ae=Y.pop()||fe.lex()||Te,typeof Ae!="number"&&(Ae instanceof Array&&(Y=Ae,Ae=Y.pop()),Ae=O.symbols_[Ae]||Ae),Ae}s(Me,"lex");for(var He,Ge,ze,Ve,et,he,ye={},be,Ie,pe,Qe;;){if(ze=B[B.length-1],this.defaultActions[ze]?Ve=this.defaultActions[ze]:((He===null||typeof He>"u")&&(He=Me()),Ve=J[ze]&&J[ze][He]),typeof Ve>"u"||!Ve.length||!Ve[0]){var W="";Qe=[];for(be in J[ze])this.terminals_[be]&&be>le&&Qe.push("'"+this.terminals_[be]+"'");fe.showPosition?W="Parse error on line "+(ie+1)+`: +`+fe.showPosition()+` +Expecting `+Qe.join(", ")+", got '"+(this.terminals_[He]||He)+"'":W="Parse error on line "+(ie+1)+": Unexpected "+(He==Te?"end of input":"'"+(this.terminals_[He]||He)+"'"),this.parseError(W,{text:fe.match,token:this.terminals_[He]||He,line:fe.yylineno,loc:Ne,expected:Qe})}if(Ve[0]instanceof Array&&Ve.length>1)throw new Error("Parse Error: multiple actions possible at state: "+ze+", token: "+He);switch(Ve[0]){case 1:B.push(He),U.push(fe.yytext),H.push(fe.yylloc),B.push(Ve[1]),He=null,Ge?(He=Ge,Ge=null):(ue=fe.yyleng,te=fe.yytext,ie=fe.yylineno,Ne=fe.yylloc,se>0&&se--);break;case 2:if(Ie=this.productions_[Ve[1]][1],ye.$=U[U.length-Ie],ye._$={first_line:H[H.length-(Ie||1)].first_line,last_line:H[H.length-1].last_line,first_column:H[H.length-(Ie||1)].first_column,last_column:H[H.length-1].last_column},xe&&(ye._$.range=[H[H.length-(Ie||1)].range[0],H[H.length-1].range[1]]),he=this.performAction.apply(ye,[te,ue,ie,ge.yy,Ve[1],U,H].concat(oe)),typeof he<"u")return he;Ie&&(B=B.slice(0,-1*Ie*2),U=U.slice(0,-1*Ie),H=H.slice(0,-1*Ie)),B.push(this.productions_[Ve[1]][0]),U.push(ye.$),H.push(ye._$),pe=J[B[B.length-2]][B[B.length-1]],B.push(pe);break;case 3:return!0}}return!0},"parse")},I=(function(){var F={EOF:1,parseError:s(function(O,B){if(this.yy.parser)this.yy.parser.parseError(O,B);else throw new Error(O)},"parseError"),setInput:s(function(O,B){return this.yy=B||this.yy||{},this._input=O,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var O=this._input[0];this.yytext+=O,this.yyleng++,this.offset++,this.match+=O,this.matched+=O;var B=O.match(/(?:\r\n?|\n).*/g);return B?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),O},"input"),unput:s(function(O){var B=O.length,Y=O.split(/(?:\r\n?|\n)/g);this._input=O+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-B),this.offset-=B;var U=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),Y.length-1&&(this.yylineno-=Y.length-1);var H=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:Y?(Y.length===U.length?this.yylloc.first_column:0)+U[U.length-Y.length].length-Y[0].length:this.yylloc.first_column-B},this.options.ranges&&(this.yylloc.range=[H[0],H[0]+this.yyleng-B]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(O){this.unput(this.match.slice(O))},"less"),pastInput:s(function(){var O=this.matched.substr(0,this.matched.length-this.match.length);return(O.length>20?"...":"")+O.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var O=this.match;return O.length<20&&(O+=this._input.substr(0,20-O.length)),(O.substr(0,20)+(O.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var O=this.pastInput(),B=new Array(O.length+1).join("-");return O+this.upcomingInput()+` +`+B+"^"},"showPosition"),test_match:s(function(O,B){var Y,U,H;if(this.options.backtrack_lexer&&(H={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(H.yylloc.range=this.yylloc.range.slice(0))),U=O[0].match(/(?:\r\n?|\n).*/g),U&&(this.yylineno+=U.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:U?U[U.length-1].length-U[U.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+O[0].length},this.yytext+=O[0],this.match+=O[0],this.matches=O,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(O[0].length),this.matched+=O[0],Y=this.performAction.call(this,this.yy,this,B,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),Y)return Y;if(this._backtrack){for(var J in H)this[J]=H[J];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var O,B,Y,U;this._more||(this.yytext="",this.match="");for(var H=this._currentRules(),J=0;JB[0].length)){if(B=Y,U=J,this.options.backtrack_lexer){if(O=this.test_match(Y,H[J]),O!==!1)return O;if(this._backtrack){B=!1;continue}else return!1}else if(!this.options.flex)break}return B?(O=this.test_match(B,H[U]),O!==!1?O:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var O=this.next();return O||this.lex()},"lex"),begin:s(function(O){this.conditionStack.push(O)},"begin"),popState:s(function(){var O=this.conditionStack.length-1;return O>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(O){return O=this.conditionStack.length-1-Math.abs(O||0),O>=0?this.conditionStack[O]:"INITIAL"},"topState"),pushState:s(function(O){this.begin(O)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(O,B,Y,U){var H=U;switch(Y){case 0:break;case 1:break;case 2:return this.popState(),36;case 3:return this.popState(),36;case 4:return 36;case 5:break;case 6:return 10;case 7:return this.pushState("acc_title"),19;case 8:return this.popState(),"acc_title_value";case 9:return this.pushState("acc_descr"),21;case 10:return this.popState(),"acc_descr_value";case 11:this.pushState("acc_descr_multiline");break;case 12:this.popState();break;case 13:return"acc_descr_multiline_value";case 14:return 5;case 15:return 5;case 16:return 8;case 17:return this.pushState("axis_data"),"X_AXIS";case 18:return this.pushState("axis_data"),"Y_AXIS";case 19:return this.pushState("axis_band_data"),24;case 20:return 33;case 21:return this.pushState("data"),16;case 22:return this.pushState("data"),18;case 23:return this.pushState("data_inner"),24;case 24:return 29;case 25:return this.popState(),26;case 26:this.popState();break;case 27:this.pushState("string");break;case 28:this.popState();break;case 29:return"STR";case 30:return 24;case 31:return 26;case 32:return 44;case 33:return"COLON";case 34:return 45;case 35:return 28;case 36:return 46;case 37:return 47;case 38:return 49;case 39:return 51;case 40:return 48;case 41:return 42;case 42:return 50;case 43:return 43;case 44:break;case 45:return 37;case 46:return 38}},"anonymous"),rules:[/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:(\r?\n))/i,/^(?:(\r?\n))/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:title\b)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:\})/i,/^(?:[^\}]*)/i,/^(?:xychart-beta\b)/i,/^(?:xychart\b)/i,/^(?:(?:vertical|horizontal))/i,/^(?:x-axis\b)/i,/^(?:y-axis\b)/i,/^(?:\[)/i,/^(?:-->)/i,/^(?:line\b)/i,/^(?:bar\b)/i,/^(?:\[)/i,/^(?:[+-]?(?:\d+(?:\.\d+)?|\.\d+))/i,/^(?:\])/i,/^(?:(?:`\) \{ this\.pushState\(md_string\); \}\n\(\?:\(\?!`"\)\.\)\+ \{ return MD_STR; \}\n\(\?:`))/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:\[)/i,/^(?:\])/i,/^(?:[A-Za-z]+)/i,/^(?::)/i,/^(?:\+)/i,/^(?:,)/i,/^(?:=)/i,/^(?:\*)/i,/^(?:#)/i,/^(?:[\_])/i,/^(?:\.)/i,/^(?:&)/i,/^(?:-)/i,/^(?:[0-9]+)/i,/^(?:\s+)/i,/^(?:;)/i,/^(?:$)/i],conditions:{data_inner:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,18,21,22,24,25,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},data:{rules:[0,1,3,4,5,6,7,9,11,14,15,16,17,18,21,22,23,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},axis_band_data:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,18,21,22,25,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},axis_data:{rules:[0,1,2,4,5,6,7,9,11,14,15,16,17,18,19,20,21,22,24,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},acc_descr_multiline:{rules:[12,13],inclusive:!1},acc_descr:{rules:[10],inclusive:!1},acc_title:{rules:[8],inclusive:!1},title:{rules:[],inclusive:!1},md_string:{rules:[],inclusive:!1},string:{rules:[28,29],inclusive:!1},INITIAL:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,18,21,22,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0}}};return F})();j.lexer=I;function z(){this.yy={}}return s(z,"Parser"),z.prototype=j,j.Parser=z,new z})(),fI.parser=fI,mdt=fI});function gI(e){return e.type==="bar"}function mI(e){return e.type==="band"}function Vb(e){return e.type==="linear"}var yI=G(()=>{"use strict";s(gI,"isBarPlot"),s(mI,"isBandAxisData"),s(Vb,"isLinearAxisData")}),vI,jhe=G(()=>{"use strict";var e;ec(),vI=(e=class{constructor(r){this.parentGroup=r}getMaxDimension(r,i){if(!this.parentGroup)return{width:r.reduce((o,l)=>Math.max(l.length,o),0)*i,height:i};let n={width:0,height:0},a=this.parentGroup.append("g").attr("visibility","hidden").attr("font-size",i);for(let o of r){let l=wwe(a,1,o),u=l?l.width:o.length*i,h=l?l.height:i;n.width=Math.max(n.width,u),n.height=Math.max(n.height,h)}return a.remove(),n}},s(e,"TextDimensionCalculatorWithFont"),e)}),Vhe,ydt=G(()=>{"use strict";var e;Vhe=(e=class{constructor(r,i,n,a){this.axisConfig=r,this.title=i,this.textDimensionCalculator=n,this.axisThemeConfig=a,this.boundingRect={x:0,y:0,width:0,height:0},this.axisPosition="left",this.showTitle=!1,this.showLabel=!1,this.showTick=!1,this.showAxisLine=!1,this.outerPadding=0,this.titleTextHeight=0,this.labelTextHeight=0,this.normalizedLabelRotationInRad=0,this.range=[0,10],this.boundingRect={x:0,y:0,width:0,height:0},this.axisPosition="left",this.normalizedLabelRotationInRad=this.axisConfig.labelRotation>=-90&&this.axisConfig.labelRotation<=90?this.axisConfig.labelRotation*Math.PI/180:0}setRange(r){this.range=r,this.axisPosition==="left"||this.axisPosition==="right"?this.boundingRect.height=r[1]-r[0]:this.boundingRect.width=r[1]-r[0],this.recalculateScale()}getRange(){return[this.range[0]+this.outerPadding,this.range[1]-this.outerPadding]}setAxisPosition(r){this.axisPosition=r,this.setRange(this.range)}getTickDistance(){let r=this.getRange();return Math.abs(r[0]-r[1])/this.getTickValues().length}getAxisOuterPadding(){return this.outerPadding}getLabelDimension(){return this.textDimensionCalculator.getMaxDimension(this.getTickValues().map(r=>r.toString()),this.axisConfig.labelFontSize)}recalculateOuterPaddingToDrawBar(){.7*this.getTickDistance()>this.outerPadding*2&&(this.outerPadding=Math.floor(.7*this.getTickDistance()/2)),this.recalculateScale()}calculateSpaceIfDrawnHorizontally(r){let i=r.height;if(this.axisConfig.showAxisLine&&i>this.axisConfig.axisLineWidth&&(i-=this.axisConfig.axisLineWidth,this.showAxisLine=!0),this.axisConfig.showLabel){let n=this.getLabelDimension(),a=.2*r.width;this.outerPadding=Math.min(n.width/2,a);let o=n.height;this.axisPosition==="bottom"&&this.normalizedLabelRotationInRad!==0&&(o=Math.max(o,Math.abs(Math.sin(this.normalizedLabelRotationInRad)*n.width)+Math.abs(Math.cos(this.normalizedLabelRotationInRad)*n.height))),o+=this.axisConfig.labelPadding*2,this.labelTextHeight=n.height,o<=i&&(i-=o,this.showLabel=!0)}if(this.axisConfig.showTick&&i>=this.axisConfig.tickLength&&(this.showTick=!0,i-=this.axisConfig.tickLength),this.axisConfig.showTitle&&this.title){let n=this.textDimensionCalculator.getMaxDimension([this.title],this.axisConfig.titleFontSize),a=n.height+this.axisConfig.titlePadding*2;this.titleTextHeight=n.height,a<=i&&(i-=a,this.showTitle=!0)}this.boundingRect.width=r.width,this.boundingRect.height=r.height-i}calculateSpaceIfDrawnVertical(r){let i=r.width;if(this.axisConfig.showAxisLine&&i>this.axisConfig.axisLineWidth&&(i-=this.axisConfig.axisLineWidth,this.showAxisLine=!0),this.axisConfig.showLabel){let n=this.getLabelDimension(),a=.2*r.height;this.outerPadding=Math.min(n.height/2,a);let o=n.width+this.axisConfig.labelPadding*2;o<=i&&(i-=o,this.showLabel=!0)}if(this.axisConfig.showTick&&i>=this.axisConfig.tickLength&&(this.showTick=!0,i-=this.axisConfig.tickLength),this.axisConfig.showTitle&&this.title){let n=this.textDimensionCalculator.getMaxDimension([this.title],this.axisConfig.titleFontSize),a=n.height+this.axisConfig.titlePadding*2;this.titleTextHeight=n.height,a<=i&&(i-=a,this.showTitle=!0)}this.boundingRect.width=r.width-i,this.boundingRect.height=r.height}calculateSpace(r){return this.axisPosition==="left"||this.axisPosition==="right"?this.calculateSpaceIfDrawnVertical(r):this.calculateSpaceIfDrawnHorizontally(r),this.recalculateScale(),{width:this.boundingRect.width,height:this.boundingRect.height}}setBoundingBoxXY(r){this.boundingRect.x=r.x,this.boundingRect.y=r.y}calculateOffsetByRotation(r){let i=this.normalizedLabelRotationInRad;return i===0?0:Math.sin(i)*this.getLabelDimension()[r]/2}getDrawableElementsForLeftAxis(){let r=[];if(this.showAxisLine){let i=this.boundingRect.x+this.boundingRect.width-this.axisConfig.axisLineWidth/2;r.push({type:"path",groupTexts:["left-axis","axisl-line"],data:[{path:`M ${i},${this.boundingRect.y} L ${i},${this.boundingRect.y+this.boundingRect.height} `,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&r.push({type:"text",groupTexts:["left-axis","label"],data:this.getTickValues().map(i=>({text:i.toString(),x:this.boundingRect.x+this.boundingRect.width-(this.showLabel?this.axisConfig.labelPadding:0)-(this.showTick?this.axisConfig.tickLength:0)-(this.showAxisLine?this.axisConfig.axisLineWidth:0),y:this.getScaleValue(i),fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"middle",horizontalPos:"right"}))}),this.showTick){let i=this.boundingRect.x+this.boundingRect.width-(this.showAxisLine?this.axisConfig.axisLineWidth:0);r.push({type:"path",groupTexts:["left-axis","ticks"],data:this.getTickValues().map(n=>({path:`M ${i},${this.getScaleValue(n)} L ${i-this.axisConfig.tickLength},${this.getScaleValue(n)}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&r.push({type:"text",groupTexts:["left-axis","title"],data:[{text:this.title,x:this.boundingRect.x+this.axisConfig.titlePadding,y:this.boundingRect.y+this.boundingRect.height/2,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:270,verticalPos:"top",horizontalPos:"center"}]}),r}getDrawableElementsForBottomAxis(){let r=[];if(this.showAxisLine){let i=this.boundingRect.y+this.axisConfig.axisLineWidth/2;r.push({type:"path",groupTexts:["bottom-axis","axis-line"],data:[{path:`M ${this.boundingRect.x},${i} L ${this.boundingRect.x+this.boundingRect.width},${i}`,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&r.push({type:"text",groupTexts:["bottom-axis","label"],data:this.getTickValues().map(i=>({text:i.toString(),x:this.getScaleValue(i)+this.calculateOffsetByRotation("height"),y:this.boundingRect.y+this.axisConfig.labelPadding+(this.showTick?this.axisConfig.tickLength:0)+(this.showAxisLine?this.axisConfig.axisLineWidth:0)+Math.abs(this.calculateOffsetByRotation("width")),fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:this.normalizedLabelRotationInRad*180/Math.PI,verticalPos:"top",horizontalPos:"center"}))}),this.showTick){let i=this.boundingRect.y+(this.showAxisLine?this.axisConfig.axisLineWidth:0);r.push({type:"path",groupTexts:["bottom-axis","ticks"],data:this.getTickValues().map(n=>({path:`M ${this.getScaleValue(n)},${i} L ${this.getScaleValue(n)},${i+this.axisConfig.tickLength}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&r.push({type:"text",groupTexts:["bottom-axis","title"],data:[{text:this.title,x:this.range[0]+(this.range[1]-this.range[0])/2,y:this.boundingRect.y+this.boundingRect.height-this.axisConfig.titlePadding-this.titleTextHeight,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}]}),r}getDrawableElementsForTopAxis(){let r=[];if(this.showAxisLine){let i=this.boundingRect.y+this.boundingRect.height-this.axisConfig.axisLineWidth/2;r.push({type:"path",groupTexts:["top-axis","axis-line"],data:[{path:`M ${this.boundingRect.x},${i} L ${this.boundingRect.x+this.boundingRect.width},${i}`,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&r.push({type:"text",groupTexts:["top-axis","label"],data:this.getTickValues().map(i=>({text:i.toString(),x:this.getScaleValue(i),y:this.boundingRect.y+(this.showTitle?this.titleTextHeight+this.axisConfig.titlePadding*2:0)+this.axisConfig.labelPadding,fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}))}),this.showTick){let i=this.boundingRect.y;r.push({type:"path",groupTexts:["top-axis","ticks"],data:this.getTickValues().map(n=>({path:`M ${this.getScaleValue(n)},${i+this.boundingRect.height-(this.showAxisLine?this.axisConfig.axisLineWidth:0)} L ${this.getScaleValue(n)},${i+this.boundingRect.height-this.axisConfig.tickLength-(this.showAxisLine?this.axisConfig.axisLineWidth:0)}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&r.push({type:"text",groupTexts:["top-axis","title"],data:[{text:this.title,x:this.boundingRect.x+this.boundingRect.width/2,y:this.boundingRect.y+this.axisConfig.titlePadding,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}]}),r}getDrawableElements(){if(this.axisPosition==="left")return this.getDrawableElementsForLeftAxis();if(this.axisPosition==="right")throw Error("Drawing of right axis is not implemented");return this.axisPosition==="bottom"?this.getDrawableElementsForBottomAxis():this.axisPosition==="top"?this.getDrawableElementsForTopAxis():[]}},s(e,"BaseAxis"),e)}),vdt,oRt=G(()=>{"use strict";var e;pi(),Nt(),ydt(),vdt=(e=class extends Vhe{constructor(r,i,n,a,o){super(r,a,o,i),this.categories=n,this.scale=S6().domain(this.categories).range(this.getRange())}setRange(r){super.setRange(r)}recalculateScale(){this.scale=S6().domain(this.categories).range(this.getRange()).paddingInner(1).paddingOuter(0).align(.5),Ee.trace("BandAxis axis final categories, range: ",this.categories,this.getRange())}getTickValues(){return this.categories}getScaleValue(r){return this.scale(r)??this.getRange()[0]}},s(e,"BandAxis"),e)}),bdt,lRt=G(()=>{"use strict";var e;pi(),ydt(),bdt=(e=class extends Vhe{constructor(r,i,n,a,o){super(r,a,o,i),this.domain=n,this.scale=og().domain(this.domain).range(this.getRange())}getTickValues(){return this.scale.ticks()}recalculateScale(){let r=[...this.domain];this.axisPosition==="left"&&r.reverse(),this.scale=og().domain(r).range(this.getRange())}getScaleValue(r){return this.scale(r)}},s(e,"LinearAxis"),e)});function Uhe(e,t,r,i){let n=new vI(i);return mI(e)?new vdt(t,r,e.categories,e.title,n):new bdt(t,r,[e.min,e.max],e.title,n)}var cRt=G(()=>{"use strict";yI(),jhe(),oRt(),lRt(),s(Uhe,"getAxis")});function xdt(e,t,r,i){let n=new vI(i);return new Tdt(n,e,t,r)}var Tdt,uRt=G(()=>{"use strict";var e;jhe(),Tdt=(e=class{constructor(r,i,n,a){this.textDimensionCalculator=r,this.chartConfig=i,this.chartData=n,this.chartThemeConfig=a,this.boundingRect={x:0,y:0,width:0,height:0},this.showChartTitle=!1}setBoundingBoxXY(r){this.boundingRect.x=r.x,this.boundingRect.y=r.y}calculateSpace(r){let i=this.textDimensionCalculator.getMaxDimension([this.chartData.title],this.chartConfig.titleFontSize),n=Math.max(i.width,r.width),a=i.height+2*this.chartConfig.titlePadding;return i.width<=n&&i.height<=a&&this.chartConfig.showTitle&&this.chartData.title&&(this.boundingRect.width=n,this.boundingRect.height=a,this.showChartTitle=!0),{width:this.boundingRect.width,height:this.boundingRect.height}}getDrawableElements(){let r=[];return this.showChartTitle&&r.push({groupTexts:["chart-title"],type:"text",data:[{fontSize:this.chartConfig.titleFontSize,text:this.chartData.title,verticalPos:"middle",horizontalPos:"center",x:this.boundingRect.x+this.boundingRect.width/2,y:this.boundingRect.y+this.boundingRect.height/2,fill:this.chartThemeConfig.titleColor,rotation:0}]}),r}},s(e,"ChartTitle"),e),s(xdt,"getChartTitleComponent")});function Yhe(e){return{fontSize:e,markerSize:e*Cdt,markerSpacing:e*Edt,itemSpacing:e*wdt}}function kdt(e,t,r,i){let n=new vI(i);return new Sdt(n,e,t,r)}var Cdt,wdt,Edt,Sdt,hRt=G(()=>{"use strict";var e;yI(),jhe(),Cdt=.75,wdt=.5,Edt=.35,s(Yhe,"getLegendLayout"),Sdt=(e=class{constructor(r,i,n,a){this.textDimensionCalculator=r,this.chartConfig=i,this.chartData=n,this.chartThemeConfig=a,this.boundingRect={x:0,y:0,width:0,height:0},this.visiblePlots=[]}setBoundingBoxXY(r){this.boundingRect.x=r.x,this.boundingRect.y=r.y}calculateSpace(r){if(this.visiblePlots=this.chartConfig.showLegend?this.chartData.plots.filter(d=>d.title):[],this.visiblePlots.length===0)return this.boundingRect.width=0,this.boundingRect.height=0,{width:0,height:0};let{fontSize:i,markerSize:n,markerSpacing:a,itemSpacing:o}=Yhe(this.chartConfig.legendFontSize),l=this.textDimensionCalculator.getMaxDimension(this.visiblePlots.map(d=>d.title),i),u=this.chartConfig.legendPadding*2+n+a+l.width,h=this.chartConfig.legendPadding*2+this.visiblePlots.length*i+(this.visiblePlots.length-1)*o;return u<=r.width&&h<=r.height?(this.boundingRect.width=u,this.boundingRect.height=h):(this.visiblePlots=[],this.boundingRect.width=0,this.boundingRect.height=0),{width:this.boundingRect.width,height:this.boundingRect.height}}getDrawableElements(){if(this.visiblePlots.length===0)return[];let{fontSize:r,markerSize:i,markerSpacing:n,itemSpacing:a}=Yhe(this.chartConfig.legendFontSize),o=r+a,l=this.boundingRect.x+this.chartConfig.legendPadding,u=this.boundingRect.y+this.chartConfig.legendPadding,h=[],d=[];for(let[p,f]of this.visiblePlots.entries())if(gI(f))h.push({x:l,y:u+p*o,width:i,height:i,fill:f.fill,strokeFill:f.fill,strokeWidth:0});else{let g=u+p*o+i/2;d.push({path:`M ${l},${g} L ${l+i},${g}`,strokeFill:f.strokeFill,strokeWidth:f.strokeWidth})}return[{groupTexts:["legend","markers"],type:"rect",data:h},{groupTexts:["legend","markers"],type:"path",data:d},{groupTexts:["legend","label"],type:"text",data:this.visiblePlots.map((p,f)=>({text:p.title,x:l+i+n,y:u+f*o+i/2,fill:this.chartThemeConfig.legendTextColor,fontSize:r,rotation:0,verticalPos:"middle",horizontalPos:"left"}))}]}},s(e,"ChartLegend"),e),s(kdt,"getChartLegendComponent")}),_dt,dRt=G(()=>{"use strict";var e;pi(),_dt=(e=class{constructor(r,i,n,a,o){this.plotData=r,this.xAxis=i,this.yAxis=n,this.orientation=a,this.plotIndex=o}getDrawableElement(){let r=this.plotData.data.map(a=>[this.xAxis.getScaleValue(a[0]),this.yAxis.getScaleValue(a[1])]),i;if(this.orientation==="horizontal"?i=r4().y(a=>a[0]).x(a=>a[1])(r):i=r4().x(a=>a[0]).y(a=>a[1])(r),!i)return[];let n=[{groupTexts:["plot",`line-plot-${this.plotIndex}`],type:"path",data:[{path:i,strokeFill:this.plotData.strokeFill,strokeWidth:this.plotData.strokeWidth}]}];if(this.plotData.pointLabels&&this.plotData.pointLabels.length>0){let a=[];for(let[o,[l,u]]of r.entries()){let h=this.plotData.pointLabels[o];h&&(this.orientation==="horizontal"?a.push({x:u+10,y:l,text:h,fill:this.plotData.strokeFill,verticalPos:"middle",horizontalPos:"left",fontSize:12,rotation:0}):a.push({x:l,y:u-10,text:h,fill:this.plotData.strokeFill,verticalPos:"middle",horizontalPos:"center",fontSize:12,rotation:0}))}a.length>0&&n.push({groupTexts:["plot",`line-plot-${this.plotIndex}`,"labels"],type:"text",data:a})}return n}},s(e,"LinePlot"),e)}),Adt,pRt=G(()=>{"use strict";var e;Adt=(e=class{constructor(r,i,n,a,o,l){this.barData=r,this.boundingRect=i,this.xAxis=n,this.yAxis=a,this.orientation=o,this.plotIndex=l}getDrawableElement(){let r=this.barData.data.map(a=>[this.xAxis.getScaleValue(a[0]),this.yAxis.getScaleValue(a[1])]),i=Math.min(this.xAxis.getAxisOuterPadding()*2,this.xAxis.getTickDistance())*(1-.05),n=i/2;return this.orientation==="horizontal"?[{groupTexts:["plot",`bar-plot-${this.plotIndex}`],type:"rect",data:r.map(a=>({x:this.boundingRect.x,y:a[0]-n,height:i,width:a[1]-this.boundingRect.x,fill:this.barData.fill,strokeWidth:0,strokeFill:this.barData.fill}))}]:[{groupTexts:["plot",`bar-plot-${this.plotIndex}`],type:"rect",data:r.map(a=>({x:a[0]-n,y:a[1],width:i,height:this.boundingRect.y+this.boundingRect.height-a[1],fill:this.barData.fill,strokeWidth:0,strokeFill:this.barData.fill}))}]}},s(e,"BarPlot"),e)});function Rdt(e,t,r){return new $dt(e,t,r)}var $dt,fRt=G(()=>{"use strict";var e;dRt(),pRt(),$dt=(e=class{constructor(r,i,n){this.chartConfig=r,this.chartData=i,this.chartThemeConfig=n,this.boundingRect={x:0,y:0,width:0,height:0}}setAxes(r,i){this.xAxis=r,this.yAxis=i}setBoundingBoxXY(r){this.boundingRect.x=r.x,this.boundingRect.y=r.y}calculateSpace(r){return this.boundingRect.width=r.width,this.boundingRect.height=r.height,{width:this.boundingRect.width,height:this.boundingRect.height}}getDrawableElements(){if(!(this.xAxis&&this.yAxis))throw Error("Axes must be passed to render Plots");let r=[];for(let[i,n]of this.chartData.plots.entries())switch(n.type){case"line":{let a=new _dt(n,this.xAxis,this.yAxis,this.chartConfig.chartOrientation,i);r.push(...a.getDrawableElement())}break;case"bar":{let a=new Adt(n,this.boundingRect,this.xAxis,this.yAxis,this.chartConfig.chartOrientation,i);r.push(...a.getDrawableElement())}break}return r}},s(e,"BasePlot"),e),s(Rdt,"getPlotComponent")}),Ldt,gRt=G(()=>{"use strict";var e;cRt(),uRt(),hRt(),fRt(),yI(),Ldt=(e=class{constructor(r,i,n,a){this.chartConfig=r,this.chartData=i,this.componentStore={title:xdt(r,i,n,a),plot:Rdt(r,i,n),legend:kdt(r,i,n,a),xAxis:Uhe(i.xAxis,r.xAxis,{titleColor:n.xAxisTitleColor,labelColor:n.xAxisLabelColor,tickColor:n.xAxisTickColor,axisLineColor:n.xAxisLineColor},a),yAxis:Uhe(i.yAxis,r.yAxis,{titleColor:n.yAxisTitleColor,labelColor:n.yAxisLabelColor,tickColor:n.yAxisTickColor,axisLineColor:n.yAxisLineColor},a)}}calculateVerticalSpace(){let r=this.chartConfig.width,i=this.chartConfig.height,n=0,a=0,o={width:0,height:0},l=Math.floor(r*this.chartConfig.plotReservedSpacePercent/100),u=Math.floor(i*this.chartConfig.plotReservedSpacePercent/100),h=this.componentStore.plot.calculateSpace({width:l,height:u});r-=h.width,i-=h.height,h=this.componentStore.title.calculateSpace({width:this.chartConfig.width,height:i}),a=h.height,i-=h.height,this.componentStore.xAxis.setAxisPosition("bottom"),h=this.componentStore.xAxis.calculateSpace({width:r,height:i}),i-=h.height,this.componentStore.yAxis.setAxisPosition("left"),h=this.componentStore.yAxis.calculateSpace({width:r,height:i}),n=h.width,r-=h.width,o=this.componentStore.legend.calculateSpace({width:r,height:u}),r-=o.width,r>0&&(l+=r,r=0),i>0&&(u+=i,i=0),this.componentStore.plot.calculateSpace({width:l,height:u}),this.componentStore.plot.setBoundingBoxXY({x:n,y:a}),this.componentStore.legend.setBoundingBoxXY({x:n+l,y:a+Math.max((u-o.height)/2,0)}),this.componentStore.xAxis.setRange([n,n+l]),this.componentStore.xAxis.setBoundingBoxXY({x:n,y:a+u}),this.componentStore.yAxis.setRange([a,a+u]),this.componentStore.yAxis.setBoundingBoxXY({x:0,y:a}),this.chartData.plots.some(d=>gI(d))&&this.componentStore.xAxis.recalculateOuterPaddingToDrawBar()}calculateHorizontalSpace(){let r=this.chartConfig.width,i=this.chartConfig.height,n=0,a=0,o=0,l={width:0,height:0},u=Math.floor(r*this.chartConfig.plotReservedSpacePercent/100),h=Math.floor(i*this.chartConfig.plotReservedSpacePercent/100),d=this.componentStore.plot.calculateSpace({width:u,height:h});r-=d.width,i-=d.height,d=this.componentStore.title.calculateSpace({width:this.chartConfig.width,height:i}),n=d.height,i-=d.height,this.componentStore.xAxis.setAxisPosition("left"),d=this.componentStore.xAxis.calculateSpace({width:r,height:i}),r-=d.width,a=d.width,this.componentStore.yAxis.setAxisPosition("top"),d=this.componentStore.yAxis.calculateSpace({width:r,height:i}),i-=d.height,o=n+d.height,l=this.componentStore.legend.calculateSpace({width:r,height:h}),r-=l.width,r>0&&(u+=r,r=0),i>0&&(h+=i,i=0),this.componentStore.plot.calculateSpace({width:u,height:h}),this.componentStore.plot.setBoundingBoxXY({x:a,y:o}),this.componentStore.legend.setBoundingBoxXY({x:a+u,y:o+Math.max((h-l.height)/2,0)}),this.componentStore.yAxis.setRange([a,a+u]),this.componentStore.yAxis.setBoundingBoxXY({x:a,y:n}),this.componentStore.xAxis.setRange([o,o+h]),this.componentStore.xAxis.setBoundingBoxXY({x:0,y:o}),this.chartData.plots.some(p=>gI(p))&&this.componentStore.xAxis.recalculateOuterPaddingToDrawBar()}calculateSpace(){this.chartConfig.chartOrientation==="horizontal"?this.calculateHorizontalSpace():this.calculateVerticalSpace()}getDrawableElement(){this.calculateSpace();let r=[];this.componentStore.plot.setAxes(this.componentStore.xAxis,this.componentStore.yAxis);for(let i of Object.values(this.componentStore))r.push(...i.getDrawableElements());return r}},s(e,"Orchestrator"),e)}),Idt,mRt=G(()=>{"use strict";var e;gRt(),Idt=(e=class{static build(r,i,n,a){return new Ldt(r,i,n,a).getDrawableElement()}},s(e,"XYChartBuilder"),e)});function Hhe(){let e=ku(),t=Rr();return Gn(e.xyChart,t.themeVariables.xyChart)}function Ghe(){let e=Rr();return Gn(vi.xyChart,e.xyChart)}function Xhe(){return{yAxis:{type:"linear",title:"",min:1/0,max:-1/0},xAxis:{type:"band",title:"",categories:[]},title:"",plots:[]}}function A1(e){let t=Rr();return Si(e.trim(),t)}function Mdt(e){Ydt=e}function Ddt(e){e==="horizontal"?Yb.chartOrientation="horizontal":Yb.chartOrientation="vertical"}function Ndt(e){Wi.xAxis.title=A1(e.text)}function Khe(e,t){Wi.xAxis={type:"linear",title:Wi.xAxis.title,min:e,max:t},S5=!0}function Odt(e){Wi.xAxis={type:"band",title:Wi.xAxis.title,categories:e.map(t=>A1(t.text))},S5=!0}function Bdt(e){Wi.yAxis.title=A1(e.text)}function Pdt(e,t){Wi.yAxis={type:"linear",title:Wi.yAxis.title,min:e,max:t},xI=!0}function Fdt(e){let t=Math.min(...e),r=Math.max(...e),i=Vb(Wi.yAxis)?Wi.yAxis.min:1/0,n=Vb(Wi.yAxis)?Wi.yAxis.max:-1/0;Wi.yAxis={type:"linear",title:Wi.yAxis.title,min:Math.min(i,t),max:Math.max(n,r)}}function Qhe(e){let t=[];if(e.length===0)return t;if(!S5){let r=Vb(Wi.xAxis)?Wi.xAxis.min:1/0,i=Vb(Wi.xAxis)?Wi.xAxis.max:-1/0;Khe(Math.min(r,1),Math.max(i,e.length))}if(mI(Wi.xAxis)&&e.length>Wi.xAxis.categories.length&&(e=e.slice(0,Wi.xAxis.categories.length)),xI||Fdt(e),mI(Wi.xAxis)&&(t=Wi.xAxis.categories.map((r,i)=>[r,e[i]])),Vb(Wi.xAxis)){let r=Wi.xAxis.min,i=Wi.xAxis.max;if(e.length===1)t=[[`${r}`,e[0]]];else{let n=(i-r)/(e.length-1);t=e.map((a,o)=>[`${r+o*n}`,a])}}return t}function Zhe(e){return bI[e===0?0:e%bI.length]}function zdt(e,t){let r=t.map(o=>o.value),i=t.map(o=>o.label?A1(o.label):""),n=Qhe(r),a=i.some(o=>o!=="");Wi.plots.push({type:"line",title:A1(e.text),strokeFill:Zhe(Ub),strokeWidth:2,data:n,...a?{pointLabels:i}:{}}),Ub++}function qdt(e,t){let r=t.map(n=>n.value),i=Qhe(r);Wi.plots.push({type:"bar",title:A1(e.text),fill:Zhe(Ub),data:i}),Ub++}function Wdt(){if(Wi.plots.length===0)throw Error("No Plot to render, please provide a plot with some data");return Wi.title=xn(),Idt.build(Yb,Wi,Hb,Ydt)}function jdt(){return Hb}function Vdt(){return Yb}function Udt(){return Wi}var Ub,Ydt,Yb,Hb,Wi,bI,S5,xI,Hdt,Gdt,yRt=G(()=>{"use strict";Gr(),Za(),Wh(),Ar(),mi(),fn(),mRt(),yI(),Ub=0,Yb=Ghe(),Hb=Hhe(),Wi=Xhe(),bI=Hb.plotColorPalette.split(",").map(e=>e.trim()),S5=!1,xI=!1,s(Hhe,"getChartDefaultThemeConfig"),s(Ghe,"getChartDefaultConfig"),s(Xhe,"getChartDefaultData"),s(A1,"textSanitizer"),s(Mdt,"setTmpSVGG"),s(Ddt,"setOrientation"),s(Ndt,"setXAxisTitle"),s(Khe,"setXAxisRangeData"),s(Odt,"setXAxisBand"),s(Bdt,"setYAxisTitle"),s(Pdt,"setYAxisRangeData"),s(Fdt,"setYAxisRangeFromPlotData"),s(Qhe,"transformDataWithoutCategory"),s(Zhe,"getPlotColorFromPalette"),s(zdt,"setLineData"),s(qdt,"setBarData"),s(Wdt,"getDrawableElem"),s(jdt,"getChartThemeConfig"),s(Vdt,"getChartConfig"),s(Udt,"getXYChartData"),Hdt=s(function(){pn(),Ub=0,Yb=Ghe(),Wi=Xhe(),Hb=Hhe(),bI=Hb.plotColorPalette.split(",").map(e=>e.trim()),S5=!1,xI=!1},"clear"),Gdt={getDrawableElem:Wdt,clear:Hdt,setAccTitle:bn,getAccTitle:Ln,setDiagramTitle:Dn,getDiagramTitle:xn,getAccDescription:Mn,setAccDescription:In,setOrientation:Ddt,setXAxisTitle:Ndt,setXAxisRangeData:Khe,setXAxisBand:Odt,setYAxisTitle:Bdt,setYAxisRangeData:Pdt,setLineData:zdt,setBarData:qdt,setTmpSVGG:Mdt,getChartThemeConfig:jdt,getChartConfig:Vdt,getXYChartData:Udt}}),Xdt,Kdt,vRt=G(()=>{"use strict";Nt(),wo(),vn(),Xdt=s((e,t,r,i)=>{let n=i.db,a=n.getChartThemeConfig(),o=n.getChartConfig(),l=n.getXYChartData().plots[0].data.map(E=>E[1]);function u(E){return E==="top"?"text-before-edge":"middle"}s(u,"getDominantBaseLine");function h(E){return E==="left"?"start":E==="right"?"end":"middle"}s(h,"getTextAnchor");function d(E){return`translate(${E.x}, ${E.y}) rotate(${E.rotation||0})`}s(d,"getTextTransformation"),Ee.debug(`Rendering xychart chart +`+e);let p=Ks(t),f=p.append("g").attr("class","main"),g=f.append("rect").attr("width",o.width).attr("height",o.height).attr("class","background");aa(p,o.height,o.width,!0),p.attr("viewBox",`0 0 ${o.width} ${o.height}`),g.attr("fill",a.backgroundColor),n.setTmpSVGG(p.append("g").attr("class","mermaid-tmp-group"));let m=n.getDrawableElem(),y={};function k(E){let _=f,b="";for(let[v]of E.entries()){let x=f;v>0&&y[b]&&(x=y[b]),b+=E[v],_=y[b],_||(_=y[b]=x.append("g").attr("class",E[v]))}return _}s(k,"getGroup");for(let E of m){if(E.data.length===0)continue;let _=k(E.groupTexts);switch(E.type){case"rect":if(_.selectAll("rect").data(E.data).enter().append("rect").attr("x",b=>b.x).attr("y",b=>b.y).attr("width",b=>b.width).attr("height",b=>b.height).attr("fill",b=>b.fill).attr("stroke",b=>b.strokeFill).attr("stroke-width",b=>b.strokeWidth),o.showDataLabel){let b=o.showDataLabelOutsideBar;if(o.chartOrientation==="horizontal"){let v=function(j,I){let{data:z,label:F}=j;return I*F.length*.7<=z.width-10};var C=v;s(v,"fitsHorizontally");let x=.7,w=10,R=E.data.map((j,I)=>({data:j,label:l[I].toString()})).filter(j=>j.data.width>0&&j.data.height>0),$=R.map(j=>{let{data:I}=j,z=I.height*.7;for(;!v(j,z)&&z>0;)z-=1;return z}),M=Math.floor(Math.min(...$)),N=s(j=>b?j.data.x+j.data.width+10:j.data.x+j.data.width-10,"determineLabelXPosition");_.selectAll("text").data(R).enter().append("text").attr("x",N).attr("y",j=>j.data.y+j.data.height/2).attr("text-anchor",b?"start":"end").attr("dominant-baseline","middle").attr("fill",a.dataLabelColor).attr("font-size",`${M}px`).text(j=>j.label)}else{let v=function(N,j,I){let{data:z,label:F}=N,O=j*F.length*.7,B=z.x+z.width/2,Y=B-O/2,U=B+O/2,H=Y>=z.x&&U<=z.x+z.width,J=z.y+I+j<=z.y+z.height;return H&&J};var S=v;s(v,"fitsInBar");let x=10,w=E.data.map((N,j)=>({data:N,label:l[j].toString()})).filter(N=>N.data.width>0&&N.data.height>0),R=w.map(N=>{let{data:j,label:I}=N,z=j.width/(I.length*.7);for(;!v(N,z,10)&&z>0;)z-=1;return z}),$=Math.floor(Math.min(...R)),M=s(N=>b?N.data.y-10:N.data.y+10,"determineLabelYPosition");_.selectAll("text").data(w).enter().append("text").attr("x",N=>N.data.x+N.data.width/2).attr("y",M).attr("text-anchor","middle").attr("dominant-baseline",b?"auto":"hanging").attr("fill",a.dataLabelColor).attr("font-size",`${$}px`).text(N=>N.label)}}break;case"text":_.selectAll("text").data(E.data).enter().append("text").attr("x",0).attr("y",0).attr("fill",b=>b.fill).attr("font-size",b=>b.fontSize).attr("dominant-baseline",b=>u(b.verticalPos)).attr("text-anchor",b=>h(b.horizontalPos)).attr("transform",b=>d(b)).text(b=>b.text);break;case"path":_.selectAll("path").data(E.data).enter().append("path").attr("d",b=>b.path).attr("fill",b=>b.fill?b.fill:"none").attr("stroke",b=>b.strokeFill).attr("stroke-width",b=>b.strokeWidth);break}}},"draw"),Kdt={draw:Xdt}}),Qdt={};Mr(Qdt,{diagram:()=>Zdt});var Zdt,bRt=G(()=>{"use strict";sRt(),yRt(),vRt(),Zdt={parser:mdt,db:Gdt,renderer:Kdt}}),TI,Jdt,xRt=G(()=>{"use strict";TI=(function(){var e=s(function(Ae,K,de,ee){for(de=de||{},ee=Ae.length;ee--;de[Ae[ee]]=K);return de},"o"),t=[1,3],r=[1,4],i=[1,5],n=[1,6],a=[5,6,8,9,11,13,21,22,23,24,41,42,43,44,45,46,54,72,74,77,89,90],o=[1,22],l=[2,7],u=[1,26],h=[1,27],d=[1,28],p=[1,29],f=[1,33],g=[1,34],m=[1,35],y=[1,36],k=[1,37],C=[1,38],S=[1,24],E=[1,31],_=[1,32],b=[1,30],v=[1,39],x=[1,40],w=[5,8,9,11,13,21,22,23,24,41,42,43,44,45,46,54,72,74,77,89,90],R=[1,61],$=[89,90],M=[5,8,9,11,13,21,22,23,24,27,29,41,42,43,44,45,46,54,61,63,72,74,75,76,77,80,81,82,83,84,85,86,87,88,89,90],N=[27,29],j=[1,70],I=[1,71],z=[1,72],F=[1,73],O=[1,74],B=[1,75],Y=[1,76],U=[1,83],H=[1,80],J=[1,84],te=[1,85],ie=[1,86],ue=[1,87],se=[1,88],le=[1,89],Te=[1,90],oe=[1,91],fe=[1,92],ge=[5,8,9,11,13,21,22,23,24,27,41,42,43,44,45,46,54,72,74,75,76,77,80,81,82,83,84,85,86,87,88,89,90],ke=[63,64],Ne=[1,101],xe=[5,8,9,11,13,21,22,23,24,41,42,43,44,45,46,54,72,74,76,77,89,90],$e=[5,8,9,11,13,21,22,23,24,41,42,43,44,45,46,54,72,74,75,76,77,80,81,82,83,84,85,86,87,88,89,90],Me=[1,110],He=[1,106],Ge=[1,107],ze=[1,108],Ve=[1,109],et=[1,111],he=[1,116],ye=[1,117],be=[1,114],Ie=[1,115],pe={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,directive:4,NEWLINE:5,RD:6,diagram:7,EOF:8,acc_title:9,acc_title_value:10,acc_descr:11,acc_descr_value:12,acc_descr_multiline_value:13,requirementDef:14,elementDef:15,relationshipDef:16,direction:17,styleStatement:18,classDefStatement:19,classStatement:20,direction_tb:21,direction_bt:22,direction_rl:23,direction_lr:24,requirementType:25,requirementName:26,STRUCT_START:27,requirementBody:28,STYLE_SEPARATOR:29,idList:30,ID:31,COLONSEP:32,id:33,TEXT:34,text:35,RISK:36,riskLevel:37,VERIFYMTHD:38,verifyType:39,STRUCT_STOP:40,REQUIREMENT:41,FUNCTIONAL_REQUIREMENT:42,INTERFACE_REQUIREMENT:43,PERFORMANCE_REQUIREMENT:44,PHYSICAL_REQUIREMENT:45,DESIGN_CONSTRAINT:46,LOW_RISK:47,MED_RISK:48,HIGH_RISK:49,VERIFY_ANALYSIS:50,VERIFY_DEMONSTRATION:51,VERIFY_INSPECTION:52,VERIFY_TEST:53,ELEMENT:54,elementName:55,elementBody:56,TYPE:57,type:58,DOCREF:59,ref:60,END_ARROW_L:61,relationship:62,LINE:63,END_ARROW_R:64,CONTAINS:65,COPIES:66,DERIVES:67,SATISFIES:68,VERIFIES:69,REFINES:70,TRACES:71,CLASSDEF:72,stylesOpt:73,CLASS:74,ALPHA:75,COMMA:76,STYLE:77,style:78,styleComponent:79,NUM:80,COLON:81,UNIT:82,SPACE:83,BRKT:84,PCT:85,MINUS:86,LABEL:87,SEMICOLON:88,unqString:89,qString:90,$accept:0,$end:1},terminals_:{2:"error",5:"NEWLINE",6:"RD",8:"EOF",9:"acc_title",10:"acc_title_value",11:"acc_descr",12:"acc_descr_value",13:"acc_descr_multiline_value",21:"direction_tb",22:"direction_bt",23:"direction_rl",24:"direction_lr",27:"STRUCT_START",29:"STYLE_SEPARATOR",31:"ID",32:"COLONSEP",34:"TEXT",36:"RISK",38:"VERIFYMTHD",40:"STRUCT_STOP",41:"REQUIREMENT",42:"FUNCTIONAL_REQUIREMENT",43:"INTERFACE_REQUIREMENT",44:"PERFORMANCE_REQUIREMENT",45:"PHYSICAL_REQUIREMENT",46:"DESIGN_CONSTRAINT",47:"LOW_RISK",48:"MED_RISK",49:"HIGH_RISK",50:"VERIFY_ANALYSIS",51:"VERIFY_DEMONSTRATION",52:"VERIFY_INSPECTION",53:"VERIFY_TEST",54:"ELEMENT",57:"TYPE",59:"DOCREF",61:"END_ARROW_L",63:"LINE",64:"END_ARROW_R",65:"CONTAINS",66:"COPIES",67:"DERIVES",68:"SATISFIES",69:"VERIFIES",70:"REFINES",71:"TRACES",72:"CLASSDEF",74:"CLASS",75:"ALPHA",76:"COMMA",77:"STYLE",80:"NUM",81:"COLON",82:"UNIT",83:"SPACE",84:"BRKT",85:"PCT",86:"MINUS",87:"LABEL",88:"SEMICOLON",89:"unqString",90:"qString"},productions_:[0,[3,3],[3,2],[3,4],[4,2],[4,2],[4,1],[7,0],[7,2],[7,2],[7,2],[7,2],[7,2],[7,2],[7,2],[7,2],[7,2],[17,1],[17,1],[17,1],[17,1],[14,5],[14,7],[28,5],[28,5],[28,5],[28,5],[28,2],[28,1],[25,1],[25,1],[25,1],[25,1],[25,1],[25,1],[37,1],[37,1],[37,1],[39,1],[39,1],[39,1],[39,1],[15,5],[15,7],[56,5],[56,5],[56,2],[56,1],[16,5],[16,5],[62,1],[62,1],[62,1],[62,1],[62,1],[62,1],[62,1],[19,3],[20,3],[20,3],[30,1],[30,3],[30,1],[30,3],[18,3],[73,1],[73,3],[78,1],[78,2],[79,1],[79,1],[79,1],[79,1],[79,1],[79,1],[79,1],[79,1],[79,1],[79,1],[26,1],[26,1],[33,1],[33,1],[35,1],[35,1],[55,1],[55,1],[58,1],[58,1],[60,1],[60,1]],performAction:s(function(Ae,K,de,ee,ve,X,me){var Ce=X.length-1;switch(ve){case 4:this.$=X[Ce].trim(),ee.setAccTitle(this.$);break;case 5:case 6:this.$=X[Ce].trim(),ee.setAccDescription(this.$);break;case 7:this.$=[];break;case 17:ee.setDirection("TB");break;case 18:ee.setDirection("BT");break;case 19:ee.setDirection("RL");break;case 20:ee.setDirection("LR");break;case 21:ee.addRequirement(X[Ce-3],X[Ce-4]);break;case 22:ee.addRequirement(X[Ce-5],X[Ce-6]),ee.setClass([X[Ce-5]],X[Ce-3]);break;case 23:ee.setNewReqId(X[Ce-2]);break;case 24:ee.setNewReqText(X[Ce-2]);break;case 25:ee.setNewReqRisk(X[Ce-2]);break;case 26:ee.setNewReqVerifyMethod(X[Ce-2]);break;case 29:this.$=ee.RequirementType.REQUIREMENT;break;case 30:this.$=ee.RequirementType.FUNCTIONAL_REQUIREMENT;break;case 31:this.$=ee.RequirementType.INTERFACE_REQUIREMENT;break;case 32:this.$=ee.RequirementType.PERFORMANCE_REQUIREMENT;break;case 33:this.$=ee.RequirementType.PHYSICAL_REQUIREMENT;break;case 34:this.$=ee.RequirementType.DESIGN_CONSTRAINT;break;case 35:this.$=ee.RiskLevel.LOW_RISK;break;case 36:this.$=ee.RiskLevel.MED_RISK;break;case 37:this.$=ee.RiskLevel.HIGH_RISK;break;case 38:this.$=ee.VerifyType.VERIFY_ANALYSIS;break;case 39:this.$=ee.VerifyType.VERIFY_DEMONSTRATION;break;case 40:this.$=ee.VerifyType.VERIFY_INSPECTION;break;case 41:this.$=ee.VerifyType.VERIFY_TEST;break;case 42:ee.addElement(X[Ce-3]);break;case 43:ee.addElement(X[Ce-5]),ee.setClass([X[Ce-5]],X[Ce-3]);break;case 44:ee.setNewElementType(X[Ce-2]);break;case 45:ee.setNewElementDocRef(X[Ce-2]);break;case 48:ee.addRelationship(X[Ce-2],X[Ce],X[Ce-4]);break;case 49:ee.addRelationship(X[Ce-2],X[Ce-4],X[Ce]);break;case 50:this.$=ee.Relationships.CONTAINS;break;case 51:this.$=ee.Relationships.COPIES;break;case 52:this.$=ee.Relationships.DERIVES;break;case 53:this.$=ee.Relationships.SATISFIES;break;case 54:this.$=ee.Relationships.VERIFIES;break;case 55:this.$=ee.Relationships.REFINES;break;case 56:this.$=ee.Relationships.TRACES;break;case 57:this.$=X[Ce-2],ee.defineClass(X[Ce-1],X[Ce]);break;case 58:ee.setClass(X[Ce-1],X[Ce]);break;case 59:ee.setClass([X[Ce-2]],X[Ce]);break;case 60:case 62:this.$=[X[Ce]];break;case 61:case 63:this.$=X[Ce-2].concat([X[Ce]]);break;case 64:this.$=X[Ce-2],ee.setCssStyle(X[Ce-1],X[Ce]);break;case 65:this.$=[X[Ce]];break;case 66:X[Ce-2].push(X[Ce]),this.$=X[Ce-2];break;case 68:this.$=X[Ce-1]+X[Ce];break}},"anonymous"),table:[{3:1,4:2,6:t,9:r,11:i,13:n},{1:[3]},{3:8,4:2,5:[1,7],6:t,9:r,11:i,13:n},{5:[1,9]},{10:[1,10]},{12:[1,11]},e(a,[2,6]),{3:12,4:2,6:t,9:r,11:i,13:n},{1:[2,2]},{4:17,5:o,7:13,8:l,9:r,11:i,13:n,14:14,15:15,16:16,17:18,18:19,19:20,20:21,21:u,22:h,23:d,24:p,25:23,33:25,41:f,42:g,43:m,44:y,45:k,46:C,54:S,72:E,74:_,77:b,89:v,90:x},e(a,[2,4]),e(a,[2,5]),{1:[2,1]},{8:[1,41]},{4:17,5:o,7:42,8:l,9:r,11:i,13:n,14:14,15:15,16:16,17:18,18:19,19:20,20:21,21:u,22:h,23:d,24:p,25:23,33:25,41:f,42:g,43:m,44:y,45:k,46:C,54:S,72:E,74:_,77:b,89:v,90:x},{4:17,5:o,7:43,8:l,9:r,11:i,13:n,14:14,15:15,16:16,17:18,18:19,19:20,20:21,21:u,22:h,23:d,24:p,25:23,33:25,41:f,42:g,43:m,44:y,45:k,46:C,54:S,72:E,74:_,77:b,89:v,90:x},{4:17,5:o,7:44,8:l,9:r,11:i,13:n,14:14,15:15,16:16,17:18,18:19,19:20,20:21,21:u,22:h,23:d,24:p,25:23,33:25,41:f,42:g,43:m,44:y,45:k,46:C,54:S,72:E,74:_,77:b,89:v,90:x},{4:17,5:o,7:45,8:l,9:r,11:i,13:n,14:14,15:15,16:16,17:18,18:19,19:20,20:21,21:u,22:h,23:d,24:p,25:23,33:25,41:f,42:g,43:m,44:y,45:k,46:C,54:S,72:E,74:_,77:b,89:v,90:x},{4:17,5:o,7:46,8:l,9:r,11:i,13:n,14:14,15:15,16:16,17:18,18:19,19:20,20:21,21:u,22:h,23:d,24:p,25:23,33:25,41:f,42:g,43:m,44:y,45:k,46:C,54:S,72:E,74:_,77:b,89:v,90:x},{4:17,5:o,7:47,8:l,9:r,11:i,13:n,14:14,15:15,16:16,17:18,18:19,19:20,20:21,21:u,22:h,23:d,24:p,25:23,33:25,41:f,42:g,43:m,44:y,45:k,46:C,54:S,72:E,74:_,77:b,89:v,90:x},{4:17,5:o,7:48,8:l,9:r,11:i,13:n,14:14,15:15,16:16,17:18,18:19,19:20,20:21,21:u,22:h,23:d,24:p,25:23,33:25,41:f,42:g,43:m,44:y,45:k,46:C,54:S,72:E,74:_,77:b,89:v,90:x},{4:17,5:o,7:49,8:l,9:r,11:i,13:n,14:14,15:15,16:16,17:18,18:19,19:20,20:21,21:u,22:h,23:d,24:p,25:23,33:25,41:f,42:g,43:m,44:y,45:k,46:C,54:S,72:E,74:_,77:b,89:v,90:x},{4:17,5:o,7:50,8:l,9:r,11:i,13:n,14:14,15:15,16:16,17:18,18:19,19:20,20:21,21:u,22:h,23:d,24:p,25:23,33:25,41:f,42:g,43:m,44:y,45:k,46:C,54:S,72:E,74:_,77:b,89:v,90:x},{26:51,89:[1,52],90:[1,53]},{55:54,89:[1,55],90:[1,56]},{29:[1,59],61:[1,57],63:[1,58]},e(w,[2,17]),e(w,[2,18]),e(w,[2,19]),e(w,[2,20]),{30:60,33:62,75:R,89:v,90:x},{30:63,33:62,75:R,89:v,90:x},{30:64,33:62,75:R,89:v,90:x},e($,[2,29]),e($,[2,30]),e($,[2,31]),e($,[2,32]),e($,[2,33]),e($,[2,34]),e(M,[2,81]),e(M,[2,82]),{1:[2,3]},{8:[2,8]},{8:[2,9]},{8:[2,10]},{8:[2,11]},{8:[2,12]},{8:[2,13]},{8:[2,14]},{8:[2,15]},{8:[2,16]},{27:[1,65],29:[1,66]},e(N,[2,79]),e(N,[2,80]),{27:[1,67],29:[1,68]},e(N,[2,85]),e(N,[2,86]),{62:69,65:j,66:I,67:z,68:F,69:O,70:B,71:Y},{62:77,65:j,66:I,67:z,68:F,69:O,70:B,71:Y},{30:78,33:62,75:R,89:v,90:x},{73:79,75:U,76:H,78:81,79:82,80:J,81:te,82:ie,83:ue,84:se,85:le,86:Te,87:oe,88:fe},e(ge,[2,60]),e(ge,[2,62]),{73:93,75:U,76:H,78:81,79:82,80:J,81:te,82:ie,83:ue,84:se,85:le,86:Te,87:oe,88:fe},{30:94,33:62,75:R,76:H,89:v,90:x},{5:[1,95]},{30:96,33:62,75:R,89:v,90:x},{5:[1,97]},{30:98,33:62,75:R,89:v,90:x},{63:[1,99]},e(ke,[2,50]),e(ke,[2,51]),e(ke,[2,52]),e(ke,[2,53]),e(ke,[2,54]),e(ke,[2,55]),e(ke,[2,56]),{64:[1,100]},e(w,[2,59],{76:H}),e(w,[2,64],{76:Ne}),{33:103,75:[1,102],89:v,90:x},e(xe,[2,65],{79:104,75:U,80:J,81:te,82:ie,83:ue,84:se,85:le,86:Te,87:oe,88:fe}),e($e,[2,67]),e($e,[2,69]),e($e,[2,70]),e($e,[2,71]),e($e,[2,72]),e($e,[2,73]),e($e,[2,74]),e($e,[2,75]),e($e,[2,76]),e($e,[2,77]),e($e,[2,78]),e(w,[2,57],{76:Ne}),e(w,[2,58],{76:H}),{5:Me,28:105,31:He,34:Ge,36:ze,38:Ve,40:et},{27:[1,112],76:H},{5:he,40:ye,56:113,57:be,59:Ie},{27:[1,118],76:H},{33:119,89:v,90:x},{33:120,89:v,90:x},{75:U,78:121,79:82,80:J,81:te,82:ie,83:ue,84:se,85:le,86:Te,87:oe,88:fe},e(ge,[2,61]),e(ge,[2,63]),e($e,[2,68]),e(w,[2,21]),{32:[1,122]},{32:[1,123]},{32:[1,124]},{32:[1,125]},{5:Me,28:126,31:He,34:Ge,36:ze,38:Ve,40:et},e(w,[2,28]),{5:[1,127]},e(w,[2,42]),{32:[1,128]},{32:[1,129]},{5:he,40:ye,56:130,57:be,59:Ie},e(w,[2,47]),{5:[1,131]},e(w,[2,48]),e(w,[2,49]),e(xe,[2,66],{79:104,75:U,80:J,81:te,82:ie,83:ue,84:se,85:le,86:Te,87:oe,88:fe}),{33:132,89:v,90:x},{35:133,89:[1,134],90:[1,135]},{37:136,47:[1,137],48:[1,138],49:[1,139]},{39:140,50:[1,141],51:[1,142],52:[1,143],53:[1,144]},e(w,[2,27]),{5:Me,28:145,31:He,34:Ge,36:ze,38:Ve,40:et},{58:146,89:[1,147],90:[1,148]},{60:149,89:[1,150],90:[1,151]},e(w,[2,46]),{5:he,40:ye,56:152,57:be,59:Ie},{5:[1,153]},{5:[1,154]},{5:[2,83]},{5:[2,84]},{5:[1,155]},{5:[2,35]},{5:[2,36]},{5:[2,37]},{5:[1,156]},{5:[2,38]},{5:[2,39]},{5:[2,40]},{5:[2,41]},e(w,[2,22]),{5:[1,157]},{5:[2,87]},{5:[2,88]},{5:[1,158]},{5:[2,89]},{5:[2,90]},e(w,[2,43]),{5:Me,28:159,31:He,34:Ge,36:ze,38:Ve,40:et},{5:Me,28:160,31:He,34:Ge,36:ze,38:Ve,40:et},{5:Me,28:161,31:He,34:Ge,36:ze,38:Ve,40:et},{5:Me,28:162,31:He,34:Ge,36:ze,38:Ve,40:et},{5:he,40:ye,56:163,57:be,59:Ie},{5:he,40:ye,56:164,57:be,59:Ie},e(w,[2,23]),e(w,[2,24]),e(w,[2,25]),e(w,[2,26]),e(w,[2,44]),e(w,[2,45])],defaultActions:{8:[2,2],12:[2,1],41:[2,3],42:[2,8],43:[2,9],44:[2,10],45:[2,11],46:[2,12],47:[2,13],48:[2,14],49:[2,15],50:[2,16],134:[2,83],135:[2,84],137:[2,35],138:[2,36],139:[2,37],141:[2,38],142:[2,39],143:[2,40],144:[2,41],147:[2,87],148:[2,88],150:[2,89],151:[2,90]},parseError:s(function(Ae,K){if(K.recoverable)this.trace(Ae);else{var de=new Error(Ae);throw de.hash=K,de}},"parseError"),parse:s(function(Ae){var K=this,de=[0],ee=[],ve=[null],X=[],me=this.table,Ce="",Be=0,Re=0,Ke=0,De=2,Ze=1,Fe=X.slice.call(arguments,1),Je=Object.create(this.lexer),Le={yy:{}};for(var ct in this.yy)Object.prototype.hasOwnProperty.call(this.yy,ct)&&(Le.yy[ct]=this.yy[ct]);Je.setInput(Ae,Le.yy),Le.yy.lexer=Je,Le.yy.parser=this,typeof Je.yylloc>"u"&&(Je.yylloc={});var qe=Je.yylloc;X.push(qe);var mt=Je.options&&Je.options.ranges;typeof Le.yy.parseError=="function"?this.parseError=Le.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function St(br){de.length=de.length-2*br,ve.length=ve.length-br,X.length=X.length-br}s(St,"popStack");function Lt(){var br;return br=ee.pop()||Je.lex()||Ze,typeof br!="number"&&(br instanceof Array&&(ee=br,br=ee.pop()),br=K.symbols_[br]||br),br}s(Lt,"lex");for(var Bt,qt,tr,Et,_t,Kt,$t={},Ot,Jt,Br,At;;){if(tr=de[de.length-1],this.defaultActions[tr]?Et=this.defaultActions[tr]:((Bt===null||typeof Bt>"u")&&(Bt=Lt()),Et=me[tr]&&me[tr][Bt]),typeof Et>"u"||!Et.length||!Et[0]){var jr="";At=[];for(Ot in me[tr])this.terminals_[Ot]&&Ot>De&&At.push("'"+this.terminals_[Ot]+"'");Je.showPosition?jr="Parse error on line "+(Be+1)+`: +`+Je.showPosition()+` +Expecting `+At.join(", ")+", got '"+(this.terminals_[Bt]||Bt)+"'":jr="Parse error on line "+(Be+1)+": Unexpected "+(Bt==Ze?"end of input":"'"+(this.terminals_[Bt]||Bt)+"'"),this.parseError(jr,{text:Je.match,token:this.terminals_[Bt]||Bt,line:Je.yylineno,loc:qe,expected:At})}if(Et[0]instanceof Array&&Et.length>1)throw new Error("Parse Error: multiple actions possible at state: "+tr+", token: "+Bt);switch(Et[0]){case 1:de.push(Bt),ve.push(Je.yytext),X.push(Je.yylloc),de.push(Et[1]),Bt=null,qt?(Bt=qt,qt=null):(Re=Je.yyleng,Ce=Je.yytext,Be=Je.yylineno,qe=Je.yylloc,Ke>0&&Ke--);break;case 2:if(Jt=this.productions_[Et[1]][1],$t.$=ve[ve.length-Jt],$t._$={first_line:X[X.length-(Jt||1)].first_line,last_line:X[X.length-1].last_line,first_column:X[X.length-(Jt||1)].first_column,last_column:X[X.length-1].last_column},mt&&($t._$.range=[X[X.length-(Jt||1)].range[0],X[X.length-1].range[1]]),Kt=this.performAction.apply($t,[Ce,Re,Be,Le.yy,Et[1],ve,X].concat(Fe)),typeof Kt<"u")return Kt;Jt&&(de=de.slice(0,-1*Jt*2),ve=ve.slice(0,-1*Jt),X=X.slice(0,-1*Jt)),de.push(this.productions_[Et[1]][0]),ve.push($t.$),X.push($t._$),Br=me[de[de.length-2]][de[de.length-1]],de.push(Br);break;case 3:return!0}}return!0},"parse")},Qe=(function(){var Ae={EOF:1,parseError:s(function(K,de){if(this.yy.parser)this.yy.parser.parseError(K,de);else throw new Error(K)},"parseError"),setInput:s(function(K,de){return this.yy=de||this.yy||{},this._input=K,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var K=this._input[0];this.yytext+=K,this.yyleng++,this.offset++,this.match+=K,this.matched+=K;var de=K.match(/(?:\r\n?|\n).*/g);return de?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),K},"input"),unput:s(function(K){var de=K.length,ee=K.split(/(?:\r\n?|\n)/g);this._input=K+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-de),this.offset-=de;var ve=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),ee.length-1&&(this.yylineno-=ee.length-1);var X=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:ee?(ee.length===ve.length?this.yylloc.first_column:0)+ve[ve.length-ee.length].length-ee[0].length:this.yylloc.first_column-de},this.options.ranges&&(this.yylloc.range=[X[0],X[0]+this.yyleng-de]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(K){this.unput(this.match.slice(K))},"less"),pastInput:s(function(){var K=this.matched.substr(0,this.matched.length-this.match.length);return(K.length>20?"...":"")+K.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var K=this.match;return K.length<20&&(K+=this._input.substr(0,20-K.length)),(K.substr(0,20)+(K.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var K=this.pastInput(),de=new Array(K.length+1).join("-");return K+this.upcomingInput()+` +`+de+"^"},"showPosition"),test_match:s(function(K,de){var ee,ve,X;if(this.options.backtrack_lexer&&(X={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(X.yylloc.range=this.yylloc.range.slice(0))),ve=K[0].match(/(?:\r\n?|\n).*/g),ve&&(this.yylineno+=ve.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:ve?ve[ve.length-1].length-ve[ve.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+K[0].length},this.yytext+=K[0],this.match+=K[0],this.matches=K,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(K[0].length),this.matched+=K[0],ee=this.performAction.call(this,this.yy,this,de,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),ee)return ee;if(this._backtrack){for(var me in X)this[me]=X[me];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var K,de,ee,ve;this._more||(this.yytext="",this.match="");for(var X=this._currentRules(),me=0;mede[0].length)){if(de=ee,ve=me,this.options.backtrack_lexer){if(K=this.test_match(ee,X[me]),K!==!1)return K;if(this._backtrack){de=!1;continue}else return!1}else if(!this.options.flex)break}return de?(K=this.test_match(de,X[ve]),K!==!1?K:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var K=this.next();return K||this.lex()},"lex"),begin:s(function(K){this.conditionStack.push(K)},"begin"),popState:s(function(){var K=this.conditionStack.length-1;return K>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(K){return K=this.conditionStack.length-1-Math.abs(K||0),K>=0?this.conditionStack[K]:"INITIAL"},"topState"),pushState:s(function(K){this.begin(K)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(K,de,ee,ve){var X=ve;switch(ee){case 0:return"title";case 1:return this.begin("acc_title"),9;case 2:return this.popState(),"acc_title_value";case 3:return this.begin("acc_descr"),11;case 4:return this.popState(),"acc_descr_value";case 5:this.begin("acc_descr_multiline");break;case 6:this.popState();break;case 7:return"acc_descr_multiline_value";case 8:return 21;case 9:return 22;case 10:return 23;case 11:return 24;case 12:return 5;case 13:break;case 14:break;case 15:break;case 16:return 8;case 17:return 6;case 18:return 27;case 19:return 40;case 20:return 29;case 21:return 32;case 22:return 31;case 23:return 34;case 24:return 36;case 25:return 38;case 26:return 41;case 27:return 42;case 28:return 43;case 29:return 44;case 30:return 45;case 31:return 46;case 32:return 47;case 33:return 48;case 34:return 49;case 35:return 50;case 36:return 51;case 37:return 52;case 38:return 53;case 39:return 54;case 40:return 65;case 41:return 66;case 42:return 67;case 43:return 68;case 44:return 69;case 45:return 70;case 46:return 71;case 47:return 57;case 48:return 59;case 49:return this.begin("style"),77;case 50:return 75;case 51:return 81;case 52:return 88;case 53:return"PERCENT";case 54:return 86;case 55:return 84;case 56:break;case 57:this.begin("string");break;case 58:this.popState();break;case 59:return this.begin("style"),72;case 60:return this.begin("style"),74;case 61:return 61;case 62:return 64;case 63:return 63;case 64:this.begin("string");break;case 65:this.popState();break;case 66:return"qString";case 67:return de.yytext=de.yytext.trim(),89;case 68:return 75;case 69:return 80;case 70:return 76}},"anonymous"),rules:[/^(?:title\s[^#\n;]+)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:(\r?\n)+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:$)/i,/^(?:requirementDiagram\b)/i,/^(?:\{)/i,/^(?:\})/i,/^(?::{3})/i,/^(?::)/i,/^(?:id\b)/i,/^(?:text\b)/i,/^(?:risk\b)/i,/^(?:verifyMethod\b)/i,/^(?:requirement\b)/i,/^(?:functionalRequirement\b)/i,/^(?:interfaceRequirement\b)/i,/^(?:performanceRequirement\b)/i,/^(?:physicalRequirement\b)/i,/^(?:designConstraint\b)/i,/^(?:low\b)/i,/^(?:medium\b)/i,/^(?:high\b)/i,/^(?:analysis\b)/i,/^(?:demonstration\b)/i,/^(?:inspection\b)/i,/^(?:test\b)/i,/^(?:element\b)/i,/^(?:contains\b)/i,/^(?:copies\b)/i,/^(?:derives\b)/i,/^(?:satisfies\b)/i,/^(?:verifies\b)/i,/^(?:refines\b)/i,/^(?:traces\b)/i,/^(?:type\b)/i,/^(?:docref\b)/i,/^(?:style\b)/i,/^(?:\w+)/i,/^(?::)/i,/^(?:;)/i,/^(?:%)/i,/^(?:-)/i,/^(?:#)/i,/^(?: )/i,/^(?:["])/i,/^(?:\n)/i,/^(?:classDef\b)/i,/^(?:class\b)/i,/^(?:<-)/i,/^(?:->)/i,/^(?:-)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[\w][^:,\r\n\{\<\>\-\=]*)/i,/^(?:\w+)/i,/^(?:[0-9]+)/i,/^(?:,)/i],conditions:{acc_descr_multiline:{rules:[6,7,68,69,70],inclusive:!1},acc_descr:{rules:[4,68,69,70],inclusive:!1},acc_title:{rules:[2,68,69,70],inclusive:!1},style:{rules:[50,51,52,53,54,55,56,57,58,68,69,70],inclusive:!1},unqString:{rules:[68,69,70],inclusive:!1},token:{rules:[68,69,70],inclusive:!1},string:{rules:[65,66,68,69,70],inclusive:!1},INITIAL:{rules:[0,1,3,5,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,59,60,61,62,63,64,67,68,69,70],inclusive:!0}}};return Ae})();pe.lexer=Qe;function W(){this.yy={}}return s(W,"Parser"),W.prototype=pe,pe.Parser=W,new W})(),TI.parser=TI,Jdt=TI}),ept,TRt=G(()=>{"use strict";var e;Er(),Nt(),fn(),ept=(e=class{constructor(){this.relations=[],this.latestRequirement=this.getInitialRequirement(),this.requirements=new Map,this.latestElement=this.getInitialElement(),this.elements=new Map,this.classes=new Map,this.direction="TB",this.RequirementType={REQUIREMENT:"Requirement",FUNCTIONAL_REQUIREMENT:"Functional Requirement",INTERFACE_REQUIREMENT:"Interface Requirement",PERFORMANCE_REQUIREMENT:"Performance Requirement",PHYSICAL_REQUIREMENT:"Physical Requirement",DESIGN_CONSTRAINT:"Design Constraint"},this.RiskLevel={LOW_RISK:"Low",MED_RISK:"Medium",HIGH_RISK:"High"},this.VerifyType={VERIFY_ANALYSIS:"Analysis",VERIFY_DEMONSTRATION:"Demonstration",VERIFY_INSPECTION:"Inspection",VERIFY_TEST:"Test"},this.Relationships={CONTAINS:"contains",COPIES:"copies",DERIVES:"derives",SATISFIES:"satisfies",VERIFIES:"verifies",REFINES:"refines",TRACES:"traces"},this.setAccTitle=bn,this.getAccTitle=Ln,this.setAccDescription=In,this.getAccDescription=Mn,this.setDiagramTitle=Dn,this.getDiagramTitle=xn,this.getConfig=s(()=>it().requirement,"getConfig"),this.clear(),this.setDirection=this.setDirection.bind(this),this.addRequirement=this.addRequirement.bind(this),this.setNewReqId=this.setNewReqId.bind(this),this.setNewReqRisk=this.setNewReqRisk.bind(this),this.setNewReqText=this.setNewReqText.bind(this),this.setNewReqVerifyMethod=this.setNewReqVerifyMethod.bind(this),this.addElement=this.addElement.bind(this),this.setNewElementType=this.setNewElementType.bind(this),this.setNewElementDocRef=this.setNewElementDocRef.bind(this),this.addRelationship=this.addRelationship.bind(this),this.setCssStyle=this.setCssStyle.bind(this),this.setClass=this.setClass.bind(this),this.defineClass=this.defineClass.bind(this),this.setAccTitle=this.setAccTitle.bind(this),this.setAccDescription=this.setAccDescription.bind(this)}getDirection(){return this.direction}setDirection(r){this.direction=r}resetLatestRequirement(){this.latestRequirement=this.getInitialRequirement()}resetLatestElement(){this.latestElement=this.getInitialElement()}getInitialRequirement(){return{requirementId:"",text:"",risk:"",verifyMethod:"",name:"",type:"",cssStyles:[],classes:["default"]}}getInitialElement(){return{name:"",type:"",docRef:"",cssStyles:[],classes:["default"]}}addRequirement(r,i){return this.requirements.has(r)||this.requirements.set(r,{name:r,type:i,requirementId:this.latestRequirement.requirementId,text:this.latestRequirement.text,risk:this.latestRequirement.risk,verifyMethod:this.latestRequirement.verifyMethod,cssStyles:[],classes:["default"]}),this.resetLatestRequirement(),this.requirements.get(r)}getRequirements(){return this.requirements}setNewReqId(r){this.latestRequirement!==void 0&&(this.latestRequirement.requirementId=r)}setNewReqText(r){this.latestRequirement!==void 0&&(this.latestRequirement.text=r)}setNewReqRisk(r){this.latestRequirement!==void 0&&(this.latestRequirement.risk=r)}setNewReqVerifyMethod(r){this.latestRequirement!==void 0&&(this.latestRequirement.verifyMethod=r)}addElement(r){return this.elements.has(r)||(this.elements.set(r,{name:r,type:this.latestElement.type,docRef:this.latestElement.docRef,cssStyles:[],classes:["default"]}),Ee.info("Added new element: ",r)),this.resetLatestElement(),this.elements.get(r)}getElements(){return this.elements}setNewElementType(r){this.latestElement!==void 0&&(this.latestElement.type=r)}setNewElementDocRef(r){this.latestElement!==void 0&&(this.latestElement.docRef=r)}addRelationship(r,i,n){this.relations.push({type:r,src:i,dst:n})}getRelationships(){return this.relations}clear(){this.relations=[],this.resetLatestRequirement(),this.requirements=new Map,this.resetLatestElement(),this.elements=new Map,this.classes=new Map,pn()}setCssStyle(r,i){for(let n of r){let a=this.requirements.get(n)??this.elements.get(n);if(!i||!a)return;for(let o of i)o.includes(",")?a.cssStyles.push(...o.split(",")):a.cssStyles.push(o)}}setClass(r,i){for(let n of r){let a=this.requirements.get(n)??this.elements.get(n);if(a)for(let o of i){a.classes.push(o);let l=this.classes.get(o)?.styles;l&&a.cssStyles.push(...l)}}}defineClass(r,i){for(let n of r){let a=this.classes.get(n);a===void 0&&(a={id:n,styles:[],textStyles:[]},this.classes.set(n,a)),i&&i.forEach(function(o){if(/color/.exec(o)){let l=o.replace("fill","bgFill");a.textStyles.push(l)}a.styles.push(o)}),this.requirements.forEach(o=>{o.classes.includes(n)&&o.cssStyles.push(...i.flatMap(l=>l.split(",")))}),this.elements.forEach(o=>{o.classes.includes(n)&&o.cssStyles.push(...i.flatMap(l=>l.split(",")))})}}getClasses(){return this.classes}getData(){let r=it(),i=[],n=[];for(let o of this.requirements.values()){let l=o;l.id=o.name,l.cssStyles=o.cssStyles,l.cssClasses=o.classes.join(" "),l.shape="requirementBox",l.look=r.look,l.colorIndex=i.length,i.push(l)}for(let o of this.elements.values()){let l=o;l.shape="requirementBox",l.look=r.look,l.id=o.name,l.cssStyles=o.cssStyles,l.cssClasses=o.classes.join(" "),l.colorIndex=i.length,i.push(l)}let a=0;for(let o of this.relations){let l=o.type===this.Relationships.CONTAINS,u={id:`${o.src}-${o.dst}-${a++}`,start:this.requirements.get(o.src)?.name??this.elements.get(o.src)?.name,end:this.requirements.get(o.dst)?.name??this.elements.get(o.dst)?.name,label:`<<${o.type}>>`,classes:"relationshipLine",style:["fill:none",l?"":"stroke-dasharray: 10,7"],labelpos:"c",thickness:"normal",type:"normal",pattern:l?"normal":"dashed",arrowTypeStart:l?"requirement_contains":"",arrowTypeEnd:l?"":"requirement_arrow",look:r.look,labelType:"markdown"};n.push(u)}return{nodes:i,edges:n,other:{},config:r,direction:this.getDirection()}}},s(e,"RequirementDB"),e)}),tpt,rpt,ipt,kRt=G(()=>{"use strict";Gr(),tpt=s(e=>{let t=Rr(),{themeVariables:r,look:i}=t,{bkgColorArray:n,borderColorArray:a}=r;if(!a?.length)return"";let o="";for(let l=0;l{let t=Rr(),{look:r,themeVariables:i}=t,{requirementEdgeLabelBackground:n}=i;return` + ${tpt(e)} + marker { + fill: ${e.relationColor}; + stroke: ${e.relationColor}; + } + + marker.cross { + stroke: ${e.lineColor}; + } + + svg { + font-family: ${e.fontFamily}; + font-size: ${e.fontSize}; + } + + .reqBox { + fill: ${e.requirementBackground}; + fill-opacity: 1.0; + stroke: ${e.requirementBorderColor}; + stroke-width: ${e.requirementBorderSize}; + } + + .reqTitle, .reqLabel{ + fill: ${e.requirementTextColor}; + } + .reqLabelBox { + fill: ${e.relationLabelBackground}; + fill-opacity: 1.0; + } + + .req-title-line { + stroke: ${e.requirementBorderColor}; + stroke-width: ${e.requirementBorderSize}; + } + .relationshipLine { + stroke: ${e.relationColor}; + stroke-width: ${r==="neo"?e.strokeWidth:"1px"}; + } + .relationshipLabel { + fill: ${e.relationLabelColor}; + } + .edgeLabel { + background-color: ${e.edgeLabelBackground}; + } + .edgeLabel .label rect { + fill: ${e.edgeLabelBackground}; + } + .edgeLabel .label text { + fill: ${e.relationLabelColor}; + } + .divider { + stroke: ${e.nodeBorder}; + stroke-width: 1; + } + .label { + font-family: ${e.fontFamily}; + color: ${e.nodeTextColor||e.textColor}; + } + .label text,span { + fill: ${e.nodeTextColor||e.textColor}; + color: ${e.nodeTextColor||e.textColor}; + } + .labelBkg { + background-color: ${n??e.edgeLabelBackground}; + } + +`},"getStyles"),ipt=rpt}),npt={};Mr(npt,{draw:()=>apt});var apt,CRt=G(()=>{"use strict";Er(),Nt(),l2(),xm(),Tm(),Ar(),apt=s(async function(e,t,r,i){Ee.info("REF0:"),Ee.info("Drawing requirement diagram (unified)",t);let{securityLevel:n,state:a,layout:o,look:l}=it(),u=i.db.getData(),h=bg(t,n);u.type=i.type,u.layoutAlgorithm=mv(o),u.nodeSpacing=a?.nodeSpacing??50,u.rankSpacing=a?.rankSpacing??50,u.markers=l==="neo"?["requirement_contains_neo","requirement_arrow_neo"]:["requirement_contains","requirement_arrow"],u.diagramId=t,await bm(u,h),ai.insertTitle(h,"requirementDiagramTitleText",a?.titleTopMargin??25,i.db.getDiagramTitle()),kf(h,8,"requirementDiagram",a?.useMaxWidth??!0)},"draw")}),spt={};Mr(spt,{diagram:()=>opt});var opt,wRt=G(()=>{"use strict";xRt(),TRt(),kRt(),CRt(),opt={parser:Jdt,get db(){return new ept},renderer:npt,styles:ipt}}),kI,lpt,ERt=G(()=>{"use strict";kI=(function(){var e=s(function(Ke,De,Ze,Fe){for(Ze=Ze||{},Fe=Ke.length;Fe--;Ze[Ke[Fe]]=De);return Ze},"o"),t=[1,2],r=[1,3],i=[1,4],n=[2,4],a=[1,9],o=[1,11],l=[1,12],u=[1,14],h=[1,15],d=[1,17],p=[1,18],f=[1,19],g=[1,25],m=[1,26],y=[1,27],k=[1,28],C=[1,29],S=[1,30],E=[1,31],_=[1,32],b=[1,33],v=[1,34],x=[1,35],w=[1,36],R=[1,37],$=[1,38],M=[1,39],N=[1,40],j=[1,42],I=[1,43],z=[1,44],F=[1,45],O=[1,46],B=[1,47],Y=[1,4,5,10,14,15,17,19,22,24,30,31,32,34,36,37,38,39,40,42,44,45,47,48,49,50,51,53,54,56,61,62,63,64,73],U=[1,74],H=[1,80],J=[1,81],te=[1,82],ie=[1,83],ue=[1,84],se=[1,85],le=[1,86],Te=[1,87],oe=[1,88],fe=[1,89],ge=[1,90],ke=[1,91],Ne=[1,92],xe=[1,93],$e=[1,94],Me=[1,95],He=[1,96],Ge=[1,97],ze=[1,98],Ve=[1,99],et=[1,100],he=[1,101],ye=[1,102],be=[1,103],Ie=[1,104],pe=[1,105],Qe=[2,78],W=[4,5,17,51,53,54],Ae=[4,5,10,14,15,17,19,22,24,30,31,32,34,36,37,38,39,40,42,44,45,47,51,53,54,56,61,62,63,64,73],K=[4,5,10,14,15,17,19,22,24,30,31,32,34,36,37,38,39,40,42,44,45,47,50,51,53,54,56,61,62,63,64,73],de=[4,5,10,14,15,17,19,22,24,30,31,32,34,36,37,38,39,40,42,44,45,47,49,51,53,54,56,61,62,63,64,73],ee=[4,5,10,14,15,17,19,22,24,30,31,32,34,36,37,38,39,40,42,44,45,47,48,51,53,54,56,61,62,63,64,73],ve=[5,52],X=[70,71,72,73],me=[1,151],Ce={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,SPACE:4,NEWLINE:5,SD:6,document:7,line:8,statement:9,INVALID:10,box_section:11,box_line:12,participant_statement:13,create:14,box:15,restOfLine:16,end:17,signal:18,autonumber:19,NUM:20,off:21,activate:22,actor:23,deactivate:24,note_statement:25,links_statement:26,link_statement:27,properties_statement:28,details_statement:29,title:30,legacy_title:31,acc_title:32,acc_title_value:33,acc_descr:34,acc_descr_value:35,acc_descr_multiline_value:36,loop:37,rect:38,opt:39,alt:40,else_sections:41,par:42,par_sections:43,par_over:44,critical:45,option_sections:46,break:47,option:48,and:49,else:50,participant:51,AS:52,participant_actor:53,destroy:54,actor_with_config:55,note:56,placement:57,text2:58,over:59,actor_pair:60,links:61,link:62,properties:63,details:64,spaceList:65,",":66,left_of:67,right_of:68,signaltype:69,"+":70,"-":71,"()":72,ACTOR:73,config_object:74,CONFIG_START:75,CONFIG_CONTENT:76,CONFIG_END:77,SOLID_OPEN_ARROW:78,DOTTED_OPEN_ARROW:79,SOLID_ARROW:80,SOLID_ARROW_TOP:81,SOLID_ARROW_BOTTOM:82,STICK_ARROW_TOP:83,STICK_ARROW_BOTTOM:84,SOLID_ARROW_TOP_DOTTED:85,SOLID_ARROW_BOTTOM_DOTTED:86,STICK_ARROW_TOP_DOTTED:87,STICK_ARROW_BOTTOM_DOTTED:88,SOLID_ARROW_TOP_REVERSE:89,SOLID_ARROW_BOTTOM_REVERSE:90,STICK_ARROW_TOP_REVERSE:91,STICK_ARROW_BOTTOM_REVERSE:92,SOLID_ARROW_TOP_REVERSE_DOTTED:93,SOLID_ARROW_BOTTOM_REVERSE_DOTTED:94,STICK_ARROW_TOP_REVERSE_DOTTED:95,STICK_ARROW_BOTTOM_REVERSE_DOTTED:96,BIDIRECTIONAL_SOLID_ARROW:97,DOTTED_ARROW:98,BIDIRECTIONAL_DOTTED_ARROW:99,SOLID_CROSS:100,DOTTED_CROSS:101,SOLID_POINT:102,DOTTED_POINT:103,TXT:104,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NEWLINE",6:"SD",10:"INVALID",14:"create",15:"box",16:"restOfLine",17:"end",19:"autonumber",20:"NUM",21:"off",22:"activate",24:"deactivate",30:"title",31:"legacy_title",32:"acc_title",33:"acc_title_value",34:"acc_descr",35:"acc_descr_value",36:"acc_descr_multiline_value",37:"loop",38:"rect",39:"opt",40:"alt",42:"par",44:"par_over",45:"critical",47:"break",48:"option",49:"and",50:"else",51:"participant",52:"AS",53:"participant_actor",54:"destroy",56:"note",59:"over",61:"links",62:"link",63:"properties",64:"details",66:",",67:"left_of",68:"right_of",70:"+",71:"-",72:"()",73:"ACTOR",75:"CONFIG_START",76:"CONFIG_CONTENT",77:"CONFIG_END",78:"SOLID_OPEN_ARROW",79:"DOTTED_OPEN_ARROW",80:"SOLID_ARROW",81:"SOLID_ARROW_TOP",82:"SOLID_ARROW_BOTTOM",83:"STICK_ARROW_TOP",84:"STICK_ARROW_BOTTOM",85:"SOLID_ARROW_TOP_DOTTED",86:"SOLID_ARROW_BOTTOM_DOTTED",87:"STICK_ARROW_TOP_DOTTED",88:"STICK_ARROW_BOTTOM_DOTTED",89:"SOLID_ARROW_TOP_REVERSE",90:"SOLID_ARROW_BOTTOM_REVERSE",91:"STICK_ARROW_TOP_REVERSE",92:"STICK_ARROW_BOTTOM_REVERSE",93:"SOLID_ARROW_TOP_REVERSE_DOTTED",94:"SOLID_ARROW_BOTTOM_REVERSE_DOTTED",95:"STICK_ARROW_TOP_REVERSE_DOTTED",96:"STICK_ARROW_BOTTOM_REVERSE_DOTTED",97:"BIDIRECTIONAL_SOLID_ARROW",98:"DOTTED_ARROW",99:"BIDIRECTIONAL_DOTTED_ARROW",100:"SOLID_CROSS",101:"DOTTED_CROSS",102:"SOLID_POINT",103:"DOTTED_POINT",104:"TXT"},productions_:[0,[3,2],[3,2],[3,2],[7,0],[7,2],[8,2],[8,1],[8,1],[8,1],[11,0],[11,2],[12,2],[12,1],[12,1],[9,1],[9,2],[9,4],[9,2],[9,4],[9,3],[9,3],[9,2],[9,3],[9,3],[9,2],[9,2],[9,2],[9,2],[9,2],[9,1],[9,1],[9,2],[9,2],[9,1],[9,4],[9,4],[9,4],[9,4],[9,4],[9,4],[9,4],[9,4],[46,1],[46,4],[43,1],[43,4],[41,1],[41,4],[13,5],[13,3],[13,5],[13,3],[13,3],[13,5],[13,3],[13,5],[13,3],[25,4],[25,4],[26,3],[27,3],[28,3],[29,3],[65,2],[65,1],[60,3],[60,1],[57,1],[57,1],[18,5],[18,5],[18,5],[18,5],[18,6],[18,4],[55,2],[74,3],[23,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[69,1],[58,1]],performAction:s(function(Ke,De,Ze,Fe,Je,Le,ct){var qe=Le.length-1;switch(Je){case 3:return Fe.apply(Le[qe]),Le[qe];case 4:case 10:this.$=[];break;case 5:case 11:Le[qe-1].push(Le[qe]),this.$=Le[qe-1];break;case 6:case 7:case 12:case 13:this.$=Le[qe];break;case 8:case 9:case 14:this.$=[];break;case 16:Le[qe].type="createParticipant",this.$=Le[qe];break;case 17:Le[qe-1].unshift({type:"boxStart",boxData:Fe.parseBoxData(Le[qe-2])}),Le[qe-1].push({type:"boxEnd",boxText:Le[qe-2]}),this.$=Le[qe-1];break;case 19:this.$={type:"sequenceIndex",sequenceIndex:Number(Le[qe-2]),sequenceIndexStep:Number(Le[qe-1]),sequenceVisible:!0,signalType:Fe.LINETYPE.AUTONUMBER};break;case 20:this.$={type:"sequenceIndex",sequenceIndex:Number(Le[qe-1]),sequenceIndexStep:1,sequenceVisible:!0,signalType:Fe.LINETYPE.AUTONUMBER};break;case 21:this.$={type:"sequenceIndex",sequenceVisible:!1,signalType:Fe.LINETYPE.AUTONUMBER};break;case 22:this.$={type:"sequenceIndex",sequenceVisible:!0,signalType:Fe.LINETYPE.AUTONUMBER};break;case 23:this.$={type:"activeStart",signalType:Fe.LINETYPE.ACTIVE_START,actor:Le[qe-1].actor};break;case 24:this.$={type:"activeEnd",signalType:Fe.LINETYPE.ACTIVE_END,actor:Le[qe-1].actor};break;case 30:Fe.setDiagramTitle(Le[qe].substring(6)),this.$=Le[qe].substring(6);break;case 31:Fe.setDiagramTitle(Le[qe].substring(7)),this.$=Le[qe].substring(7);break;case 32:this.$=Le[qe].trim(),Fe.setAccTitle(this.$);break;case 33:case 34:this.$=Le[qe].trim(),Fe.setAccDescription(this.$);break;case 35:Le[qe-1].unshift({type:"loopStart",loopText:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.LOOP_START}),Le[qe-1].push({type:"loopEnd",loopText:Le[qe-2],signalType:Fe.LINETYPE.LOOP_END}),this.$=Le[qe-1];break;case 36:Le[qe-1].unshift({type:"rectStart",color:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.RECT_START}),Le[qe-1].push({type:"rectEnd",color:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.RECT_END}),this.$=Le[qe-1];break;case 37:Le[qe-1].unshift({type:"optStart",optText:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.OPT_START}),Le[qe-1].push({type:"optEnd",optText:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.OPT_END}),this.$=Le[qe-1];break;case 38:Le[qe-1].unshift({type:"altStart",altText:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.ALT_START}),Le[qe-1].push({type:"altEnd",signalType:Fe.LINETYPE.ALT_END}),this.$=Le[qe-1];break;case 39:Le[qe-1].unshift({type:"parStart",parText:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.PAR_START}),Le[qe-1].push({type:"parEnd",signalType:Fe.LINETYPE.PAR_END}),this.$=Le[qe-1];break;case 40:Le[qe-1].unshift({type:"parStart",parText:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.PAR_OVER_START}),Le[qe-1].push({type:"parEnd",signalType:Fe.LINETYPE.PAR_END}),this.$=Le[qe-1];break;case 41:Le[qe-1].unshift({type:"criticalStart",criticalText:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.CRITICAL_START}),Le[qe-1].push({type:"criticalEnd",signalType:Fe.LINETYPE.CRITICAL_END}),this.$=Le[qe-1];break;case 42:Le[qe-1].unshift({type:"breakStart",breakText:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.BREAK_START}),Le[qe-1].push({type:"breakEnd",optText:Fe.parseMessage(Le[qe-2]),signalType:Fe.LINETYPE.BREAK_END}),this.$=Le[qe-1];break;case 44:this.$=Le[qe-3].concat([{type:"option",optionText:Fe.parseMessage(Le[qe-1]),signalType:Fe.LINETYPE.CRITICAL_OPTION},Le[qe]]);break;case 46:this.$=Le[qe-3].concat([{type:"and",parText:Fe.parseMessage(Le[qe-1]),signalType:Fe.LINETYPE.PAR_AND},Le[qe]]);break;case 48:this.$=Le[qe-3].concat([{type:"else",altText:Fe.parseMessage(Le[qe-1]),signalType:Fe.LINETYPE.ALT_ELSE},Le[qe]]);break;case 49:Le[qe-3].draw="participant",Le[qe-3].type="addParticipant",Le[qe-3].description=Fe.parseMessage(Le[qe-1]),this.$=Le[qe-3];break;case 50:Le[qe-1].draw="participant",Le[qe-1].type="addParticipant",this.$=Le[qe-1];break;case 51:Le[qe-3].draw="actor",Le[qe-3].type="addParticipant",Le[qe-3].description=Fe.parseMessage(Le[qe-1]),this.$=Le[qe-3];break;case 52:case 57:Le[qe-1].draw="actor",Le[qe-1].type="addParticipant",this.$=Le[qe-1];break;case 53:Le[qe-1].type="destroyParticipant",this.$=Le[qe-1];break;case 54:Le[qe-3].draw="participant",Le[qe-3].type="addParticipant",Le[qe-3].description=Fe.parseMessage(Le[qe-1]),this.$=Le[qe-3];break;case 55:Le[qe-1].draw="participant",Le[qe-1].type="addParticipant",this.$=Le[qe-1];break;case 56:Le[qe-3].draw="actor",Le[qe-3].type="addParticipant",Le[qe-3].description=Fe.parseMessage(Le[qe-1]),this.$=Le[qe-3];break;case 58:this.$=[Le[qe-1],{type:"addNote",placement:Le[qe-2],actor:Le[qe-1].actor,text:Le[qe]}];break;case 59:Le[qe-2]=[].concat(Le[qe-1],Le[qe-1]).slice(0,2),Le[qe-2][0]=Le[qe-2][0].actor,Le[qe-2][1]=Le[qe-2][1].actor,this.$=[Le[qe-1],{type:"addNote",placement:Fe.PLACEMENT.OVER,actor:Le[qe-2].slice(0,2),text:Le[qe]}];break;case 60:this.$=[Le[qe-1],{type:"addLinks",actor:Le[qe-1].actor,text:Le[qe]}];break;case 61:this.$=[Le[qe-1],{type:"addALink",actor:Le[qe-1].actor,text:Le[qe]}];break;case 62:this.$=[Le[qe-1],{type:"addProperties",actor:Le[qe-1].actor,text:Le[qe]}];break;case 63:this.$=[Le[qe-1],{type:"addDetails",actor:Le[qe-1].actor,text:Le[qe]}];break;case 66:this.$=[Le[qe-2],Le[qe]];break;case 67:this.$=Le[qe];break;case 68:this.$=Fe.PLACEMENT.LEFTOF;break;case 69:this.$=Fe.PLACEMENT.RIGHTOF;break;case 70:this.$=[Le[qe-4],Le[qe-1],{type:"addMessage",from:Le[qe-4].actor,to:Le[qe-1].actor,signalType:Le[qe-3],msg:Le[qe],activate:!0},{type:"activeStart",signalType:Fe.LINETYPE.ACTIVE_START,actor:Le[qe-1].actor}];break;case 71:this.$=[Le[qe-4],Le[qe-1],{type:"addMessage",from:Le[qe-4].actor,to:Le[qe-1].actor,signalType:Le[qe-3],msg:Le[qe]},{type:"activeEnd",signalType:Fe.LINETYPE.ACTIVE_END,actor:Le[qe-4].actor}];break;case 72:this.$=[Le[qe-4],Le[qe-1],{type:"addMessage",from:Le[qe-4].actor,to:Le[qe-1].actor,signalType:Le[qe-3],msg:Le[qe],activate:!0,centralConnection:Fe.LINETYPE.CENTRAL_CONNECTION},{type:"centralConnection",signalType:Fe.LINETYPE.CENTRAL_CONNECTION,actor:Le[qe-1].actor}];break;case 73:this.$=[Le[qe-4],Le[qe-1],{type:"addMessage",from:Le[qe-4].actor,to:Le[qe-1].actor,signalType:Le[qe-2],msg:Le[qe],activate:!1,centralConnection:Fe.LINETYPE.CENTRAL_CONNECTION_REVERSE},{type:"centralConnectionReverse",signalType:Fe.LINETYPE.CENTRAL_CONNECTION_REVERSE,actor:Le[qe-4].actor}];break;case 74:this.$=[Le[qe-5],Le[qe-1],{type:"addMessage",from:Le[qe-5].actor,to:Le[qe-1].actor,signalType:Le[qe-3],msg:Le[qe],activate:!0,centralConnection:Fe.LINETYPE.CENTRAL_CONNECTION_DUAL},{type:"centralConnection",signalType:Fe.LINETYPE.CENTRAL_CONNECTION,actor:Le[qe-1].actor},{type:"centralConnectionReverse",signalType:Fe.LINETYPE.CENTRAL_CONNECTION_REVERSE,actor:Le[qe-5].actor}];break;case 75:this.$=[Le[qe-3],Le[qe-1],{type:"addMessage",from:Le[qe-3].actor,to:Le[qe-1].actor,signalType:Le[qe-2],msg:Le[qe]}];break;case 76:this.$={type:"addParticipant",actor:Le[qe-1],config:Le[qe]};break;case 77:this.$=Le[qe-1].trim();break;case 78:this.$={type:"addParticipant",actor:Le[qe]};break;case 79:this.$=Fe.LINETYPE.SOLID_OPEN;break;case 80:this.$=Fe.LINETYPE.DOTTED_OPEN;break;case 81:this.$=Fe.LINETYPE.SOLID;break;case 82:this.$=Fe.LINETYPE.SOLID_TOP;break;case 83:this.$=Fe.LINETYPE.SOLID_BOTTOM;break;case 84:this.$=Fe.LINETYPE.STICK_TOP;break;case 85:this.$=Fe.LINETYPE.STICK_BOTTOM;break;case 86:this.$=Fe.LINETYPE.SOLID_TOP_DOTTED;break;case 87:this.$=Fe.LINETYPE.SOLID_BOTTOM_DOTTED;break;case 88:this.$=Fe.LINETYPE.STICK_TOP_DOTTED;break;case 89:this.$=Fe.LINETYPE.STICK_BOTTOM_DOTTED;break;case 90:this.$=Fe.LINETYPE.SOLID_ARROW_TOP_REVERSE;break;case 91:this.$=Fe.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE;break;case 92:this.$=Fe.LINETYPE.STICK_ARROW_TOP_REVERSE;break;case 93:this.$=Fe.LINETYPE.STICK_ARROW_BOTTOM_REVERSE;break;case 94:this.$=Fe.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED;break;case 95:this.$=Fe.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED;break;case 96:this.$=Fe.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED;break;case 97:this.$=Fe.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED;break;case 98:this.$=Fe.LINETYPE.BIDIRECTIONAL_SOLID;break;case 99:this.$=Fe.LINETYPE.DOTTED;break;case 100:this.$=Fe.LINETYPE.BIDIRECTIONAL_DOTTED;break;case 101:this.$=Fe.LINETYPE.SOLID_CROSS;break;case 102:this.$=Fe.LINETYPE.DOTTED_CROSS;break;case 103:this.$=Fe.LINETYPE.SOLID_POINT;break;case 104:this.$=Fe.LINETYPE.DOTTED_POINT;break;case 105:this.$=Fe.parseMessage(Le[qe].trim().substring(1));break}},"anonymous"),table:[{3:1,4:t,5:r,6:i},{1:[3]},{3:5,4:t,5:r,6:i},{3:6,4:t,5:r,6:i},e([1,4,5,10,14,15,19,22,24,30,31,32,34,36,37,38,39,40,42,44,45,47,51,53,54,56,61,62,63,64,73],n,{7:7}),{1:[2,1]},{1:[2,2]},{1:[2,3],4:a,5:o,8:8,9:10,10:l,13:13,14:u,15:h,18:16,19:d,22:p,23:41,24:f,25:20,26:21,27:22,28:23,29:24,30:g,31:m,32:y,34:k,36:C,37:S,38:E,39:_,40:b,42:v,44:x,45:w,47:R,51:$,53:M,54:N,56:j,61:I,62:z,63:F,64:O,73:B},e(Y,[2,5]),{9:48,13:13,14:u,15:h,18:16,19:d,22:p,23:41,24:f,25:20,26:21,27:22,28:23,29:24,30:g,31:m,32:y,34:k,36:C,37:S,38:E,39:_,40:b,42:v,44:x,45:w,47:R,51:$,53:M,54:N,56:j,61:I,62:z,63:F,64:O,73:B},e(Y,[2,7]),e(Y,[2,8]),e(Y,[2,9]),e(Y,[2,15]),{13:49,51:$,53:M,54:N},{16:[1,50]},{5:[1,51]},{5:[1,54],20:[1,52],21:[1,53]},{23:55,73:B},{23:56,73:B},{5:[1,57]},{5:[1,58]},{5:[1,59]},{5:[1,60]},{5:[1,61]},e(Y,[2,30]),e(Y,[2,31]),{33:[1,62]},{35:[1,63]},e(Y,[2,34]),{16:[1,64]},{16:[1,65]},{16:[1,66]},{16:[1,67]},{16:[1,68]},{16:[1,69]},{16:[1,70]},{16:[1,71]},{23:72,55:73,73:U},{23:75,55:76,73:U},{23:77,73:B},{69:78,72:[1,79],78:H,79:J,80:te,81:ie,82:ue,83:se,84:le,85:Te,86:oe,87:fe,88:ge,89:ke,90:Ne,91:xe,92:$e,93:Me,94:He,95:Ge,96:ze,97:Ve,98:et,99:he,100:ye,101:be,102:Ie,103:pe},{57:106,59:[1,107],67:[1,108],68:[1,109]},{23:110,73:B},{23:111,73:B},{23:112,73:B},{23:113,73:B},e([5,66,72,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104],Qe),e(Y,[2,6]),e(Y,[2,16]),e(W,[2,10],{11:114}),e(Y,[2,18]),{5:[1,116],20:[1,115]},{5:[1,117]},e(Y,[2,22]),{5:[1,118]},{5:[1,119]},e(Y,[2,25]),e(Y,[2,26]),e(Y,[2,27]),e(Y,[2,28]),e(Y,[2,29]),e(Y,[2,32]),e(Y,[2,33]),e(Ae,n,{7:120}),e(Ae,n,{7:121}),e(Ae,n,{7:122}),e(K,n,{41:123,7:124}),e(de,n,{43:125,7:126}),e(de,n,{7:126,43:127}),e(ee,n,{46:128,7:129}),e(Ae,n,{7:130}),{5:[1,132],52:[1,131]},{5:[1,134],52:[1,133]},e(ve,Qe,{74:135,75:[1,136]}),{5:[1,138],52:[1,137]},{5:[1,140],52:[1,139]},{5:[1,141]},{23:145,70:[1,142],71:[1,143],72:[1,144],73:B},{69:146,78:H,79:J,80:te,81:ie,82:ue,83:se,84:le,85:Te,86:oe,87:fe,88:ge,89:ke,90:Ne,91:xe,92:$e,93:Me,94:He,95:Ge,96:ze,97:Ve,98:et,99:he,100:ye,101:be,102:Ie,103:pe},e(X,[2,79]),e(X,[2,80]),e(X,[2,81]),e(X,[2,82]),e(X,[2,83]),e(X,[2,84]),e(X,[2,85]),e(X,[2,86]),e(X,[2,87]),e(X,[2,88]),e(X,[2,89]),e(X,[2,90]),e(X,[2,91]),e(X,[2,92]),e(X,[2,93]),e(X,[2,94]),e(X,[2,95]),e(X,[2,96]),e(X,[2,97]),e(X,[2,98]),e(X,[2,99]),e(X,[2,100]),e(X,[2,101]),e(X,[2,102]),e(X,[2,103]),e(X,[2,104]),{23:147,73:B},{23:149,60:148,73:B},{73:[2,68]},{73:[2,69]},{58:150,104:me},{58:152,104:me},{58:153,104:me},{58:154,104:me},{4:[1,157],5:[1,159],12:156,13:158,17:[1,155],51:$,53:M,54:N},{5:[1,160]},e(Y,[2,20]),e(Y,[2,21]),e(Y,[2,23]),e(Y,[2,24]),{4:a,5:o,8:8,9:10,10:l,13:13,14:u,15:h,17:[1,161],18:16,19:d,22:p,23:41,24:f,25:20,26:21,27:22,28:23,29:24,30:g,31:m,32:y,34:k,36:C,37:S,38:E,39:_,40:b,42:v,44:x,45:w,47:R,51:$,53:M,54:N,56:j,61:I,62:z,63:F,64:O,73:B},{4:a,5:o,8:8,9:10,10:l,13:13,14:u,15:h,17:[1,162],18:16,19:d,22:p,23:41,24:f,25:20,26:21,27:22,28:23,29:24,30:g,31:m,32:y,34:k,36:C,37:S,38:E,39:_,40:b,42:v,44:x,45:w,47:R,51:$,53:M,54:N,56:j,61:I,62:z,63:F,64:O,73:B},{4:a,5:o,8:8,9:10,10:l,13:13,14:u,15:h,17:[1,163],18:16,19:d,22:p,23:41,24:f,25:20,26:21,27:22,28:23,29:24,30:g,31:m,32:y,34:k,36:C,37:S,38:E,39:_,40:b,42:v,44:x,45:w,47:R,51:$,53:M,54:N,56:j,61:I,62:z,63:F,64:O,73:B},{17:[1,164]},{4:a,5:o,8:8,9:10,10:l,13:13,14:u,15:h,17:[2,47],18:16,19:d,22:p,23:41,24:f,25:20,26:21,27:22,28:23,29:24,30:g,31:m,32:y,34:k,36:C,37:S,38:E,39:_,40:b,42:v,44:x,45:w,47:R,50:[1,165],51:$,53:M,54:N,56:j,61:I,62:z,63:F,64:O,73:B},{17:[1,166]},{4:a,5:o,8:8,9:10,10:l,13:13,14:u,15:h,17:[2,45],18:16,19:d,22:p,23:41,24:f,25:20,26:21,27:22,28:23,29:24,30:g,31:m,32:y,34:k,36:C,37:S,38:E,39:_,40:b,42:v,44:x,45:w,47:R,49:[1,167],51:$,53:M,54:N,56:j,61:I,62:z,63:F,64:O,73:B},{17:[1,168]},{17:[1,169]},{4:a,5:o,8:8,9:10,10:l,13:13,14:u,15:h,17:[2,43],18:16,19:d,22:p,23:41,24:f,25:20,26:21,27:22,28:23,29:24,30:g,31:m,32:y,34:k,36:C,37:S,38:E,39:_,40:b,42:v,44:x,45:w,47:R,48:[1,170],51:$,53:M,54:N,56:j,61:I,62:z,63:F,64:O,73:B},{4:a,5:o,8:8,9:10,10:l,13:13,14:u,15:h,17:[1,171],18:16,19:d,22:p,23:41,24:f,25:20,26:21,27:22,28:23,29:24,30:g,31:m,32:y,34:k,36:C,37:S,38:E,39:_,40:b,42:v,44:x,45:w,47:R,51:$,53:M,54:N,56:j,61:I,62:z,63:F,64:O,73:B},{16:[1,172]},e(Y,[2,50]),{16:[1,173]},e(Y,[2,55]),e(ve,[2,76]),{76:[1,174]},{16:[1,175]},e(Y,[2,52]),{16:[1,176]},e(Y,[2,57]),e(Y,[2,53]),{23:177,73:B},{23:178,73:B},{23:179,73:B},{58:180,104:me},{23:181,72:[1,182],73:B},{58:183,104:me},{58:184,104:me},{66:[1,185],104:[2,67]},{5:[2,60]},{5:[2,105]},{5:[2,61]},{5:[2,62]},{5:[2,63]},e(Y,[2,17]),e(W,[2,11]),{13:186,51:$,53:M,54:N},e(W,[2,13]),e(W,[2,14]),e(Y,[2,19]),e(Y,[2,35]),e(Y,[2,36]),e(Y,[2,37]),e(Y,[2,38]),{16:[1,187]},e(Y,[2,39]),{16:[1,188]},e(Y,[2,40]),e(Y,[2,41]),{16:[1,189]},e(Y,[2,42]),{5:[1,190]},{5:[1,191]},{77:[1,192]},{5:[1,193]},{5:[1,194]},{58:195,104:me},{58:196,104:me},{58:197,104:me},{5:[2,75]},{58:198,104:me},{23:199,73:B},{5:[2,58]},{5:[2,59]},{23:200,73:B},e(W,[2,12]),e(K,n,{7:124,41:201}),e(de,n,{7:126,43:202}),e(ee,n,{7:129,46:203}),e(Y,[2,49]),e(Y,[2,54]),e(ve,[2,77]),e(Y,[2,51]),e(Y,[2,56]),{5:[2,70]},{5:[2,71]},{5:[2,72]},{5:[2,73]},{58:204,104:me},{104:[2,66]},{17:[2,48]},{17:[2,46]},{17:[2,44]},{5:[2,74]}],defaultActions:{5:[2,1],6:[2,2],108:[2,68],109:[2,69],150:[2,60],151:[2,105],152:[2,61],153:[2,62],154:[2,63],180:[2,75],183:[2,58],184:[2,59],195:[2,70],196:[2,71],197:[2,72],198:[2,73],200:[2,66],201:[2,48],202:[2,46],203:[2,44],204:[2,74]},parseError:s(function(Ke,De){if(De.recoverable)this.trace(Ke);else{var Ze=new Error(Ke);throw Ze.hash=De,Ze}},"parseError"),parse:s(function(Ke){var De=this,Ze=[0],Fe=[],Je=[null],Le=[],ct=this.table,qe="",mt=0,St=0,Lt=0,Bt=2,qt=1,tr=Le.slice.call(arguments,1),Et=Object.create(this.lexer),_t={yy:{}};for(var Kt in this.yy)Object.prototype.hasOwnProperty.call(this.yy,Kt)&&(_t.yy[Kt]=this.yy[Kt]);Et.setInput(Ke,_t.yy),_t.yy.lexer=Et,_t.yy.parser=this,typeof Et.yylloc>"u"&&(Et.yylloc={});var $t=Et.yylloc;Le.push($t);var Ot=Et.options&&Et.options.ranges;typeof _t.yy.parseError=="function"?this.parseError=_t.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function Jt(Ir){Ze.length=Ze.length-2*Ir,Je.length=Je.length-Ir,Le.length=Le.length-Ir}s(Jt,"popStack");function Br(){var Ir;return Ir=Fe.pop()||Et.lex()||qt,typeof Ir!="number"&&(Ir instanceof Array&&(Fe=Ir,Ir=Fe.pop()),Ir=De.symbols_[Ir]||Ir),Ir}s(Br,"lex");for(var At,jr,br,Hr,Ei,Li,Cr={},Ti,vt,wt,Wt;;){if(br=Ze[Ze.length-1],this.defaultActions[br]?Hr=this.defaultActions[br]:((At===null||typeof At>"u")&&(At=Br()),Hr=ct[br]&&ct[br][At]),typeof Hr>"u"||!Hr.length||!Hr[0]){var kr="";Wt=[];for(Ti in ct[br])this.terminals_[Ti]&&Ti>Bt&&Wt.push("'"+this.terminals_[Ti]+"'");Et.showPosition?kr="Parse error on line "+(mt+1)+`: +`+Et.showPosition()+` +Expecting `+Wt.join(", ")+", got '"+(this.terminals_[At]||At)+"'":kr="Parse error on line "+(mt+1)+": Unexpected "+(At==qt?"end of input":"'"+(this.terminals_[At]||At)+"'"),this.parseError(kr,{text:Et.match,token:this.terminals_[At]||At,line:Et.yylineno,loc:$t,expected:Wt})}if(Hr[0]instanceof Array&&Hr.length>1)throw new Error("Parse Error: multiple actions possible at state: "+br+", token: "+At);switch(Hr[0]){case 1:Ze.push(At),Je.push(Et.yytext),Le.push(Et.yylloc),Ze.push(Hr[1]),At=null,jr?(At=jr,jr=null):(St=Et.yyleng,qe=Et.yytext,mt=Et.yylineno,$t=Et.yylloc,Lt>0&&Lt--);break;case 2:if(vt=this.productions_[Hr[1]][1],Cr.$=Je[Je.length-vt],Cr._$={first_line:Le[Le.length-(vt||1)].first_line,last_line:Le[Le.length-1].last_line,first_column:Le[Le.length-(vt||1)].first_column,last_column:Le[Le.length-1].last_column},Ot&&(Cr._$.range=[Le[Le.length-(vt||1)].range[0],Le[Le.length-1].range[1]]),Li=this.performAction.apply(Cr,[qe,St,mt,_t.yy,Hr[1],Je,Le].concat(tr)),typeof Li<"u")return Li;vt&&(Ze=Ze.slice(0,-1*vt*2),Je=Je.slice(0,-1*vt),Le=Le.slice(0,-1*vt)),Ze.push(this.productions_[Hr[1]][0]),Je.push(Cr.$),Le.push(Cr._$),wt=ct[Ze[Ze.length-2]][Ze[Ze.length-1]],Ze.push(wt);break;case 3:return!0}}return!0},"parse")},Be=(function(){var Ke={EOF:1,parseError:s(function(De,Ze){if(this.yy.parser)this.yy.parser.parseError(De,Ze);else throw new Error(De)},"parseError"),setInput:s(function(De,Ze){return this.yy=Ze||this.yy||{},this._input=De,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var De=this._input[0];this.yytext+=De,this.yyleng++,this.offset++,this.match+=De,this.matched+=De;var Ze=De.match(/(?:\r\n?|\n).*/g);return Ze?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),De},"input"),unput:s(function(De){var Ze=De.length,Fe=De.split(/(?:\r\n?|\n)/g);this._input=De+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-Ze),this.offset-=Ze;var Je=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),Fe.length-1&&(this.yylineno-=Fe.length-1);var Le=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:Fe?(Fe.length===Je.length?this.yylloc.first_column:0)+Je[Je.length-Fe.length].length-Fe[0].length:this.yylloc.first_column-Ze},this.options.ranges&&(this.yylloc.range=[Le[0],Le[0]+this.yyleng-Ze]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(De){this.unput(this.match.slice(De))},"less"),pastInput:s(function(){var De=this.matched.substr(0,this.matched.length-this.match.length);return(De.length>20?"...":"")+De.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var De=this.match;return De.length<20&&(De+=this._input.substr(0,20-De.length)),(De.substr(0,20)+(De.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var De=this.pastInput(),Ze=new Array(De.length+1).join("-");return De+this.upcomingInput()+` +`+Ze+"^"},"showPosition"),test_match:s(function(De,Ze){var Fe,Je,Le;if(this.options.backtrack_lexer&&(Le={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(Le.yylloc.range=this.yylloc.range.slice(0))),Je=De[0].match(/(?:\r\n?|\n).*/g),Je&&(this.yylineno+=Je.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:Je?Je[Je.length-1].length-Je[Je.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+De[0].length},this.yytext+=De[0],this.match+=De[0],this.matches=De,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(De[0].length),this.matched+=De[0],Fe=this.performAction.call(this,this.yy,this,Ze,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),Fe)return Fe;if(this._backtrack){for(var ct in Le)this[ct]=Le[ct];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var De,Ze,Fe,Je;this._more||(this.yytext="",this.match="");for(var Le=this._currentRules(),ct=0;ctZe[0].length)){if(Ze=Fe,Je=ct,this.options.backtrack_lexer){if(De=this.test_match(Fe,Le[ct]),De!==!1)return De;if(this._backtrack){Ze=!1;continue}else return!1}else if(!this.options.flex)break}return Ze?(De=this.test_match(Ze,Le[Je]),De!==!1?De:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var De=this.next();return De||this.lex()},"lex"),begin:s(function(De){this.conditionStack.push(De)},"begin"),popState:s(function(){var De=this.conditionStack.length-1;return De>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(De){return De=this.conditionStack.length-1-Math.abs(De||0),De>=0?this.conditionStack[De]:"INITIAL"},"topState"),pushState:s(function(De){this.begin(De)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(De,Ze,Fe,Je){var Le=Je;switch(Fe){case 0:return 5;case 1:break;case 2:break;case 3:break;case 4:break;case 5:break;case 6:return 20;case 7:return this.begin("CONFIG"),75;case 8:return 76;case 9:return this.popState(),this.begin("ALIAS"),77;case 10:return this.popState(),this.popState(),77;case 11:return Ze.yytext=Ze.yytext.trim(),73;case 12:return Ze.yytext=Ze.yytext.trim(),this.begin("ALIAS"),73;case 13:return Ze.yytext=Ze.yytext.trim(),this.popState(),73;case 14:return this.popState(),10;case 15:return Ze.yytext=Ze.yytext.trim(),this.popState(),10;case 16:return this.begin("LINE"),15;case 17:return this.begin("ID"),51;case 18:return this.begin("ID"),53;case 19:return 14;case 20:return this.begin("ID"),54;case 21:return this.popState(),this.popState(),this.begin("LINE"),52;case 22:return this.popState(),this.popState(),5;case 23:return this.begin("LINE"),37;case 24:return this.begin("LINE"),38;case 25:return this.begin("LINE"),39;case 26:return this.begin("LINE"),40;case 27:return this.begin("LINE"),50;case 28:return this.begin("LINE"),42;case 29:return this.begin("LINE"),44;case 30:return this.begin("LINE"),49;case 31:return this.begin("LINE"),45;case 32:return this.begin("LINE"),48;case 33:return this.begin("LINE"),47;case 34:return this.popState(),16;case 35:return 17;case 36:return 67;case 37:return 68;case 38:return 61;case 39:return 62;case 40:return 63;case 41:return 64;case 42:return 59;case 43:return 56;case 44:return this.begin("ID"),22;case 45:return this.begin("ID"),24;case 46:return 30;case 47:return 31;case 48:return this.begin("acc_title"),32;case 49:return this.popState(),"acc_title_value";case 50:return this.begin("acc_descr"),34;case 51:return this.popState(),"acc_descr_value";case 52:this.begin("acc_descr_multiline");break;case 53:this.popState();break;case 54:return"acc_descr_multiline_value";case 55:return 6;case 56:return 19;case 57:return 21;case 58:return 66;case 59:return 5;case 60:return Ze.yytext=Ze.yytext.trim(),73;case 61:return 80;case 62:return 97;case 63:return 98;case 64:return 99;case 65:return 78;case 66:return 79;case 67:return 100;case 68:return 101;case 69:return 102;case 70:return 103;case 71:return 85;case 72:return 86;case 73:return 87;case 74:return 88;case 75:return 93;case 76:return 94;case 77:return 95;case 78:return 96;case 79:return 81;case 80:return 82;case 81:return 83;case 82:return 84;case 83:return 89;case 84:return 90;case 85:return 91;case 86:return 92;case 87:return 104;case 88:return 104;case 89:return 70;case 90:return 71;case 91:return 72;case 92:return 5;case 93:return 10}},"anonymous"),rules:[/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:([0-9]+(\.[0-9]{1,2})?|\.[0-9]{1,2})(?=[ \n]+))/i,/^(?:@\{)/i,/^(?:[^\}]+)/i,/^(?:\}(?=\s+as\s))/i,/^(?:\})/i,/^(?:[^\<->\->:\n,;@\s]+(?=@\{))/i,/^(?:[^<>:\n,;@\s]+(?=\s+as\s))/i,/^(?:[^<>:\n,;@]+(?=\s*[\n;#]|$))/i,/^(?:[^<>:\n,;@]*<[^\n]*)/i,/^(?:[^\n]+)/i,/^(?:box\b)/i,/^(?:participant\b)/i,/^(?:actor\b)/i,/^(?:create\b)/i,/^(?:destroy\b)/i,/^(?:as\b)/i,/^(?:(?:))/i,/^(?:loop\b)/i,/^(?:rect\b)/i,/^(?:opt\b)/i,/^(?:alt\b)/i,/^(?:else\b)/i,/^(?:par\b)/i,/^(?:par_over\b)/i,/^(?:and\b)/i,/^(?:critical\b)/i,/^(?:option\b)/i,/^(?:break\b)/i,/^(?:(?:[:]?(?:no)?wrap)?[^#\n;]*)/i,/^(?:end\b)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:links\b)/i,/^(?:link\b)/i,/^(?:properties\b)/i,/^(?:details\b)/i,/^(?:over\b)/i,/^(?:note\b)/i,/^(?:activate\b)/i,/^(?:deactivate\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:title:\s[^#\n;]+)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:sequenceDiagram\b)/i,/^(?:autonumber\b)/i,/^(?:off\b)/i,/^(?:,)/i,/^(?:;)/i,/^(?:[^\/\\\+\()\+<\->\->:\n,;]+((?!(-x|--x|-\)|--\)|-\|\\|-\\|-\/|-\/\/|-\|\/|\/\|-|\\\|-|\/\/-|\\\\-|\/\|-|--\|\\|--|\(\)))[\-]*[^\+<\->\->:\n,;]+)*)/i,/^(?:->>)/i,/^(?:<<->>)/i,/^(?:-->>)/i,/^(?:<<-->>)/i,/^(?:->)/i,/^(?:-->)/i,/^(?:-[x])/i,/^(?:--[x])/i,/^(?:-[\)])/i,/^(?:--[\)])/i,/^(?:--\|\\)/i,/^(?:--\|\/)/i,/^(?:--\\\\)/i,/^(?:--\/\/)/i,/^(?:\/\|--)/i,/^(?:\\\|--)/i,/^(?:\/\/--)/i,/^(?:\\\\--)/i,/^(?:-\|\\)/i,/^(?:-\|\/)/i,/^(?:-\\\\)/i,/^(?:-\/\/)/i,/^(?:\/\|-)/i,/^(?:\\\|-)/i,/^(?:\/\/-)/i,/^(?:\\\\-)/i,/^(?::(?:(?:no)?wrap)?[^#\n;]*)/i,/^(?::)/i,/^(?:\+)/i,/^(?:-)/i,/^(?:\(\))/i,/^(?:$)/i,/^(?:.)/i],conditions:{acc_descr_multiline:{rules:[53,54],inclusive:!1},acc_descr:{rules:[51],inclusive:!1},acc_title:{rules:[49],inclusive:!1},ID:{rules:[2,3,7,11,12,13,14,15],inclusive:!1},ALIAS:{rules:[2,3,21,22],inclusive:!1},LINE:{rules:[2,3,34],inclusive:!1},CONFIG:{rules:[8,9,10],inclusive:!1},CONFIG_DATA:{rules:[],inclusive:!1},INITIAL:{rules:[0,1,3,4,5,6,16,17,18,19,20,23,24,25,26,27,28,29,30,31,32,33,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,52,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93],inclusive:!0}}};return Ke})();Ce.lexer=Be;function Re(){this.yy={}}return s(Re,"Parser"),Re.prototype=Ce,Ce.Parser=Re,new Re})(),kI.parser=kI,lpt=kI}),cpt,upt,hpt,_5,dpt,ppt=G(()=>{"use strict";var e;Er(),X_(),Nt(),fhe(),mi(),fn(),cpt={SOLID:0,DOTTED:1,NOTE:2,SOLID_CROSS:3,DOTTED_CROSS:4,SOLID_OPEN:5,DOTTED_OPEN:6,LOOP_START:10,LOOP_END:11,ALT_START:12,ALT_ELSE:13,ALT_END:14,OPT_START:15,OPT_END:16,ACTIVE_START:17,ACTIVE_END:18,PAR_START:19,PAR_AND:20,PAR_END:21,RECT_START:22,RECT_END:23,SOLID_POINT:24,DOTTED_POINT:25,AUTONUMBER:26,CRITICAL_START:27,CRITICAL_OPTION:28,CRITICAL_END:29,BREAK_START:30,BREAK_END:31,PAR_OVER_START:32,BIDIRECTIONAL_SOLID:33,BIDIRECTIONAL_DOTTED:34,SOLID_TOP:41,SOLID_BOTTOM:42,STICK_TOP:43,STICK_BOTTOM:44,SOLID_ARROW_TOP_REVERSE:45,SOLID_ARROW_BOTTOM_REVERSE:46,STICK_ARROW_TOP_REVERSE:47,STICK_ARROW_BOTTOM_REVERSE:48,SOLID_TOP_DOTTED:51,SOLID_BOTTOM_DOTTED:52,STICK_TOP_DOTTED:53,STICK_BOTTOM_DOTTED:54,SOLID_ARROW_TOP_REVERSE_DOTTED:55,SOLID_ARROW_BOTTOM_REVERSE_DOTTED:56,STICK_ARROW_TOP_REVERSE_DOTTED:57,STICK_ARROW_BOTTOM_REVERSE_DOTTED:58,CENTRAL_CONNECTION:59,CENTRAL_CONNECTION_REVERSE:60,CENTRAL_CONNECTION_DUAL:61},upt={FILLED:0,OPEN:1},hpt={LEFTOF:0,RIGHTOF:1,OVER:2},_5={ACTOR:"actor",BOUNDARY:"boundary",COLLECTIONS:"collections",CONTROL:"control",DATABASE:"database",ENTITY:"entity",PARTICIPANT:"participant",QUEUE:"queue"},dpt=(e=class{constructor(){this.state=new H9(()=>({prevActor:void 0,actors:new Map,createdActors:new Map,destroyedActors:new Map,boxes:[],messages:[],notes:[],sequenceNumbersEnabled:!1,wrapEnabled:void 0,currentBox:void 0,lastCreated:void 0,lastDestroyed:void 0})),this.setAccTitle=bn,this.setAccDescription=In,this.setDiagramTitle=Dn,this.getAccTitle=Ln,this.getAccDescription=Mn,this.getDiagramTitle=xn,this.apply=this.apply.bind(this),this.parseBoxData=this.parseBoxData.bind(this),this.parseMessage=this.parseMessage.bind(this),this.clear(),this.setWrap(it().wrap),this.LINETYPE=cpt,this.ARROWTYPE=upt,this.PLACEMENT=hpt}addBox(r){this.state.records.boxes.push({name:r.text,wrap:r.wrap??this.autoWrap(),fill:r.color,actorKeys:[]}),this.state.records.currentBox=this.state.records.boxes.slice(-1)[0]}addActor(r,i,n,a,o){let l=this.state.records.currentBox,u;if(o!==void 0){let d;o.includes(` +`)?d=o+` +`:d=`{ +`+o+` +}`,u=B4(d,{schema:O4})}a=u?.type??a,u?.alias&&(!n||n.text===i)&&(n={text:u.alias,wrap:n?.wrap,type:a});let h=this.state.records.actors.get(r);if(h){if(this.state.records.currentBox&&h.box&&this.state.records.currentBox!==h.box)throw new Error(`A same participant should only be defined in one Box: ${h.name} can't be in '${h.box.name}' and in '${this.state.records.currentBox.name}' at the same time.`);if(l=h.box?h.box:this.state.records.currentBox,h.box=l,h&&i===h.name&&n==null)return}if(n?.text==null&&(n={text:i,type:a}),(a==null||n.text==null)&&(n={text:i,type:a}),this.state.records.actors.set(r,{box:l,name:i,description:n.text,wrap:n.wrap??this.autoWrap(),prevActor:this.state.records.prevActor,links:{},properties:{},actorCnt:null,rectData:null,type:a??"participant"}),this.state.records.prevActor){let d=this.state.records.actors.get(this.state.records.prevActor);d&&(d.nextActor=r)}this.state.records.currentBox&&this.state.records.currentBox.actorKeys.push(r),this.state.records.prevActor=r}activationCount(r){let i,n=0;if(!r)return 0;for(i=0;i>-",token:"->>-",line:"1",loc:{first_line:1,last_line:1,first_column:1,last_column:1},expected:["'ACTIVE_PARTICIPANT'"]},u}return this.state.records.messages.push({id:this.state.records.messages.length.toString(),from:r,to:i,message:n?.text??"",wrap:n?.wrap??this.autoWrap(),type:a,activate:o,centralConnection:l??0}),!0}hasAtLeastOneBox(){return this.state.records.boxes.length>0}hasAtLeastOneBoxWithTitle(){return this.state.records.boxes.some(r=>r.name)}getMessages(){return this.state.records.messages}getBoxes(){return this.state.records.boxes}getActors(){return this.state.records.actors}getCreatedActors(){return this.state.records.createdActors}getDestroyedActors(){return this.state.records.destroyedActors}getActor(r){return this.state.records.actors.get(r)}getActorKeys(){return[...this.state.records.actors.keys()]}enableSequenceNumbers(){this.state.records.sequenceNumbersEnabled=!0}disableSequenceNumbers(){this.state.records.sequenceNumbersEnabled=!1}showSequenceNumbers(){return this.state.records.sequenceNumbersEnabled}setWrap(r){this.state.records.wrapEnabled=r}extractWrap(r){if(r===void 0)return{};r=r.trim();let i=/^:?wrap:/.exec(r)!==null?!0:/^:?nowrap:/.exec(r)!==null?!1:void 0;return{cleanedText:(i===void 0?r:r.replace(/^:?(?:no)?wrap:/,"")).trim(),wrap:i}}autoWrap(){return this.state.records.wrapEnabled!==void 0?this.state.records.wrapEnabled:it().sequence?.wrap??!1}clear(){this.state.reset(),pn()}parseMessage(r){let i=r.trim(),{wrap:n,cleanedText:a}=this.extractWrap(i),o={text:a,wrap:n};return Ee.debug(`parseMessage: ${JSON.stringify(o)}`),o}parseBoxData(r){let i=/^((?:rgba?|hsla?)\s*\(.*\)|\w*)(.*)$/.exec(r),n=i?.[1]?i[1].trim():"transparent",a=i?.[2]?i[2].trim():void 0;if(window?.CSS)window.CSS.supports("color",n)||(n="transparent",a=r.trim());else{let u=new Option().style;u.color=n,u.color!==n&&(n="transparent",a=r.trim())}let{wrap:o,cleanedText:l}=this.extractWrap(a);return{text:l?Si(l,it()):void 0,color:n,wrap:o}}addNote(r,i,n){let a={actor:r,placement:i,message:n.text,wrap:n.wrap??this.autoWrap()},o=[].concat(r,r);this.state.records.notes.push(a),this.state.records.messages.push({id:this.state.records.messages.length.toString(),from:o[0],to:o[1],message:n.text,wrap:n.wrap??this.autoWrap(),type:this.LINETYPE.NOTE,placement:i})}addLinks(r,i){let n=this.getActor(r);try{let a=Si(i.text,it());a=a.replace(/=/g,"="),a=a.replace(/&/g,"&");let o=JSON.parse(a);this.insertLinks(n,o)}catch(a){Ee.error("error while parsing actor link text",a)}}addALink(r,i){let n=this.getActor(r);try{let a={},o=Si(i.text,it()),l=o.indexOf("@");o=o.replace(/=/g,"="),o=o.replace(/&/g,"&");let u=o.slice(0,l-1).trim(),h=o.slice(l+1).trim();a[u]=h,this.insertLinks(n,a)}catch(a){Ee.error("error while parsing actor link text",a)}}insertLinks(r,i){if(r.links==null)r.links=i;else for(let n in i)r.links[n]=i[n]}addProperties(r,i){let n=this.getActor(r);try{let a=Si(i.text,it()),o=JSON.parse(a);this.insertProperties(n,o)}catch(a){Ee.error("error while parsing actor properties text",a)}}insertProperties(r,i){if(r.properties==null)r.properties=i;else for(let n in i)r.properties[n]=i[n]}boxEnd(){this.state.records.currentBox=void 0}addDetails(r,i){let n=this.getActor(r),a=document.getElementById(i.text);try{let o=a.innerHTML,l=JSON.parse(o);l.properties&&this.insertProperties(n,l.properties),l.links&&this.insertLinks(n,l.links)}catch(o){Ee.error("error while parsing actor details text",o)}}getActorProperty(r,i){if(r?.properties!==void 0)return r.properties[i]}apply(r){if(Array.isArray(r))r.forEach(i=>{this.apply(i)});else switch(r.type){case"sequenceIndex":this.state.records.messages.push({id:this.state.records.messages.length.toString(),from:void 0,to:void 0,message:{start:r.sequenceIndex,step:r.sequenceIndexStep,visible:r.sequenceVisible},wrap:!1,type:r.signalType});break;case"addParticipant":this.addActor(r.actor,r.actor,r.description,r.draw,r.config);break;case"createParticipant":if(this.state.records.actors.has(r.actor))throw new Error("It is not possible to have actors with the same id, even if one is destroyed before the next is created. Use 'AS' aliases to simulate the behavior");this.state.records.lastCreated=r.actor,this.addActor(r.actor,r.actor,r.description,r.draw,r.config),this.state.records.createdActors.set(r.actor,this.state.records.messages.length);break;case"destroyParticipant":this.state.records.lastDestroyed=r.actor,this.state.records.destroyedActors.set(r.actor,this.state.records.messages.length);break;case"activeStart":this.addSignal(r.actor,void 0,void 0,r.signalType);break;case"centralConnection":this.addSignal(r.actor,void 0,void 0,r.signalType);break;case"centralConnectionReverse":this.addSignal(r.actor,void 0,void 0,r.signalType);break;case"activeEnd":this.addSignal(r.actor,void 0,void 0,r.signalType);break;case"addNote":this.addNote(r.actor,r.placement,r.text);break;case"addLinks":this.addLinks(r.actor,r.text);break;case"addALink":this.addALink(r.actor,r.text);break;case"addProperties":this.addProperties(r.actor,r.text);break;case"addDetails":this.addDetails(r.actor,r.text);break;case"addMessage":if(this.state.records.lastCreated){if(r.to!==this.state.records.lastCreated)throw new Error("The created participant "+this.state.records.lastCreated.name+" does not have an associated creating message after its declaration. Please check the sequence diagram.");this.state.records.lastCreated=void 0}else if(this.state.records.lastDestroyed){if(r.to!==this.state.records.lastDestroyed&&r.from!==this.state.records.lastDestroyed)throw new Error("The destroyed participant "+this.state.records.lastDestroyed.name+" does not have an associated destroying message after its declaration. Please check the sequence diagram.");this.state.records.lastDestroyed=void 0}this.addSignal(r.from,r.to,r.msg,r.signalType,r.activate,r.centralConnection);break;case"boxStart":this.addBox(r.boxData);break;case"boxEnd":this.boxEnd();break;case"loopStart":this.addSignal(void 0,void 0,r.loopText,r.signalType);break;case"loopEnd":this.addSignal(void 0,void 0,void 0,r.signalType);break;case"rectStart":this.addSignal(void 0,void 0,r.color,r.signalType);break;case"rectEnd":this.addSignal(void 0,void 0,void 0,r.signalType);break;case"optStart":this.addSignal(void 0,void 0,r.optText,r.signalType);break;case"optEnd":this.addSignal(void 0,void 0,void 0,r.signalType);break;case"altStart":this.addSignal(void 0,void 0,r.altText,r.signalType);break;case"else":this.addSignal(void 0,void 0,r.altText,r.signalType);break;case"altEnd":this.addSignal(void 0,void 0,void 0,r.signalType);break;case"setAccTitle":bn(r.text);break;case"parStart":this.addSignal(void 0,void 0,r.parText,r.signalType);break;case"and":this.addSignal(void 0,void 0,r.parText,r.signalType);break;case"parEnd":this.addSignal(void 0,void 0,void 0,r.signalType);break;case"criticalStart":this.addSignal(void 0,void 0,r.criticalText,r.signalType);break;case"option":this.addSignal(void 0,void 0,r.optionText,r.signalType);break;case"criticalEnd":this.addSignal(void 0,void 0,void 0,r.signalType);break;case"breakStart":this.addSignal(void 0,void 0,r.breakText,r.signalType);break;case"breakEnd":this.addSignal(void 0,void 0,void 0,r.signalType);break}}getConfig(){return it().sequence}},s(e,"SequenceDB"),e)}),fpt,gpt,SRt=G(()=>{"use strict";Er(),fpt=s(e=>{let t=e.dropShadow??"none",{look:r}=it();return`.actor { + stroke: ${e.actorBorder}; + fill: ${e.actorBkg}; + stroke-width: ${e.strokeWidth??1}; + } + + rect.actor.outer-path[data-look="neo"] { + filter: ${t}; + } + + rect.note[data-look="neo"] { + stroke:${e.noteBorderColor}; + fill:${e.noteBkgColor}; + filter: ${t}; + } + + text.actor > tspan { + fill: ${e.actorTextColor}; + stroke: none; + } + + .actor-line { + stroke: ${e.actorLineColor}; + } + + .innerArc { + stroke-width: 1.5; + stroke-dasharray: none; + } + + .messageLine0 { + stroke-width: 1.5; + stroke-dasharray: none; + stroke: ${e.signalColor}; + } + + .messageLine1 { + stroke-width: 1.5; + stroke-dasharray: 2, 2; + stroke: ${e.signalColor}; + } + + [id$="-arrowhead"] path { + fill: ${e.signalColor}; + stroke: ${e.signalColor}; + } + + .sequenceNumber { + fill: ${e.sequenceNumberColor}; + } + + [id$="-sequencenumber"] { + fill: ${e.signalColor}; + } + + [id$="-crosshead"] path { + fill: ${e.signalColor}; + stroke: ${e.signalColor}; + } + + .messageText { + fill: ${e.signalTextColor}; + stroke: none; + } + + .labelBox { + stroke: ${e.labelBoxBorderColor}; + fill: ${e.labelBoxBkgColor}; + filter: ${r==="neo"?t:"none"}; + } + + .labelText, .labelText > tspan { + fill: ${e.labelTextColor}; + stroke: none; + } + + .loopText, .loopText > tspan { + fill: ${e.loopTextColor}; + stroke: none; + } + + .sectionTitle, .sectionTitle > tspan { + fill: ${e.loopTextColor}; + stroke: none; + } + + .loopLine { + stroke-width: 2px; + stroke-dasharray: 2, 2; + stroke: ${e.labelBoxBorderColor}; + fill: ${e.labelBoxBorderColor}; + } + + .note { + //stroke: #decc93; + stroke: ${e.noteBorderColor}; + fill: ${e.noteBkgColor}; + } + + .noteText, .noteText > tspan { + fill: ${e.noteTextColor}; + stroke: none; + ${e.noteFontWeight?`font-weight: ${e.noteFontWeight};`:""} + } + + .activation0 { + fill: ${e.activationBkgColor}; + stroke: ${e.activationBorderColor}; + } + + .activation1 { + fill: ${e.activationBkgColor}; + stroke: ${e.activationBorderColor}; + } + + .activation2 { + fill: ${e.activationBkgColor}; + stroke: ${e.activationBorderColor}; + } + + .actorPopupMenu { + position: absolute; + } + + .actorPopupMenuPanel { + position: absolute; + fill: ${e.actorBkg}; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + filter: drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4)); +} + .actor-man circle, line { + fill: ${e.actorBkg}; + stroke-width: 2px; + } + + g rect.rect { + filter: ${t}; + stroke: ${e.nodeBorder}; + } +`},"getStyles"),gpt=fpt}),Jhe,S0,sp,op,A5,_0,Ih,Gb,mpt,R5,$5,R1,ede,$i,tde,ypt,vpt,bpt,xpt,Tpt,kpt,Cpt,wpt,Ept,Spt,_pt,Apt,Rpt,rde,$pt,Lpt,Ipt,Mpt,Dpt,Npt,Opt,Bpt,ide,Ppt,Mh,Fpt,zpt,qpt,Wpt,jpt,_n,_Rt=G(()=>{"use strict";Jhe=$o(zS(),1),Gr(),Ar(),mi(),pg(),S0=36,sp="actor-top",op="actor-bottom",A5="actor-box",_0="actor-man",Ih=new Set(["redux-color","redux-dark-color"]),Gb=s(function(e,t){let r=c4(e,t);return Rr().look==="neo"&&r.attr("data-look","neo"),r},"drawRect"),mpt=s(function(e,t,r,i,n){if(t.links===void 0||t.links===null||Object.keys(t.links).length===0)return{height:0,width:0};let a=t.links,o=t.actorCnt,l=t.rectData;var u="none";n&&(u="block !important");let h=e.append("g");h.attr("id","actor"+o+"_popup"),h.attr("class","actorPopupMenu"),h.attr("display",u);var d="";l.class!==void 0&&(d=" "+l.class);let p=l.width>r?l.width:r,f=h.append("rect");if(f.attr("class","actorPopupMenuPanel"+d),f.attr("x",l.x),f.attr("y",l.height),f.attr("fill",l.fill),f.attr("stroke",l.stroke),f.attr("width",p),f.attr("height",l.height),f.attr("rx",l.rx),f.attr("ry",l.ry),a!=null){var g=20;for(let k in a){var m=h.append("a"),y=(0,Jhe.sanitizeUrl)(a[k]);m.attr("xlink:href",y),m.attr("target","_blank"),Fpt(i)(k,m,l.x+10,l.height+g,p,20,{class:"actor"},i),g+=30}}return f.attr("height",g),{height:l.height+g,width:p}},"drawPopup"),R5=s(function(e){return"var pu = document.getElementById('"+e+"'); if (pu != null) { pu.style.display = pu.style.display == 'block' ? 'none' : 'block'; }"},"popupMenuToggle"),$5=s(async function(e,t,r=null){let i=e.append("foreignObject"),n=await mT(t.text,Rr()),a=i.append("xhtml:div").attr("style","width: fit-content;").attr("xmlns","http://www.w3.org/1999/xhtml").html(n).node().getBoundingClientRect();if(i.attr("height",Math.round(a.height)).attr("width",Math.round(a.width)),t.class==="noteText"){let o=e.node().firstChild;o.setAttribute("height",a.height+2*t.textMargin);let l=o.getBBox();i.attr("x",Math.round(l.x+l.width/2-a.width/2)).attr("y",Math.round(l.y+l.height/2-a.height/2))}else if(r){let{startx:o,stopx:l,starty:u}=r;if(o>l){let h=o;o=l,l=h}i.attr("x",Math.round(o+Math.abs(o-l)/2-a.width/2)),t.class==="loopText"?i.attr("y",Math.round(u)):i.attr("y",Math.round(u-a.height))}return[i]},"drawKatex"),R1=s(function(e,t){let r=0,i=0,n=t.text.split(Vt.lineBreakRegex),[a,o]=zu(t.fontSize),l=[],u=0,h=s(()=>t.y,"yfunc");if(t.valign!==void 0&&t.textMargin!==void 0&&t.textMargin>0)switch(t.valign){case"top":case"start":h=s(()=>Math.round(t.y+t.textMargin),"yfunc");break;case"middle":case"center":h=s(()=>Math.round(t.y+(r+i+t.textMargin)/2),"yfunc");break;case"bottom":case"end":h=s(()=>Math.round(t.y+(r+i+2*t.textMargin)-t.textMargin),"yfunc");break}if(t.anchor!==void 0&&t.textMargin!==void 0&&t.width!==void 0)switch(t.anchor){case"left":case"start":t.x=Math.round(t.x+t.textMargin),t.anchor="start",t.dominantBaseline="middle",t.alignmentBaseline="middle";break;case"middle":case"center":t.x=Math.round(t.x+t.width/2),t.anchor="middle",t.dominantBaseline="middle",t.alignmentBaseline="middle";break;case"right":case"end":t.x=Math.round(t.x+t.width-t.textMargin),t.anchor="end",t.dominantBaseline="middle",t.alignmentBaseline="middle";break}for(let[d,p]of n.entries()){t.textMargin!==void 0&&t.textMargin===0&&a!==void 0&&(u=d*a);let f=e.append("text");f.attr("x",t.x),f.attr("y",h()),t.anchor!==void 0&&f.attr("text-anchor",t.anchor).attr("dominant-baseline",t.dominantBaseline).attr("alignment-baseline",t.alignmentBaseline),t.fontFamily!==void 0&&f.style("font-family",t.fontFamily),o!==void 0&&f.style("font-size",o),t.fontWeight!==void 0&&f.style("font-weight",t.fontWeight),t.fill!==void 0&&f.attr("fill",t.fill),t.class!==void 0&&f.attr("class",t.class),t.dy!==void 0?f.attr("dy",t.dy):u!==0&&f.attr("dy",u);let g=p||zF;if(t.tspan){let m=f.append("tspan");m.attr("x",t.x),t.fill!==void 0&&m.attr("fill",t.fill),m.text(g)}else f.text(g);t.valign!==void 0&&t.textMargin!==void 0&&t.textMargin>0&&(i+=(f._groups||f)[0][0].getBBox().height,r=i),l.push(f)}return l},"drawText"),ede=s(function(e,t){function r(n,a,o,l,u){return n+","+a+" "+(n+o)+","+a+" "+(n+o)+","+(a+l-u)+" "+(n+o-u*1.2)+","+(a+l)+" "+n+","+(a+l)}s(r,"genPoints");let i=e.append("polygon");return i.attr("points",r(t.x,t.y,t.width,t.height,7)),i.attr("class","labelBox"),t.y=t.y+t.height/2,R1(e,t),i},"drawLabel"),$i=-1,tde=s((e,t,r,i)=>{e.select&&r.forEach(n=>{let a=t.get(n),o=e.select("#actor"+a.actorCnt);!i.mirrorActors&&a.stopy?o.attr("y2",a.stopy+a.height/2):i.mirrorActors&&o.attr("y2",a.stopy)})},"fixLifeLineHeights"),ypt=s(function(e,t,r,i,n){let a=i?t.stopy:t.starty,o=t.x+t.width/2,l=a+t.height,{look:u,theme:h,themeVariables:d}=r,{bkgColorArray:p,borderColorArray:f}=d,g=e.append("g").lower();var m=g;i||($i++,Object.keys(t.links||{}).length&&!r.forceMenus&&m.attr("onclick",R5(`actor${$i}_popup`)).attr("cursor","pointer"),m.append("line").attr("id","actor"+$i).attr("x1",o).attr("y1",l).attr("x2",o).attr("y2",2e3).attr("class","actor-line 200").attr("stroke-width","0.5px").attr("stroke","#999").attr("name",t.name).attr("data-et","life-line").attr("data-id",t.name),m=g.append("g"),t.actorCnt=$i,t.links!=null&&m.attr("id","root-"+$i),u==="neo"&&m.attr("data-look","neo"));let y=El();var k="actor";t.properties?.class?k=t.properties.class:y.fill="#eaeaea",i?k+=` ${op}`:k+=` ${sp}`,y.x=t.x,y.y=a,y.width=t.width,y.height=t.height,y.class=k,y.rx=3,y.ry=3,y.name=t.name,u==="neo"&&(y.rx=6,y.ry=6);let C=Gb(m,y),S=n.get(t.name)??0;if(Ih.has(h)&&(C.style("stroke",f[S%f.length]),C.style("fill",p[S%f.length])),u==="neo"&&C.attr("filter","url(#drop-shadow)"),t.rectData=y,t.properties?.icon){let _=t.properties.icon.trim();_.charAt(0)==="@"?U6(m,y.x+y.width-20,y.y+10,_.substr(1)):V6(m,y.x+y.width-20,y.y+10,_)}i||(m.attr("data-et","participant"),m.attr("data-type","participant"),m.attr("data-id",t.name)),Mh(r,pa(t.description))(t.description,m,y.x,y.y,y.width,y.height,{class:`actor ${A5}`},r);let E=t.height;if(C.node){let _=C.node().getBBox();t.height=_.height,E=_.height}return E},"drawActorTypeParticipant"),vpt=s(function(e,t,r,i,n){let a=i?t.stopy:t.starty,o=t.x+t.width/2,l=a+t.height,{look:u,theme:h,themeVariables:d}=r,{bkgColorArray:p,borderColorArray:f}=d,g=e.append("g").lower();var m=g;i||($i++,Object.keys(t.links||{}).length&&!r.forceMenus&&m.attr("onclick",R5(`actor${$i}_popup`)).attr("cursor","pointer"),m.append("line").attr("id","actor"+$i).attr("x1",o).attr("y1",l).attr("x2",o).attr("y2",2e3).attr("class","actor-line 200").attr("stroke-width","0.5px").attr("stroke","#999").attr("name",t.name).attr("data-et","life-line").attr("data-id",t.name),m=g.append("g"),t.actorCnt=$i,t.links!=null&&m.attr("id","root-"+$i),u==="neo"&&m.attr("data-look","neo"));let y=El();var k="actor";t.properties?.class?k=t.properties.class:y.fill="#eaeaea",i?k+=` ${op}`:k+=` ${sp}`,y.x=t.x,y.y=a,y.width=t.width,y.height=t.height,y.class=k,y.name=t.name;let C=6,S={...y,x:y.x+-C,y:y.y+ +C,class:"actor"},E=Gb(m,y),_=Gb(m,S);t.rectData=y,u==="neo"&&m.attr("filter","url(#drop-shadow)");let b=n.get(t.name)??0;if(Ih.has(h)&&(E.style("stroke",f[b%f.length]),E.style("fill",p[b%f.length]),_.style("stroke",f[b%f.length]),_.style("fill",p[b%f.length])),t.properties?.icon){let x=t.properties.icon.trim();x.charAt(0)==="@"?U6(m,y.x+y.width-20,y.y+10,x.substr(1)):V6(m,y.x+y.width-20,y.y+10,x)}Mh(r,pa(t.description))(t.description,m,y.x-C,y.y+C,y.width,y.height,{class:`actor ${A5}`},r);let v=t.height;if(E.node){let x=E.node().getBBox();t.height=x.height,v=x.height}return i||(m.attr("data-et","participant"),m.attr("data-type","collections"),m.attr("data-id",t.name)),v},"drawActorTypeCollections"),bpt=s(function(e,t,r,i,n){let a=i?t.stopy:t.starty,o=t.x+t.width/2,l=a+t.height,{look:u,theme:h,themeVariables:d}=r,{bkgColorArray:p,borderColorArray:f}=d,g=e.append("g").lower(),m=g;i||($i++,Object.keys(t.links||{}).length&&!r.forceMenus&&m.attr("onclick",R5(`actor${$i}_popup`)).attr("cursor","pointer"),m.append("line").attr("id","actor"+$i).attr("x1",o).attr("y1",l).attr("x2",o).attr("y2",2e3).attr("class","actor-line 200").attr("stroke-width","0.5px").attr("stroke","#999").attr("name",t.name).attr("data-et","life-line").attr("data-id",t.name),m=g.append("g"),t.actorCnt=$i,t.links!=null&&m.attr("id","root-"+$i),u==="neo"&&m.attr("data-look","neo"));let y=El(),k="actor";t.properties?.class?k=t.properties.class:y.fill="#eaeaea",i?k+=` ${op}`:k+=` ${sp}`,m.attr("class",k),y.x=t.x,y.y=a,y.width=t.width,y.height=t.height,y.name=t.name;let C=y.height/2,S=C/(2.5+y.height/50),E=m.append("g"),_=m.append("g"),b=`M ${y.x},${y.y+C} + a ${S},${C} 0 0 0 0,${y.height} + h ${y.width-2*S} + a ${S},${C} 0 0 0 0,-${y.height} + Z + `;E.append("path").attr("d",b),_.append("path").attr("d",`M ${y.x},${y.y+C} + a ${S},${C} 0 0 0 0,${y.height}`),E.attr("transform",`translate(${S}, ${-(y.height/2)})`),_.attr("transform",`translate(${y.width-S}, ${-y.height/2})`),t.rectData=y,u==="neo"&&E.attr("filter","url(#drop-shadow)");let v=n.get(t.name)??0;if(Ih.has(h)&&(E.style("stroke",f[v%f.length]),E.style("fill",p[v%f.length]),_.style("stroke",f[v%f.length]),_.style("fill",p[v%f.length])),t.properties?.icon){let R=t.properties.icon.trim(),$=y.x+y.width-20,M=y.y+10;R.charAt(0)==="@"?U6(m,$,M,R.substr(1)):V6(m,$,M,R)}Mh(r,pa(t.description))(t.description,m,y.x,y.y,y.width,y.height,{class:`actor ${A5}`},r);let x=t.height,w=E.select("path:last-child");if(w.node()){let R=w.node().getBBox();t.height=R.height,x=R.height}return i||(m.attr("data-et","participant"),m.attr("data-type","queue"),m.attr("data-id",t.name)),x},"drawActorTypeQueue"),xpt=s(function(e,t,r,i,n,a){let o=i?t.stopy:t.starty,l=t.x+t.width/2,u=o+75,{look:h,theme:d,themeVariables:p}=r,{bkgColorArray:f,borderColorArray:g,actorBorder:m,actorBkg:y}=p,k=e.append("g").lower();i||($i++,k.append("line").attr("id","actor"+$i).attr("x1",l).attr("y1",u).attr("x2",l).attr("y2",2e3).attr("class","actor-line 200").attr("stroke-width","0.5px").attr("stroke","#999").attr("name",t.name).attr("data-et","life-line").attr("data-id",t.name),t.actorCnt=$i);let C=e.append("g"),S=_0;i?S+=` ${op}`:S+=` ${sp}`,C.attr("class",S),C.attr("name",t.name);let E=El();E.x=t.x,E.y=o,E.fill="#eaeaea",E.width=t.width,E.height=t.height,E.class="actor";let _=t.x+t.width/2,b=o+32,v=22;C.append("defs").append("marker").attr("id",n+"-filled-head-control").attr("refX",11).attr("refY",5.8).attr("markerWidth",20).attr("markerHeight",28).attr("orient","172.5").attr("stroke-width",1.2).append("path").attr("d","M 14.4 5.6 L 7.2 10.4 L 8.8 5.6 L 7.2 0.8 Z"),C.append("circle").attr("cx",_).attr("cy",b).attr("r",v).attr("filter",`${h==="neo"?"url(#drop-shadow)":""}`),C.append("line").attr("marker-end","url(#"+n+"-filled-head-control)").attr("transform",`translate(${_}, ${b-v})`);let x=a.get(t.name)??0;Ih.has(d)?(C.style("stroke",g[x%g.length]),C.style("fill",f[x%g.length])):(C.style("stroke",m),C.style("fill",y));let w=C.node().getBBox();return t.height=w.height+2*(r?.sequence?.labelBoxHeight??0),Mh(r,pa(t.description))(t.description,C,E.x,E.y+v+(i?5:12),E.width,E.height,{class:`actor ${_0}`},r),i||(C.attr("data-et","participant"),C.attr("data-type","control"),C.attr("data-id",t.name)),t.height},"drawActorTypeControl"),Tpt=s(function(e,t,r,i,n){let a=i?t.stopy:t.starty,o=t.x+t.width/2,l=a+75,{look:u,theme:h,themeVariables:d}=r,{bkgColorArray:p,borderColorArray:f}=d,g=e.append("g").lower(),m=e.append("g"),y="actor";i?y+=` ${op}`:y+=` ${sp}`,m.attr("class",y),m.attr("name",t.name);let k=El();k.x=t.x,k.y=a,k.fill="#eaeaea",k.width=t.width,k.height=t.height,k.class="actor";let C=t.x+t.width/2,S=a+(i?10:25),E=22;m.append("circle").attr("cx",C).attr("cy",S).attr("r",E).attr("width",t.width).attr("height",t.height),m.append("line").attr("x1",C-E).attr("x2",C+E).attr("y1",S+E).attr("y2",S+E).attr("stroke-width",2),u==="neo"&&m.attr("filter","url(#drop-shadow)");let _=n.get(t.name)??0;Ih.has(h)&&(m.style("stroke",f[_%f.length]),m.style("fill",p[_%f.length]));let b=m.node().getBBox();return t.height=b.height+(r?.sequence?.labelBoxHeight??0),i||($i++,g.append("line").attr("id","actor"+$i).attr("x1",o).attr("y1",l).attr("x2",o).attr("y2",2e3).attr("class","actor-line 200").attr("stroke-width","0.5px").attr("stroke","#999").attr("name",t.name).attr("data-et","life-line").attr("data-id",t.name),t.actorCnt=$i),Mh(r,pa(t.description))(t.description,m,k.x,k.y+(i?15:30),k.width,k.height,{class:`actor ${_0}`},r),i?m.attr("transform",`translate(0, ${E})`):(m.attr("transform",`translate(0, ${E/2-5})`),m.attr("data-et","participant"),m.attr("data-type","entity"),m.attr("data-id",t.name)),t.height},"drawActorTypeEntity"),kpt=s(function(e,t,r,i,n){let a=i?t.stopy:t.starty,o=t.x+t.width/2,l=a+t.height+2*r.boxTextMargin,{theme:u,themeVariables:h,look:d}=r,{bkgColorArray:p,borderColorArray:f,actorBorder:g}=h,m=e.append("g").lower(),y=m;i||($i++,Object.keys(t.links||{}).length&&!r.forceMenus&&y.attr("onclick",R5(`actor${$i}_popup`)).attr("cursor","pointer"),y.append("line").attr("id","actor"+$i).attr("x1",o).attr("y1",l).attr("x2",o).attr("y2",2e3).attr("class","actor-line 200").attr("stroke-width","0.5px").attr("stroke","#999").attr("name",t.name).attr("data-et","life-line").attr("data-id",t.name),y=m.append("g"),t.actorCnt=$i,t.links!=null&&y.attr("id","root-"+$i),d==="neo"&&y.attr("data-look","neo"));let k=El(),C="actor";t.properties?.class?C=t.properties.class:k.fill="#eaeaea",i?C+=` ${op}`:C+=` ${sp}`,k.x=t.x,k.y=a,k.width=t.width,k.height=t.height,k.class=C,k.name=t.name,k.x=t.x,k.y=a;let S=k.width/3,E=k.width/3,_=S/2,b=_/(2.5+S/50),v=y.append("g");v.attr("class",C);let x=` + M ${k.x},${k.y+b} + a ${_},${b} 0 0 0 ${S},0 + a ${_},${b} 0 0 0 -${S},0 + l 0,${E-2*b} + a ${_},${b} 0 0 0 ${S},0 + l 0,-${E-2*b} +`;v.append("path").attr("d",x),d==="neo"&&v.attr("filter","url(#drop-shadow)");let w=n.get(t.name)??0;Ih.has(u)?(v.style("stroke",f[w%f.length]),v.style("fill",p[w%f.length])):v.style("stroke",g),v.attr("transform",`translate(${S}, ${b})`),t.rectData=k,Mh(r,pa(t.description))(t.description,y,k.x,k.y+35,k.width,k.height,{class:`actor ${A5}`},r);let R=v.select("path:last-child");if(R.node()){let $=R.node().getBBox();t.height=$.height+(r.sequence.labelBoxHeight??0)}return i||(y.attr("data-et","participant"),y.attr("data-type","database"),y.attr("data-id",t.name)),t.height},"drawActorTypeDatabase"),Cpt=s(function(e,t,r,i,n){let a=i?t.stopy:t.starty,o=t.x+t.width/2,l=a+80,u=22,h=e.append("g").lower(),{look:d,theme:p,themeVariables:f}=r,{bkgColorArray:g,borderColorArray:m,actorBorder:y}=f;i||($i++,h.append("line").attr("id","actor"+$i).attr("x1",o).attr("y1",l).attr("x2",o).attr("y2",2e3).attr("class","actor-line 200").attr("stroke-width","0.5px").attr("stroke","#999").attr("name",t.name).attr("data-et","life-line").attr("data-id",t.name),t.actorCnt=$i);let k=e.append("g"),C=_0;i?C+=` ${op}`:C+=` ${sp}`,k.attr("class",C),k.attr("name",t.name);let S=El();S.x=t.x,S.y=a,S.fill="#eaeaea",S.width=t.width,S.height=t.height,S.class="actor",k.append("line").attr("id","actor-man-torso"+$i).attr("x1",t.x+t.width/2-u*2.5).attr("y1",a+12).attr("x2",t.x+t.width/2-15).attr("y2",a+12),k.append("line").attr("id","actor-man-arms"+$i).attr("x1",t.x+t.width/2-u*2.5).attr("y1",a+2).attr("x2",t.x+t.width/2-u*2.5).attr("y2",a+22),k.append("circle").attr("cx",t.x+t.width/2).attr("cy",a+12).attr("r",u),d==="neo"&&k.attr("filter","url(#drop-shadow)");let E=n.get(t.name)??0;Ih.has(p)?(k.style("stroke",m[E%m.length]),k.style("fill",g[E%m.length])):k.style("stroke",y);let _=k.node().getBBox();return t.height=_.height+(r.sequence.labelBoxHeight??0),Mh(r,pa(t.description))(t.description,k,S.x,S.y+15,S.width,S.height,{class:`actor ${_0}`},r),k.attr("transform",`translate(0,${u/2+10})`),i||(k.attr("data-et","participant"),k.attr("data-type","boundary"),k.attr("data-id",t.name)),t.height},"drawActorTypeBoundary"),wpt=s(function(e,t,r,i,n){let a=i?t.stopy:t.starty,o=t.x+t.width/2,l=a+80,{look:u,theme:h,themeVariables:d}=r,{bkgColorArray:p,borderColorArray:f,actorBorder:g}=d,m=e.append("g").lower();i||($i++,m.append("line").attr("id","actor"+$i).attr("x1",o).attr("y1",l).attr("x2",o).attr("y2",2e3).attr("class","actor-line 200").attr("stroke-width","0.5px").attr("stroke","#999").attr("name",t.name).attr("data-et","life-line").attr("data-id",t.name),t.actorCnt=$i);let y=e.append("g"),k=_0;i?k+=` ${op}`:k+=` ${sp}`,y.attr("class",k),y.attr("name",t.name),i||y.attr("data-et","participant").attr("data-type","actor").attr("data-id",t.name);let C=u==="neo"?.5:1,S=u==="neo"?a+(1-C)*30:a;y.append("line").attr("id","actor-man-torso"+$i).attr("x1",o).attr("y1",S+25*C).attr("x2",o).attr("y2",S+45*C),y.append("line").attr("id","actor-man-arms"+$i).attr("x1",o-S0/2*C).attr("y1",S+33*C).attr("x2",o+S0/2*C).attr("y2",S+33*C),y.append("line").attr("x1",o-S0/2*C).attr("y1",S+60*C).attr("x2",o).attr("y2",S+45*C),y.append("line").attr("x1",o).attr("y1",S+45*C).attr("x2",o+(S0/2-2)*C).attr("y2",S+60*C);let E=y.append("circle");E.attr("cx",t.x+t.width/2),E.attr("cy",S+10*C),E.attr("r",15*C),E.attr("width",t.width*C),E.attr("height",t.height*C);let _=y.node().getBBox();t.height=_.height;let b=El();b.x=t.x,b.y=S,b.fill="#eaeaea",b.width=t.width,b.height=t.height/C,b.class="actor",b.rx=3,b.ry=3;let v=n.get(t.name)??0;return Ih.has(h)?(y.style("stroke",f[v%f.length]),y.style("fill",p[v%f.length])):y.style("stroke",g),Mh(r,pa(t.description))(t.description,y,b.x,S+35*C-(u==="neo"?10:0),b.width,b.height,{class:`actor ${_0}`},r),t.height},"drawActorTypeActor"),Ept=s(async function(e,t,r,i,n,a,o){let l=o??new Map([...a.db.getActors().values()].map((u,h)=>[u.name,h]));switch(t.type){case"actor":return await wpt(e,t,r,i,l);case"participant":return await ypt(e,t,r,i,l);case"boundary":return await Cpt(e,t,r,i,l);case"control":return await xpt(e,t,r,i,n,l);case"entity":return await Tpt(e,t,r,i,l);case"database":return await kpt(e,t,r,i,l);case"collections":return await vpt(e,t,r,i,l);case"queue":return await bpt(e,t,r,i,l)}},"drawActor"),Spt=s(function(e,t,r){let i=e.append("g");rde(i,t),t.name&&Mh(r)(t.name,i,t.x,t.y+r.boxTextMargin+(t.textMaxHeight||0)/2,t.width,0,{class:"text"},r),i.lower()},"drawBox"),_pt=s(function(e){return e.append("g")},"anchorElement"),Apt=s(function(e,t,r,i,n,a,o){let{theme:l,themeVariables:u}=i,{bkgColorArray:h,borderColorArray:d,mainBkg:p}=u,f=El(),g=t.anchored,m=t.actor;f.x=t.startx,f.y=t.starty,f.class="activation"+n%3,f.width=t.stopx-t.startx,f.height=r-t.starty;let y=Gb(g,f),k=(o??new Map([...a.db.getActors().values()].map((C,S)=>[C.name,S]))).get(m)??0;Ih.has(l)&&(y.style("stroke",d[k%d.length]),y.style("fill",h[k%d.length]??p))},"drawActivation"),Rpt=s(async function(e,t,r,i,n){let{boxMargin:a,boxTextMargin:o,labelBoxHeight:l,labelBoxWidth:u,messageFontFamily:h,messageFontSize:d,messageFontWeight:p}=i,f=e.append("g").attr("data-et","control-structure").attr("data-id","i"+n.id),g=s(function(k,C,S,E){return f.append("line").attr("x1",k).attr("y1",C).attr("x2",S).attr("y2",E).attr("class","loopLine")},"drawLoopLine");g(t.startx,t.starty,t.stopx,t.starty),g(t.stopx,t.starty,t.stopx,t.stopy),g(t.startx,t.stopy,t.stopx,t.stopy),g(t.startx,t.starty,t.startx,t.stopy),t.sections!==void 0&&t.sections.forEach(function(k){g(t.startx,k.y,t.stopx,k.y).style("stroke-dasharray","3, 3")});let m=Y6();m.text=r,m.x=t.startx,m.y=t.starty,m.fontFamily=h,m.fontSize=d,m.fontWeight=p,m.anchor="middle",m.valign="middle",m.tspan=!1,m.width=Math.max(u??0,50),m.height=l+(i.look==="neo"?15:0)||20,m.textMargin=o,m.class="labelText",ede(f,m),m=ide(),m.text=t.title,m.x=t.startx+u/2+(t.stopx-t.startx)/2,m.y=t.starty+a+o,m.anchor="middle",m.valign="middle",m.textMargin=o,m.class="loopText",m.fontFamily=h,m.fontSize=d,m.fontWeight=p,m.wrap=!0;let y=pa(m.text)?await $5(f,m,t):R1(f,m);if(t.sectionTitles!==void 0){for(let[k,C]of Object.entries(t.sectionTitles))if(C.message){m.text=C.message,m.x=t.startx+(t.stopx-t.startx)/2,m.y=t.sections[k].y+a+o,m.class="sectionTitle",m.anchor="middle",m.valign="middle",m.tspan=!1,m.fontFamily=h,m.fontSize=d,m.fontWeight=p,m.wrap=t.wrap,pa(m.text)?(t.starty=t.sections[k].y,await $5(f,m,t)):R1(f,m);let S=Math.round(y.map(E=>(E._groups||E)[0][0].getBBox().height).reduce((E,_)=>E+_));t.sections[k].height+=S-(a+o)}}return t.height=Math.round(t.stopy-t.starty),f},"drawLoop"),rde=s(function(e,t){mF(e,t)},"drawBackgroundRect"),$pt=s(function(e,t){e.append("defs").append("symbol").attr("id",t+"-database").attr("fill-rule","evenodd").attr("clip-rule","evenodd").append("path").attr("transform","scale(.5)").attr("d","M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z")},"insertDatabaseIcon"),Lpt=s(function(e,t){e.append("defs").append("symbol").attr("id",t+"-computer").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z")},"insertComputerIcon"),Ipt=s(function(e,t){e.append("defs").append("symbol").attr("id",t+"-clock").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z")},"insertClockIcon"),Mpt=s(function(e,t){e.append("defs").append("marker").attr("id",t+"-arrowhead").attr("refX",7.9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto-start-reverse").append("path").attr("d","M -1 0 L 10 5 L 0 10 z")},"insertArrowHead"),Dpt=s(function(e,t){e.append("defs").append("marker").attr("id",t+"-filled-head").attr("refX",15.5).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},"insertArrowFilledHead"),Npt=s(function(e,t){e.append("defs").append("marker").attr("id",t+"-sequencenumber").attr("refX",15).attr("refY",15).attr("markerWidth",60).attr("markerHeight",40).attr("orient","auto").append("circle").attr("cx",15).attr("cy",15).attr("r",6)},"insertSequenceNumber"),Opt=s(function(e,t){e.append("defs").append("marker").attr("id",t+"-crosshead").attr("markerWidth",15).attr("markerHeight",8).attr("orient","auto").attr("refX",4).attr("refY",4.5).append("path").attr("fill","none").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1pt").attr("d","M 1,2 L 6,7 M 6,2 L 1,7")},"insertArrowCrossHead"),Bpt=s(function(e,t){let{theme:r}=t;e.append("defs").append("filter").attr("id","drop-shadow").attr("height","130%").attr("width","130%").append("feDropShadow").attr("dx","4").attr("dy","4").attr("stdDeviation",0).attr("flood-opacity","0.06").attr("flood-color",`${r==="redux"||r==="redux-color"?"#000000":"#FFFFFF"}`)},"insertDropShadow"),ide=s(function(){return{x:0,y:0,fill:void 0,anchor:void 0,style:"#666",width:void 0,height:void 0,textMargin:0,rx:0,ry:0,tspan:!0,valign:void 0}},"getTextObj"),Ppt=s(function(){return{x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0}},"getNoteRect"),Mh=(function(){function e(a,o,l,u,h,d,p){let f=o.append("text").attr("x",l+h/2).attr("y",u+d/2+5).style("text-anchor","middle").text(a);n(f,p)}s(e,"byText");function t(a,o,l,u,h,d,p,f){let{actorFontSize:g,actorFontFamily:m,actorFontWeight:y}=f,[k,C]=zu(g),S=a.split(Vt.lineBreakRegex);for(let E=0;E{let o=A0(ot),l=a.actorKeys.reduce((p,f)=>p+=e.get(f).width+(e.get(f).margin||0),0),u=ot.boxMargin*8;l+=u,l-=2*ot.boxTextMargin,a.wrap&&(a.name=ai.wrapLabel(a.name,l-2*ot.wrapPadding,o));let h=ai.calculateTextDimensions(a.name,o);n=Vt.getMax(h.height,n);let d=Vt.getMax(l,h.width+2*ot.wrapPadding);if(a.margin=ot.boxTextMargin,la.textMaxHeight=n),Vt.getMax(i,ot.height)}var ot,Pt,Gpt,nde,A0,$1,CI,Xpt,Kpt,wI,ade,sde,L5,ode,Qpt,Zpt,Jpt,eft,tft,EI,rft,lde,ift,nft,aft,sft,ARt=G(()=>{"use strict";pi(),_Rt(),Nt(),mi(),mi(),pg(),Er(),$x(),Ar(),vn(),ppt(),ot={},Pt={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],activations:[],models:{getHeight:s(function(){return Math.max.apply(null,this.actors.length===0?[0]:this.actors.map(e=>e.height||0))+(this.loops.length===0?0:this.loops.map(e=>e.height||0).reduce((e,t)=>e+t))+(this.messages.length===0?0:this.messages.map(e=>e.height||0).reduce((e,t)=>e+t))+(this.notes.length===0?0:this.notes.map(e=>e.height||0).reduce((e,t)=>e+t))},"getHeight"),clear:s(function(){this.actors=[],this.boxes=[],this.loops=[],this.messages=[],this.notes=[]},"clear"),addBox:s(function(e){this.boxes.push(e)},"addBox"),addActor:s(function(e){this.actors.push(e)},"addActor"),addLoop:s(function(e){this.loops.push(e)},"addLoop"),addMessage:s(function(e){this.messages.push(e)},"addMessage"),addNote:s(function(e){this.notes.push(e)},"addNote"),lastActor:s(function(){return this.actors[this.actors.length-1]},"lastActor"),lastLoop:s(function(){return this.loops[this.loops.length-1]},"lastLoop"),lastMessage:s(function(){return this.messages[this.messages.length-1]},"lastMessage"),lastNote:s(function(){return this.notes[this.notes.length-1]},"lastNote"),actors:[],boxes:[],loops:[],messages:[],notes:[]},init:s(function(){this.sequenceItems=[],this.activations=[],this.models.clear(),this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0,sde(it())},"init"),updateVal:s(function(e,t,r,i){e[t]===void 0?e[t]=r:e[t]=i(r,e[t])},"updateVal"),updateBounds:s(function(e,t,r,i){let n=this,a=0;function o(l){return s(function(u){a++;let h=n.sequenceItems.length-a+1;n.updateVal(u,"starty",t-h*ot.boxMargin,Math.min),n.updateVal(u,"stopy",i+h*ot.boxMargin,Math.max),n.updateVal(Pt.data,"startx",e-h*ot.boxMargin,Math.min),n.updateVal(Pt.data,"stopx",r+h*ot.boxMargin,Math.max),l!=="activation"&&(n.updateVal(u,"startx",e-h*ot.boxMargin,Math.min),n.updateVal(u,"stopx",r+h*ot.boxMargin,Math.max),n.updateVal(Pt.data,"starty",t-h*ot.boxMargin,Math.min),n.updateVal(Pt.data,"stopy",i+h*ot.boxMargin,Math.max))},"updateItemBounds")}s(o,"updateFn"),this.sequenceItems.forEach(o()),this.activations.forEach(o("activation"))},"updateBounds"),insert:s(function(e,t,r,i){let n=Vt.getMin(e,r),a=Vt.getMax(e,r),o=Vt.getMin(t,i),l=Vt.getMax(t,i);this.updateVal(Pt.data,"startx",n,Math.min),this.updateVal(Pt.data,"starty",o,Math.min),this.updateVal(Pt.data,"stopx",a,Math.max),this.updateVal(Pt.data,"stopy",l,Math.max),this.updateBounds(n,o,a,l)},"insert"),newActivation:s(function(e,t,r){let i=r.get(e.from),n=L5(e.from).length||0,a=i.x+i.width/2+(n-1)*ot.activationWidth/2;this.activations.push({startx:a,starty:this.verticalPos+2,stopx:a+ot.activationWidth,stopy:void 0,actor:e.from,anchored:_n.anchorElement(t)})},"newActivation"),endActivation:s(function(e){let t=this.activations.map(function(r){return r.actor}).lastIndexOf(e.from);return this.activations.splice(t,1)[0]},"endActivation"),createLoop:s(function(e={message:void 0,wrap:!1,width:void 0},t){return{startx:void 0,starty:this.verticalPos,stopx:void 0,stopy:void 0,title:e.message,wrap:e.wrap,width:e.width,height:0,fill:t}},"createLoop"),newLoop:s(function(e={message:void 0,wrap:!1,width:void 0},t){this.sequenceItems.push(this.createLoop(e,t))},"newLoop"),endLoop:s(function(){return this.sequenceItems.pop()},"endLoop"),isLoopOverlap:s(function(){return this.sequenceItems.length?this.sequenceItems[this.sequenceItems.length-1].overlap:!1},"isLoopOverlap"),addSectionToLoop:s(function(e){let t=this.sequenceItems.pop();t.sections=t.sections||[],t.sectionTitles=t.sectionTitles||[],t.sections.push({y:Pt.getVerticalPos(),height:0}),t.sectionTitles.push(e),this.sequenceItems.push(t)},"addSectionToLoop"),saveVerticalPos:s(function(){this.isLoopOverlap()&&(this.savedVerticalPos=this.verticalPos)},"saveVerticalPos"),resetVerticalPos:s(function(){this.isLoopOverlap()&&(this.verticalPos=this.savedVerticalPos)},"resetVerticalPos"),bumpVerticalPos:s(function(e){this.verticalPos=this.verticalPos+e,this.data.stopy=Vt.getMax(this.data.stopy,this.verticalPos)},"bumpVerticalPos"),getVerticalPos:s(function(){return this.verticalPos},"getVerticalPos"),getBounds:s(function(){return{bounds:this.data,models:this.models}},"getBounds")},Gpt=s(async function(e,t,r){Pt.bumpVerticalPos(ot.boxMargin),t.height=ot.boxMargin,t.starty=Pt.getVerticalPos();let i=El();i.x=t.startx,i.y=t.starty,i.width=t.width||ot.width,i.class="note";let n=e.append("g");n.attr("data-et","note"),n.attr("data-id","i"+r);let a=_n.drawRect(n,i),o=Y6();o.x=t.startx,o.y=t.starty,o.width=i.width,o.dy="1em",o.text=t.message,o.class="noteText",o.fontFamily=ot.noteFontFamily,o.fontSize=ot.noteFontSize,o.fontWeight=ot.noteFontWeight,o.anchor=ot.noteAlign,o.textMargin=ot.noteMargin,o.valign="center";let l=pa(o.text)?await $5(n,o):R1(n,o),u=Math.round(l.map(h=>(h._groups||h)[0][0].getBBox().height).reduce((h,d)=>h+d));a.attr("height",u+2*ot.noteMargin),t.height+=u+2*ot.noteMargin,Pt.bumpVerticalPos(u+2*ot.noteMargin),t.stopy=t.starty+u+2*ot.noteMargin,t.stopx=t.startx+i.width,Pt.insert(t.startx,t.starty,t.stopx,t.stopy),Pt.models.addNote(t)},"drawNote"),nde=s(function(e,t,r,i,n,a,o){let l=i.db.getActors(),u=l.get(t.from),h=l.get(t.to),d=r.sequenceVisible,p=u.x+u.width/2,f=h.x+h.width/2,g=p<=f,m=lde(t,i),y=e.append("g"),k=16.5,C=s((v,x)=>{let w=v?k:-k;return x?-w:w},"getCircleOffset"),S=s(v=>{y.append("circle").attr("cx",v).attr("cy",o).attr("r",5).attr("width",10).attr("height",10)},"drawCircle"),{CENTRAL_CONNECTION:E,CENTRAL_CONNECTION_REVERSE:_,CENTRAL_CONNECTION_DUAL:b}=i.db.LINETYPE;if(d)switch(t.centralConnection){case E:m&&(f+=C(g,!0));break;case _:m||(p+=C(g,!1));break;case b:m?f+=C(g,!0):p+=C(g,!1);break}switch(t.centralConnection){case E:S(f);break;case _:S(p);break;case b:S(p),S(f);break}},"drawCentralConnection"),A0=s(e=>({fontFamily:e.messageFontFamily,fontSize:e.messageFontSize,fontWeight:e.messageFontWeight}),"messageFont"),$1=s(e=>({fontFamily:e.noteFontFamily,fontSize:e.noteFontSize,fontWeight:e.noteFontWeight}),"noteFont"),CI=s(e=>({fontFamily:e.actorFontFamily,fontSize:e.actorFontSize,fontWeight:e.actorFontWeight}),"actorFont"),s(Vpt,"boundMessage"),Xpt=s(async function(e,t,r,i,n,a){let{startx:o,stopx:l,starty:u,message:h,type:d,sequenceIndex:p,sequenceVisible:f}=t,g=ai.calculateTextDimensions(h,A0(ot)),m=Y6();m.x=Math.min(o,l),m.y=u+10,m.width=Math.abs(l-o),m.class="messageText",m.dy="1em",m.text=h,m.fontFamily=ot.messageFontFamily,m.fontSize=ot.messageFontSize,m.fontWeight=ot.messageFontWeight,m.anchor=ot.messageAlign,m.valign="center",m.textMargin=ot.wrapPadding,m.tspan=!1,pa(m.text)?await $5(e,m,{startx:o,stopx:l,starty:r}):R1(e,m);let y=g.width,k;if(o===l){let S=f||ot.showSequenceNumbers,E=lde(n,i),_=ift(n,i),b=o+(S&&(E||_)?10:0);ot.rightAngles?k=e.append("path").attr("d",`M ${b},${r} H ${o+Vt.getMax(ot.width/2,y/2)} V ${r+25} H ${o}`):k=e.append("path").attr("d","M "+b+","+r+" C "+(b+60)+","+(r-10)+" "+(o+60)+","+(r+30)+" "+o+","+(r+20)),EI(n,i)&&nde(e,n,t,i,o,l,r)}else k=e.append("line"),k.attr("x1",o),k.attr("y1",r),k.attr("x2",l),k.attr("y2",r),EI(n,i)&&nde(e,n,t,i,o,l,r);d===i.db.LINETYPE.DOTTED||d===i.db.LINETYPE.DOTTED_CROSS||d===i.db.LINETYPE.DOTTED_POINT||d===i.db.LINETYPE.DOTTED_OPEN||d===i.db.LINETYPE.BIDIRECTIONAL_DOTTED||d===i.db.LINETYPE.SOLID_TOP_DOTTED||d===i.db.LINETYPE.SOLID_BOTTOM_DOTTED||d===i.db.LINETYPE.STICK_TOP_DOTTED||d===i.db.LINETYPE.STICK_BOTTOM_DOTTED||d===i.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED||d===i.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED||d===i.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED||d===i.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED?(k.style("stroke-dasharray","3, 3"),k.attr("class","messageLine1")):k.attr("class","messageLine0"),k.attr("data-et","message"),k.attr("data-id","i"+t.id),k.attr("data-from",t.from),k.attr("data-to",t.to);let C="";if(ot.arrowMarkerAbsolute&&(C=CS(!0)),k.attr("stroke-width",2),k.attr("stroke","none"),k.style("fill","none"),(d===i.db.LINETYPE.SOLID_TOP||d===i.db.LINETYPE.SOLID_TOP_DOTTED)&&k.attr("marker-end","url("+C+"#"+a+"-solidTopArrowHead)"),(d===i.db.LINETYPE.SOLID_BOTTOM||d===i.db.LINETYPE.SOLID_BOTTOM_DOTTED)&&k.attr("marker-end","url("+C+"#"+a+"-solidBottomArrowHead)"),(d===i.db.LINETYPE.STICK_TOP||d===i.db.LINETYPE.STICK_TOP_DOTTED)&&k.attr("marker-end","url("+C+"#"+a+"-stickTopArrowHead)"),(d===i.db.LINETYPE.STICK_BOTTOM||d===i.db.LINETYPE.STICK_BOTTOM_DOTTED)&&k.attr("marker-end","url("+C+"#"+a+"-stickBottomArrowHead)"),(d===i.db.LINETYPE.SOLID_ARROW_TOP_REVERSE||d===i.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED)&&k.attr("marker-start","url("+C+"#"+a+"-solidBottomArrowHead)"),(d===i.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE||d===i.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED)&&k.attr("marker-start","url("+C+"#"+a+"-solidTopArrowHead)"),(d===i.db.LINETYPE.STICK_ARROW_TOP_REVERSE||d===i.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED)&&k.attr("marker-start","url("+C+"#"+a+"-stickBottomArrowHead)"),(d===i.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE||d===i.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED)&&k.attr("marker-start","url("+C+"#"+a+"-stickTopArrowHead)"),(d===i.db.LINETYPE.SOLID||d===i.db.LINETYPE.DOTTED)&&k.attr("marker-end","url("+C+"#"+a+"-arrowhead)"),(d===i.db.LINETYPE.BIDIRECTIONAL_SOLID||d===i.db.LINETYPE.BIDIRECTIONAL_DOTTED)&&(k.attr("marker-start","url("+C+"#"+a+"-arrowhead)"),k.attr("marker-end","url("+C+"#"+a+"-arrowhead)")),(d===i.db.LINETYPE.SOLID_POINT||d===i.db.LINETYPE.DOTTED_POINT)&&k.attr("marker-end","url("+C+"#"+a+"-filled-head)"),(d===i.db.LINETYPE.SOLID_CROSS||d===i.db.LINETYPE.DOTTED_CROSS)&&k.attr("marker-end","url("+C+"#"+a+"-crosshead)"),f||ot.showSequenceNumbers){let S=d===i.db.LINETYPE.BIDIRECTIONAL_SOLID||d===i.db.LINETYPE.BIDIRECTIONAL_DOTTED,E=d===i.db.LINETYPE.SOLID_ARROW_TOP_REVERSE||d===i.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED||d===i.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE||d===i.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED||d===i.db.LINETYPE.STICK_ARROW_TOP_REVERSE||d===i.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED||d===i.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE||d===i.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED,_=6,b=EI(n,i),v=o,x=l;S?(oo?x=l-2*_:(x=l-_,v+=n?.centralConnection===i.db.LINETYPE.CENTRAL_CONNECTION_DUAL||n?.centralConnection===i.db.LINETYPE.CENTRAL_CONNECTION_REVERSE?-7.5:0),x+=b?15:0,k.attr("x2",x),k.attr("x1",v)):k.attr("x1",o+_);let w=0,R=o===l,$=o<=l;R?w=t.fromBounds+1:E?w=$?t.toBounds-1:t.fromBounds+1:w=$?t.fromBounds+1:t.toBounds-1;let M="12px",N=p.toString().length;N>5?M="7px":N>3&&(M="9px"),e.append("line").attr("x1",w).attr("y1",r).attr("x2",w).attr("y2",r).attr("stroke-width",0).attr("marker-start","url("+C+"#"+a+"-sequencenumber)"),e.append("text").attr("x",w).attr("y",r+4).attr("font-family","sans-serif").attr("font-size",M).attr("text-anchor","middle").attr("class","sequenceNumber").text(p)}},"drawMessage"),Kpt=s(function(e,t,r,i,n,a,o){let l=0,u=0,h,d=0;for(let p of i){let f=t.get(p),g=f.box;h&&h!=g&&(o||Pt.models.addBox(h),u+=ot.boxMargin+h.margin),g&&g!=h&&(o||(g.x=l+u,g.y=n),u+=g.margin),f.width=Vt.getMax(f.width||ot.width,ot.width),f.height=Vt.getMax(f.height||ot.height,ot.height),f.margin=f.margin||ot.actorMargin,d=Vt.getMax(d,f.height),r.get(f.name)&&(u+=f.width/2),f.x=l+u,f.starty=Pt.getVerticalPos(),Pt.insert(f.x,n,f.x+f.width,f.height),l+=f.width+u,f.box&&(f.box.width=l+g.margin-f.box.x),u=f.margin,h=f.box,Pt.models.addActor(f)}h&&!o&&Pt.models.addBox(h),Pt.bumpVerticalPos(d)},"addActorRenderingData"),wI=s(async function(e,t,r,i,n,a,o){if(i){let l=0;Pt.bumpVerticalPos(ot.boxMargin*2);for(let u of r){let h=t.get(u);h.stopy||(h.stopy=Pt.getVerticalPos());let d=await _n.drawActor(e,h,ot,!0,n,a,o);l=Vt.getMax(l,d)}Pt.bumpVerticalPos(l+ot.boxMargin)}else for(let l of r){let u=t.get(l);await _n.drawActor(e,u,ot,!1,n,a,o)}},"drawActors"),ade=s(function(e,t,r,i){let n=0,a=0;for(let o of r){let l=t.get(o),u=Zpt(l),h=_n.drawPopup(e,l,u,ot,ot.forceMenus,i);h.height>n&&(n=h.height),h.width+l.x>a&&(a=h.width+l.x)}return{maxHeight:n,maxWidth:a}},"drawActorsPopup"),sde=s(function(e){Sa(ot,e),e.fontFamily&&(ot.actorFontFamily=ot.noteFontFamily=ot.messageFontFamily=e.fontFamily),e.fontSize&&(ot.actorFontSize=ot.noteFontSize=ot.messageFontSize=e.fontSize),e.fontWeight&&(ot.actorFontWeight=ot.noteFontWeight=ot.messageFontWeight=e.fontWeight)},"setConf"),L5=s(function(e){return Pt.activations.filter(function(t){return t.actor===e})},"actorActivations"),ode=s(function(e,t){let r=t.get(e),i=L5(e),n=i.reduce(function(o,l){return Vt.getMin(o,l.startx)},r.x+r.width/2-1),a=i.reduce(function(o,l){return Vt.getMax(o,l.stopx)},r.x+r.width/2+1);return[n,a]},"activationBounds"),s(pu,"adjustLoopHeightForWrap"),s(Upt,"adjustCreatedDestroyedData"),Qpt=s(async function(e,t,r,i){let{securityLevel:n,sequence:a,look:o,themeVariables:l}=it();ot=a;let u;n==="sandbox"&&(u=zt("#i"+t));let h=zt(n==="sandbox"?u.nodes()[0].contentDocument.body:"body"),d=n==="sandbox"?u.nodes()[0].contentDocument:document;Pt.init(),Ee.debug(i.db);let p=n==="sandbox"?h.select(`[id="${t}"]`):zt(`[id="${t}"]`),f=i.db.getActors(),g=i.db.getCreatedActors(),m=i.db.getDestroyedActors(),y=i.db.getBoxes(),k=i.db.getActorKeys(),C=i.db.getMessages(),S=i.db.getDiagramTitle(),E=i.db.hasAtLeastOneBox(),_=i.db.hasAtLeastOneBoxWithTitle(),b=await Ypt(f,C,i);if(ot.height=await Hpt(f,b,y),_n.insertComputerIcon(p,t),_n.insertDatabaseIcon(p,t),_n.insertClockIcon(p,t),E&&(Pt.bumpVerticalPos(ot.boxMargin),_&&Pt.bumpVerticalPos(y[0].textMaxHeight)),ot.hideUnusedParticipants===!0){let J=new Set;C.forEach(te=>{J.add(te.from),J.add(te.to)}),k=k.filter(te=>J.has(te))}let v=new Map(k.map((J,te)=>[f.get(J)?.name??J,te]));Kpt(p,f,g,k,0,C,!1);let x=await aft(C,f,b,i);_n.insertArrowHead(p,t),_n.insertArrowCrossHead(p,t),_n.insertArrowFilledHead(p,t),_n.insertSequenceNumber(p,t),_n.insertSolidTopArrowHead(p,t),_n.insertSolidBottomArrowHead(p,t),_n.insertStickTopArrowHead(p,t),_n.insertStickBottomArrowHead(p,t),o==="neo"&&_n.insertDropShadow(p,ot);function w(J,te){let ie=Pt.endActivation(J);ie.starty+18>te&&(ie.starty=te-6,te+=12),_n.drawActivation(p,ie,te,ot,L5(J.from).length,i,v),Pt.insert(ie.startx,te-10,ie.stopx,te)}s(w,"activeEnd");let R=1,$=1,M=[],N=[],j=0;for(let J of C){let te,ie,ue;switch(J.type){case i.db.LINETYPE.NOTE:Pt.resetVerticalPos(),ie=J.noteModel,await Gpt(p,ie,J.id);break;case i.db.LINETYPE.ACTIVE_START:Pt.newActivation(J,p,f);break;case i.db.LINETYPE.CENTRAL_CONNECTION:Pt.newActivation(J,p,f);break;case i.db.LINETYPE.CENTRAL_CONNECTION_REVERSE:Pt.newActivation(J,p,f);break;case i.db.LINETYPE.ACTIVE_END:w(J,Pt.getVerticalPos());break;case i.db.LINETYPE.LOOP_START:pu(x,J,ot.boxMargin,ot.boxMargin+ot.boxTextMargin,se=>Pt.newLoop(se));break;case i.db.LINETYPE.LOOP_END:te=Pt.endLoop(),await _n.drawLoop(p,te,"loop",ot,J),Pt.bumpVerticalPos(te.stopy-Pt.getVerticalPos()),Pt.models.addLoop(te);break;case i.db.LINETYPE.RECT_START:pu(x,J,ot.boxMargin,ot.boxMargin,se=>{let le=se.message;le||(le=l?.rectBkgColor||l?.actorBkg||"rgba(128, 128, 128, 0.5)"),Pt.newLoop(void 0,le)});break;case i.db.LINETYPE.RECT_END:te=Pt.endLoop(),N.push(te),Pt.models.addLoop(te),Pt.bumpVerticalPos(te.stopy-Pt.getVerticalPos());break;case i.db.LINETYPE.OPT_START:pu(x,J,ot.boxMargin,ot.boxMargin+ot.boxTextMargin,se=>Pt.newLoop(se));break;case i.db.LINETYPE.OPT_END:te=Pt.endLoop(),await _n.drawLoop(p,te,"opt",ot,J),Pt.bumpVerticalPos(te.stopy-Pt.getVerticalPos()),Pt.models.addLoop(te);break;case i.db.LINETYPE.ALT_START:pu(x,J,ot.boxMargin,ot.boxMargin+ot.boxTextMargin,se=>Pt.newLoop(se));break;case i.db.LINETYPE.ALT_ELSE:pu(x,J,ot.boxMargin+ot.boxTextMargin,ot.boxMargin,se=>Pt.addSectionToLoop(se));break;case i.db.LINETYPE.ALT_END:te=Pt.endLoop(),await _n.drawLoop(p,te,"alt",ot,J),Pt.bumpVerticalPos(te.stopy-Pt.getVerticalPos()),Pt.models.addLoop(te);break;case i.db.LINETYPE.PAR_START:case i.db.LINETYPE.PAR_OVER_START:pu(x,J,ot.boxMargin,ot.boxMargin+ot.boxTextMargin,se=>Pt.newLoop(se)),Pt.saveVerticalPos();break;case i.db.LINETYPE.PAR_AND:pu(x,J,ot.boxMargin+ot.boxTextMargin,ot.boxMargin,se=>Pt.addSectionToLoop(se));break;case i.db.LINETYPE.PAR_END:te=Pt.endLoop(),await _n.drawLoop(p,te,"par",ot,J),Pt.bumpVerticalPos(te.stopy-Pt.getVerticalPos()),Pt.models.addLoop(te);break;case i.db.LINETYPE.AUTONUMBER:R=J.message.start||R,$=J.message.step||$,J.message.visible?i.db.enableSequenceNumbers():i.db.disableSequenceNumbers();break;case i.db.LINETYPE.CRITICAL_START:pu(x,J,ot.boxMargin,ot.boxMargin+ot.boxTextMargin,se=>Pt.newLoop(se));break;case i.db.LINETYPE.CRITICAL_OPTION:pu(x,J,ot.boxMargin+ot.boxTextMargin,ot.boxMargin,se=>Pt.addSectionToLoop(se));break;case i.db.LINETYPE.CRITICAL_END:te=Pt.endLoop(),await _n.drawLoop(p,te,"critical",ot,J),Pt.bumpVerticalPos(te.stopy-Pt.getVerticalPos()),Pt.models.addLoop(te);break;case i.db.LINETYPE.BREAK_START:pu(x,J,ot.boxMargin,ot.boxMargin+ot.boxTextMargin,se=>Pt.newLoop(se));break;case i.db.LINETYPE.BREAK_END:te=Pt.endLoop(),await _n.drawLoop(p,te,"break",ot,J),Pt.bumpVerticalPos(te.stopy-Pt.getVerticalPos()),Pt.models.addLoop(te);break;default:try{ue=J.msgModel,ue.starty=Pt.getVerticalPos(),ue.sequenceIndex=R,ue.sequenceVisible=i.db.showSequenceNumbers(),ue.id=J.id,ue.from=J.from,ue.to=J.to;let se=await Vpt(p,ue);Upt(J,ue,se,j,f,g,m),M.push({messageModel:ue,lineStartY:se,msg:J}),Pt.models.addMessage(ue)}catch(se){Ee.error("error while drawing message",se)}}[i.db.LINETYPE.SOLID_OPEN,i.db.LINETYPE.DOTTED_OPEN,i.db.LINETYPE.SOLID,i.db.LINETYPE.SOLID_TOP,i.db.LINETYPE.SOLID_BOTTOM,i.db.LINETYPE.STICK_TOP,i.db.LINETYPE.STICK_BOTTOM,i.db.LINETYPE.SOLID_TOP_DOTTED,i.db.LINETYPE.SOLID_BOTTOM_DOTTED,i.db.LINETYPE.STICK_TOP_DOTTED,i.db.LINETYPE.STICK_BOTTOM_DOTTED,i.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,i.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE,i.db.LINETYPE.STICK_ARROW_TOP_REVERSE,i.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE,i.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,i.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,i.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED,i.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED,i.db.LINETYPE.DOTTED,i.db.LINETYPE.SOLID_CROSS,i.db.LINETYPE.DOTTED_CROSS,i.db.LINETYPE.SOLID_POINT,i.db.LINETYPE.DOTTED_POINT,i.db.LINETYPE.BIDIRECTIONAL_SOLID,i.db.LINETYPE.BIDIRECTIONAL_DOTTED].includes(J.type)&&(R=Math.round((R+$)*100)/100),j++}Ee.debug("createdActors",g),Ee.debug("destroyedActors",m),await wI(p,f,k,!1,t,i,v);for(let J of M)await Xpt(p,J.messageModel,J.lineStartY,i,J.msg,t);ot.mirrorActors&&await wI(p,f,k,!0,t,i,v),N.forEach(J=>_n.drawBackgroundRect(p,J)),tde(p,f,k,ot);for(let J of Pt.models.boxes){J.height=Pt.getVerticalPos()-J.y,Pt.insert(J.x,J.y,J.x+J.width,J.height);let te=ot.boxMargin*2;J.startx=J.x-te,J.starty=J.y-te*.25,J.stopx=J.startx+J.width+2*te,J.stopy=J.starty+J.height+te*.75,J.stroke="rgb(0,0,0, 0.5)",_n.drawBox(p,J,ot)}E&&Pt.bumpVerticalPos(ot.boxMargin);let I=ade(p,f,k,d),{bounds:z}=Pt.getBounds();z.startx===void 0&&(z.startx=0),z.starty===void 0&&(z.starty=0),z.stopx===void 0&&(z.stopx=0),z.stopy===void 0&&(z.stopy=0);let F=z.stopy-z.starty;F2,f=s(k=>u?-k:k,"adjustValue");e.from===e.to?d=h:(e.activate&&!p&&(d+=f(ot.activationWidth/2-1)),[r.db.LINETYPE.SOLID_OPEN,r.db.LINETYPE.DOTTED_OPEN,r.db.LINETYPE.STICK_TOP,r.db.LINETYPE.STICK_BOTTOM,r.db.LINETYPE.STICK_TOP_DOTTED,r.db.LINETYPE.STICK_BOTTOM_DOTTED,r.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,r.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,r.db.LINETYPE.STICK_ARROW_TOP_REVERSE,r.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE,r.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED,r.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED,r.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,r.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE].includes(e.type)||(d+=f(3)),[r.db.LINETYPE.BIDIRECTIONAL_SOLID,r.db.LINETYPE.BIDIRECTIONAL_DOTTED,r.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,r.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,r.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,r.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE].includes(e.type)&&(h-=f(3)));let g=[n,a,o,l],m=Math.abs(h-d);e.wrap&&e.message&&(e.message=ai.wrapLabel(e.message,Vt.getMax(m+2*ot.wrapPadding,ot.width),A0(ot)));let y=ai.calculateTextDimensions(e.message,A0(ot));return{width:Vt.getMax(e.wrap?0:y.width+2*ot.wrapPadding,m+2*ot.wrapPadding,ot.width),height:0,startx:h,stopx:d,starty:0,stopy:0,message:e.message,type:e.type,wrap:e.wrap,fromBounds:Math.min.apply(null,g),toBounds:Math.max.apply(null,g)}},"buildMessageModel"),aft=s(async function(e,t,r,i){let n={},a=[],o,l,u;for(let h of e){switch(h.type){case i.db.LINETYPE.LOOP_START:case i.db.LINETYPE.ALT_START:case i.db.LINETYPE.OPT_START:case i.db.LINETYPE.PAR_START:case i.db.LINETYPE.PAR_OVER_START:case i.db.LINETYPE.CRITICAL_START:case i.db.LINETYPE.BREAK_START:a.push({id:h.id,msg:h.message,from:Number.MAX_SAFE_INTEGER,to:Number.MIN_SAFE_INTEGER,width:0});break;case i.db.LINETYPE.ALT_ELSE:case i.db.LINETYPE.PAR_AND:case i.db.LINETYPE.CRITICAL_OPTION:h.message&&(o=a.pop(),n[o.id]=o,n[h.id]=o,a.push(o));break;case i.db.LINETYPE.LOOP_END:case i.db.LINETYPE.ALT_END:case i.db.LINETYPE.OPT_END:case i.db.LINETYPE.PAR_END:case i.db.LINETYPE.CRITICAL_END:case i.db.LINETYPE.BREAK_END:o=a.pop(),n[o.id]=o;break;case i.db.LINETYPE.ACTIVE_START:{let d=t.get(h.from?h.from:h.to.actor),p=L5(h.from?h.from:h.to.actor).length,f=d.x+d.width/2+(p-1)*ot.activationWidth/2,g={startx:f,stopx:f+ot.activationWidth,actor:h.from,enabled:!0};Pt.activations.push(g)}break;case i.db.LINETYPE.ACTIVE_END:{let d=Pt.activations.map(p=>p.actor).lastIndexOf(h.from);Pt.activations.splice(d,1).splice(0,1)}break}h.placement!==void 0?(l=await Jpt(h,t,i),h.noteModel=l,a.forEach(d=>{o=d,o.from=Vt.getMin(o.from,l.startx),o.to=Vt.getMax(o.to,l.startx+l.width),o.width=Vt.getMax(o.width,Math.abs(o.from-o.to))-ot.labelBoxWidth})):(u=nft(h,t,i),h.msgModel=u,u.startx&&u.stopx&&a.length>0&&a.forEach(d=>{if(o=d,u.startx===u.stopx){let p=t.get(h.from),f=t.get(h.to);o.from=Vt.getMin(p.x-u.width/2,p.x-p.width/2,o.from),o.to=Vt.getMax(f.x+u.width/2,f.x+p.width/2,o.to),o.width=Vt.getMax(o.width,Math.abs(o.to-o.from))-ot.labelBoxWidth}else o.from=Vt.getMin(u.startx,o.from),o.to=Vt.getMax(u.stopx,o.to),o.width=Vt.getMax(o.width,u.width)-ot.labelBoxWidth}))}return Pt.activations=[],Ee.debug("Loop type widths:",n),n},"calculateLoopBounds"),sft={bounds:Pt,drawActors:wI,drawActorsPopup:ade,setConf:sde,draw:Qpt}}),oft={};Mr(oft,{diagram:()=>lft});var lft,RRt=G(()=>{"use strict";ERt(),ppt(),SRt(),Er(),ARt(),lft={parser:lpt,get db(){return new dpt},renderer:sft,styles:gpt,init:s(e=>{e.sequence||(e.sequence={}),e.wrap&&(e.sequence.wrap=e.wrap,MS({sequence:{wrap:e.wrap}}))},"init")}}),SI,cde,cft=G(()=>{"use strict";SI=(function(){var e=s(function(Qe,W,Ae,K){for(Ae=Ae||{},K=Qe.length;K--;Ae[Qe[K]]=W);return Ae},"o"),t=[1,18],r=[1,19],i=[1,20],n=[1,41],a=[1,26],o=[1,42],l=[1,24],u=[1,25],h=[1,32],d=[1,33],p=[1,34],f=[1,45],g=[1,35],m=[1,36],y=[1,37],k=[1,38],C=[1,27],S=[1,28],E=[1,29],_=[1,30],b=[1,31],v=[1,44],x=[1,46],w=[1,43],R=[1,47],$=[1,9],M=[1,8,9],N=[1,58],j=[1,59],I=[1,60],z=[1,61],F=[1,62],O=[1,63],B=[1,64],Y=[1,8,9,41],U=[1,77],H=[1,8,9,12,13,22,39,41,44,46,68,69,70,71,72,73,74,79,81],J=[1,8,9,12,13,18,20,22,39,41,44,46,47,60,68,69,70,71,72,73,74,79,81,86,100,102,103],te=[13,60,86,100,102,103],ie=[13,60,73,74,86,100,102,103],ue=[13,60,68,69,70,71,72,86,100,102,103],se=[1,103],le=[1,121],Te=[1,117],oe=[1,113],fe=[1,119],ge=[1,114],ke=[1,115],Ne=[1,116],xe=[1,118],$e=[1,120],Me=[22,50,60,61,82,86,87,88,89,90],He=[1,128],Ge=[12,39],ze=[1,8,9,39,41,44,46],Ve=[1,8,9,22],et=[1,153],he=[1,8,9,61],ye=[1,8,9,22,50,60,61,82,86,87,88,89,90],be={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mermaidDoc:4,statements:5,graphConfig:6,CLASS_DIAGRAM:7,NEWLINE:8,EOF:9,statement:10,classLabel:11,SQS:12,STR:13,SQE:14,namespaceName:15,alphaNumToken:16,classLiteralName:17,DOT:18,className:19,GENERICTYPE:20,relationStatement:21,LABEL:22,namespaceStatement:23,classStatement:24,memberStatement:25,annotationStatement:26,clickStatement:27,styleStatement:28,cssClassStatement:29,noteStatement:30,classDefStatement:31,direction:32,acc_title:33,acc_title_value:34,acc_descr:35,acc_descr_value:36,acc_descr_multiline_value:37,namespaceIdentifier:38,STRUCT_START:39,classStatements:40,STRUCT_STOP:41,NAMESPACE:42,classIdentifier:43,STYLE_SEPARATOR:44,members:45,ANNOTATION_START:46,ANNOTATION_END:47,CLASS:48,emptyBody:49,SPACE:50,MEMBER:51,SEPARATOR:52,relation:53,NOTE_FOR:54,noteText:55,NOTE:56,CLASSDEF:57,classList:58,stylesOpt:59,ALPHA:60,COMMA:61,direction_tb:62,direction_bt:63,direction_rl:64,direction_lr:65,relationType:66,lineType:67,AGGREGATION:68,EXTENSION:69,COMPOSITION:70,DEPENDENCY:71,LOLLIPOP:72,LINE:73,DOTTED_LINE:74,CALLBACK:75,LINK:76,LINK_TARGET:77,CLICK:78,CALLBACK_NAME:79,CALLBACK_ARGS:80,HREF:81,STYLE:82,CSSCLASS:83,style:84,styleComponent:85,NUM:86,COLON:87,UNIT:88,BRKT:89,PCT:90,commentToken:91,textToken:92,graphCodeTokens:93,textNoTagsToken:94,TAGSTART:95,TAGEND:96,"==":97,"--":98,DEFAULT:99,MINUS:100,keywords:101,UNICODE_TEXT:102,BQUOTE_STR:103,$accept:0,$end:1},terminals_:{2:"error",7:"CLASS_DIAGRAM",8:"NEWLINE",9:"EOF",12:"SQS",13:"STR",14:"SQE",18:"DOT",20:"GENERICTYPE",22:"LABEL",33:"acc_title",34:"acc_title_value",35:"acc_descr",36:"acc_descr_value",37:"acc_descr_multiline_value",39:"STRUCT_START",41:"STRUCT_STOP",42:"NAMESPACE",44:"STYLE_SEPARATOR",46:"ANNOTATION_START",47:"ANNOTATION_END",48:"CLASS",50:"SPACE",51:"MEMBER",52:"SEPARATOR",54:"NOTE_FOR",56:"NOTE",57:"CLASSDEF",60:"ALPHA",61:"COMMA",62:"direction_tb",63:"direction_bt",64:"direction_rl",65:"direction_lr",68:"AGGREGATION",69:"EXTENSION",70:"COMPOSITION",71:"DEPENDENCY",72:"LOLLIPOP",73:"LINE",74:"DOTTED_LINE",75:"CALLBACK",76:"LINK",77:"LINK_TARGET",78:"CLICK",79:"CALLBACK_NAME",80:"CALLBACK_ARGS",81:"HREF",82:"STYLE",83:"CSSCLASS",86:"NUM",87:"COLON",88:"UNIT",89:"BRKT",90:"PCT",93:"graphCodeTokens",95:"TAGSTART",96:"TAGEND",97:"==",98:"--",99:"DEFAULT",100:"MINUS",101:"keywords",102:"UNICODE_TEXT",103:"BQUOTE_STR"},productions_:[0,[3,1],[3,1],[4,1],[6,4],[5,1],[5,2],[5,3],[11,3],[15,1],[15,1],[15,3],[15,2],[19,1],[19,3],[19,1],[19,2],[19,2],[19,2],[10,1],[10,2],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,2],[10,2],[10,1],[23,4],[23,5],[38,2],[38,3],[40,1],[40,2],[40,3],[40,1],[40,2],[40,3],[40,1],[40,2],[40,3],[24,1],[24,3],[24,4],[24,3],[24,6],[24,4],[24,7],[24,6],[43,2],[43,3],[49,0],[49,2],[49,2],[26,4],[45,1],[45,2],[25,1],[25,2],[25,1],[25,1],[21,3],[21,4],[21,4],[21,5],[30,3],[30,2],[31,3],[58,1],[58,3],[32,1],[32,1],[32,1],[32,1],[53,3],[53,2],[53,2],[53,1],[66,1],[66,1],[66,1],[66,1],[66,1],[67,1],[67,1],[27,3],[27,4],[27,3],[27,4],[27,4],[27,5],[27,3],[27,4],[27,4],[27,5],[27,4],[27,5],[27,5],[27,6],[28,3],[29,3],[59,1],[59,3],[84,1],[84,2],[85,1],[85,1],[85,1],[85,1],[85,1],[85,1],[85,1],[85,1],[85,1],[91,1],[91,1],[92,1],[92,1],[92,1],[92,1],[92,1],[92,1],[92,1],[94,1],[94,1],[94,1],[94,1],[16,1],[16,1],[16,1],[16,1],[17,1],[55,1]],performAction:s(function(Qe,W,Ae,K,de,ee,ve){var X=ee.length-1;switch(de){case 8:this.$=ee[X-1];break;case 9:case 10:case 13:case 15:this.$=ee[X];break;case 11:case 14:this.$=ee[X-2]+"."+ee[X];break;case 12:case 16:this.$=ee[X-1]+ee[X];break;case 17:case 18:this.$=ee[X-1]+"~"+ee[X]+"~";break;case 19:K.addRelation(ee[X]);break;case 20:ee[X-1].title=K.cleanupLabel(ee[X]),K.addRelation(ee[X-1]);break;case 31:this.$=ee[X].trim(),K.setAccTitle(this.$);break;case 32:case 33:this.$=ee[X].trim(),K.setAccDescription(this.$);break;case 34:K.addClassesToNamespace(ee[X-3],ee[X-1][0],ee[X-1][1]),K.popNamespace();break;case 35:K.addClassesToNamespace(ee[X-4],ee[X-1][0],ee[X-1][1]),K.popNamespace();break;case 36:this.$=K.addNamespace(ee[X]);break;case 37:this.$=K.addNamespace(ee[X-1],ee[X]);break;case 38:this.$=[[ee[X]],[]];break;case 39:this.$=[[ee[X-1]],[]];break;case 40:ee[X][0].unshift(ee[X-2]),this.$=ee[X];break;case 41:this.$=[[],[ee[X]]];break;case 42:this.$=[[],[ee[X-1]]];break;case 43:ee[X][1].unshift(ee[X-2]),this.$=ee[X];break;case 44:case 45:this.$=[[],[]];break;case 46:this.$=ee[X];break;case 48:K.setCssClass(ee[X-2],ee[X]);break;case 49:K.addMembers(ee[X-3],ee[X-1]);break;case 51:K.setCssClass(ee[X-5],ee[X-3]),K.addMembers(ee[X-5],ee[X-1]);break;case 52:K.addAnnotation(ee[X-3],ee[X-1]);break;case 53:K.addAnnotation(ee[X-6],ee[X-4]),K.addMembers(ee[X-6],ee[X-1]);break;case 54:K.addAnnotation(ee[X-5],ee[X-3]);break;case 55:this.$=ee[X],K.addClass(ee[X]);break;case 56:this.$=ee[X-1],K.addClass(ee[X-1]),K.setClassLabel(ee[X-1],ee[X]);break;case 60:K.addAnnotation(ee[X],ee[X-2]);break;case 61:case 74:this.$=[ee[X]];break;case 62:ee[X].push(ee[X-1]),this.$=ee[X];break;case 63:break;case 64:K.addMember(ee[X-1],K.cleanupLabel(ee[X]));break;case 65:break;case 66:break;case 67:this.$={id1:ee[X-2],id2:ee[X],relation:ee[X-1],relationTitle1:"none",relationTitle2:"none"};break;case 68:this.$={id1:ee[X-3],id2:ee[X],relation:ee[X-1],relationTitle1:ee[X-2],relationTitle2:"none"};break;case 69:this.$={id1:ee[X-3],id2:ee[X],relation:ee[X-2],relationTitle1:"none",relationTitle2:ee[X-1]};break;case 70:this.$={id1:ee[X-4],id2:ee[X],relation:ee[X-2],relationTitle1:ee[X-3],relationTitle2:ee[X-1]};break;case 71:this.$=K.addNote(ee[X],ee[X-1]);break;case 72:this.$=K.addNote(ee[X]);break;case 73:this.$=ee[X-2],K.defineClass(ee[X-1],ee[X]);break;case 75:this.$=ee[X-2].concat([ee[X]]);break;case 76:K.setDirection("TB");break;case 77:K.setDirection("BT");break;case 78:K.setDirection("RL");break;case 79:K.setDirection("LR");break;case 80:this.$={type1:ee[X-2],type2:ee[X],lineType:ee[X-1]};break;case 81:this.$={type1:"none",type2:ee[X],lineType:ee[X-1]};break;case 82:this.$={type1:ee[X-1],type2:"none",lineType:ee[X]};break;case 83:this.$={type1:"none",type2:"none",lineType:ee[X]};break;case 84:this.$=K.relationType.AGGREGATION;break;case 85:this.$=K.relationType.EXTENSION;break;case 86:this.$=K.relationType.COMPOSITION;break;case 87:this.$=K.relationType.DEPENDENCY;break;case 88:this.$=K.relationType.LOLLIPOP;break;case 89:this.$=K.lineType.LINE;break;case 90:this.$=K.lineType.DOTTED_LINE;break;case 91:case 97:this.$=ee[X-2],K.setClickEvent(ee[X-1],ee[X]);break;case 92:case 98:this.$=ee[X-3],K.setClickEvent(ee[X-2],ee[X-1]),K.setTooltip(ee[X-2],ee[X]);break;case 93:this.$=ee[X-2],K.setLink(ee[X-1],ee[X]);break;case 94:this.$=ee[X-3],K.setLink(ee[X-2],ee[X-1],ee[X]);break;case 95:this.$=ee[X-3],K.setLink(ee[X-2],ee[X-1]),K.setTooltip(ee[X-2],ee[X]);break;case 96:this.$=ee[X-4],K.setLink(ee[X-3],ee[X-2],ee[X]),K.setTooltip(ee[X-3],ee[X-1]);break;case 99:this.$=ee[X-3],K.setClickEvent(ee[X-2],ee[X-1],ee[X]);break;case 100:this.$=ee[X-4],K.setClickEvent(ee[X-3],ee[X-2],ee[X-1]),K.setTooltip(ee[X-3],ee[X]);break;case 101:this.$=ee[X-3],K.setLink(ee[X-2],ee[X]);break;case 102:this.$=ee[X-4],K.setLink(ee[X-3],ee[X-1],ee[X]);break;case 103:this.$=ee[X-4],K.setLink(ee[X-3],ee[X-1]),K.setTooltip(ee[X-3],ee[X]);break;case 104:this.$=ee[X-5],K.setLink(ee[X-4],ee[X-2],ee[X]),K.setTooltip(ee[X-4],ee[X-1]);break;case 105:this.$=ee[X-2],K.setCssStyle(ee[X-1],ee[X]);break;case 106:K.setCssClass(ee[X-1],ee[X]);break;case 107:this.$=[ee[X]];break;case 108:ee[X-2].push(ee[X]),this.$=ee[X-2];break;case 110:this.$=ee[X-1]+ee[X];break}},"anonymous"),table:[{3:1,4:2,5:3,6:4,7:[1,6],10:5,16:39,17:40,19:21,21:7,23:8,24:9,25:10,26:11,27:12,28:13,29:14,30:15,31:16,32:17,33:t,35:r,37:i,38:22,42:n,43:23,46:a,48:o,51:l,52:u,54:h,56:d,57:p,60:f,62:g,63:m,64:y,65:k,75:C,76:S,78:E,82:_,83:b,86:v,100:x,102:w,103:R},{1:[3]},{1:[2,1]},{1:[2,2]},{1:[2,3]},e($,[2,5],{8:[1,48]}),{8:[1,49]},e(M,[2,19],{22:[1,50]}),e(M,[2,21]),e(M,[2,22]),e(M,[2,23]),e(M,[2,24]),e(M,[2,25]),e(M,[2,26]),e(M,[2,27]),e(M,[2,28]),e(M,[2,29]),e(M,[2,30]),{34:[1,51]},{36:[1,52]},e(M,[2,33]),e(M,[2,63],{53:53,66:56,67:57,13:[1,54],22:[1,55],68:N,69:j,70:I,71:z,72:F,73:O,74:B}),{39:[1,65]},e(Y,[2,47],{39:[1,67],44:[1,66],46:[1,68]}),e(M,[2,65]),e(M,[2,66]),{16:69,60:f,86:v,100:x,102:w},{16:39,17:40,19:70,60:f,86:v,100:x,102:w,103:R},{16:39,17:40,19:71,60:f,86:v,100:x,102:w,103:R},{16:39,17:40,19:72,60:f,86:v,100:x,102:w,103:R},{60:[1,73]},{13:[1,74]},{16:39,17:40,19:75,60:f,86:v,100:x,102:w,103:R},{13:U,55:76},{58:78,60:[1,79]},e(M,[2,76]),e(M,[2,77]),e(M,[2,78]),e(M,[2,79]),e(H,[2,13],{16:39,17:40,19:81,18:[1,80],20:[1,82],60:f,86:v,100:x,102:w,103:R}),e(H,[2,15],{20:[1,83]}),{15:84,16:85,17:86,60:f,86:v,100:x,102:w,103:R},{16:39,17:40,19:87,60:f,86:v,100:x,102:w,103:R},e(J,[2,133]),e(J,[2,134]),e(J,[2,135]),e(J,[2,136]),e([1,8,9,12,13,20,22,39,41,44,46,68,69,70,71,72,73,74,79,81],[2,137]),e($,[2,6],{10:5,21:7,23:8,24:9,25:10,26:11,27:12,28:13,29:14,30:15,31:16,32:17,19:21,38:22,43:23,16:39,17:40,5:88,33:t,35:r,37:i,42:n,46:a,48:o,51:l,52:u,54:h,56:d,57:p,60:f,62:g,63:m,64:y,65:k,75:C,76:S,78:E,82:_,83:b,86:v,100:x,102:w,103:R}),{5:89,10:5,16:39,17:40,19:21,21:7,23:8,24:9,25:10,26:11,27:12,28:13,29:14,30:15,31:16,32:17,33:t,35:r,37:i,38:22,42:n,43:23,46:a,48:o,51:l,52:u,54:h,56:d,57:p,60:f,62:g,63:m,64:y,65:k,75:C,76:S,78:E,82:_,83:b,86:v,100:x,102:w,103:R},e(M,[2,20]),e(M,[2,31]),e(M,[2,32]),{13:[1,91],16:39,17:40,19:90,60:f,86:v,100:x,102:w,103:R},{53:92,66:56,67:57,68:N,69:j,70:I,71:z,72:F,73:O,74:B},e(M,[2,64]),{67:93,73:O,74:B},e(te,[2,83],{66:94,68:N,69:j,70:I,71:z,72:F}),e(ie,[2,84]),e(ie,[2,85]),e(ie,[2,86]),e(ie,[2,87]),e(ie,[2,88]),e(ue,[2,89]),e(ue,[2,90]),{8:[1,96],23:99,24:97,30:98,38:22,40:95,42:n,43:23,48:o,54:h,56:d},{16:100,60:f,86:v,100:x,102:w},{41:[1,102],45:101,51:se},{16:104,60:f,86:v,100:x,102:w},{47:[1,105]},{13:[1,106]},{13:[1,107]},{79:[1,108],81:[1,109]},{22:le,50:Te,59:110,60:oe,82:fe,84:111,85:112,86:ge,87:ke,88:Ne,89:xe,90:$e},{60:[1,122]},{13:U,55:123},e(Y,[2,72]),e(Y,[2,138]),{22:le,50:Te,59:124,60:oe,61:[1,125],82:fe,84:111,85:112,86:ge,87:ke,88:Ne,89:xe,90:$e},e(Me,[2,74]),{16:39,17:40,19:126,60:f,86:v,100:x,102:w,103:R},e(H,[2,16]),e(H,[2,17]),e(H,[2,18]),{11:127,12:He,39:[2,36]},e(Ge,[2,9],{16:85,17:86,15:130,18:[1,129],60:f,86:v,100:x,102:w,103:R}),e(Ge,[2,10]),e(ze,[2,55],{11:131,12:He}),e($,[2,7]),{9:[1,132]},e(Ve,[2,67]),{16:39,17:40,19:133,60:f,86:v,100:x,102:w,103:R},{13:[1,135],16:39,17:40,19:134,60:f,86:v,100:x,102:w,103:R},e(te,[2,82],{66:136,68:N,69:j,70:I,71:z,72:F}),e(te,[2,81]),{41:[1,137]},{23:99,24:97,30:98,38:22,40:138,42:n,43:23,48:o,54:h,56:d},{8:[1,139],41:[2,38]},{8:[1,140],41:[2,41]},{8:[1,141],41:[2,44]},e(Y,[2,48],{39:[1,142]}),{41:[1,143]},e(Y,[2,50]),{41:[2,61],45:144,51:se},{47:[1,145]},{16:39,17:40,19:146,60:f,86:v,100:x,102:w,103:R},e(M,[2,91],{13:[1,147]}),e(M,[2,93],{13:[1,149],77:[1,148]}),e(M,[2,97],{13:[1,150],80:[1,151]}),{13:[1,152]},e(M,[2,105],{61:et}),e(he,[2,107],{85:154,22:le,50:Te,60:oe,82:fe,86:ge,87:ke,88:Ne,89:xe,90:$e}),e(ye,[2,109]),e(ye,[2,111]),e(ye,[2,112]),e(ye,[2,113]),e(ye,[2,114]),e(ye,[2,115]),e(ye,[2,116]),e(ye,[2,117]),e(ye,[2,118]),e(ye,[2,119]),e(M,[2,106]),e(Y,[2,71]),e(M,[2,73],{61:et}),{60:[1,155]},e(H,[2,14]),{39:[2,37]},{13:[1,156]},{15:157,16:85,17:86,60:f,86:v,100:x,102:w,103:R},e(Ge,[2,12]),e(ze,[2,56]),{1:[2,4]},e(Ve,[2,69]),e(Ve,[2,68]),{16:39,17:40,19:158,60:f,86:v,100:x,102:w,103:R},e(te,[2,80]),e(Y,[2,34]),{41:[1,159]},{23:99,24:97,30:98,38:22,40:160,41:[2,39],42:n,43:23,48:o,54:h,56:d},{23:99,24:97,30:98,38:22,40:161,41:[2,42],42:n,43:23,48:o,54:h,56:d},{23:99,24:97,30:98,38:22,40:162,41:[2,45],42:n,43:23,48:o,54:h,56:d},{45:163,51:se},e(Y,[2,49]),{41:[2,62]},e(Y,[2,52],{39:[1,164]}),e(M,[2,60]),e(M,[2,92]),e(M,[2,94]),e(M,[2,95],{77:[1,165]}),e(M,[2,98]),e(M,[2,99],{13:[1,166]}),e(M,[2,101],{13:[1,168],77:[1,167]}),{22:le,50:Te,60:oe,82:fe,84:169,85:112,86:ge,87:ke,88:Ne,89:xe,90:$e},e(ye,[2,110]),e(Me,[2,75]),{14:[1,170]},e(Ge,[2,11]),e(Ve,[2,70]),e(Y,[2,35]),{41:[2,40]},{41:[2,43]},{41:[2,46]},{41:[1,171]},{41:[1,173],45:172,51:se},e(M,[2,96]),e(M,[2,100]),e(M,[2,102]),e(M,[2,103],{77:[1,174]}),e(he,[2,108],{85:154,22:le,50:Te,60:oe,82:fe,86:ge,87:ke,88:Ne,89:xe,90:$e}),e(ze,[2,8]),e(Y,[2,51]),{41:[1,175]},e(Y,[2,54]),e(M,[2,104]),e(Y,[2,53])],defaultActions:{2:[2,1],3:[2,2],4:[2,3],127:[2,37],132:[2,4],144:[2,62],160:[2,40],161:[2,43],162:[2,46]},parseError:s(function(Qe,W){if(W.recoverable)this.trace(Qe);else{var Ae=new Error(Qe);throw Ae.hash=W,Ae}},"parseError"),parse:s(function(Qe){var W=this,Ae=[0],K=[],de=[null],ee=[],ve=this.table,X="",me=0,Ce=0,Be=0,Re=2,Ke=1,De=ee.slice.call(arguments,1),Ze=Object.create(this.lexer),Fe={yy:{}};for(var Je in this.yy)Object.prototype.hasOwnProperty.call(this.yy,Je)&&(Fe.yy[Je]=this.yy[Je]);Ze.setInput(Qe,Fe.yy),Fe.yy.lexer=Ze,Fe.yy.parser=this,typeof Ze.yylloc>"u"&&(Ze.yylloc={});var Le=Ze.yylloc;ee.push(Le);var ct=Ze.options&&Ze.options.ranges;typeof Fe.yy.parseError=="function"?this.parseError=Fe.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function qe(At){Ae.length=Ae.length-2*At,de.length=de.length-At,ee.length=ee.length-At}s(qe,"popStack");function mt(){var At;return At=K.pop()||Ze.lex()||Ke,typeof At!="number"&&(At instanceof Array&&(K=At,At=K.pop()),At=W.symbols_[At]||At),At}s(mt,"lex");for(var St,Lt,Bt,qt,tr,Et,_t={},Kt,$t,Ot,Jt;;){if(Bt=Ae[Ae.length-1],this.defaultActions[Bt]?qt=this.defaultActions[Bt]:((St===null||typeof St>"u")&&(St=mt()),qt=ve[Bt]&&ve[Bt][St]),typeof qt>"u"||!qt.length||!qt[0]){var Br="";Jt=[];for(Kt in ve[Bt])this.terminals_[Kt]&&Kt>Re&&Jt.push("'"+this.terminals_[Kt]+"'");Ze.showPosition?Br="Parse error on line "+(me+1)+`: +`+Ze.showPosition()+` +Expecting `+Jt.join(", ")+", got '"+(this.terminals_[St]||St)+"'":Br="Parse error on line "+(me+1)+": Unexpected "+(St==Ke?"end of input":"'"+(this.terminals_[St]||St)+"'"),this.parseError(Br,{text:Ze.match,token:this.terminals_[St]||St,line:Ze.yylineno,loc:Le,expected:Jt})}if(qt[0]instanceof Array&&qt.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Bt+", token: "+St);switch(qt[0]){case 1:Ae.push(St),de.push(Ze.yytext),ee.push(Ze.yylloc),Ae.push(qt[1]),St=null,Lt?(St=Lt,Lt=null):(Ce=Ze.yyleng,X=Ze.yytext,me=Ze.yylineno,Le=Ze.yylloc,Be>0&&Be--);break;case 2:if($t=this.productions_[qt[1]][1],_t.$=de[de.length-$t],_t._$={first_line:ee[ee.length-($t||1)].first_line,last_line:ee[ee.length-1].last_line,first_column:ee[ee.length-($t||1)].first_column,last_column:ee[ee.length-1].last_column},ct&&(_t._$.range=[ee[ee.length-($t||1)].range[0],ee[ee.length-1].range[1]]),Et=this.performAction.apply(_t,[X,Ce,me,Fe.yy,qt[1],de,ee].concat(De)),typeof Et<"u")return Et;$t&&(Ae=Ae.slice(0,-1*$t*2),de=de.slice(0,-1*$t),ee=ee.slice(0,-1*$t)),Ae.push(this.productions_[qt[1]][0]),de.push(_t.$),ee.push(_t._$),Ot=ve[Ae[Ae.length-2]][Ae[Ae.length-1]],Ae.push(Ot);break;case 3:return!0}}return!0},"parse")},Ie=(function(){var Qe={EOF:1,parseError:s(function(W,Ae){if(this.yy.parser)this.yy.parser.parseError(W,Ae);else throw new Error(W)},"parseError"),setInput:s(function(W,Ae){return this.yy=Ae||this.yy||{},this._input=W,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var W=this._input[0];this.yytext+=W,this.yyleng++,this.offset++,this.match+=W,this.matched+=W;var Ae=W.match(/(?:\r\n?|\n).*/g);return Ae?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),W},"input"),unput:s(function(W){var Ae=W.length,K=W.split(/(?:\r\n?|\n)/g);this._input=W+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-Ae),this.offset-=Ae;var de=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),K.length-1&&(this.yylineno-=K.length-1);var ee=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:K?(K.length===de.length?this.yylloc.first_column:0)+de[de.length-K.length].length-K[0].length:this.yylloc.first_column-Ae},this.options.ranges&&(this.yylloc.range=[ee[0],ee[0]+this.yyleng-Ae]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(W){this.unput(this.match.slice(W))},"less"),pastInput:s(function(){var W=this.matched.substr(0,this.matched.length-this.match.length);return(W.length>20?"...":"")+W.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var W=this.match;return W.length<20&&(W+=this._input.substr(0,20-W.length)),(W.substr(0,20)+(W.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var W=this.pastInput(),Ae=new Array(W.length+1).join("-");return W+this.upcomingInput()+` +`+Ae+"^"},"showPosition"),test_match:s(function(W,Ae){var K,de,ee;if(this.options.backtrack_lexer&&(ee={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(ee.yylloc.range=this.yylloc.range.slice(0))),de=W[0].match(/(?:\r\n?|\n).*/g),de&&(this.yylineno+=de.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:de?de[de.length-1].length-de[de.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+W[0].length},this.yytext+=W[0],this.match+=W[0],this.matches=W,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(W[0].length),this.matched+=W[0],K=this.performAction.call(this,this.yy,this,Ae,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),K)return K;if(this._backtrack){for(var ve in ee)this[ve]=ee[ve];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var W,Ae,K,de;this._more||(this.yytext="",this.match="");for(var ee=this._currentRules(),ve=0;veAe[0].length)){if(Ae=K,de=ve,this.options.backtrack_lexer){if(W=this.test_match(K,ee[ve]),W!==!1)return W;if(this._backtrack){Ae=!1;continue}else return!1}else if(!this.options.flex)break}return Ae?(W=this.test_match(Ae,ee[de]),W!==!1?W:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var W=this.next();return W||this.lex()},"lex"),begin:s(function(W){this.conditionStack.push(W)},"begin"),popState:s(function(){var W=this.conditionStack.length-1;return W>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(W){return W=this.conditionStack.length-1-Math.abs(W||0),W>=0?this.conditionStack[W]:"INITIAL"},"topState"),pushState:s(function(W){this.begin(W)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:s(function(W,Ae,K,de){var ee=de;switch(K){case 0:return 62;case 1:return 63;case 2:return 64;case 3:return 65;case 4:break;case 5:break;case 6:return this.begin("acc_title"),33;case 7:return this.popState(),"acc_title_value";case 8:return this.begin("acc_descr"),35;case 9:return this.popState(),"acc_descr_value";case 10:this.begin("acc_descr_multiline");break;case 11:this.popState();break;case 12:return"acc_descr_multiline_value";case 13:return 8;case 14:break;case 15:return 7;case 16:return 7;case 17:return"EDGE_STATE";case 18:this.begin("callback_name");break;case 19:this.popState();break;case 20:this.popState(),this.begin("callback_args");break;case 21:return 79;case 22:this.popState();break;case 23:return 80;case 24:this.popState();break;case 25:return"STR";case 26:this.begin("string");break;case 27:return 82;case 28:return 57;case 29:return this.begin("namespace"),42;case 30:return this.popState(),8;case 31:break;case 32:return this.begin("namespace-body"),39;case 33:this.popState(),this.less(0);break;case 34:return this.popState(),41;case 35:return"EOF_IN_STRUCT";case 36:return 8;case 37:break;case 38:return"EDGE_STATE";case 39:return this.begin("class"),48;case 40:return this.popState(),8;case 41:break;case 42:return this.popState(),this.popState(),41;case 43:return this.begin("class-body"),39;case 44:return this.popState(),41;case 45:return"EOF_IN_STRUCT";case 46:return"EDGE_STATE";case 47:return"OPEN_IN_STRUCT";case 48:break;case 49:return"MEMBER";case 50:return 83;case 51:return 75;case 52:return 76;case 53:return 78;case 54:return 54;case 55:return 56;case 56:return 46;case 57:return 47;case 58:return 81;case 59:this.popState();break;case 60:return"GENERICTYPE";case 61:this.begin("generic");break;case 62:this.popState();break;case 63:return"BQUOTE_STR";case 64:this.begin("bqstring");break;case 65:return 77;case 66:return 77;case 67:return 77;case 68:return 77;case 69:return 69;case 70:return 69;case 71:return 71;case 72:return 71;case 73:return 70;case 74:return 68;case 75:return 72;case 76:return 73;case 77:return 74;case 78:return 22;case 79:return 44;case 80:return 100;case 81:return 18;case 82:return"PLUS";case 83:return 87;case 84:return 61;case 85:return 89;case 86:return 89;case 87:return 90;case 88:return"EQUALS";case 89:return"EQUALS";case 90:return 60;case 91:return 12;case 92:return 14;case 93:return"PUNCTUATION";case 94:return 86;case 95:return 102;case 96:return 50;case 97:return 50;case 98:return 9}},"anonymous"),rules:[/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:%%(?!\{)*[^\n]*(\r?\n?)+)/,/^(?:%%[^\n]*(\r?\n)*)/,/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:classDiagram-v2\b)/,/^(?:classDiagram\b)/,/^(?:\[\*\])/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:["])/,/^(?:[^"]*)/,/^(?:["])/,/^(?:style\b)/,/^(?:classDef\b)/,/^(?:namespace\b)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:[{])/,/^(?:[}])/,/^(?:[}])/,/^(?:$)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:\[\*\])/,/^(?:class\b)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:[}])/,/^(?:[{])/,/^(?:[}])/,/^(?:$)/,/^(?:\[\*\])/,/^(?:[{])/,/^(?:[\n])/,/^(?:[^{}\n]*)/,/^(?:cssClass\b)/,/^(?:callback\b)/,/^(?:link\b)/,/^(?:click\b)/,/^(?:note for\b)/,/^(?:note\b)/,/^(?:<<)/,/^(?:>>)/,/^(?:href\b)/,/^(?:[~])/,/^(?:[^~]*)/,/^(?:~)/,/^(?:[`])/,/^(?:[^`]+)/,/^(?:[`])/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:\s*<\|)/,/^(?:\s*\|>)/,/^(?:\s*>)/,/^(?:\s*<)/,/^(?:\s*\*)/,/^(?:\s*o\b)/,/^(?:\s*\(\))/,/^(?:--)/,/^(?:\.\.)/,/^(?::{1}[^:\n;]+)/,/^(?::{3})/,/^(?:-)/,/^(?:\.)/,/^(?:\+)/,/^(?::)/,/^(?:,)/,/^(?:#)/,/^(?:#)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:\w+)/,/^(?:\[)/,/^(?:\])/,/^(?:[!"#$%&'*+,-.`?\\/])/,/^(?:[0-9]+)/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\s)/,/^(?:\s)/,/^(?:$)/],conditions:{"namespace-body":{rules:[26,29,34,35,36,37,38,39,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},namespace:{rules:[26,29,30,31,32,33,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},"class-body":{rules:[26,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},class:{rules:[26,40,41,42,43,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},acc_descr_multiline:{rules:[11,12,26,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},acc_descr:{rules:[9,26,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},acc_title:{rules:[7,26,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},callback_args:{rules:[22,23,26,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},callback_name:{rules:[19,20,21,26,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},href:{rules:[26,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},struct:{rules:[26,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},generic:{rules:[26,50,51,52,53,54,55,56,57,58,59,60,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},bqstring:{rules:[26,50,51,52,53,54,55,56,57,58,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},string:{rules:[24,25,26,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,87,88,89,90,91,92,93,94,95,96,98],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,8,10,13,14,15,16,17,18,26,27,28,29,39,50,51,52,53,54,55,56,57,58,61,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98],inclusive:!0}}};return Qe})();be.lexer=Ie;function pe(){this.yy={}}return s(pe,"Parser"),pe.prototype=be,be.Parser=pe,new pe})(),SI.parser=SI,cde=SI}),ude,hde,$Rt=G(()=>{"use strict";var e;Er(),mi(),ude=["#","+","~","-",""],hde=(e=class{constructor(r,i){this.memberType=i,this.visibility="",this.classifier="",this.text="";let n=Si(r,it());this.parseMember(n)}getDisplayDetails(){let r=this.visibility+Jh(this.id);this.memberType==="method"&&(r+=`(${Jh(this.parameters.trim())})`,this.returnType&&(r+=" : "+Jh(this.returnType))),r=r.trim();let i=this.parseClassifier();return{displayText:r,cssStyle:i}}parseMember(r){let i="";if(this.memberType==="method"){let a=/([#+~-])?(.+)\((.*)\)([\s$*])?(.*)([$*])?/.exec(r);if(a){let o=a[1]?a[1].trim():"";if(ude.includes(o)&&(this.visibility=o),this.id=a[2],this.parameters=a[3]?a[3].trim():"",i=a[4]?a[4].trim():"",this.returnType=a[5]?a[5].trim():"",i===""){let l=this.returnType.substring(this.returnType.length-1);/[$*]/.exec(l)&&(i=l,this.returnType=this.returnType.substring(0,this.returnType.length-1))}}}else{let a=r.length,o=r.substring(0,1),l=r.substring(a-1);ude.includes(o)&&(this.visibility=o),/[$*]/.exec(l)&&(i=l),this.id=r.substring(this.visibility===""?0:1,i===""?a:a-1)}this.classifier=i,this.id=this.id.startsWith(" ")?" "+this.id.trim():this.id.trim();let n=`${this.visibility?"\\"+this.visibility:""}${Jh(this.id)}${this.memberType==="method"?`(${Jh(this.parameters)})${this.returnType?" : "+Jh(this.returnType):""}`:""}`;this.text=n.replaceAll("<","<").replaceAll(">",">"),this.text.startsWith("\\<")&&(this.text=this.text.replace("\\<","~"))}parseClassifier(){switch(this.classifier){case"*":return"font-style:italic;";case"$":return"text-decoration:underline;";default:return""}}},s(e,"ClassMember"),e)}),I5,dde,R0,pde,uft=G(()=>{"use strict";var e;pi(),Nt(),Er(),mi(),Ar(),fn(),pg(),$Rt(),zx(),I5="classId-",dde=0,R0=s(t=>Vt.sanitizeText(t,it()),"sanitizeText"),pde=(e=class{constructor(){this.relations=[],this.classes=new Map,this.styleClasses=new Map,this.notes=new Map,this.interfaces=[],this.namespaces=new Map,this.namespaceCounter=0,this.namespaceStack=[],this.diagramId="",this.functions=[],this.lineType={LINE:0,DOTTED_LINE:1},this.relationType={AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3,LOLLIPOP:4},this.setupToolTips=s(r=>{let i=H6();zt(r).select("svg").selectAll("g").filter(function(){return zt(this).attr("title")!==null}).on("mouseover",n=>{let a=zt(n.currentTarget),o=a.attr("title");if(!o)return;let l=n.currentTarget.getBoundingClientRect();i.transition().duration(200).style("opacity",".9"),i.html(wu.sanitize(o)).style("left",`${window.scrollX+l.left+l.width/2}px`).style("top",`${window.scrollY+l.bottom+4}px`),a.classed("hover",!0)}).on("mouseout",n=>{i.transition().duration(500).style("opacity",0),zt(n.currentTarget).classed("hover",!1)})},"setupToolTips"),this.direction="TB",this.setAccTitle=bn,this.getAccTitle=Ln,this.setAccDescription=In,this.getAccDescription=Mn,this.setDiagramTitle=Dn,this.getDiagramTitle=xn,this.getConfig=s(()=>it().class,"getConfig"),this.functions.push(this.setupToolTips.bind(this)),this.clear(),this.addRelation=this.addRelation.bind(this),this.addClassesToNamespace=this.addClassesToNamespace.bind(this),this.addNamespace=this.addNamespace.bind(this),this.popNamespace=this.popNamespace.bind(this),this.setCssClass=this.setCssClass.bind(this),this.addMembers=this.addMembers.bind(this),this.addClass=this.addClass.bind(this),this.setClassLabel=this.setClassLabel.bind(this),this.addAnnotation=this.addAnnotation.bind(this),this.addMember=this.addMember.bind(this),this.cleanupLabel=this.cleanupLabel.bind(this),this.addNote=this.addNote.bind(this),this.defineClass=this.defineClass.bind(this),this.setDirection=this.setDirection.bind(this),this.setLink=this.setLink.bind(this),this.bindFunctions=this.bindFunctions.bind(this),this.clear=this.clear.bind(this),this.setTooltip=this.setTooltip.bind(this),this.setClickEvent=this.setClickEvent.bind(this),this.setCssStyle=this.setCssStyle.bind(this)}splitClassNameAndType(r){let i=Vt.sanitizeText(r,it()),n="",a=i;if(i.indexOf("~")>0){let o=i.split("~");a=R0(o[0]),n=R0(o[1])}return{className:a,type:n}}setClassLabel(r,i){let n=Vt.sanitizeText(r,it());i&&(i=R0(i));let{className:a}=this.splitClassNameAndType(n);this.classes.get(a).label=i,this.classes.get(a).text=`${i}${this.classes.get(a).type?`<${this.classes.get(a).type}>`:""}`}addClass(r){let i=Vt.sanitizeText(r,it()),{className:n,type:a}=this.splitClassNameAndType(i);if(this.classes.has(n))return;let o=Vt.sanitizeText(n,it());this.classes.set(o,{id:o,type:a,label:o,text:`${o}${a?`<${a}>`:""}`,shape:"classBox",cssClasses:"default",methods:[],members:[],annotations:[],styles:[],domId:I5+o+"-"+dde}),dde++}addInterface(r,i){let n={id:`interface${this.interfaces.length}`,label:r,classId:i};this.interfaces.push(n)}setDiagramId(r){this.diagramId=r}lookUpDomId(r){let i=Vt.sanitizeText(r,it());if(this.classes.has(i)){let n=this.classes.get(i).domId;return this.diagramId?`${this.diagramId}-${n}`:n}throw new Error("Class not found: "+i)}clear(){this.relations=[],this.classes=new Map,this.notes=new Map,this.interfaces=[],this.functions=[],this.functions.push(this.setupToolTips.bind(this)),this.namespaces=new Map,this.namespaceCounter=0,this.namespaceStack=[],this.diagramId="",this.direction="TB",pn()}getClass(r){return this.classes.get(r)}getClasses(){return this.classes}getRelations(){return this.relations}getNote(r){let i=typeof r=="number"?`note${r}`:r;return this.notes.get(i)}getNotes(){return this.notes}addRelation(r){Ee.debug("Adding relation: "+JSON.stringify(r));let i=[this.relationType.LOLLIPOP,this.relationType.AGGREGATION,this.relationType.COMPOSITION,this.relationType.DEPENDENCY,this.relationType.EXTENSION];r.relation.type1===this.relationType.LOLLIPOP&&!i.includes(r.relation.type2)?(this.addClass(r.id2),this.addInterface(r.id1,r.id2),r.id1=`interface${this.interfaces.length-1}`):r.relation.type2===this.relationType.LOLLIPOP&&!i.includes(r.relation.type1)?(this.addClass(r.id1),this.addInterface(r.id2,r.id1),r.id2=`interface${this.interfaces.length-1}`):(this.addClass(r.id1),this.addClass(r.id2)),r.id1=this.splitClassNameAndType(r.id1).className,r.id2=this.splitClassNameAndType(r.id2).className,r.relationTitle1=Vt.sanitizeText(r.relationTitle1.trim(),it()),r.relationTitle2=Vt.sanitizeText(r.relationTitle2.trim(),it()),this.relations.push(r)}addAnnotation(r,i){let n=this.splitClassNameAndType(r).className;this.classes.get(n).annotations.push(i)}addMember(r,i){this.addClass(r);let n=this.splitClassNameAndType(r).className,a=this.classes.get(n);if(typeof i=="string"){let o=i.trim();o.startsWith("<<")&&o.endsWith(">>")?a.annotations.push(R0(o.substring(2,o.length-2))):o.indexOf(")")>0?a.methods.push(new hde(o,"method")):o&&a.members.push(new hde(o,"attribute"))}}addMembers(r,i){Array.isArray(i)&&(i.reverse(),i.forEach(n=>this.addMember(r,n)))}addNote(r,i){let n=this.notes.size,a={id:`note${n}`,class:i,text:r,index:n};return this.notes.set(a.id,a),a.id}cleanupLabel(r){return r.startsWith(":")&&(r=r.substring(1)),R0(r.trim())}setCssClass(r,i){r.split(",").forEach(n=>{let a=n;/\d/.exec(n[0])&&(a=I5+a),a=this.splitClassNameAndType(a).className;let o=this.classes.get(a);o&&(o.cssClasses+=" "+i)})}defineClass(r,i){for(let n of r){let a=this.styleClasses.get(n);a===void 0&&(a={id:n,styles:[],textStyles:[]},this.styleClasses.set(n,a)),i&&i.forEach(o=>{if(/color/.exec(o)){let l=o.replace("fill","bgFill");a.textStyles.push(l)}a.styles.push(o)}),this.classes.forEach(o=>{o.cssClasses.includes(n)&&o.styles.push(...i.flatMap(l=>l.split(",")))})}}setTooltip(r,i){r.split(",").forEach(n=>{if(i!==void 0){let a=this.splitClassNameAndType(n).className,o=this.classes.get(a);o&&(o.tooltip=R0(i))}})}getTooltip(r,i){return i&&this.namespaces.has(i)?this.namespaces.get(i).classes.get(r).tooltip:this.classes.get(r).tooltip}setLink(r,i,n){let a=it();r.split(",").forEach(o=>{let l=o;/\d/.exec(o[0])&&(l=I5+l),l=this.splitClassNameAndType(l).className;let u=this.classes.get(l);u&&(u.link=ai.formatUrl(i,a),a.securityLevel==="sandbox"?u.linkTarget="_top":typeof n=="string"?u.linkTarget=R0(n):u.linkTarget="_blank")}),this.setCssClass(r,"clickable")}setClickEvent(r,i,n){r.split(",").forEach(a=>{this.setClickFunc(a,i,n);let o=this.splitClassNameAndType(a).className,l=this.classes.get(o);l&&(l.haveCallback=!0)}),this.setCssClass(r,"clickable")}setClickFunc(r,i,n){let a=Vt.sanitizeText(r,it());if(it().securityLevel!=="loose"||i===void 0)return;let o=this.splitClassNameAndType(a).className;if(this.classes.has(o)){let l=[];if(typeof n=="string"){l=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(let u=0;u{let u=this.lookUpDomId(o),h=document.querySelector(`[id="${u}"]`);h!==null&&h.addEventListener("click",()=>{ai.runFunc(i,...l)},!1)})}}bindFunctions(r){this.functions.forEach(i=>{i(r)})}escapeHtml(r){return r.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}getDirection(){return this.direction}setDirection(r){this.direction=r}static resolveQualifiedId(r,i){let n=i.at(-1);return n?`${n}.${r}`:r}static getAncestorIds(r){let i=r.split("."),n=new Array(i.length);n[0]=i[0];for(let a=1;a0?o[l-1]:void 0,d=l===o.length-1,p=d&&i?i:a[l];this.namespaces.has(u)?d&&(this.namespaces.get(u).explicit=!0):this.namespaces.set(u,this.createNamespaceNode(u,p,h,d)),h&&this.linkParentChild(h,u)}return n}popNamespace(){this.namespaceStack.pop()}getNamespace(r){return this.namespaces.get(r)}getNamespaces(){return this.namespaces}addClassesToNamespace(r,i,n){if(this.namespaces.has(r)){for(let a of i){let{className:o}=this.splitClassNameAndType(a),l=this.getClass(o);l.parent=r,this.namespaces.get(r).classes.set(o,l)}for(let a of n){let o=this.getNote(a);o.parent=r,this.namespaces.get(r).notes.set(a,o)}}}setCssStyle(r,i){let n=this.classes.get(r);if(!(!i||!n))for(let a of i)a.includes(",")?n.styles.push(...a.split(",")):n.styles.push(a)}getArrowMarker(r){let i;switch(r){case 0:i="aggregation";break;case 1:i="extension";break;case 2:i="composition";break;case 3:i="dependency";break;case 4:i="lollipop";break;default:i="none"}return i}resolveExplicitAncestor(r){let i=r;for(;i;){let n=this.namespaces.get(i);if(!n)return;if(n.explicit)return i;i=n.parent}}getData(){let r=[],i=[],n=it(),a=n.class?.hierarchicalNamespaces??!0;for(let l of this.namespaces.values()){if(!a&&!l.explicit)continue;let u={id:l.id,label:a?l.label:l.id,isGroup:!0,padding:n.class.padding??16,shape:"rect",cssStyles:[],look:n.look,parentId:a?l.parent:void 0};r.push(u)}for(let l of this.classes.values()){let u=a?l.parent:this.resolveExplicitAncestor(l.parent),h={...l,type:void 0,isGroup:!1,parentId:u,look:n.look};r.push(h)}for(let l of this.notes.values()){let u=a?l.parent:this.resolveExplicitAncestor(l.parent),h={id:l.id,label:l.text,isGroup:!1,shape:"note",padding:n.class.padding??6,cssStyles:["text-align: left","white-space: nowrap",`fill: ${n.themeVariables.noteBkgColor}`,`stroke: ${n.themeVariables.noteBorderColor}`],look:n.look,parentId:u,labelType:"markdown"};r.push(h);let d=this.classes.get(l.class)?.id;if(d){let p={id:`edgeNote${l.index}`,start:l.id,end:d,type:"normal",thickness:"normal",classes:"relation",arrowTypeStart:"none",arrowTypeEnd:"none",arrowheadStyle:"",labelStyle:[""],style:["fill: none"],pattern:"dotted",look:n.look};i.push(p)}}for(let l of this.interfaces){let u={id:l.id,label:l.label,isGroup:!1,shape:"rect",cssStyles:["opacity: 0;"],look:n.look};r.push(u)}let o=0;for(let l of this.relations){o++;let u={id:gg(l.id1,l.id2,{prefix:"id",counter:o}),start:l.id1,end:l.id2,type:"normal",label:l.title,labelpos:"c",thickness:"normal",classes:"relation",arrowTypeStart:this.getArrowMarker(l.relation.type1),arrowTypeEnd:this.getArrowMarker(l.relation.type2),startLabelRight:l.relationTitle1==="none"?"":l.relationTitle1,endLabelLeft:l.relationTitle2==="none"?"":l.relationTitle2,arrowheadStyle:"",labelStyle:["display: inline-block"],style:l.style||"",pattern:l.relation.lineType==1?"dashed":"solid",look:n.look,labelType:"markdown"};i.push(u)}return{nodes:r,edges:i,other:{},config:n,direction:this.getDirection()}}},s(e,"ClassDB"),e)}),hft,fde,dft=G(()=>{"use strict";Lk(),hft=s(e=>`g.classGroup text { + fill: ${e.nodeBorder||e.classText}; + stroke: none; + font-family: ${e.fontFamily}; + font-size: 10px; + + .title { + font-weight: bolder; + } + +} + + .cluster-label text { + fill: ${e.titleColor}; + } + .cluster-label span { + color: ${e.titleColor}; + } + .cluster-label span p { + background-color: transparent; + } + + .cluster rect { + fill: ${e.clusterBkg}; + stroke: ${e.clusterBorder}; + stroke-width: 1px; + } + + .cluster text { + fill: ${e.titleColor}; + } + + .cluster span { + color: ${e.titleColor}; + } + +.nodeLabel, .edgeLabel { + color: ${e.classText}; +} + +.noteLabel .nodeLabel, .noteLabel .edgeLabel { + color: ${e.noteTextColor}; +} +.edgeLabel .label rect { + fill: ${e.mainBkg}; +} +.label text { + fill: ${e.classText}; +} + +.labelBkg { + background: ${e.mainBkg}; +} +.edgeLabel .label span { + background: ${e.mainBkg}; +} + +.classTitle { + font-weight: bolder; +} +.node rect, + .node circle, + .node ellipse, + .node polygon, + .node path { + fill: ${e.mainBkg}; + stroke: ${e.nodeBorder}; + stroke-width: ${e.strokeWidth}; + } + + +.divider { + stroke: ${e.nodeBorder}; + stroke-width: 1; +} + +g.clickable { + cursor: pointer; +} + +g.classGroup rect { + fill: ${e.mainBkg}; + stroke: ${e.nodeBorder}; +} + +g.classGroup line { + stroke: ${e.nodeBorder}; + stroke-width: 1; +} + +.classLabel .box { + stroke: none; + stroke-width: 0; + fill: ${e.mainBkg}; + opacity: 0.5; +} + +.classLabel .label { + fill: ${e.nodeBorder}; + font-size: 10px; +} + +.relation { + stroke: ${e.lineColor}; + stroke-width: ${e.strokeWidth}; + fill: none; +} + +.dashed-line{ + stroke-dasharray: 3; +} + +.dotted-line{ + stroke-dasharray: 1 2; +} + +[id$="-compositionStart"], .composition { + fill: ${e.lineColor} !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; +} + +[id$="-compositionEnd"], .composition { + fill: ${e.lineColor} !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; +} + +[id$="-dependencyStart"], .dependency { + fill: ${e.lineColor} !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; +} + +[id$="-dependencyEnd"], .dependency { + fill: ${e.lineColor} !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; +} + +[id$="-extensionStart"], .extension { + fill: transparent !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; +} + +[id$="-extensionEnd"], .extension { + fill: transparent !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; +} + +[id$="-aggregationStart"], .aggregation { + fill: transparent !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; +} + +[id$="-aggregationEnd"], .aggregation { + fill: transparent !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; +} + +[id$="-lollipopStart"], .lollipop { + fill: ${e.mainBkg} !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; +} + +[id$="-lollipopEnd"], .lollipop { + fill: ${e.mainBkg} !important; + stroke: ${e.lineColor} !important; + stroke-width: 1; +} + +.edgeTerminals { + font-size: 11px; + line-height: initial; +} + +.classTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${e.textColor}; +} + +.edgeLabel[data-look="neo"] { + background-color: ${e.edgeLabelBackground}; + p { + background-color: ${e.edgeLabelBackground}; + } + rect { + opacity: 0.5; + background-color: ${e.edgeLabelBackground}; + fill: ${e.edgeLabelBackground}; + } + text-align: center; +} + ${yv()} +`,"getStyles"),fde=hft}),pft,fft,gft,gde,mft=G(()=>{"use strict";Er(),Nt(),l2(),xm(),Tm(),Ar(),pft=s((e,t="TB")=>{if(!e.doc)return t;let r=t;for(let i of e.doc)i.stmt==="dir"&&(r=i.value);return r},"getDir"),fft=s(function(e,t){return t.db.getClasses()},"getClasses"),gft=s(async function(e,t,r,i){Ee.info("REF0:"),Ee.info("Drawing class diagram (v3)",t);let{securityLevel:n,state:a,layout:o}=it();i.db.setDiagramId(t);let l=i.db.getData(),u=bg(t,n);l.type=i.type,l.layoutAlgorithm=mv(o),l.nodeSpacing=a?.nodeSpacing||50,l.rankSpacing=a?.rankSpacing||50,l.markers=["aggregation","extension","composition","dependency","lollipop"],l.diagramId=t,await bm(l,u),ai.insertTitle(u,"classDiagramTitleText",a?.titleTopMargin??25,i.db.getDiagramTitle()),kf(u,8,"classDiagram",a?.useMaxWidth??!0)},"draw"),gde={getClasses:fft,draw:gft,getDir:pft}}),yft={};Mr(yft,{diagram:()=>vft});var vft,LRt=G(()=>{"use strict";cft(),uft(),dft(),mft(),vft={parser:cde,get db(){return new pde},renderer:gde,styles:fde,init:s(e=>{e.class||(e.class={}),e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},"init")}}),bft={};Mr(bft,{diagram:()=>xft});var xft,IRt=G(()=>{"use strict";cft(),uft(),dft(),mft(),xft={parser:cde,get db(){return new pde},renderer:gde,styles:fde,init:s(e=>{e.class||(e.class={}),e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},"init")}}),_I,mde,Tft=G(()=>{"use strict";_I=(function(){var e=s(function(Y,U,H,J){for(H=H||{},J=Y.length;J--;H[Y[J]]=U);return H},"o"),t=[1,2],r=[1,3],i=[1,4],n=[2,4],a=[1,9],o=[1,11],l=[1,16],u=[1,17],h=[1,18],d=[1,19],p=[1,33],f=[1,20],g=[1,21],m=[1,22],y=[1,23],k=[1,24],C=[1,26],S=[1,27],E=[1,28],_=[1,29],b=[1,30],v=[1,31],x=[1,32],w=[1,35],R=[1,36],$=[1,37],M=[1,38],N=[1,34],j=[1,4,5,16,17,19,21,22,24,25,26,27,28,29,33,35,37,38,41,45,48,51,52,53,54,57],I=[1,4,5,14,15,16,17,19,21,22,24,25,26,27,28,29,33,35,37,38,39,40,41,45,48,51,52,53,54,57],z=[4,5,16,17,19,21,22,24,25,26,27,28,29,33,35,37,38,41,45,48,51,52,53,54,57],F={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,SPACE:4,NL:5,SD:6,document:7,line:8,statement:9,classDefStatement:10,styleStatement:11,cssClassStatement:12,idStatement:13,DESCR:14,"-->":15,HIDE_EMPTY:16,scale:17,WIDTH:18,COMPOSIT_STATE:19,STRUCT_START:20,STRUCT_STOP:21,STATE_DESCR:22,AS:23,ID:24,FORK:25,JOIN:26,CHOICE:27,CONCURRENT:28,note:29,notePosition:30,NOTE_TEXT:31,direction:32,acc_title:33,acc_title_value:34,acc_descr:35,acc_descr_value:36,acc_descr_multiline_value:37,CLICK:38,STRING:39,HREF:40,classDef:41,CLASSDEF_ID:42,CLASSDEF_STYLEOPTS:43,DEFAULT:44,style:45,STYLE_IDS:46,STYLEDEF_STYLEOPTS:47,class:48,CLASSENTITY_IDS:49,STYLECLASS:50,direction_tb:51,direction_bt:52,direction_rl:53,direction_lr:54,eol:55,";":56,EDGE_STATE:57,STYLE_SEPARATOR:58,left_of:59,right_of:60,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NL",6:"SD",14:"DESCR",15:"-->",16:"HIDE_EMPTY",17:"scale",18:"WIDTH",19:"COMPOSIT_STATE",20:"STRUCT_START",21:"STRUCT_STOP",22:"STATE_DESCR",23:"AS",24:"ID",25:"FORK",26:"JOIN",27:"CHOICE",28:"CONCURRENT",29:"note",31:"NOTE_TEXT",33:"acc_title",34:"acc_title_value",35:"acc_descr",36:"acc_descr_value",37:"acc_descr_multiline_value",38:"CLICK",39:"STRING",40:"HREF",41:"classDef",42:"CLASSDEF_ID",43:"CLASSDEF_STYLEOPTS",44:"DEFAULT",45:"style",46:"STYLE_IDS",47:"STYLEDEF_STYLEOPTS",48:"class",49:"CLASSENTITY_IDS",50:"STYLECLASS",51:"direction_tb",52:"direction_bt",53:"direction_rl",54:"direction_lr",56:";",57:"EDGE_STATE",58:"STYLE_SEPARATOR",59:"left_of",60:"right_of"},productions_:[0,[3,2],[3,2],[3,2],[7,0],[7,2],[8,2],[8,1],[8,1],[9,1],[9,1],[9,1],[9,1],[9,2],[9,3],[9,4],[9,1],[9,2],[9,1],[9,4],[9,3],[9,6],[9,1],[9,1],[9,1],[9,1],[9,4],[9,4],[9,1],[9,2],[9,2],[9,1],[9,5],[9,5],[10,3],[10,3],[11,3],[12,3],[32,1],[32,1],[32,1],[32,1],[55,1],[55,1],[13,1],[13,1],[13,3],[13,3],[30,1],[30,1]],performAction:s(function(Y,U,H,J,te,ie,ue){var se=ie.length-1;switch(te){case 3:return J.setRootDoc(ie[se]),ie[se];case 4:this.$=[];break;case 5:ie[se]!="nl"&&(ie[se-1].push(ie[se]),this.$=ie[se-1]);break;case 6:case 7:this.$=ie[se];break;case 8:this.$="nl";break;case 12:this.$=ie[se];break;case 13:let fe=ie[se-1];fe.description=J.trimColon(ie[se]),this.$=fe;break;case 14:this.$={stmt:"relation",state1:ie[se-2],state2:ie[se]};break;case 15:let ge=J.trimColon(ie[se]);this.$={stmt:"relation",state1:ie[se-3],state2:ie[se-1],description:ge};break;case 19:this.$={stmt:"state",id:ie[se-3],type:"default",description:"",doc:ie[se-1]};break;case 20:var le=ie[se],Te=ie[se-2].trim();if(ie[se].match(":")){var oe=ie[se].split(":");le=oe[0],Te=[Te,oe[1]]}this.$={stmt:"state",id:le,type:"default",description:Te};break;case 21:this.$={stmt:"state",id:ie[se-3],type:"default",description:ie[se-5],doc:ie[se-1]};break;case 22:this.$={stmt:"state",id:ie[se],type:"fork"};break;case 23:this.$={stmt:"state",id:ie[se],type:"join"};break;case 24:this.$={stmt:"state",id:ie[se],type:"choice"};break;case 25:this.$={stmt:"state",id:J.getDividerId(),type:"divider"};break;case 26:this.$={stmt:"state",id:ie[se-1].trim(),note:{position:ie[se-2].trim(),text:ie[se].trim()}};break;case 29:this.$=ie[se].trim(),J.setAccTitle(this.$);break;case 30:case 31:this.$=ie[se].trim(),J.setAccDescription(this.$);break;case 32:this.$={stmt:"click",id:ie[se-3],url:ie[se-2],tooltip:ie[se-1]};break;case 33:this.$={stmt:"click",id:ie[se-3],url:ie[se-1],tooltip:""};break;case 34:case 35:this.$={stmt:"classDef",id:ie[se-1].trim(),classes:ie[se].trim()};break;case 36:this.$={stmt:"style",id:ie[se-1].trim(),styleClass:ie[se].trim()};break;case 37:this.$={stmt:"applyClass",id:ie[se-1].trim(),styleClass:ie[se].trim()};break;case 38:J.setDirection("TB"),this.$={stmt:"dir",value:"TB"};break;case 39:J.setDirection("BT"),this.$={stmt:"dir",value:"BT"};break;case 40:J.setDirection("RL"),this.$={stmt:"dir",value:"RL"};break;case 41:J.setDirection("LR"),this.$={stmt:"dir",value:"LR"};break;case 44:case 45:this.$={stmt:"state",id:ie[se].trim(),type:"default",description:""};break;case 46:this.$={stmt:"state",id:ie[se-2].trim(),classes:[ie[se].trim()],type:"default",description:""};break;case 47:this.$={stmt:"state",id:ie[se-2].trim(),classes:[ie[se].trim()],type:"default",description:""};break}},"anonymous"),table:[{3:1,4:t,5:r,6:i},{1:[3]},{3:5,4:t,5:r,6:i},{3:6,4:t,5:r,6:i},e([1,4,5,16,17,19,22,24,25,26,27,28,29,33,35,37,38,41,45,48,51,52,53,54,57],n,{7:7}),{1:[2,1]},{1:[2,2]},{1:[2,3],4:a,5:o,8:8,9:10,10:12,11:13,12:14,13:15,16:l,17:u,19:h,22:d,24:p,25:f,26:g,27:m,28:y,29:k,32:25,33:C,35:S,37:E,38:_,41:b,45:v,48:x,51:w,52:R,53:$,54:M,57:N},e(j,[2,5]),{9:39,10:12,11:13,12:14,13:15,16:l,17:u,19:h,22:d,24:p,25:f,26:g,27:m,28:y,29:k,32:25,33:C,35:S,37:E,38:_,41:b,45:v,48:x,51:w,52:R,53:$,54:M,57:N},e(j,[2,7]),e(j,[2,8]),e(j,[2,9]),e(j,[2,10]),e(j,[2,11]),e(j,[2,12],{14:[1,40],15:[1,41]}),e(j,[2,16]),{18:[1,42]},e(j,[2,18],{20:[1,43]}),{23:[1,44]},e(j,[2,22]),e(j,[2,23]),e(j,[2,24]),e(j,[2,25]),{30:45,31:[1,46],59:[1,47],60:[1,48]},e(j,[2,28]),{34:[1,49]},{36:[1,50]},e(j,[2,31]),{13:51,24:p,57:N},{42:[1,52],44:[1,53]},{46:[1,54]},{49:[1,55]},e(I,[2,44],{58:[1,56]}),e(I,[2,45],{58:[1,57]}),e(j,[2,38]),e(j,[2,39]),e(j,[2,40]),e(j,[2,41]),e(j,[2,6]),e(j,[2,13]),{13:58,24:p,57:N},e(j,[2,17]),e(z,n,{7:59}),{24:[1,60]},{24:[1,61]},{23:[1,62]},{24:[2,48]},{24:[2,49]},e(j,[2,29]),e(j,[2,30]),{39:[1,63],40:[1,64]},{43:[1,65]},{43:[1,66]},{47:[1,67]},{50:[1,68]},{24:[1,69]},{24:[1,70]},e(j,[2,14],{14:[1,71]}),{4:a,5:o,8:8,9:10,10:12,11:13,12:14,13:15,16:l,17:u,19:h,21:[1,72],22:d,24:p,25:f,26:g,27:m,28:y,29:k,32:25,33:C,35:S,37:E,38:_,41:b,45:v,48:x,51:w,52:R,53:$,54:M,57:N},e(j,[2,20],{20:[1,73]}),{31:[1,74]},{24:[1,75]},{39:[1,76]},{39:[1,77]},e(j,[2,34]),e(j,[2,35]),e(j,[2,36]),e(j,[2,37]),e(I,[2,46]),e(I,[2,47]),e(j,[2,15]),e(j,[2,19]),e(z,n,{7:78}),e(j,[2,26]),e(j,[2,27]),{5:[1,79]},{5:[1,80]},{4:a,5:o,8:8,9:10,10:12,11:13,12:14,13:15,16:l,17:u,19:h,21:[1,81],22:d,24:p,25:f,26:g,27:m,28:y,29:k,32:25,33:C,35:S,37:E,38:_,41:b,45:v,48:x,51:w,52:R,53:$,54:M,57:N},e(j,[2,32]),e(j,[2,33]),e(j,[2,21])],defaultActions:{5:[2,1],6:[2,2],47:[2,48],48:[2,49]},parseError:s(function(Y,U){if(U.recoverable)this.trace(Y);else{var H=new Error(Y);throw H.hash=U,H}},"parseError"),parse:s(function(Y){var U=this,H=[0],J=[],te=[null],ie=[],ue=this.table,se="",le=0,Te=0,oe=0,fe=2,ge=1,ke=ie.slice.call(arguments,1),Ne=Object.create(this.lexer),xe={yy:{}};for(var $e in this.yy)Object.prototype.hasOwnProperty.call(this.yy,$e)&&(xe.yy[$e]=this.yy[$e]);Ne.setInput(Y,xe.yy),xe.yy.lexer=Ne,xe.yy.parser=this,typeof Ne.yylloc>"u"&&(Ne.yylloc={});var Me=Ne.yylloc;ie.push(Me);var He=Ne.options&&Ne.options.ranges;typeof xe.yy.parseError=="function"?this.parseError=xe.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function Ge(ee){H.length=H.length-2*ee,te.length=te.length-ee,ie.length=ie.length-ee}s(Ge,"popStack");function ze(){var ee;return ee=J.pop()||Ne.lex()||ge,typeof ee!="number"&&(ee instanceof Array&&(J=ee,ee=J.pop()),ee=U.symbols_[ee]||ee),ee}s(ze,"lex");for(var Ve,et,he,ye,be,Ie,pe={},Qe,W,Ae,K;;){if(he=H[H.length-1],this.defaultActions[he]?ye=this.defaultActions[he]:((Ve===null||typeof Ve>"u")&&(Ve=ze()),ye=ue[he]&&ue[he][Ve]),typeof ye>"u"||!ye.length||!ye[0]){var de="";K=[];for(Qe in ue[he])this.terminals_[Qe]&&Qe>fe&&K.push("'"+this.terminals_[Qe]+"'");Ne.showPosition?de="Parse error on line "+(le+1)+`: +`+Ne.showPosition()+` +Expecting `+K.join(", ")+", got '"+(this.terminals_[Ve]||Ve)+"'":de="Parse error on line "+(le+1)+": Unexpected "+(Ve==ge?"end of input":"'"+(this.terminals_[Ve]||Ve)+"'"),this.parseError(de,{text:Ne.match,token:this.terminals_[Ve]||Ve,line:Ne.yylineno,loc:Me,expected:K})}if(ye[0]instanceof Array&&ye.length>1)throw new Error("Parse Error: multiple actions possible at state: "+he+", token: "+Ve);switch(ye[0]){case 1:H.push(Ve),te.push(Ne.yytext),ie.push(Ne.yylloc),H.push(ye[1]),Ve=null,et?(Ve=et,et=null):(Te=Ne.yyleng,se=Ne.yytext,le=Ne.yylineno,Me=Ne.yylloc,oe>0&&oe--);break;case 2:if(W=this.productions_[ye[1]][1],pe.$=te[te.length-W],pe._$={first_line:ie[ie.length-(W||1)].first_line,last_line:ie[ie.length-1].last_line,first_column:ie[ie.length-(W||1)].first_column,last_column:ie[ie.length-1].last_column},He&&(pe._$.range=[ie[ie.length-(W||1)].range[0],ie[ie.length-1].range[1]]),Ie=this.performAction.apply(pe,[se,Te,le,xe.yy,ye[1],te,ie].concat(ke)),typeof Ie<"u")return Ie;W&&(H=H.slice(0,-1*W*2),te=te.slice(0,-1*W),ie=ie.slice(0,-1*W)),H.push(this.productions_[ye[1]][0]),te.push(pe.$),ie.push(pe._$),Ae=ue[H[H.length-2]][H[H.length-1]],H.push(Ae);break;case 3:return!0}}return!0},"parse")},O=(function(){var Y={EOF:1,parseError:s(function(U,H){if(this.yy.parser)this.yy.parser.parseError(U,H);else throw new Error(U)},"parseError"),setInput:s(function(U,H){return this.yy=H||this.yy||{},this._input=U,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var U=this._input[0];this.yytext+=U,this.yyleng++,this.offset++,this.match+=U,this.matched+=U;var H=U.match(/(?:\r\n?|\n).*/g);return H?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),U},"input"),unput:s(function(U){var H=U.length,J=U.split(/(?:\r\n?|\n)/g);this._input=U+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-H),this.offset-=H;var te=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),J.length-1&&(this.yylineno-=J.length-1);var ie=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:J?(J.length===te.length?this.yylloc.first_column:0)+te[te.length-J.length].length-J[0].length:this.yylloc.first_column-H},this.options.ranges&&(this.yylloc.range=[ie[0],ie[0]+this.yyleng-H]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(U){this.unput(this.match.slice(U))},"less"),pastInput:s(function(){var U=this.matched.substr(0,this.matched.length-this.match.length);return(U.length>20?"...":"")+U.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var U=this.match;return U.length<20&&(U+=this._input.substr(0,20-U.length)),(U.substr(0,20)+(U.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var U=this.pastInput(),H=new Array(U.length+1).join("-");return U+this.upcomingInput()+` +`+H+"^"},"showPosition"),test_match:s(function(U,H){var J,te,ie;if(this.options.backtrack_lexer&&(ie={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(ie.yylloc.range=this.yylloc.range.slice(0))),te=U[0].match(/(?:\r\n?|\n).*/g),te&&(this.yylineno+=te.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:te?te[te.length-1].length-te[te.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+U[0].length},this.yytext+=U[0],this.match+=U[0],this.matches=U,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(U[0].length),this.matched+=U[0],J=this.performAction.call(this,this.yy,this,H,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),J)return J;if(this._backtrack){for(var ue in ie)this[ue]=ie[ue];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var U,H,J,te;this._more||(this.yytext="",this.match="");for(var ie=this._currentRules(),ue=0;ueH[0].length)){if(H=J,te=ue,this.options.backtrack_lexer){if(U=this.test_match(J,ie[ue]),U!==!1)return U;if(this._backtrack){H=!1;continue}else return!1}else if(!this.options.flex)break}return H?(U=this.test_match(H,ie[te]),U!==!1?U:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var U=this.next();return U||this.lex()},"lex"),begin:s(function(U){this.conditionStack.push(U)},"begin"),popState:s(function(){var U=this.conditionStack.length-1;return U>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(U){return U=this.conditionStack.length-1-Math.abs(U||0),U>=0?this.conditionStack[U]:"INITIAL"},"topState"),pushState:s(function(U){this.begin(U)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(U,H,J,te){function ie(){let se=H.yytext.indexOf("%%");if(se===0)return!1;if(se>0){let le=H.yytext.slice(0,se),Te=H.yytext.slice(se);Te&&U.lexer.unput(Te),H.yytext=le}return!0}s(ie,"processId");var ue=te;switch(J){case 0:return 38;case 1:return 40;case 2:return 39;case 3:return 44;case 4:return 51;case 5:return 52;case 6:return 53;case 7:return 54;case 8:return 5;case 9:break;case 10:break;case 11:break;case 12:break;case 13:return this.pushState("SCALE"),17;case 14:return 18;case 15:this.popState();break;case 16:return this.begin("acc_title"),33;case 17:return this.popState(),"acc_title_value";case 18:return this.begin("acc_descr"),35;case 19:return this.popState(),"acc_descr_value";case 20:this.begin("acc_descr_multiline");break;case 21:this.popState();break;case 22:return"acc_descr_multiline_value";case 23:return this.pushState("CLASSDEF"),41;case 24:return this.popState(),this.pushState("CLASSDEFID"),"DEFAULT_CLASSDEF_ID";case 25:return this.popState(),this.pushState("CLASSDEFID"),42;case 26:return this.popState(),43;case 27:return this.pushState("CLASS"),48;case 28:return this.popState(),this.pushState("CLASS_STYLE"),49;case 29:return this.popState(),50;case 30:return this.pushState("STYLE"),45;case 31:return this.popState(),this.pushState("STYLEDEF_STYLES"),46;case 32:return this.popState(),47;case 33:return this.pushState("SCALE"),17;case 34:return 18;case 35:this.popState();break;case 36:this.pushState("STATE");break;case 37:return this.popState(),H.yytext=H.yytext.slice(0,-8).trim(),25;case 38:return this.popState(),H.yytext=H.yytext.slice(0,-8).trim(),26;case 39:return this.popState(),H.yytext=H.yytext.slice(0,-10).trim(),27;case 40:return this.popState(),H.yytext=H.yytext.slice(0,-8).trim(),25;case 41:return this.popState(),H.yytext=H.yytext.slice(0,-8).trim(),26;case 42:return this.popState(),H.yytext=H.yytext.slice(0,-10).trim(),27;case 43:return 51;case 44:return 52;case 45:return 53;case 46:return 54;case 47:this.pushState("STATE_STRING");break;case 48:return this.pushState("STATE_ID"),"AS";case 49:return ie()?(this.popState(),"ID"):void 0;case 50:this.popState();break;case 51:return"STATE_DESCR";case 52:throw new Error('Error: State name must be a single word. Found: "'+H.yytext.trim()+'"');case 53:return 19;case 54:this.popState();break;case 55:return this.popState(),this.pushState("struct"),20;case 56:return this.popState(),21;case 57:break;case 58:return this.begin("NOTE"),29;case 59:return this.popState(),this.pushState("NOTE_ID"),59;case 60:return this.popState(),this.pushState("NOTE_ID"),60;case 61:this.popState(),this.pushState("FLOATING_NOTE");break;case 62:return this.popState(),this.pushState("FLOATING_NOTE_ID"),"AS";case 63:break;case 64:return"NOTE_TEXT";case 65:return ie()?(this.popState(),"ID"):void 0;case 66:return ie()?(this.popState(),this.pushState("NOTE_TEXT"),24):void 0;case 67:return this.popState(),H.yytext=H.yytext.substr(2).trim(),31;case 68:return this.popState(),H.yytext=H.yytext.slice(0,-8).trim(),31;case 69:return 6;case 70:return 6;case 71:return 16;case 72:return 57;case 73:return ie()?24:void 0;case 74:return H.yytext=H.yytext.trim(),14;case 75:return 15;case 76:return 28;case 77:return 58;case 78:return 5;case 79:return"INVALID"}},"anonymous"),rules:[/^(?:click\b)/i,/^(?:href\b)/i,/^(?:"[^"]*")/i,/^(?:default\b)/i,/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:[\n]+)/i,/^(?:[\s]+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:scale\s+)/i,/^(?:\d+)/i,/^(?:\s+width\b)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:classDef\s+)/i,/^(?:DEFAULT\s+)/i,/^(?:\w+\s+)/i,/^(?:[^\n]*)/i,/^(?:class\s+)/i,/^(?:(\w+)+((,\s*\w+)*))/i,/^(?:[^\n]*)/i,/^(?:style\s+)/i,/^(?:[\w,]+\s+)/i,/^(?:[^\n]*)/i,/^(?:scale\s+)/i,/^(?:\d+)/i,/^(?:\s+width\b)/i,/^(?:state\s+)/i,/^(?:.*<>)/i,/^(?:.*<>)/i,/^(?:.*<>)/i,/^(?:.*\[\[fork\]\])/i,/^(?:.*\[\[join\]\])/i,/^(?:.*\[\[choice\]\])/i,/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:["])/i,/^(?:\s*as\s+)/i,/^(?:[^\n\{]*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:\w+\s+\w+.*?\{)/i,/^(?:[^\n\s\{]+)/i,/^(?:\n)/i,/^(?:\{)/i,/^(?:\})/i,/^(?:[\n])/i,/^(?:note\s+)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:")/i,/^(?:\s*as\s*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n]*)/i,/^(?:\s*[^:\n\s\-]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:[\s\S]*?\n\s*end note\b)/i,/^(?:stateDiagram\s+)/i,/^(?:stateDiagram-v2\s+)/i,/^(?:hide empty description\b)/i,/^(?:\[\*\])/i,/^(?:[^:\n\s\-\{]+)/i,/^(?:\s*:(?:[^:\n;]|:[^:\n;])+)/i,/^(?:-->)/i,/^(?:--)/i,/^(?::::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{LINE:{rules:[10,11,12],inclusive:!1},struct:{rules:[10,11,12,23,27,30,36,43,44,45,46,56,57,58,72,73,74,75,76,77],inclusive:!1},FLOATING_NOTE_ID:{rules:[65],inclusive:!1},FLOATING_NOTE:{rules:[62,63,64],inclusive:!1},NOTE_TEXT:{rules:[67,68],inclusive:!1},NOTE_ID:{rules:[66],inclusive:!1},NOTE:{rules:[59,60,61],inclusive:!1},STYLEDEF_STYLEOPTS:{rules:[],inclusive:!1},STYLEDEF_STYLES:{rules:[32],inclusive:!1},STYLE_IDS:{rules:[],inclusive:!1},STYLE:{rules:[31],inclusive:!1},CLASS_STYLE:{rules:[29],inclusive:!1},CLASS:{rules:[28],inclusive:!1},CLASSDEFID:{rules:[26],inclusive:!1},CLASSDEF:{rules:[24,25],inclusive:!1},acc_descr_multiline:{rules:[21,22],inclusive:!1},acc_descr:{rules:[19],inclusive:!1},acc_title:{rules:[17],inclusive:!1},SCALE:{rules:[14,15,34,35],inclusive:!1},ALIAS:{rules:[],inclusive:!1},STATE_ID:{rules:[49],inclusive:!1},STATE_STRING:{rules:[50,51],inclusive:!1},FORK_STATE:{rules:[],inclusive:!1},STATE:{rules:[10,11,12,37,38,39,40,41,42,47,48,52,53,54,55],inclusive:!1},ID:{rules:[10,11,12],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,11,12,13,16,18,20,23,27,30,33,36,55,58,69,70,71,72,73,74,75,77,78,79],inclusive:!0}}};return Y})();F.lexer=O;function B(){this.yy={}}return s(B,"Parser"),B.prototype=F,F.Parser=B,new B})(),_I.parser=_I,mde=_I}),L1,I1,AI,kft,Cft,wft,Xb,yde,vde,bde,xde,Tde,RI,$I,Eft,Sft,kde,Cde,_ft,Aft,Kb,Rft,$ft,wde,Lft,Ift,Mft,Dft,Nft,Oft,Bft,Pft,Ede,Sde,Fft,LI,zft,_de,Ade=G(()=>{"use strict";L1="state",I1="root",AI="relation",kft="classDef",Cft="style",wft="applyClass",Xb="default",yde="divider",vde="fill:none",bde="fill: #333",xde="markdown",Tde="normal",RI="rect",$I="rectWithTitle",Eft="stateStart",Sft="stateEnd",kde="divider",Cde="roundedWithTitle",_ft="note",Aft="noteGroup",Kb="statediagram",Rft="state",$ft=`${Kb}-${Rft}`,wde="transition",Lft="note",Ift="note-edge",Mft=`${wde} ${Ift}`,Dft=`${Kb}-${Lft}`,Nft="cluster",Oft=`${Kb}-${Nft}`,Bft="cluster-alt",Pft=`${Kb}-${Bft}`,Ede="parent",Sde="note",Fft="state",LI="----",zft=`${LI}${Sde}`,_de=`${LI}${Ede}`});function II(e="",t=0,r="",i=LI){let n=r!==null&&r.length>0?`${i}${r}`:"";return`${Fft}-${e}${n}-${t}`}function M5(e,t,r){if(!t.id||t.id===""||t.id==="")return;t.cssClasses&&(Array.isArray(t.cssCompiledStyles)||(t.cssCompiledStyles=[]),t.cssClasses.split(" ").forEach(n=>{let a=r.get(n);a&&(t.cssCompiledStyles=[...t.cssCompiledStyles??[],...a.styles])}));let i=e.find(n=>n.id===t.id);i?Object.assign(i,t):e.push(t)}function qft(e){return e?.classes?.join(" ")??""}function Wft(e){return e?.styles??[]}var D5,lp,jft,Rde,Qb,Vft,MRt=G(()=>{"use strict";Er(),Nt(),mi(),Ade(),D5=new Map,lp=0,s(II,"stateDomId"),jft=s((e,t,r,i,n,a,o,l)=>{Ee.trace("items",t),t.forEach(u=>{switch(u.stmt){case L1:Qb(e,u,r,i,n,a,o,l);break;case Xb:Qb(e,u,r,i,n,a,o,l);break;case AI:{Qb(e,u.state1,r,i,n,a,o,l),Qb(e,u.state2,r,i,n,a,o,l);let h=o==="neo",d={id:"edge"+lp,start:u.state1.id,end:u.state2.id,arrowhead:"normal",arrowTypeEnd:h?"arrow_barb_neo":"arrow_barb",style:vde,labelStyle:"",label:Vt.sanitizeText(u.description??"",it()),arrowheadStyle:bde,labelpos:"c",labelType:xde,thickness:Tde,classes:wde,look:o};n.push(d),lp++}break}})},"setupDoc"),Rde=s((e,t="TB")=>{let r=t;if(e.doc)for(let i of e.doc)i.stmt==="dir"&&(r=i.value);return r},"getDir"),s(M5,"insertOrUpdateNode"),s(qft,"getClassesFromDbInfo"),s(Wft,"getStylesFromDbInfo"),Qb=s((e,t,r,i,n,a,o,l)=>{let u=t.id,h=r.get(u),d=qft(h),p=Wft(h),f=it();if(Ee.info("dataFetcher parsedItem",t,h,p),u!=="root"){let g=RI;t.start===!0?g=Eft:t.start===!1&&(g=Sft),t.type!==Xb&&(g=t.type),D5.get(u)||D5.set(u,{id:u,shape:g,description:Vt.sanitizeText(u,f),cssClasses:`${d} ${$ft}`,cssStyles:p});let m=D5.get(u);t.description&&(Array.isArray(m.description)?(m.shape=$I,m.description.push(t.description)):m.description?.length&&m.description.length>0?(m.shape=$I,m.description===u?m.description=[t.description]:m.description=[m.description,t.description]):(m.shape=RI,m.description=t.description),m.description=Vt.sanitizeTextOrArray(m.description,f)),m.description?.length===1&&m.shape===$I&&(m.type==="group"?m.shape=Cde:m.shape=RI),!m.type&&t.doc&&(Ee.info("Setting cluster for XCX",u,Rde(t)),m.type="group",m.isGroup=!0,m.dir=Rde(t),m.shape=t.type===yde?kde:Cde,m.cssClasses=`${m.cssClasses} ${Oft} ${a?Pft:""}`);let y={labelStyle:"",shape:m.shape,label:m.description,cssClasses:m.cssClasses,cssCompiledStyles:[],cssStyles:m.cssStyles,id:u,dir:m.dir,domId:II(u,lp),type:m.type,isGroup:m.type==="group",padding:8,rx:10,ry:10,look:o,labelType:"markdown"};if(y.shape===kde&&(y.label=""),e&&e.id!=="root"&&(Ee.trace("Setting node ",u," to be child of its parent ",e.id),y.parentId=e.id),y.centerLabel=!0,t.note){let k={labelStyle:"",shape:_ft,label:t.note.text,labelType:"markdown",cssClasses:Dft,cssStyles:[],cssCompiledStyles:[],id:u+zft+"-"+lp,domId:II(u,lp,Sde),type:m.type,isGroup:m.type==="group",padding:f.flowchart?.padding,look:o,position:t.note.position},C=u+_de,S={labelStyle:"",shape:Aft,label:t.note.text,cssClasses:m.cssClasses,cssStyles:[],id:u+_de,domId:II(u,lp,Ede),type:"group",isGroup:!0,padding:16,look:o,position:t.note.position};lp++,S.id=C,k.parentId=C,M5(i,S,l),M5(i,k,l),M5(i,y,l);let E=u,_=k.id;t.note.position==="left of"&&(E=k.id,_=u),n.push({id:E+"-"+_,start:E,end:_,arrowhead:"none",arrowTypeEnd:"",style:vde,labelStyle:"",classes:Mft,arrowheadStyle:bde,labelpos:"c",labelType:xde,thickness:Tde,look:o})}else M5(i,y,l)}t.doc&&(Ee.trace("Adding nodes children "),jft(t,t.doc,r,i,n,!a,o,l))},"dataFetcher"),Vft=s(()=>{D5.clear(),lp=0},"reset")}),$de,Uft,Yft,Hft,Gft=G(()=>{"use strict";Er(),Nt(),l2(),xm(),Tm(),Ar(),Ade(),$de=s((e,t="TB")=>{if(!e.doc)return t;let r=t;for(let i of e.doc)i.stmt==="dir"&&(r=i.value);return r},"getDir"),Uft=s(function(e,t){return t.db.getClasses()},"getClasses"),Yft=s(async function(e,t,r,i){Ee.info("REF0:"),Ee.info("Drawing state diagram (v2)",t);let{securityLevel:n,state:a,layout:o}=it();i.db.extract(i.db.getRootDocV2());let l=i.db.getData(),u=bg(t,n);l.type=i.type,l.layoutAlgorithm=o,l.nodeSpacing=a?.nodeSpacing||50,l.rankSpacing=a?.rankSpacing||50,it().look==="neo"?l.markers=["barbNeo"]:l.markers=["barb"],l.diagramId=t,await bm(l,u);let h=8;try{(typeof i.db.getLinks=="function"?i.db.getLinks():new Map).forEach((d,p)=>{let f=typeof p=="string"?p:typeof p?.id=="string"?p.id:"",g=l.nodes.find(E=>E.id===f);if(!f){Ee.warn("⚠️ Invalid or missing stateId from key:",JSON.stringify(p));return}let m=u.node()?.querySelectorAll("g.node, g.rough-node"),y;if(m?.forEach(E=>{let _=E.textContent?.trim();(E.id===g?.domId||_===f)&&(y=E)}),!y){Ee.warn("⚠️ Could not find node matching text:",f);return}let k=y.parentNode;if(!k){Ee.warn("⚠️ Node has no parent, cannot wrap:",f);return}let C=document.createElementNS("http://www.w3.org/2000/svg","a"),S=d.url.replace(/^"+|"+$/g,"");if(C.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",S),C.setAttribute("target","_blank"),d.tooltip){let E=d.tooltip.replace(/^"+|"+$/g,"");C.setAttribute("title",E),y.setAttribute("title",E)}k.replaceChild(C,y),C.appendChild(y),Ee.info("🔗 Wrapped node in
    tag for:",f,d.url)})}catch(d){Ee.error("❌ Error injecting clickable links:",d)}ai.insertTitle(u,"statediagramTitleText",a?.titleTopMargin??25,i.db.getDiagramTitle()),kf(u,h,Kb,a?.useMaxWidth??!0)},"draw"),Hft={getClasses:Uft,draw:Yft,getDir:$de}}),So,Lde,Ide,N5,$0,Mde=G(()=>{"use strict";var e;pi(),zx(),Er(),Nt(),Ar(),mi(),fn(),pg(),MRt(),Gft(),Ade(),So={START_NODE:"[*]",START_TYPE:"start",END_NODE:"[*]",END_TYPE:"end",COLOR_KEYWORD:"color",FILL_KEYWORD:"fill",BG_FILL:"bgFill",STYLECLASS_SEP:","},Lde=s(()=>new Map,"newClassesList"),Ide=s(()=>({relations:[],states:new Map,documents:{}}),"newDoc"),N5=s(t=>JSON.parse(JSON.stringify(t)),"clone"),$0=(e=class{constructor(r){this.version=r,this.nodes=[],this.edges=[],this.rootDoc=[],this.classes=Lde(),this.documents={root:Ide()},this.currentDocument=this.documents.root,this.startEndCount=0,this.dividerCnt=0,this.links=new Map,this.funs=[],this.getAccTitle=Ln,this.setAccTitle=bn,this.getAccDescription=Mn,this.setAccDescription=In,this.setDiagramTitle=Dn,this.getDiagramTitle=xn,this.clear(),this.setRootDoc=this.setRootDoc.bind(this),this.getDividerId=this.getDividerId.bind(this),this.setDirection=this.setDirection.bind(this),this.trimColon=this.trimColon.bind(this),this.bindFunctions=this.bindFunctions.bind(this)}extract(r){this.clear(!0);for(let a of Array.isArray(r)?r:r.doc)switch(a.stmt){case L1:this.addState(a.id.trim(),a.type,a.doc,a.description,a.note);break;case AI:this.addRelation(a.state1,a.state2,a.description);break;case kft:this.addStyleClass(a.id.trim(),a.classes);break;case Cft:this.handleStyleDef(a);break;case wft:this.setCssClass(a.id.trim(),a.styleClass);break;case"click":this.addLink(a.id,a.url,a.tooltip);break}let i=this.getStates(),n=it();Vft(),Qb(void 0,this.getRootDocV2(),i,this.nodes,this.edges,!0,n.look,this.classes);for(let a of this.nodes)if(Array.isArray(a.label)){if(a.description=a.label.slice(1),a.isGroup&&a.description.length>0)throw new Error(`Group nodes can only have label. Remove the additional description for node [${a.id}]`);a.label=a.label[0]}}handleStyleDef(r){let i=r.id.trim().split(","),n=r.styleClass.split(",");for(let a of i){let o=this.getState(a);if(!o){let l=a.trim();this.addState(l),o=this.getState(l)}o&&(o.styles=n.map(l=>l.replace(/;/g,"")?.trim()))}}setRootDoc(r){Ee.info("Setting root doc",r),this.rootDoc=r,this.version===1?this.extract(r):this.extract(this.getRootDocV2())}docTranslator(r,i,n){if(i.stmt===AI){this.docTranslator(r,i.state1,!0),this.docTranslator(r,i.state2,!1);return}if(i.stmt===L1&&(i.id===So.START_NODE?(i.id=r.id+(n?"_start":"_end"),i.start=n):i.id=i.id.trim()),i.stmt!==I1&&i.stmt!==L1||!i.doc)return;let a=[],o=[];for(let l of i.doc)if(l.type===yde){let u=N5(l);u.doc=N5(o),a.push(u),o=[]}else o.push(l);if(a.length>0&&o.length>0){let l={stmt:L1,id:VF(),type:"divider",doc:N5(o)};a.push(N5(l)),i.doc=a}i.doc.forEach(l=>this.docTranslator(i,l,!0))}getRootDocV2(){return this.docTranslator({id:I1,stmt:I1},{id:I1,stmt:I1,doc:this.rootDoc},!0),{id:I1,doc:this.rootDoc}}addState(r,i=Xb,n=void 0,a=void 0,o=void 0,l=void 0,u=void 0,h=void 0){let d=r?.trim();if(!this.currentDocument.states.has(d))Ee.info("Adding state ",d,a),this.currentDocument.states.set(d,{stmt:L1,id:d,descriptions:[],type:i,doc:n,note:o,classes:[],styles:[],textStyles:[]});else{let p=this.currentDocument.states.get(d);if(!p)throw new Error(`State not found: ${d}`);p.doc||(p.doc=n),p.type||(p.type=i)}if(a&&(Ee.info("Setting state description",d,a),(Array.isArray(a)?a:[a]).forEach(p=>this.addDescription(d,p.trim()))),o){let p=this.currentDocument.states.get(d);if(!p)throw new Error(`State not found: ${d}`);p.note=o,p.note.text=Vt.sanitizeText(p.note.text,it())}l&&(Ee.info("Setting state classes",d,l),(Array.isArray(l)?l:[l]).forEach(p=>this.setCssClass(d,p.trim()))),u&&(Ee.info("Setting state styles",d,u),(Array.isArray(u)?u:[u]).forEach(p=>this.setStyle(d,p.trim()))),h&&(Ee.info("Setting state styles",d,u),(Array.isArray(h)?h:[h]).forEach(p=>this.setTextStyle(d,p.trim())))}clear(r){this.nodes=[],this.edges=[],this.funs=[this.setupToolTips.bind(this)],this.documents={root:Ide()},this.currentDocument=this.documents.root,this.startEndCount=0,this.classes=Lde(),r||(this.links=new Map,pn())}getState(r){return this.currentDocument.states.get(r)}getStates(){return this.currentDocument.states}logDocuments(){Ee.info("Documents = ",this.documents)}getRelations(){return this.currentDocument.relations}addLink(r,i,n){this.links.set(r,{url:i,tooltip:n}),Ee.warn("Adding link",r,i,n)}getLinks(){return this.links}startIdIfNeeded(r=""){return r===So.START_NODE?(this.startEndCount++,`${So.START_TYPE}${this.startEndCount}`):r}startTypeIfNeeded(r="",i=Xb){return r===So.START_NODE?So.START_TYPE:i}endIdIfNeeded(r=""){return r===So.END_NODE?(this.startEndCount++,`${So.END_TYPE}${this.startEndCount}`):r}endTypeIfNeeded(r="",i=Xb){return r===So.END_NODE?So.END_TYPE:i}addRelationObjs(r,i,n=""){let a=this.startIdIfNeeded(r.id.trim()),o=this.startTypeIfNeeded(r.id.trim(),r.type),l=this.startIdIfNeeded(i.id.trim()),u=this.startTypeIfNeeded(i.id.trim(),i.type);this.addState(a,o,r.doc,r.description,r.note,r.classes,r.styles,r.textStyles),this.addState(l,u,i.doc,i.description,i.note,i.classes,i.styles,i.textStyles),this.currentDocument.relations.push({id1:a,id2:l,relationTitle:Vt.sanitizeText(n,it())})}addRelation(r,i,n){if(typeof r=="object"&&typeof i=="object")this.addRelationObjs(r,i,n);else if(typeof r=="string"&&typeof i=="string"){let a=this.startIdIfNeeded(r.trim()),o=this.startTypeIfNeeded(r),l=this.endIdIfNeeded(i.trim()),u=this.endTypeIfNeeded(i);this.addState(a,o),this.addState(l,u),this.currentDocument.relations.push({id1:a,id2:l,relationTitle:n?Vt.sanitizeText(n,it()):void 0})}}addDescription(r,i){let n=this.currentDocument.states.get(r),a=i.startsWith(":")?i.replace(":","").trim():i;n?.descriptions?.push(Vt.sanitizeText(a,it()))}cleanupLabel(r){return r.startsWith(":")?r.slice(2).trim():r.trim()}getDividerId(){return this.dividerCnt++,`divider-id-${this.dividerCnt}`}addStyleClass(r,i=""){this.classes.has(r)||this.classes.set(r,{id:r,styles:[],textStyles:[]});let n=this.classes.get(r);i&&n&&i.split(So.STYLECLASS_SEP).forEach(a=>{let o=a.replace(/([^;]*);/,"$1").trim();if(RegExp(So.COLOR_KEYWORD).exec(a)){let l=o.replace(So.FILL_KEYWORD,So.BG_FILL).replace(So.COLOR_KEYWORD,So.FILL_KEYWORD);n.textStyles.push(l)}n.styles.push(o)})}getClasses(){return this.classes}setupToolTips(r){let i=H6();zt(r).select("svg").selectAll("g.node, g.rough-node").on("mouseover",n=>{let a=zt(n.currentTarget),o=a.attr("title");if(o===null)return;let l=n.currentTarget?.getBoundingClientRect();i.transition().duration(200).style("opacity",".9"),i.style("left",window.scrollX+l.left+(l.right-l.left)/2+"px").style("top",window.scrollY+l.bottom+"px"),i.html(wu.sanitize(o)),a.classed("hover",!0)}).on("mouseout",n=>{i.transition().duration(500).style("opacity",0),zt(n.currentTarget).classed("hover",!1)})}setCssClass(r,i){r.split(",").forEach(n=>{let a=this.getState(n);if(!a){let o=n.trim();this.addState(o),a=this.getState(o)}a?.classes?.push(i)})}setStyle(r,i){this.getState(r)?.styles?.push(i)}setTextStyle(r,i){this.getState(r)?.textStyles?.push(i)}bindFunctions(r){this.funs.forEach(i=>{i(r)})}getDirectionStatement(){return this.rootDoc.find(r=>r.stmt==="dir")}getDirection(){return this.getDirectionStatement()?.value??"TB"}setDirection(r){let i=this.getDirectionStatement();i?i.value=r:this.rootDoc.unshift({stmt:"dir",value:r})}trimColon(r){return r.startsWith(":")?r.slice(1).trim():r.trim()}getData(){let r=it();return{nodes:this.nodes,edges:this.edges,other:{},config:r,direction:$de(this.getRootDocV2())}}getConfig(){return it().state}},s(e,"StateDB"),e.relationType={AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3},e)}),Xft,Dde,Kft=G(()=>{"use strict";Xft=s(e=>` +defs [id$="-barbEnd"] { + fill: ${e.transitionColor}; + stroke: ${e.transitionColor}; + } +g.stateGroup text { + fill: ${e.nodeBorder}; + stroke: none; + font-size: 10px; +} +g.stateGroup text { + fill: ${e.textColor}; + stroke: none; + font-size: 10px; + +} +g.stateGroup .state-title { + font-weight: bolder; + fill: ${e.stateLabelColor}; +} + +g.stateGroup rect { + fill: ${e.mainBkg}; + stroke: ${e.nodeBorder}; +} + +g.stateGroup line { + stroke: ${e.lineColor}; + stroke-width: ${e.strokeWidth||1}; +} + +.transition { + stroke: ${e.transitionColor}; + stroke-width: ${e.strokeWidth||1}; + fill: none; +} + +.stateGroup .composit { + fill: ${e.background}; + border-bottom: 1px +} + +.stateGroup .alt-composit { + fill: #e0e0e0; + border-bottom: 1px +} + +.state-note { + stroke: ${e.noteBorderColor}; + fill: ${e.noteBkgColor}; + + text { + fill: ${e.noteTextColor}; + stroke: none; + font-size: 10px; + } +} + +.stateLabel .box { + stroke: none; + stroke-width: 0; + fill: ${e.mainBkg}; + opacity: 0.5; +} + +.edgeLabel .label rect { + fill: ${e.labelBackgroundColor}; + opacity: 0.5; +} +.edgeLabel { + background-color: ${e.edgeLabelBackground}; + p { + background-color: ${e.edgeLabelBackground}; + } + rect { + opacity: 0.5; + background-color: ${e.edgeLabelBackground}; + fill: ${e.edgeLabelBackground}; + } + text-align: center; +} +.edgeLabel .label text { + fill: ${e.transitionLabelColor||e.tertiaryTextColor}; +} +.label div .edgeLabel { + color: ${e.transitionLabelColor||e.tertiaryTextColor}; +} + +.stateLabel text { + fill: ${e.stateLabelColor}; + font-size: 10px; + font-weight: bold; +} + +.node circle.state-start { + fill: ${e.specialStateColor}; + stroke: ${e.specialStateColor}; +} + +.node .fork-join { + fill: ${e.specialStateColor}; + stroke: ${e.specialStateColor}; +} + +.node circle.state-end { + fill: ${e.innerEndBackground}; + stroke: ${e.background}; + stroke-width: 1.5 +} +.end-state-inner { + fill: ${e.compositeBackground||e.background}; + // stroke: ${e.background}; + stroke-width: 1.5 +} + +.node rect { + fill: ${e.stateBkg||e.mainBkg}; + stroke: ${e.stateBorder||e.nodeBorder}; + stroke-width: ${e.strokeWidth||1}px; +} +.node polygon { + fill: ${e.mainBkg}; + stroke: ${e.stateBorder||e.nodeBorder};; + stroke-width: ${e.strokeWidth||1}px; +} +[id$="-barbEnd"] { + fill: ${e.lineColor}; +} + +.statediagram-cluster rect { + fill: ${e.compositeTitleBackground}; + stroke: ${e.stateBorder||e.nodeBorder}; + stroke-width: ${e.strokeWidth||1}px; +} + +.cluster-label, .nodeLabel { + color: ${e.stateLabelColor}; + // line-height: 1; +} + +.statediagram-cluster rect.outer { + rx: 5px; + ry: 5px; +} +.statediagram-state .divider { + stroke: ${e.stateBorder||e.nodeBorder}; +} + +.statediagram-state .title-state { + rx: 5px; + ry: 5px; +} +.statediagram-cluster.statediagram-cluster .inner { + fill: ${e.compositeBackground||e.background}; +} +.statediagram-cluster.statediagram-cluster-alt .inner { + fill: ${e.altBackground?e.altBackground:"#efefef"}; +} + +.statediagram-cluster .inner { + rx:0; + ry:0; +} + +.statediagram-state rect.basic { + rx: 5px; + ry: 5px; +} +.statediagram-state rect.divider { + stroke-dasharray: 10,10; + fill: ${e.altBackground?e.altBackground:"#efefef"}; +} + +.note-edge { + stroke-dasharray: 5; +} + +.statediagram-note rect { + fill: ${e.noteBkgColor}; + stroke: ${e.noteBorderColor}; + stroke-width: 1px; + rx: 0; + ry: 0; +} +.statediagram-note rect { + fill: ${e.noteBkgColor}; + stroke: ${e.noteBorderColor}; + stroke-width: 1px; + rx: 0; + ry: 0; +} + +.statediagram-note text { + fill: ${e.noteTextColor}; +} + +.statediagram-note .nodeLabel { + color: ${e.noteTextColor}; +} +.statediagram .edgeLabel { + color: red; // ${e.noteTextColor}; +} + +[id$="-dependencyStart"], [id$="-dependencyEnd"] { + fill: ${e.lineColor}; + stroke: ${e.lineColor}; + stroke-width: ${e.strokeWidth||1}; +} + +.statediagramTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${e.textColor}; +} + +[data-look="neo"].statediagram-cluster rect { + fill: ${e.mainBkg}; + stroke: ${e.useGradient?"url("+e.svgId+"-gradient)":e.stateBorder||e.nodeBorder}; + stroke-width: ${e.strokeWidth??1}; +} +[data-look="neo"].statediagram-cluster rect.outer { + rx: ${e.radius}px; + ry: ${e.radius}px; + filter: ${e.dropShadow?e.dropShadow.replace("url(#drop-shadow)",`url(${e.svgId}-drop-shadow)`):"none"} +} +`,"getStyles"),Dde=Xft}),Qft,Zft,Jft,e0t,t0t,r0t,i0t,n0t,a0t,Nde,Ode,s0t,DRt=G(()=>{"use strict";pi(),Mde(),Ar(),mi(),Er(),Nt(),Qft=s(e=>e.append("circle").attr("class","start-state").attr("r",it().state.sizeUnit).attr("cx",it().state.padding+it().state.sizeUnit).attr("cy",it().state.padding+it().state.sizeUnit),"drawStartState"),Zft=s(e=>e.append("line").style("stroke","grey").style("stroke-dasharray","3").attr("x1",it().state.textHeight).attr("class","divider").attr("x2",it().state.textHeight*2).attr("y1",0).attr("y2",0),"drawDivider"),Jft=s((e,t)=>{let r=e.append("text").attr("x",2*it().state.padding).attr("y",it().state.textHeight+2*it().state.padding).attr("font-size",it().state.fontSize).attr("class","state-title").text(t.id),i=r.node().getBBox();return e.insert("rect",":first-child").attr("x",it().state.padding).attr("y",it().state.padding).attr("width",i.width+2*it().state.padding).attr("height",i.height+2*it().state.padding).attr("rx",it().state.radius),r},"drawSimpleState"),e0t=s((e,t)=>{let r=s(function(p,f,g){let m=p.append("tspan").attr("x",2*it().state.padding).text(f);g||m.attr("dy",it().state.textHeight)},"addTspan"),i=e.append("text").attr("x",2*it().state.padding).attr("y",it().state.textHeight+1.3*it().state.padding).attr("font-size",it().state.fontSize).attr("class","state-title").text(t.descriptions[0]).node().getBBox(),n=i.height,a=e.append("text").attr("x",it().state.padding).attr("y",n+it().state.padding*.4+it().state.dividerMargin+it().state.textHeight).attr("class","state-description"),o=!0,l=!0;t.descriptions.forEach(function(p){o||(r(a,p,l),l=!1),o=!1});let u=e.append("line").attr("x1",it().state.padding).attr("y1",it().state.padding+n+it().state.dividerMargin/2).attr("y2",it().state.padding+n+it().state.dividerMargin/2).attr("class","descr-divider"),h=a.node().getBBox(),d=Math.max(h.width,i.width);return u.attr("x2",d+3*it().state.padding),e.insert("rect",":first-child").attr("x",it().state.padding).attr("y",it().state.padding).attr("width",d+2*it().state.padding).attr("height",h.height+n+2*it().state.padding).attr("rx",it().state.radius),e},"drawDescrState"),t0t=s((e,t,r)=>{let i=it().state.padding,n=2*it().state.padding,a=e.node().getBBox(),o=a.width,l=a.x,u=e.append("text").attr("x",0).attr("y",it().state.titleShift).attr("font-size",it().state.fontSize).attr("class","state-title").text(t.id),h=u.node().getBBox().width+n,d=Math.max(h,o);d===o&&(d=d+n);let p,f=e.node().getBBox();t.doc,p=l-i,h>o&&(p=(o-d)/2+i),Math.abs(l-f.x)o&&(p=l-(h-o)/2);let g=1-it().state.textHeight;return e.insert("rect",":first-child").attr("x",p).attr("y",g).attr("class",r?"alt-composit":"composit").attr("width",d).attr("height",f.height+it().state.textHeight+it().state.titleShift+1).attr("rx","0"),u.attr("x",p+i),h<=o&&u.attr("x",l+(d-n)/2-h/2+i),e.insert("rect",":first-child").attr("x",p).attr("y",it().state.titleShift-it().state.textHeight-it().state.padding).attr("width",d).attr("height",it().state.textHeight*3).attr("rx",it().state.radius),e.insert("rect",":first-child").attr("x",p).attr("y",it().state.titleShift-it().state.textHeight-it().state.padding).attr("width",d).attr("height",f.height+3+2*it().state.textHeight).attr("rx",it().state.radius),e},"addTitleAndBox"),r0t=s(e=>(e.append("circle").attr("class","end-state-outer").attr("r",it().state.sizeUnit+it().state.miniPadding).attr("cx",it().state.padding+it().state.sizeUnit+it().state.miniPadding).attr("cy",it().state.padding+it().state.sizeUnit+it().state.miniPadding),e.append("circle").attr("class","end-state-inner").attr("r",it().state.sizeUnit).attr("cx",it().state.padding+it().state.sizeUnit+2).attr("cy",it().state.padding+it().state.sizeUnit+2)),"drawEndState"),i0t=s((e,t)=>{let r=it().state.forkWidth,i=it().state.forkHeight;if(t.parentId){let n=r;r=i,i=n}return e.append("rect").style("stroke","black").style("fill","black").attr("width",r).attr("height",i).attr("x",it().state.padding).attr("y",it().state.padding)},"drawForkJoinState"),n0t=s((e,t,r,i)=>{let n=0,a=i.append("text");a.style("text-anchor","start"),a.attr("class","noteText");let o=e.replace(/\r\n/g,"
    ");o=o.replace(/\n/g,"
    ");let l=o.split(Vt.lineBreakRegex),u=1.25*it().state.noteMargin;for(let h of l){let d=h.trim();if(d.length>0){let p=a.append("tspan");if(p.text(d),u===0){let f=p.node().getBBox();u+=f.height}n+=u,p.attr("x",t+it().state.noteMargin),p.attr("y",r+n+1.25*it().state.noteMargin)}}return{textWidth:a.node().getBBox().width,textHeight:n}},"_drawLongText"),a0t=s((e,t)=>{t.attr("class","state-note");let r=t.append("rect").attr("x",0).attr("y",it().state.padding),i=t.append("g"),{textWidth:n,textHeight:a}=n0t(e,0,0,i);return r.attr("height",a+2*it().state.noteMargin),r.attr("width",n+it().state.noteMargin*2),r},"drawNote"),Nde=s(function(e,t){let r=t.id,i={id:r,label:t.id,width:0,height:0},n=e.append("g").attr("id",r).attr("class","stateGroup");t.type==="start"&&Qft(n),t.type==="end"&&r0t(n),(t.type==="fork"||t.type==="join")&&i0t(n,t),t.type==="note"&&a0t(t.note.text,n),t.type==="divider"&&Zft(n),t.type==="default"&&t.descriptions.length===0&&Jft(n,t),t.type==="default"&&t.descriptions.length>0&&e0t(n,t);let a=n.node().getBBox();return i.width=a.width+2*it().state.padding,i.height=a.height+2*it().state.padding,i},"drawState"),Ode=0,s0t=s(function(e,t,r){let i=s(function(u){switch(u){case $0.relationType.AGGREGATION:return"aggregation";case $0.relationType.EXTENSION:return"extension";case $0.relationType.COMPOSITION:return"composition";case $0.relationType.DEPENDENCY:return"dependency"}},"getRelationType");t.points=t.points.filter(u=>!Number.isNaN(u.y));let n=t.points,a=r4().x(function(u){return u.x}).y(function(u){return u.y}).curve(a4),o=e.append("path").attr("d",a(n)).attr("id","edge"+Ode).attr("class","transition"),l="";if(it().state.arrowMarkerAbsolute&&(l=CS(!0)),o.attr("marker-end","url("+l+"#"+i($0.relationType.DEPENDENCY)+"End)"),r.title!==void 0){let u=e.append("g").attr("class","stateLabel"),{x:h,y:d}=ai.calcLabelPosition(t.points),p=Vt.getRows(r.title),f=0,g=[],m=0,y=0;for(let S=0;S<=p.length;S++){let E=u.append("text").attr("text-anchor","middle").text(p[S]).attr("x",h).attr("y",d+f),_=E.node().getBBox();m=Math.max(m,_.width),y=Math.min(y,_.x),Ee.info(_.x,h,d+f),f===0&&(f=E.node().getBBox().height,Ee.info("Title height",f,d)),g.push(E)}let k=f*p.length;if(p.length>1){let S=(p.length-1)*f*.5;g.forEach((E,_)=>E.attr("y",d+_*f-S)),k=f*p.length}let C=u.node().getBBox();u.insert("rect",":first-child").attr("class","box").attr("x",h-m/2-it().state.padding/2).attr("y",d-k/2-it().state.padding/2-3.5).attr("width",m+it().state.padding).attr("height",k+it().state.padding),Ee.info(C)}Ode++},"drawEdge")}),zl,MI,o0t,l0t,c0t,u0t,Bde,h0t,NRt=G(()=>{"use strict";pi(),oBe(),ic(),Nt(),mi(),DRt(),Er(),vn(),MI={},o0t=s(function(){},"setConf"),l0t=s(function(e){e.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z")},"insertMarkers"),c0t=s(function(e,t,r,i){zl=it().state;let n=it().securityLevel,a;n==="sandbox"&&(a=zt("#i"+t));let o=zt(n==="sandbox"?a.nodes()[0].contentDocument.body:"body"),l=n==="sandbox"?a.nodes()[0].contentDocument:document;Ee.debug("Rendering diagram "+e);let u=o.select(`[id='${t}']`);l0t(u);let h=i.db.getRootDoc(),d=u.append("g").attr("id",t+"-root");Bde(h,d,void 0,!1,o,l,i);let p=zl.padding,f=u.node().getBBox(),g=f.width+p*2,m=f.height+p*2,y=g*1.75;aa(u,m,y,zl.useMaxWidth),u.attr("viewBox",`${f.x-zl.padding} ${f.y-zl.padding} `+g+" "+m)},"draw"),u0t=s(e=>e?e.length*zl.fontSizeFactor:1,"getLabelWidth"),Bde=s((e,t,r,i,n,a,o)=>{let l=new lo({compound:!0,multigraph:!0}),u,h=!0;for(u=0;u{let _=E.parentElement,b=0,v=0;_&&(_.parentElement&&(b=_.parentElement.getBBox().width),v=parseInt(_.getAttribute("data-x-shift"),10),Number.isNaN(v)&&(v=0)),E.setAttribute("x1",0-v+8),E.setAttribute("x2",b-v-8)})):Ee.debug("No Node "+S+": "+JSON.stringify(l.node(S)))});let k=y.getBBox();l.edges().forEach(function(S){S!==void 0&&l.edge(S)!==void 0&&(Ee.debug("Edge "+S.v+" -> "+S.w+": "+JSON.stringify(l.edge(S))),s0t(t,l.edge(S),l.edge(S).relation))}),k=y.getBBox();let C={id:r||"root",label:r||"root",width:0,height:0};return C.width=k.width+2*zl.padding,C.height=k.height+2*zl.padding,Ee.debug("Doc rendered",C,l),C},"renderDoc"),h0t={setConf:o0t,draw:c0t}}),d0t={};Mr(d0t,{diagram:()=>p0t});var p0t,ORt=G(()=>{"use strict";Tft(),Mde(),Kft(),NRt(),p0t={parser:mde,get db(){return new $0(1)},renderer:h0t,styles:Dde,init:s(e=>{e.state||(e.state={}),e.state.arrowMarkerAbsolute=e.arrowMarkerAbsolute},"init")}}),f0t={};Mr(f0t,{diagram:()=>g0t});var g0t,BRt=G(()=>{"use strict";Tft(),Mde(),Kft(),Gft(),g0t={parser:mde,get db(){return new $0(2)},renderer:Hft,styles:Dde,init:s(e=>{e.state||(e.state={}),e.state.arrowMarkerAbsolute=e.arrowMarkerAbsolute},"init")}}),DI,m0t,PRt=G(()=>{"use strict";DI=(function(){var e=s(function(p,f,g,m){for(g=g||{},m=p.length;m--;g[p[m]]=f);return g},"o"),t=[6,8,10,11,12,14,16,17,18],r=[1,9],i=[1,10],n=[1,11],a=[1,12],o=[1,13],l=[1,14],u={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,journey:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NEWLINE:10,title:11,acc_title:12,acc_title_value:13,acc_descr:14,acc_descr_value:15,acc_descr_multiline_value:16,section:17,taskName:18,taskData:19,$accept:0,$end:1},terminals_:{2:"error",4:"journey",6:"EOF",8:"SPACE",10:"NEWLINE",11:"title",12:"acc_title",13:"acc_title_value",14:"acc_descr",15:"acc_descr_value",16:"acc_descr_multiline_value",17:"section",18:"taskName",19:"taskData"},productions_:[0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,2],[9,2],[9,1],[9,1],[9,2]],performAction:s(function(p,f,g,m,y,k,C){var S=k.length-1;switch(y){case 1:return k[S-1];case 2:this.$=[];break;case 3:k[S-1].push(k[S]),this.$=k[S-1];break;case 4:case 5:this.$=k[S];break;case 6:case 7:this.$=[];break;case 8:m.setDiagramTitle(k[S].substr(6)),this.$=k[S].substr(6);break;case 9:this.$=k[S].trim(),m.setAccTitle(this.$);break;case 10:case 11:this.$=k[S].trim(),m.setAccDescription(this.$);break;case 12:m.addSection(k[S].substr(8)),this.$=k[S].substr(8);break;case 13:m.addTask(k[S-1],k[S]),this.$="task";break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},e(t,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:r,12:i,14:n,16:a,17:o,18:l},e(t,[2,7],{1:[2,1]}),e(t,[2,3]),{9:15,11:r,12:i,14:n,16:a,17:o,18:l},e(t,[2,5]),e(t,[2,6]),e(t,[2,8]),{13:[1,16]},{15:[1,17]},e(t,[2,11]),e(t,[2,12]),{19:[1,18]},e(t,[2,4]),e(t,[2,9]),e(t,[2,10]),e(t,[2,13])],defaultActions:{},parseError:s(function(p,f){if(f.recoverable)this.trace(p);else{var g=new Error(p);throw g.hash=f,g}},"parseError"),parse:s(function(p){var f=this,g=[0],m=[],y=[null],k=[],C=this.table,S="",E=0,_=0,b=0,v=2,x=1,w=k.slice.call(arguments,1),R=Object.create(this.lexer),$={yy:{}};for(var M in this.yy)Object.prototype.hasOwnProperty.call(this.yy,M)&&($.yy[M]=this.yy[M]);R.setInput(p,$.yy),$.yy.lexer=R,$.yy.parser=this,typeof R.yylloc>"u"&&(R.yylloc={});var N=R.yylloc;k.push(N);var j=R.options&&R.options.ranges;typeof $.yy.parseError=="function"?this.parseError=$.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function I(Te){g.length=g.length-2*Te,y.length=y.length-Te,k.length=k.length-Te}s(I,"popStack");function z(){var Te;return Te=m.pop()||R.lex()||x,typeof Te!="number"&&(Te instanceof Array&&(m=Te,Te=m.pop()),Te=f.symbols_[Te]||Te),Te}s(z,"lex");for(var F,O,B,Y,U,H,J={},te,ie,ue,se;;){if(B=g[g.length-1],this.defaultActions[B]?Y=this.defaultActions[B]:((F===null||typeof F>"u")&&(F=z()),Y=C[B]&&C[B][F]),typeof Y>"u"||!Y.length||!Y[0]){var le="";se=[];for(te in C[B])this.terminals_[te]&&te>v&&se.push("'"+this.terminals_[te]+"'");R.showPosition?le="Parse error on line "+(E+1)+`: +`+R.showPosition()+` +Expecting `+se.join(", ")+", got '"+(this.terminals_[F]||F)+"'":le="Parse error on line "+(E+1)+": Unexpected "+(F==x?"end of input":"'"+(this.terminals_[F]||F)+"'"),this.parseError(le,{text:R.match,token:this.terminals_[F]||F,line:R.yylineno,loc:N,expected:se})}if(Y[0]instanceof Array&&Y.length>1)throw new Error("Parse Error: multiple actions possible at state: "+B+", token: "+F);switch(Y[0]){case 1:g.push(F),y.push(R.yytext),k.push(R.yylloc),g.push(Y[1]),F=null,O?(F=O,O=null):(_=R.yyleng,S=R.yytext,E=R.yylineno,N=R.yylloc,b>0&&b--);break;case 2:if(ie=this.productions_[Y[1]][1],J.$=y[y.length-ie],J._$={first_line:k[k.length-(ie||1)].first_line,last_line:k[k.length-1].last_line,first_column:k[k.length-(ie||1)].first_column,last_column:k[k.length-1].last_column},j&&(J._$.range=[k[k.length-(ie||1)].range[0],k[k.length-1].range[1]]),H=this.performAction.apply(J,[S,_,E,$.yy,Y[1],y,k].concat(w)),typeof H<"u")return H;ie&&(g=g.slice(0,-1*ie*2),y=y.slice(0,-1*ie),k=k.slice(0,-1*ie)),g.push(this.productions_[Y[1]][0]),y.push(J.$),k.push(J._$),ue=C[g[g.length-2]][g[g.length-1]],g.push(ue);break;case 3:return!0}}return!0},"parse")},h=(function(){var p={EOF:1,parseError:s(function(f,g){if(this.yy.parser)this.yy.parser.parseError(f,g);else throw new Error(f)},"parseError"),setInput:s(function(f,g){return this.yy=g||this.yy||{},this._input=f,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var f=this._input[0];this.yytext+=f,this.yyleng++,this.offset++,this.match+=f,this.matched+=f;var g=f.match(/(?:\r\n?|\n).*/g);return g?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),f},"input"),unput:s(function(f){var g=f.length,m=f.split(/(?:\r\n?|\n)/g);this._input=f+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-g),this.offset-=g;var y=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),m.length-1&&(this.yylineno-=m.length-1);var k=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:m?(m.length===y.length?this.yylloc.first_column:0)+y[y.length-m.length].length-m[0].length:this.yylloc.first_column-g},this.options.ranges&&(this.yylloc.range=[k[0],k[0]+this.yyleng-g]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(f){this.unput(this.match.slice(f))},"less"),pastInput:s(function(){var f=this.matched.substr(0,this.matched.length-this.match.length);return(f.length>20?"...":"")+f.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var f=this.match;return f.length<20&&(f+=this._input.substr(0,20-f.length)),(f.substr(0,20)+(f.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var f=this.pastInput(),g=new Array(f.length+1).join("-");return f+this.upcomingInput()+` +`+g+"^"},"showPosition"),test_match:s(function(f,g){var m,y,k;if(this.options.backtrack_lexer&&(k={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(k.yylloc.range=this.yylloc.range.slice(0))),y=f[0].match(/(?:\r\n?|\n).*/g),y&&(this.yylineno+=y.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:y?y[y.length-1].length-y[y.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+f[0].length},this.yytext+=f[0],this.match+=f[0],this.matches=f,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(f[0].length),this.matched+=f[0],m=this.performAction.call(this,this.yy,this,g,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),m)return m;if(this._backtrack){for(var C in k)this[C]=k[C];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var f,g,m,y;this._more||(this.yytext="",this.match="");for(var k=this._currentRules(),C=0;Cg[0].length)){if(g=m,y=C,this.options.backtrack_lexer){if(f=this.test_match(m,k[C]),f!==!1)return f;if(this._backtrack){g=!1;continue}else return!1}else if(!this.options.flex)break}return g?(f=this.test_match(g,k[y]),f!==!1?f:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var f=this.next();return f||this.lex()},"lex"),begin:s(function(f){this.conditionStack.push(f)},"begin"),popState:s(function(){var f=this.conditionStack.length-1;return f>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(f){return f=this.conditionStack.length-1-Math.abs(f||0),f>=0?this.conditionStack[f]:"INITIAL"},"topState"),pushState:s(function(f){this.begin(f)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(f,g,m,y){var k=y;switch(m){case 0:break;case 1:break;case 2:return 10;case 3:break;case 4:break;case 5:return 4;case 6:return 11;case 7:return this.begin("acc_title"),12;case 8:return this.popState(),"acc_title_value";case 9:return this.begin("acc_descr"),14;case 10:return this.popState(),"acc_descr_value";case 11:this.begin("acc_descr_multiline");break;case 12:this.popState();break;case 13:return"acc_descr_multiline_value";case 14:return 17;case 15:return 18;case 16:return 19;case 17:return":";case 18:return 6;case 19:return"INVALID"}},"anonymous"),rules:[/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:journey\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{acc_descr_multiline:{rules:[12,13],inclusive:!1},acc_descr:{rules:[10],inclusive:!1},acc_title:{rules:[8],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,9,11,14,15,16,17,18,19],inclusive:!0}}};return p})();u.lexer=h;function d(){this.yy={}}return s(d,"Parser"),d.prototype=u,u.Parser=d,new d})(),DI.parser=DI,m0t=DI}),M1,NI,Zb,Jb,y0t,v0t,b0t,x0t,T0t,k0t,C0t,Pde,w0t,Fde,FRt=G(()=>{"use strict";Er(),fn(),M1="",NI=[],Zb=[],Jb=[],y0t=s(function(){NI.length=0,Zb.length=0,M1="",Jb.length=0,pn()},"clear"),v0t=s(function(e){M1=e,NI.push(e)},"addSection"),b0t=s(function(){return NI},"getSections"),x0t=s(function(){let e=Pde(),t=100,r=0;for(;!e&&r{t.people&&e.push(...t.people)}),[...new Set(e)].sort()},"updateActors"),k0t=s(function(e,t){let r=t.substr(1).split(":"),i=0,n=[];r.length===1?(i=Number(r[0]),n=[]):(i=Number(r[0]),n=r[1].split(","));let a=n.map(l=>l.trim()),o={section:M1,type:M1,people:a,task:e,score:i};Jb.push(o)},"addTask"),C0t=s(function(e){let t={section:M1,type:M1,description:e,task:e,classes:[]};Zb.push(t)},"addTaskOrg"),Pde=s(function(){let e=s(function(r){return Jb[r].processed},"compileTask"),t=!0;for(let[r,i]of Jb.entries())e(r),t=t&&i.processed;return t},"compileTasks"),w0t=s(function(){return T0t()},"getActors"),Fde={getConfig:s(()=>it().journey,"getConfig"),clear:y0t,setDiagramTitle:Dn,getDiagramTitle:xn,setAccTitle:bn,getAccTitle:Ln,setAccDescription:In,getAccDescription:Mn,addSection:v0t,getSections:b0t,getTasks:x0t,addTask:k0t,addTaskOrg:C0t,getActors:w0t}}),E0t,S0t,zRt=G(()=>{"use strict";Lk(),E0t=s(e=>`.label { + font-family: ${e.fontFamily}; + color: ${e.textColor}; + } + .mouth { + stroke: #666; + } + + line { + stroke: ${e.textColor} + } + + .legend { + fill: ${e.textColor}; + font-family: ${e.fontFamily}; + } + + .label text { + fill: #333; + } + .label { + color: ${e.textColor} + } + + .face { + ${e.faceColor?`fill: ${e.faceColor}`:"fill: #FFF8DC"}; + stroke: #999; + } + + .node rect, + .node circle, + .node ellipse, + .node polygon, + .node path { + fill: ${e.mainBkg}; + stroke: ${e.nodeBorder}; + stroke-width: 1px; + } + + .node .label { + text-align: center; + } + .node.clickable { + cursor: pointer; + } + + .arrowheadPath { + fill: ${e.arrowheadColor}; + } + + .edgePaths .path { + stroke: ${e.lineColor}; + stroke-width: 1.5px; + } + + .flowchart-link { + stroke: ${e.lineColor}; + fill: none; + } + + .edgeLabel { + background-color: ${e.edgeLabelBackground}; + rect { + opacity: 0.5; + } + text-align: center; + } + + .cluster rect { + } + + .cluster text { + fill: ${e.titleColor}; + } + + div.mermaidTooltip { + position: absolute; + text-align: center; + max-width: 200px; + padding: 2px; + font-family: ${e.fontFamily}; + font-size: 12px; + background: ${e.tertiaryColor}; + border: 1px solid ${e.border2}; + border-radius: 2px; + pointer-events: none; + z-index: 100; + } + + .task-type-0, .section-type-0 { + ${e.fillType0?`fill: ${e.fillType0}`:""}; + } + .task-type-1, .section-type-1 { + ${e.fillType0?`fill: ${e.fillType1}`:""}; + } + .task-type-2, .section-type-2 { + ${e.fillType0?`fill: ${e.fillType2}`:""}; + } + .task-type-3, .section-type-3 { + ${e.fillType0?`fill: ${e.fillType3}`:""}; + } + .task-type-4, .section-type-4 { + ${e.fillType0?`fill: ${e.fillType4}`:""}; + } + .task-type-5, .section-type-5 { + ${e.fillType0?`fill: ${e.fillType5}`:""}; + } + .task-type-6, .section-type-6 { + ${e.fillType0?`fill: ${e.fillType6}`:""}; + } + .task-type-7, .section-type-7 { + ${e.fillType0?`fill: ${e.fillType7}`:""}; + } + + .actor-0 { + ${e.actor0?`fill: ${e.actor0}`:""}; + } + .actor-1 { + ${e.actor1?`fill: ${e.actor1}`:""}; + } + .actor-2 { + ${e.actor2?`fill: ${e.actor2}`:""}; + } + .actor-3 { + ${e.actor3?`fill: ${e.actor3}`:""}; + } + .actor-4 { + ${e.actor4?`fill: ${e.actor4}`:""}; + } + .actor-5 { + ${e.actor5?`fill: ${e.actor5}`:""}; + } + ${yv()} +`,"getStyles"),S0t=E0t}),OI,_0t,zde,qde,A0t,R0t,BI,$0t,L0t,Wde,I0t,ex,qRt=G(()=>{"use strict";pi(),pg(),OI=s(function(e,t){return c4(e,t)},"drawRect"),_0t=s(function(e,t){let r=e.append("circle").attr("cx",t.cx).attr("cy",t.cy).attr("class","face").attr("r",15).attr("stroke-width",2).attr("overflow","visible"),i=e.append("g");i.append("circle").attr("cx",t.cx-15/3).attr("cy",t.cy-15/3).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),i.append("circle").attr("cx",t.cx+15/3).attr("cy",t.cy-15/3).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666");function n(l){let u=hg().startAngle(Math.PI/2).endAngle(3*(Math.PI/2)).innerRadius(7.5).outerRadius(6.8181818181818175);l.append("path").attr("class","mouth").attr("d",u).attr("transform","translate("+t.cx+","+(t.cy+2)+")")}s(n,"smile");function a(l){let u=hg().startAngle(3*Math.PI/2).endAngle(5*(Math.PI/2)).innerRadius(7.5).outerRadius(6.8181818181818175);l.append("path").attr("class","mouth").attr("d",u).attr("transform","translate("+t.cx+","+(t.cy+7)+")")}s(a,"sad");function o(l){l.append("line").attr("class","mouth").attr("stroke",2).attr("x1",t.cx-5).attr("y1",t.cy+7).attr("x2",t.cx+5).attr("y2",t.cy+7).attr("class","mouth").attr("stroke-width","1px").attr("stroke","#666")}return s(o,"ambivalent"),t.score>3?n(i):t.score<3?a(i):o(i),r},"drawFace"),zde=s(function(e,t){let r=e.append("circle");return r.attr("cx",t.cx),r.attr("cy",t.cy),r.attr("class","actor-"+t.pos),r.attr("fill",t.fill),r.attr("stroke",t.stroke),r.attr("r",t.r),r.class!==void 0&&r.attr("class",r.class),t.title!==void 0&&r.append("title").text(t.title),r},"drawCircle"),qde=s(function(e,t){return $ke(e,t)},"drawText"),A0t=s(function(e,t){function r(n,a,o,l,u){return n+","+a+" "+(n+o)+","+a+" "+(n+o)+","+(a+l-u)+" "+(n+o-u*1.2)+","+(a+l)+" "+n+","+(a+l)}s(r,"genPoints");let i=e.append("polygon");i.attr("points",r(t.x,t.y,50,20,7)),i.attr("class","labelBox"),t.y=t.y+t.labelMargin,t.x=t.x+.5*t.labelMargin,qde(e,t)},"drawLabel"),R0t=s(function(e,t,r){let i=e.append("g"),n=El();n.x=t.x,n.y=t.y,n.fill=t.fill,n.width=r.width*t.taskCount+r.diagramMarginX*(t.taskCount-1),n.height=r.height,n.class="journey-section section-type-"+t.num,n.rx=3,n.ry=3,OI(i,n),Wde(r)(t.text,i,n.x,n.y,n.width,n.height,{class:"journey-section section-type-"+t.num},r,t.colour)},"drawSection"),BI=-1,$0t=s(function(e,t,r,i){let n=t.x+r.width/2,a=e.append("g");BI++,a.append("line").attr("id",i+"-task"+BI).attr("x1",n).attr("y1",t.y).attr("x2",n).attr("y2",450).attr("class","task-line").attr("stroke-width","1px").attr("stroke-dasharray","4 2").attr("stroke","#666"),_0t(a,{cx:n,cy:300+(5-t.score)*30,score:t.score});let o=El();o.x=t.x,o.y=t.y,o.fill=t.fill,o.width=r.width,o.height=r.height,o.class="task task-type-"+t.num,o.rx=3,o.ry=3,OI(a,o);let l=t.x+14;t.people.forEach(u=>{let h=t.actors[u].color,d={cx:l,cy:t.y,r:7,fill:h,stroke:"#000",title:u,pos:t.actors[u].position};zde(a,d),l+=10}),Wde(r)(t.task,a,o.x,o.y,o.width,o.height,{class:"task"},r,t.colour)},"drawTask"),L0t=s(function(e,t){mF(e,t)},"drawBackgroundRect"),Wde=(function(){function e(n,a,o,l,u,h,d,p){let f=a.append("text").attr("x",o+u/2).attr("y",l+h/2+5).style("font-color",p).style("text-anchor","middle").text(n);i(f,d)}s(e,"byText");function t(n,a,o,l,u,h,d,p,f){let{taskFontSize:g,taskFontFamily:m}=p,y=n.split(//gi);for(let k=0;k{let a=Dh[n].color,o={cx:20,cy:i,r:7,fill:a,stroke:"#000",pos:Dh[n].position};ex.drawCircle(e,o);let l=e.append("text").attr("visibility","hidden").text(n),u=l.node().getBoundingClientRect().width;l.remove();let h=[];if(u<=r)h=[n];else{let d=n.split(" "),p="";l=e.append("text").attr("visibility","hidden"),d.forEach(f=>{let g=p?`${p} ${f}`:f;if(l.text(g),l.node().getBoundingClientRect().width>r){if(p&&h.push(p),p=f,l.text(f),l.node().getBoundingClientRect().width>r){let m="";for(let y of f)m+=y,l.text(m+"-"),l.node().getBoundingClientRect().width>r&&(h.push(m.slice(0,-1)+"-"),m=y);p=m}}else p=g}),p&&h.push(p),l.remove()}h.forEach((d,p)=>{let f={x:40,y:i+7+p*20,fill:"#666",text:d,textMargin:t.boxTextMargin??5},g=ex.drawText(e,f).node().getBoundingClientRect().width;g>O5&&g>t.leftMargin-g&&(O5=g)}),i+=Math.max(20,h.length*20)})}var D0t,Dh,O5,bc,cp,N0t,ql,PI,jde,O0t,Vde,WRt=G(()=>{"use strict";pi(),qRt(),Er(),vn(),D0t=s(function(e){Object.keys(e).forEach(function(t){bc[t]=e[t]})},"setConf"),Dh={},O5=0,s(M0t,"drawActorLegend"),bc=it().journey,cp=0,N0t=s(function(e,t,r,i){let n=it(),a=n.journey.titleColor,o=n.journey.titleFontSize,l=n.journey.titleFontFamily,u=n.securityLevel,h;u==="sandbox"&&(h=zt("#i"+t));let d=zt(u==="sandbox"?h.nodes()[0].contentDocument.body:"body");ql.init();let p=d.select("#"+t);ex.initGraphics(p,t);let f=i.db.getTasks(),g=i.db.getDiagramTitle(),m=i.db.getActors();for(let _ in Dh)delete Dh[_];let y=0;m.forEach(_=>{Dh[_]={color:bc.actorColours[y%bc.actorColours.length],position:y},y++}),M0t(p),cp=bc.leftMargin+O5,ql.insert(0,0,cp,Object.keys(Dh).length*50),O0t(p,f,0,t);let k=ql.getBounds();g&&p.append("text").text(g).attr("x",cp).attr("font-size",o).attr("font-weight","bold").attr("y",25).attr("fill",a).attr("font-family",l);let C=k.stopy-k.starty+2*bc.diagramMarginY,S=cp+k.stopx+2*bc.diagramMarginX;aa(p,C,S,bc.useMaxWidth),p.append("line").attr("x1",cp).attr("y1",bc.height*4).attr("x2",S-cp-4).attr("y2",bc.height*4).attr("stroke-width",4).attr("stroke","black").attr("marker-end","url(#"+t+"-arrowhead)");let E=g?70:0;p.attr("viewBox",`${k.startx} -25 ${S} ${C+E}`),p.attr("preserveAspectRatio","xMinYMin meet"),p.attr("height",C+E+25)},"draw"),ql={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],init:s(function(){this.sequenceItems=[],this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0},"init"),updateVal:s(function(e,t,r,i){e[t]===void 0?e[t]=r:e[t]=i(r,e[t])},"updateVal"),updateBounds:s(function(e,t,r,i){let n=it().journey,a=this,o=0;function l(u){return s(function(h){o++;let d=a.sequenceItems.length-o+1;a.updateVal(h,"starty",t-d*n.boxMargin,Math.min),a.updateVal(h,"stopy",i+d*n.boxMargin,Math.max),a.updateVal(ql.data,"startx",e-d*n.boxMargin,Math.min),a.updateVal(ql.data,"stopx",r+d*n.boxMargin,Math.max),u!=="activation"&&(a.updateVal(h,"startx",e-d*n.boxMargin,Math.min),a.updateVal(h,"stopx",r+d*n.boxMargin,Math.max),a.updateVal(ql.data,"starty",t-d*n.boxMargin,Math.min),a.updateVal(ql.data,"stopy",i+d*n.boxMargin,Math.max))},"updateItemBounds")}s(l,"updateFn"),this.sequenceItems.forEach(l())},"updateBounds"),insert:s(function(e,t,r,i){let n=Math.min(e,r),a=Math.max(e,r),o=Math.min(t,i),l=Math.max(t,i);this.updateVal(ql.data,"startx",n,Math.min),this.updateVal(ql.data,"starty",o,Math.min),this.updateVal(ql.data,"stopx",a,Math.max),this.updateVal(ql.data,"stopy",l,Math.max),this.updateBounds(n,o,a,l)},"insert"),bumpVerticalPos:s(function(e){this.verticalPos=this.verticalPos+e,this.data.stopy=this.verticalPos},"bumpVerticalPos"),getVerticalPos:s(function(){return this.verticalPos},"getVerticalPos"),getBounds:s(function(){return this.data},"getBounds")},PI=bc.sectionFills,jde=bc.sectionColours,O0t=s(function(e,t,r,i){let n=it().journey,a="",o=n.height*2+n.diagramMarginY,l=r+o,u=0,h="#CCC",d="black",p=0;for(let[f,g]of t.entries()){if(a!==g.section){h=PI[u%PI.length],p=u%PI.length,d=jde[u%jde.length];let y=0,k=g.section;for(let S=f;S(Dh[k]&&(y[k]=Dh[k]),y),{});g.x=f*n.taskMargin+f*n.width+cp,g.y=l,g.width=n.diagramMarginX,g.height=n.diagramMarginY,g.colour=d,g.fill=h,g.num=p,g.actors=m,ex.drawTask(e,g,n,i),ql.insert(g.x,g.y,g.x+g.width+n.taskMargin,450)}},"drawTasks"),Vde={setConf:D0t,draw:N0t}}),B0t={};Mr(B0t,{diagram:()=>P0t});var P0t,jRt=G(()=>{"use strict";PRt(),FRt(),zRt(),WRt(),P0t={parser:m0t,db:Fde,renderer:Vde,styles:S0t,init:s(e=>{Vde.setConf(e.journey),Fde.clear()},"init")}}),FI,F0t,VRt=G(()=>{"use strict";FI=(function(){var e=s(function(f,g,m,y){for(m=m||{},y=f.length;y--;m[f[y]]=g);return m},"o"),t=[6,11,13,14,15,17,19,20,23,24],r=[1,12],i=[1,13],n=[1,14],a=[1,15],o=[1,16],l=[1,19],u=[1,20],h={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,timeline_header:4,document:5,EOF:6,timeline:7,timeline_lr:8,timeline_td:9,line:10,SPACE:11,statement:12,NEWLINE:13,title:14,acc_title:15,acc_title_value:16,acc_descr:17,acc_descr_value:18,acc_descr_multiline_value:19,section:20,period_statement:21,event_statement:22,period:23,event:24,$accept:0,$end:1},terminals_:{2:"error",6:"EOF",7:"timeline",8:"timeline_lr",9:"timeline_td",11:"SPACE",13:"NEWLINE",14:"title",15:"acc_title",16:"acc_title_value",17:"acc_descr",18:"acc_descr_value",19:"acc_descr_multiline_value",20:"section",23:"period",24:"event"},productions_:[0,[3,3],[4,1],[4,1],[4,1],[5,0],[5,2],[10,2],[10,1],[10,1],[10,1],[12,1],[12,2],[12,2],[12,1],[12,1],[12,1],[12,1],[21,1],[22,1]],performAction:s(function(f,g,m,y,k,C,S){var E=C.length-1;switch(k){case 1:return C[E-1];case 3:y.setDirection("LR");break;case 4:y.setDirection("TD");break;case 5:this.$=[];break;case 6:C[E-1].push(C[E]),this.$=C[E-1];break;case 7:case 8:this.$=C[E];break;case 9:case 10:this.$=[];break;case 11:y.getCommonDb().setDiagramTitle(C[E].substr(6)),this.$=C[E].substr(6);break;case 12:this.$=C[E].trim(),y.getCommonDb().setAccTitle(this.$);break;case 13:case 14:this.$=C[E].trim(),y.getCommonDb().setAccDescription(this.$);break;case 15:y.addSection(C[E].substr(8)),this.$=C[E].substr(8);break;case 18:y.addTask(C[E],0,""),this.$=C[E];break;case 19:y.addEvent(C[E].substr(2)),this.$=C[E];break}},"anonymous"),table:[{3:1,4:2,7:[1,3],8:[1,4],9:[1,5]},{1:[3]},e(t,[2,5],{5:6}),e(t,[2,2]),e(t,[2,3]),e(t,[2,4]),{6:[1,7],10:8,11:[1,9],12:10,13:[1,11],14:r,15:i,17:n,19:a,20:o,21:17,22:18,23:l,24:u},e(t,[2,10],{1:[2,1]}),e(t,[2,6]),{12:21,14:r,15:i,17:n,19:a,20:o,21:17,22:18,23:l,24:u},e(t,[2,8]),e(t,[2,9]),e(t,[2,11]),{16:[1,22]},{18:[1,23]},e(t,[2,14]),e(t,[2,15]),e(t,[2,16]),e(t,[2,17]),e(t,[2,18]),e(t,[2,19]),e(t,[2,7]),e(t,[2,12]),e(t,[2,13])],defaultActions:{},parseError:s(function(f,g){if(g.recoverable)this.trace(f);else{var m=new Error(f);throw m.hash=g,m}},"parseError"),parse:s(function(f){var g=this,m=[0],y=[],k=[null],C=[],S=this.table,E="",_=0,b=0,v=0,x=2,w=1,R=C.slice.call(arguments,1),$=Object.create(this.lexer),M={yy:{}};for(var N in this.yy)Object.prototype.hasOwnProperty.call(this.yy,N)&&(M.yy[N]=this.yy[N]);$.setInput(f,M.yy),M.yy.lexer=$,M.yy.parser=this,typeof $.yylloc>"u"&&($.yylloc={});var j=$.yylloc;C.push(j);var I=$.options&&$.options.ranges;typeof M.yy.parseError=="function"?this.parseError=M.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function z(oe){m.length=m.length-2*oe,k.length=k.length-oe,C.length=C.length-oe}s(z,"popStack");function F(){var oe;return oe=y.pop()||$.lex()||w,typeof oe!="number"&&(oe instanceof Array&&(y=oe,oe=y.pop()),oe=g.symbols_[oe]||oe),oe}s(F,"lex");for(var O,B,Y,U,H,J,te={},ie,ue,se,le;;){if(Y=m[m.length-1],this.defaultActions[Y]?U=this.defaultActions[Y]:((O===null||typeof O>"u")&&(O=F()),U=S[Y]&&S[Y][O]),typeof U>"u"||!U.length||!U[0]){var Te="";le=[];for(ie in S[Y])this.terminals_[ie]&&ie>x&&le.push("'"+this.terminals_[ie]+"'");$.showPosition?Te="Parse error on line "+(_+1)+`: +`+$.showPosition()+` +Expecting `+le.join(", ")+", got '"+(this.terminals_[O]||O)+"'":Te="Parse error on line "+(_+1)+": Unexpected "+(O==w?"end of input":"'"+(this.terminals_[O]||O)+"'"),this.parseError(Te,{text:$.match,token:this.terminals_[O]||O,line:$.yylineno,loc:j,expected:le})}if(U[0]instanceof Array&&U.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Y+", token: "+O);switch(U[0]){case 1:m.push(O),k.push($.yytext),C.push($.yylloc),m.push(U[1]),O=null,B?(O=B,B=null):(b=$.yyleng,E=$.yytext,_=$.yylineno,j=$.yylloc,v>0&&v--);break;case 2:if(ue=this.productions_[U[1]][1],te.$=k[k.length-ue],te._$={first_line:C[C.length-(ue||1)].first_line,last_line:C[C.length-1].last_line,first_column:C[C.length-(ue||1)].first_column,last_column:C[C.length-1].last_column},I&&(te._$.range=[C[C.length-(ue||1)].range[0],C[C.length-1].range[1]]),J=this.performAction.apply(te,[E,b,_,M.yy,U[1],k,C].concat(R)),typeof J<"u")return J;ue&&(m=m.slice(0,-1*ue*2),k=k.slice(0,-1*ue),C=C.slice(0,-1*ue)),m.push(this.productions_[U[1]][0]),k.push(te.$),C.push(te._$),se=S[m[m.length-2]][m[m.length-1]],m.push(se);break;case 3:return!0}}return!0},"parse")},d=(function(){var f={EOF:1,parseError:s(function(g,m){if(this.yy.parser)this.yy.parser.parseError(g,m);else throw new Error(g)},"parseError"),setInput:s(function(g,m){return this.yy=m||this.yy||{},this._input=g,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var g=this._input[0];this.yytext+=g,this.yyleng++,this.offset++,this.match+=g,this.matched+=g;var m=g.match(/(?:\r\n?|\n).*/g);return m?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),g},"input"),unput:s(function(g){var m=g.length,y=g.split(/(?:\r\n?|\n)/g);this._input=g+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-m),this.offset-=m;var k=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),y.length-1&&(this.yylineno-=y.length-1);var C=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:y?(y.length===k.length?this.yylloc.first_column:0)+k[k.length-y.length].length-y[0].length:this.yylloc.first_column-m},this.options.ranges&&(this.yylloc.range=[C[0],C[0]+this.yyleng-m]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(g){this.unput(this.match.slice(g))},"less"),pastInput:s(function(){var g=this.matched.substr(0,this.matched.length-this.match.length);return(g.length>20?"...":"")+g.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var g=this.match;return g.length<20&&(g+=this._input.substr(0,20-g.length)),(g.substr(0,20)+(g.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var g=this.pastInput(),m=new Array(g.length+1).join("-");return g+this.upcomingInput()+` +`+m+"^"},"showPosition"),test_match:s(function(g,m){var y,k,C;if(this.options.backtrack_lexer&&(C={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(C.yylloc.range=this.yylloc.range.slice(0))),k=g[0].match(/(?:\r\n?|\n).*/g),k&&(this.yylineno+=k.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:k?k[k.length-1].length-k[k.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+g[0].length},this.yytext+=g[0],this.match+=g[0],this.matches=g,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(g[0].length),this.matched+=g[0],y=this.performAction.call(this,this.yy,this,m,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),y)return y;if(this._backtrack){for(var S in C)this[S]=C[S];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var g,m,y,k;this._more||(this.yytext="",this.match="");for(var C=this._currentRules(),S=0;Sm[0].length)){if(m=y,k=S,this.options.backtrack_lexer){if(g=this.test_match(y,C[S]),g!==!1)return g;if(this._backtrack){m=!1;continue}else return!1}else if(!this.options.flex)break}return m?(g=this.test_match(m,C[k]),g!==!1?g:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var g=this.next();return g||this.lex()},"lex"),begin:s(function(g){this.conditionStack.push(g)},"begin"),popState:s(function(){var g=this.conditionStack.length-1;return g>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(g){return g=this.conditionStack.length-1-Math.abs(g||0),g>=0?this.conditionStack[g]:"INITIAL"},"topState"),pushState:s(function(g){this.begin(g)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(g,m,y,k){var C=k;switch(y){case 0:break;case 1:break;case 2:return 13;case 3:break;case 4:break;case 5:return 8;case 6:return 9;case 7:return 7;case 8:return 14;case 9:return this.begin("acc_title"),15;case 10:return this.popState(),"acc_title_value";case 11:return this.begin("acc_descr"),17;case 12:return this.popState(),"acc_descr_value";case 13:this.begin("acc_descr_multiline");break;case 14:this.popState();break;case 15:return"acc_descr_multiline_value";case 16:return 20;case 17:return 24;case 18:return 23;case 19:return 6;case 20:return"INVALID"}},"anonymous"),rules:[/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:timeline[ \t]+LR\b)/i,/^(?:timeline[ \t]+TD\b)/i,/^(?:timeline\b)/i,/^(?:title\s[^\n]+)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:section\s[^:\n]+)/i,/^(?::\s(?:[^:\n]|:(?!\s))+)/i,/^(?:[^#:\n]+)/i,/^(?:$)/i,/^(?:.)/i],conditions:{acc_descr_multiline:{rules:[14,15],inclusive:!1},acc_descr:{rules:[12],inclusive:!1},acc_title:{rules:[10],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,11,13,16,17,18,19,20],inclusive:!0}}};return f})();h.lexer=d;function p(){this.yy={}}return s(p,"Parser"),p.prototype=h,h.Parser=p,new p})(),FI.parser=FI,F0t=FI}),z0t={};Mr(z0t,{addEvent:()=>epe,addSection:()=>Kde,addTask:()=>Jde,addTaskOrg:()=>tpe,clear:()=>Hde,default:()=>q0t,getCommonDb:()=>Yde,getDirection:()=>Xde,getSections:()=>Qde,getTasks:()=>Zde,setDirection:()=>Gde});var D1,Ude,zI,qI,B5,N1,Yde,Hde,Gde,Xde,Kde,Qde,Zde,Jde,epe,tpe,rpe,q0t,URt=G(()=>{"use strict";fn(),D1="",Ude=0,zI="LR",qI=[],B5=[],N1=[],Yde=s(()=>FO,"getCommonDb"),Hde=s(function(){qI.length=0,B5.length=0,D1="",N1.length=0,zI="LR",pn()},"clear"),Gde=s(function(e){zI=e},"setDirection"),Xde=s(function(){return zI},"getDirection"),Kde=s(function(e){D1=e,qI.push(e)},"addSection"),Qde=s(function(){return qI},"getSections"),Zde=s(function(){let e=rpe(),t=100,r=0;for(;!e&&rt.id===Ude-1).events.push(e)},"addEvent"),tpe=s(function(e){let t={section:D1,type:D1,description:e,task:e,classes:[]};B5.push(t)},"addTaskOrg"),rpe=s(function(){let e=s(function(r){return N1[r].processed},"compileTask"),t=!0;for(let[r,i]of N1.entries())e(r),t=t&&i.processed;return t},"compileTasks"),q0t={clear:Hde,getCommonDb:Yde,getDirection:Xde,setDirection:Gde,addSection:Kde,getSections:Qde,getTasks:Zde,addTask:Jde,addTaskOrg:tpe,addEvent:epe}});function ipe(e,t){e.each(function(){var r=zt(this),i=r.text().split(/(\s+|
    )/).reverse(),n,a=[],o=1.1,l=r.attr("y"),u=parseFloat(r.attr("dy")),h=r.text(null).append("tspan").attr("x",0).attr("y",l).attr("dy",u+"em");for(let d=0;dt||n==="
    ")&&(a.pop(),h.text(a.join(" ").trim()),n==="
    "?a=[""]:a=[n],h=r.append("tspan").attr("x",0).attr("y",l).attr("dy",o+"em").text(n))})}var npe,P5,W0t,j0t,ape,V0t,U0t,WI,Y0t,H0t,G0t,jI,spe,X0t,K0t,Q0t,Z0t,Xo,J0t=G(()=>{"use strict";pi(),npe=0,P5=s(function(e,t){let r=e.append("rect");return r.attr("x",t.x),r.attr("y",t.y),r.attr("fill",t.fill),r.attr("stroke",t.stroke),r.attr("width",t.width),r.attr("height",t.height),r.attr("rx",t.rx),r.attr("ry",t.ry),t.class!==void 0&&r.attr("class",t.class),r},"drawRect"),W0t=s(function(e,t){let r=e.append("circle").attr("cx",t.cx).attr("cy",t.cy).attr("class","face").attr("r",15).attr("stroke-width",2).attr("overflow","visible"),i=e.append("g");i.append("circle").attr("cx",t.cx-15/3).attr("cy",t.cy-15/3).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),i.append("circle").attr("cx",t.cx+15/3).attr("cy",t.cy-15/3).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666");function n(l){let u=hg().startAngle(Math.PI/2).endAngle(3*(Math.PI/2)).innerRadius(7.5).outerRadius(6.8181818181818175);l.append("path").attr("class","mouth").attr("d",u).attr("transform","translate("+t.cx+","+(t.cy+2)+")")}s(n,"smile");function a(l){let u=hg().startAngle(3*Math.PI/2).endAngle(5*(Math.PI/2)).innerRadius(7.5).outerRadius(6.8181818181818175);l.append("path").attr("class","mouth").attr("d",u).attr("transform","translate("+t.cx+","+(t.cy+7)+")")}s(a,"sad");function o(l){l.append("line").attr("class","mouth").attr("stroke",2).attr("x1",t.cx-5).attr("y1",t.cy+7).attr("x2",t.cx+5).attr("y2",t.cy+7).attr("class","mouth").attr("stroke-width","1px").attr("stroke","#666")}return s(o,"ambivalent"),t.score>3?n(i):t.score<3?a(i):o(i),r},"drawFace"),j0t=s(function(e,t){let r=e.append("circle");return r.attr("cx",t.cx),r.attr("cy",t.cy),r.attr("class","actor-"+t.pos),r.attr("fill",t.fill),r.attr("stroke",t.stroke),r.attr("r",t.r),r.class!==void 0&&r.attr("class",r.class),t.title!==void 0&&r.append("title").text(t.title),r},"drawCircle"),ape=s(function(e,t){let r=t.text.replace(//gi," "),i=e.append("text");i.attr("x",t.x),i.attr("y",t.y),i.attr("class","legend"),i.style("text-anchor",t.anchor),t.class!==void 0&&i.attr("class",t.class);let n=i.append("tspan");return n.attr("x",t.x+t.textMargin*2),n.text(r),i},"drawText"),V0t=s(function(e,t){function r(n,a,o,l,u){return n+","+a+" "+(n+o)+","+a+" "+(n+o)+","+(a+l-u)+" "+(n+o-u*1.2)+","+(a+l)+" "+n+","+(a+l)}s(r,"genPoints");let i=e.append("polygon");i.attr("points",r(t.x,t.y,50,20,7)),i.attr("class","labelBox"),t.y=t.y+t.labelMargin,t.x=t.x+.5*t.labelMargin,ape(e,t)},"drawLabel"),U0t=s(function(e,t,r){let i=e.append("g"),n=jI();n.x=t.x,n.y=t.y,n.fill=t.fill,n.width=r.width,n.height=r.height,n.class="journey-section section-type-"+t.num,n.rx=3,n.ry=3,P5(i,n),spe(r)(t.text,i,n.x,n.y,n.width,n.height,{class:"journey-section section-type-"+t.num},r,t.colour)},"drawSection"),WI=-1,Y0t=s(function(e,t,r,i){let n=t.x+r.width/2,a=e.append("g");WI++,a.append("line").attr("id",i+"-task"+WI).attr("x1",n).attr("y1",t.y).attr("x2",n).attr("y2",450).attr("class","task-line").attr("stroke-width","1px").attr("stroke-dasharray","4 2").attr("stroke","#666"),W0t(a,{cx:n,cy:300+(5-t.score)*30,score:t.score});let o=jI();o.x=t.x,o.y=t.y,o.fill=t.fill,o.width=r.width,o.height=r.height,o.class="task task-type-"+t.num,o.rx=3,o.ry=3,P5(a,o),spe(r)(t.task,a,o.x,o.y,o.width,o.height,{class:"task"},r,t.colour)},"drawTask"),H0t=s(function(e,t){P5(e,{x:t.startx,y:t.starty,width:t.stopx-t.startx,height:t.stopy-t.starty,fill:t.fill,class:"rect"}).lower()},"drawBackgroundRect"),G0t=s(function(){return{x:0,y:0,fill:void 0,"text-anchor":"start",width:100,height:100,textMargin:0,rx:0,ry:0}},"getTextObj"),jI=s(function(){return{x:0,y:0,width:100,anchor:"start",height:100,rx:0,ry:0}},"getNoteRect"),spe=(function(){function e(n,a,o,l,u,h,d,p){let f=a.append("text").attr("x",o+u/2).attr("y",l+h/2+5).style("font-color",p).style("text-anchor","middle").text(n);i(f,d)}s(e,"byText");function t(n,a,o,l,u,h,d,p,f){let{taskFontSize:g,taskFontFamily:m}=p,y=n.split(//gi);for(let k=0;k0?`M0 ${t.height-l} v${-t.height+2*l} q0,-${o},${o},-${o} h${t.width-2*l} q${o},0,${o},${o} v${t.height-l} H0 Z`:`M0 ${t.height-l} v${-(t.height-l)} h${t.width} v${t.height} H0 Z`;e.append("path").attr("id",i+"-node-"+npe++).attr("class","node-bkg node-"+t.type).attr("d",u),a?.includes("redux")||e.append("line").attr("class","node-line-"+r).attr("x1",0).attr("y1",t.height).attr("x2",t.width).attr("y2",t.height)},"defaultBkg"),Xo={drawRect:P5,drawCircle:j0t,drawSection:U0t,drawText:ape,drawLabel:V0t,drawTask:Y0t,drawBackgroundRect:H0t,getTextObj:G0t,getNoteRect:jI,initGraphics:X0t,drawNode:K0t,getVirtualNodeHeight:Q0t}}),egt,ope,tgt,rgt,YRt=G(()=>{"use strict";pi(),J0t(),Nt(),Er(),vn(),egt=s(function(e,t,r,i){let n=it(),{look:a,theme:o,themeVariables:l}=n,{useGradient:u,gradientStart:h,gradientStop:d}=l,p=n.timeline?.leftMargin??50;Ee.debug("timeline",i.db);let f=n.securityLevel,g;f==="sandbox"&&(g=zt("#i"+t));let m=zt(f==="sandbox"?g.nodes()[0].contentDocument.body:"body").select("#"+t);m.append("g");let y=i.db.getTasks(),k=i.db.getCommonDb().getDiagramTitle();Ee.debug("task",y),Xo.initGraphics(m,t);let C=i.db.getSections();Ee.debug("sections",C);let S=0,E=0,_=0,b=0,v=50+p,x=50;b=50;let w=0,R=!0;C.forEach(function(j){let I={number:w,descr:j,section:w,width:150,padding:20,maxHeight:S},z=Xo.getVirtualNodeHeight(m,I,n);Ee.debug("sectionHeight before draw",z),S=Math.max(S,z+20)});let $=0,M=0;Ee.debug("tasks.length",y.length);for(let[j,I]of y.entries()){let z={number:j,descr:I,section:I.section,width:150,padding:20,maxHeight:E},F=Xo.getVirtualNodeHeight(m,z,n);Ee.debug("taskHeight before draw",F),E=Math.max(E,F+20),$=Math.max($,I.events.length);let O=0;for(let B of I.events){let Y={descr:B,section:I.section,number:I.section,width:150,padding:20,maxHeight:50};O+=Xo.getVirtualNodeHeight(m,Y,n)}I.events.length>0&&(O+=(I.events.length-1)*10),M=Math.max(M,O)}Ee.debug("maxSectionHeight before draw",S),Ee.debug("maxTaskHeight before draw",E),C&&C.length>0?C.forEach(j=>{let I=y.filter(B=>B.section===j),z={number:w,descr:j,section:w,width:200*Math.max(I.length,1)-50,padding:20,maxHeight:S};Ee.debug("sectionNode",z);let F=m.append("g"),O=Xo.drawNode(F,z,w,n,t);Ee.debug("sectionNode output",O),F.attr("transform",`translate(${v}, ${b})`),x+=S+50,I.length>0&&ope(m,I,w,v,x,E,n,$,M,S,!1,t),v+=200*Math.max(I.length,1),x=b,w++}):(R=!1,ope(m,y,w,v,x,E,n,$,M,S,!0,t));let N=m.node().getBBox();if(Ee.debug("bounds",N),k&&m.append("text").text(k).attr("x",a==="neo"?N.x*2+p:N.width/2-p).attr("font-size","4ex").attr("font-weight","bold").attr("y",20),_=R?S+E+150:E+100,m.append("g").attr("class","lineWrapper").append("line").attr("x1",p).attr("y1",_).attr("x2",N.width+3*p).attr("y2",_).attr("stroke-width",4).attr("stroke","black").attr("marker-end",`url(#${t}-arrowhead)`),a==="neo"&&u&&o!=="neutral"){let j=m.select("defs"),I=(j.empty()?m.append("defs"):j).append("linearGradient").attr("id",m.attr("id")+"-gradient").attr("gradientUnits","objectBoundingBox").attr("x1","0%").attr("y1","0%").attr("x2","100%").attr("y2","0%");I.append("stop").attr("offset","0%").attr("stop-color",h).attr("stop-opacity",1),I.append("stop").attr("offset","100%").attr("stop-color",d).attr("stop-opacity",1)}Q0(void 0,m,n.timeline?.padding??50,n.timeline?.useMaxWidth??!1)},"draw"),ope=s(function(e,t,r,i,n,a,o,l,u,h,d,p){for(let f of t){let g={descr:f.task,section:r,number:r,width:150,padding:20,maxHeight:a};Ee.debug("taskNode",g);let m=e.append("g").attr("class","taskWrapper"),y=Xo.drawNode(m,g,r,o,p).height;if(Ee.debug("taskHeight after draw",y),m.attr("transform",`translate(${i}, ${n})`),a=Math.max(a,y),f.events){let k=e.append("g").attr("class","lineWrapper"),C=a;n+=100,C=C+tgt(e,f.events,r,i,n,o,p),n-=100,k.append("line").attr("x1",i+190/2).attr("y1",n+a).attr("x2",i+190/2).attr("y2",n+a+100+u+100).attr("stroke-width",2).attr("stroke","black").attr("marker-end",`url(#${p}-arrowhead)`).attr("stroke-dasharray","5,5")}i=i+200,d&&!o.timeline?.disableMulticolor&&r++}n=n-10},"drawTasks"),tgt=s(function(e,t,r,i,n,a,o){let l=0,u=n;n=n+100;for(let h of t){let d={descr:h,section:r,number:r,width:150,padding:20,maxHeight:50};Ee.debug("eventNode",d);let p=e.append("g").attr("class","eventWrapper"),f=Xo.drawNode(p,d,r,a,o,!0).height;l=l+f,p.attr("transform",`translate(${i}, ${n})`),n=n+10+f}return n=u,l},"drawEvents"),rgt={setConf:s(()=>{},"setConf"),draw:egt}}),F5,Nh,igt,VI,ngt,lpe,agt,cpe,upe,hpe,dpe,sgt,ppe,ogt,lgt,HRt=G(()=>{"use strict";J0t(),Nt(),Er(),vn(),wo(),Ar(),F5=200,Nh=5,igt=F5+Nh*2,VI=F5+100,ngt=VI+Nh*2,lpe=10,agt=0,cpe=20,upe=20,hpe=30,dpe=50,sgt=s(function(e,t,r,i){let n=it(),a=n.timeline?.leftMargin??50;Ee.debug("timeline",i.db);let o=Ks(t);o.append("g");let l=i.db.getTasks(),u=i.db.getCommonDb().getDiagramTitle();Ee.debug("task",l),Xo.initGraphics(o);let h=i.db.getSections();Ee.debug("sections",h);let d=0,p=0,f=50+a,g=50,m=g,y=f,k=igt+upe,C=ngt+dpe,S=y+k,E=0,_=h&&h.length>0,b=_?S:f+k,v=Math.max(50,k+C-Nh*2);h.forEach(function(I){let z={number:E,descr:I,section:E,width:v,padding:Nh,maxHeight:d},F=Xo.getVirtualNodeHeight(o,z,n);Ee.debug("sectionHeight before draw",F),d=Math.max(d,F)});let x=0;Ee.debug("tasks.length",l.length);for(let[I,z]of l.entries()){let F={number:I,descr:z,section:z.section,width:F5,padding:Nh,maxHeight:p},O=Xo.getVirtualNodeHeight(o,F,n);Ee.debug("taskHeight before draw",O),p=Math.max(p,O);let B=0;for(let Y of z.events){let U={descr:Y,section:z.section,number:z.section,width:VI,padding:Nh,maxHeight:50};B+=Xo.getVirtualNodeHeight(o,U,n)}z.events.length>0&&(B+=(z.events.length-1)*lpe),x=Math.max(x,B)+agt}Ee.debug("maxSectionHeight before draw",d),Ee.debug("maxTaskHeight before draw",p);let w=Math.max(p,x)+hpe;_?h.forEach(I=>{let z=l.filter(te=>te.section===I),F={number:E,descr:I,section:E,width:v,padding:Nh,maxHeight:d};Ee.debug("sectionNode",F);let O=o.append("g"),B=Xo.drawNode(O,F,E,n);Ee.debug("sectionNode output",B);let Y=b-k;O.attr("transform",`translate(${Y}, ${g})`);let U=g+B.height+cpe;z.length>0&&ppe(o,z,E,b,U,p,n,w,!1);let H=z.length,J=B.height+cpe+w*Math.max(H,1)-(H>0?hpe*2:0);g+=J,E++}):ppe(o,l,E,b,g,p,n,w,!0);let R=o.node()?.getBBox();if(!R)throw new Error("bbox not found");if(Ee.debug("bounds",R),u){if(o.append("text").text(u).attr("x",R.width/2-a).attr("font-size","4ex").attr("font-weight","bold").attr("y",20),R=o.node()?.getBBox(),!R)throw new Error("bbox not found");Ee.debug("bounds after title",R)}let[$]=zu(n.fontSize),M=($??16)*2,N=($??16)*.5+20,j=o.append("g").attr("class","lineWrapper");j.append("line").attr("x1",b).attr("y1",m-M).attr("x2",b).attr("y2",R.y+R.height+N).attr("stroke-width",4).attr("stroke","black").attr("marker-end","url(#arrowhead)"),j.lower(),Q0(void 0,o,n.timeline?.padding??50,n.timeline?.useMaxWidth??!1)},"draw"),ppe=s(function(e,t,r,i,n,a,o,l,u){for(let h of t){let d={descr:h.task,section:r,number:r,width:F5,padding:Nh,maxHeight:a};Ee.debug("taskNode",d);let p=e.append("g").attr("class","taskWrapper"),f=Xo.drawNode(p,d,r,o),g=f.height;Ee.debug("taskHeight after draw",g);let m=i-upe-f.width;if(p.attr("transform",`translate(${m}, ${n})`),a=Math.max(a,g),h.events&&h.events.length>0){let y=n,k=i+dpe;ogt(e,h.events,r,i,k,y,o)}n=n+l,u&&!o.timeline?.disableMulticolor&&r++}},"drawTasks"),ogt=s(function(e,t,r,i,n,a,o){let l=a;for(let u of t){let h={descr:u,section:r,number:r,width:VI,padding:Nh,maxHeight:0};Ee.debug("eventNode",h);let d=e.append("g").attr("class","eventWrapper"),p=Xo.drawNode(d,h,r,o).height;d.attr("transform",`translate(${n}, ${l})`);let f=e.append("g").attr("class","lineWrapper"),g=l+p/2;f.append("line").attr("x1",i).attr("y1",g).attr("x2",n).attr("y2",g).attr("stroke-width",2).attr("stroke","black").attr("marker-end","url(#arrowhead)").attr("stroke-dasharray","5,5"),l=l+p+lpe}return l-a},"drawEvents"),lgt={setConf:s(()=>{},"setConf"),draw:sgt}}),cgt,ugt,hgt,dgt,GRt=G(()=>{"use strict";Qa(),Gr(),cgt=s(e=>{let{theme:t}=Rr(),r=t?.includes("dark"),i=t?.includes("color"),n=e.svgId?.replace(/^#/,"")??"",a=n?`url(#${n}-drop-shadow)`:e.dropShadow??"none",o="";for(let l=0;l{let t="";for(let r=0;r{let{theme:t}=Rr(),r=t?.includes("redux"),i=t==="neutral",n=e.svgId?.replace(/^#/,"")??"",a="";if(e.useGradient&&n&&e.THEME_COLOR_LIMIT&&!i)for(let o=0;oggt});var fgt,ggt,XRt=G(()=>{"use strict";VRt(),URt(),YRt(),HRt(),GRt(),fgt={setConf:s(()=>{},"setConf"),draw:s((e,t,r,i)=>(i?.db?.getDirection?.()??"LR")==="TD"?lgt.draw(e,t,r,i):rgt.draw(e,t,r,i),"draw")},ggt={db:z0t,renderer:fgt,parser:F0t,styles:dgt}}),UI,mgt,KRt=G(()=>{"use strict";UI=(function(){var e=s(function(_,b,v,x){for(v=v||{},x=_.length;x--;v[_[x]]=b);return v},"o"),t=[1,4],r=[1,13],i=[1,12],n=[1,15],a=[1,16],o=[1,20],l=[1,19],u=[6,7,8],h=[1,26],d=[1,24],p=[1,25],f=[6,7,11],g=[1,6,13,15,16,19,22],m=[1,33],y=[1,34],k=[1,6,7,11,13,15,16,19,22],C={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mindMap:4,spaceLines:5,SPACELINE:6,NL:7,MINDMAP:8,document:9,stop:10,EOF:11,statement:12,SPACELIST:13,node:14,ICON:15,CLASS:16,nodeWithId:17,nodeWithoutId:18,NODE_DSTART:19,NODE_DESCR:20,NODE_DEND:21,NODE_ID:22,$accept:0,$end:1},terminals_:{2:"error",6:"SPACELINE",7:"NL",8:"MINDMAP",11:"EOF",13:"SPACELIST",15:"ICON",16:"CLASS",19:"NODE_DSTART",20:"NODE_DESCR",21:"NODE_DEND",22:"NODE_ID"},productions_:[0,[3,1],[3,2],[5,1],[5,2],[5,2],[4,2],[4,3],[10,1],[10,1],[10,1],[10,2],[10,2],[9,3],[9,2],[12,2],[12,2],[12,2],[12,1],[12,1],[12,1],[12,1],[12,1],[14,1],[14,1],[18,3],[17,1],[17,4]],performAction:s(function(_,b,v,x,w,R,$){var M=R.length-1;switch(w){case 6:case 7:return x;case 8:x.getLogger().trace("Stop NL ");break;case 9:x.getLogger().trace("Stop EOF ");break;case 11:x.getLogger().trace("Stop NL2 ");break;case 12:x.getLogger().trace("Stop EOF2 ");break;case 15:x.getLogger().info("Node: ",R[M].id),x.addNode(R[M-1].length,R[M].id,R[M].descr,R[M].type);break;case 16:x.getLogger().trace("Icon: ",R[M]),x.decorateNode({icon:R[M]});break;case 17:case 21:x.decorateNode({class:R[M]});break;case 18:x.getLogger().trace("SPACELIST");break;case 19:x.getLogger().trace("Node: ",R[M].id),x.addNode(0,R[M].id,R[M].descr,R[M].type);break;case 20:x.decorateNode({icon:R[M]});break;case 25:x.getLogger().trace("node found ..",R[M-2]),this.$={id:R[M-1],descr:R[M-1],type:x.getType(R[M-2],R[M])};break;case 26:this.$={id:R[M],descr:R[M],type:x.nodeType.DEFAULT};break;case 27:x.getLogger().trace("node found ..",R[M-3]),this.$={id:R[M-3],descr:R[M-1],type:x.getType(R[M-2],R[M])};break}},"anonymous"),table:[{3:1,4:2,5:3,6:[1,5],8:t},{1:[3]},{1:[2,1]},{4:6,6:[1,7],7:[1,8],8:t},{6:r,7:[1,10],9:9,12:11,13:i,14:14,15:n,16:a,17:17,18:18,19:o,22:l},e(u,[2,3]),{1:[2,2]},e(u,[2,4]),e(u,[2,5]),{1:[2,6],6:r,12:21,13:i,14:14,15:n,16:a,17:17,18:18,19:o,22:l},{6:r,9:22,12:11,13:i,14:14,15:n,16:a,17:17,18:18,19:o,22:l},{6:h,7:d,10:23,11:p},e(f,[2,22],{17:17,18:18,14:27,15:[1,28],16:[1,29],19:o,22:l}),e(f,[2,18]),e(f,[2,19]),e(f,[2,20]),e(f,[2,21]),e(f,[2,23]),e(f,[2,24]),e(f,[2,26],{19:[1,30]}),{20:[1,31]},{6:h,7:d,10:32,11:p},{1:[2,7],6:r,12:21,13:i,14:14,15:n,16:a,17:17,18:18,19:o,22:l},e(g,[2,14],{7:m,11:y}),e(k,[2,8]),e(k,[2,9]),e(k,[2,10]),e(f,[2,15]),e(f,[2,16]),e(f,[2,17]),{20:[1,35]},{21:[1,36]},e(g,[2,13],{7:m,11:y}),e(k,[2,11]),e(k,[2,12]),{21:[1,37]},e(f,[2,25]),e(f,[2,27])],defaultActions:{2:[2,1],6:[2,2]},parseError:s(function(_,b){if(b.recoverable)this.trace(_);else{var v=new Error(_);throw v.hash=b,v}},"parseError"),parse:s(function(_){var b=this,v=[0],x=[],w=[null],R=[],$=this.table,M="",N=0,j=0,I=0,z=2,F=1,O=R.slice.call(arguments,1),B=Object.create(this.lexer),Y={yy:{}};for(var U in this.yy)Object.prototype.hasOwnProperty.call(this.yy,U)&&(Y.yy[U]=this.yy[U]);B.setInput(_,Y.yy),Y.yy.lexer=B,Y.yy.parser=this,typeof B.yylloc>"u"&&(B.yylloc={});var H=B.yylloc;R.push(H);var J=B.options&&B.options.ranges;typeof Y.yy.parseError=="function"?this.parseError=Y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function te(He){v.length=v.length-2*He,w.length=w.length-He,R.length=R.length-He}s(te,"popStack");function ie(){var He;return He=x.pop()||B.lex()||F,typeof He!="number"&&(He instanceof Array&&(x=He,He=x.pop()),He=b.symbols_[He]||He),He}s(ie,"lex");for(var ue,se,le,Te,oe,fe,ge={},ke,Ne,xe,$e;;){if(le=v[v.length-1],this.defaultActions[le]?Te=this.defaultActions[le]:((ue===null||typeof ue>"u")&&(ue=ie()),Te=$[le]&&$[le][ue]),typeof Te>"u"||!Te.length||!Te[0]){var Me="";$e=[];for(ke in $[le])this.terminals_[ke]&&ke>z&&$e.push("'"+this.terminals_[ke]+"'");B.showPosition?Me="Parse error on line "+(N+1)+`: +`+B.showPosition()+` +Expecting `+$e.join(", ")+", got '"+(this.terminals_[ue]||ue)+"'":Me="Parse error on line "+(N+1)+": Unexpected "+(ue==F?"end of input":"'"+(this.terminals_[ue]||ue)+"'"),this.parseError(Me,{text:B.match,token:this.terminals_[ue]||ue,line:B.yylineno,loc:H,expected:$e})}if(Te[0]instanceof Array&&Te.length>1)throw new Error("Parse Error: multiple actions possible at state: "+le+", token: "+ue);switch(Te[0]){case 1:v.push(ue),w.push(B.yytext),R.push(B.yylloc),v.push(Te[1]),ue=null,se?(ue=se,se=null):(j=B.yyleng,M=B.yytext,N=B.yylineno,H=B.yylloc,I>0&&I--);break;case 2:if(Ne=this.productions_[Te[1]][1],ge.$=w[w.length-Ne],ge._$={first_line:R[R.length-(Ne||1)].first_line,last_line:R[R.length-1].last_line,first_column:R[R.length-(Ne||1)].first_column,last_column:R[R.length-1].last_column},J&&(ge._$.range=[R[R.length-(Ne||1)].range[0],R[R.length-1].range[1]]),fe=this.performAction.apply(ge,[M,j,N,Y.yy,Te[1],w,R].concat(O)),typeof fe<"u")return fe;Ne&&(v=v.slice(0,-1*Ne*2),w=w.slice(0,-1*Ne),R=R.slice(0,-1*Ne)),v.push(this.productions_[Te[1]][0]),w.push(ge.$),R.push(ge._$),xe=$[v[v.length-2]][v[v.length-1]],v.push(xe);break;case 3:return!0}}return!0},"parse")},S=(function(){var _={EOF:1,parseError:s(function(b,v){if(this.yy.parser)this.yy.parser.parseError(b,v);else throw new Error(b)},"parseError"),setInput:s(function(b,v){return this.yy=v||this.yy||{},this._input=b,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var b=this._input[0];this.yytext+=b,this.yyleng++,this.offset++,this.match+=b,this.matched+=b;var v=b.match(/(?:\r\n?|\n).*/g);return v?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),b},"input"),unput:s(function(b){var v=b.length,x=b.split(/(?:\r\n?|\n)/g);this._input=b+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-v),this.offset-=v;var w=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),x.length-1&&(this.yylineno-=x.length-1);var R=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:x?(x.length===w.length?this.yylloc.first_column:0)+w[w.length-x.length].length-x[0].length:this.yylloc.first_column-v},this.options.ranges&&(this.yylloc.range=[R[0],R[0]+this.yyleng-v]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(b){this.unput(this.match.slice(b))},"less"),pastInput:s(function(){var b=this.matched.substr(0,this.matched.length-this.match.length);return(b.length>20?"...":"")+b.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var b=this.match;return b.length<20&&(b+=this._input.substr(0,20-b.length)),(b.substr(0,20)+(b.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var b=this.pastInput(),v=new Array(b.length+1).join("-");return b+this.upcomingInput()+` +`+v+"^"},"showPosition"),test_match:s(function(b,v){var x,w,R;if(this.options.backtrack_lexer&&(R={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(R.yylloc.range=this.yylloc.range.slice(0))),w=b[0].match(/(?:\r\n?|\n).*/g),w&&(this.yylineno+=w.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:w?w[w.length-1].length-w[w.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],x=this.performAction.call(this,this.yy,this,v,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),x)return x;if(this._backtrack){for(var $ in R)this[$]=R[$];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var b,v,x,w;this._more||(this.yytext="",this.match="");for(var R=this._currentRules(),$=0;$v[0].length)){if(v=x,w=$,this.options.backtrack_lexer){if(b=this.test_match(x,R[$]),b!==!1)return b;if(this._backtrack){v=!1;continue}else return!1}else if(!this.options.flex)break}return v?(b=this.test_match(v,R[w]),b!==!1?b:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var b=this.next();return b||this.lex()},"lex"),begin:s(function(b){this.conditionStack.push(b)},"begin"),popState:s(function(){var b=this.conditionStack.length-1;return b>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(b){return b=this.conditionStack.length-1-Math.abs(b||0),b>=0?this.conditionStack[b]:"INITIAL"},"topState"),pushState:s(function(b){this.begin(b)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(b,v,x,w){var R=w;switch(x){case 0:return b.getLogger().trace("Found comment",v.yytext),6;case 1:return 8;case 2:this.begin("CLASS");break;case 3:return this.popState(),16;case 4:this.popState();break;case 5:b.getLogger().trace("Begin icon"),this.begin("ICON");break;case 6:return b.getLogger().trace("SPACELINE"),6;case 7:return 7;case 8:return 15;case 9:b.getLogger().trace("end icon"),this.popState();break;case 10:return b.getLogger().trace("Exploding node"),this.begin("NODE"),19;case 11:return b.getLogger().trace("Cloud"),this.begin("NODE"),19;case 12:return b.getLogger().trace("Explosion Bang"),this.begin("NODE"),19;case 13:return b.getLogger().trace("Cloud Bang"),this.begin("NODE"),19;case 14:return this.begin("NODE"),19;case 15:return this.begin("NODE"),19;case 16:return this.begin("NODE"),19;case 17:return this.begin("NODE"),19;case 18:return 13;case 19:return 22;case 20:return 11;case 21:this.begin("NSTR2");break;case 22:return"NODE_DESCR";case 23:this.popState();break;case 24:b.getLogger().trace("Starting NSTR"),this.begin("NSTR");break;case 25:return b.getLogger().trace("description:",v.yytext),"NODE_DESCR";case 26:this.popState();break;case 27:return this.popState(),b.getLogger().trace("node end ))"),"NODE_DEND";case 28:return this.popState(),b.getLogger().trace("node end )"),"NODE_DEND";case 29:return this.popState(),b.getLogger().trace("node end ...",v.yytext),"NODE_DEND";case 30:return this.popState(),b.getLogger().trace("node end (("),"NODE_DEND";case 31:return this.popState(),b.getLogger().trace("node end (-"),"NODE_DEND";case 32:return this.popState(),b.getLogger().trace("node end (-"),"NODE_DEND";case 33:return this.popState(),b.getLogger().trace("node end (("),"NODE_DEND";case 34:return this.popState(),b.getLogger().trace("node end (("),"NODE_DEND";case 35:return b.getLogger().trace("Long description:",v.yytext),20;case 36:return b.getLogger().trace("Long description:",v.yytext),20}},"anonymous"),rules:[/^(?:\s*%%.*)/i,/^(?:mindmap\b)/i,/^(?::::)/i,/^(?:.+)/i,/^(?:\n)/i,/^(?:::icon\()/i,/^(?:[\s]+[\n])/i,/^(?:[\n]+)/i,/^(?:[^\)]+)/i,/^(?:\))/i,/^(?:-\))/i,/^(?:\(-)/i,/^(?:\)\))/i,/^(?:\))/i,/^(?:\(\()/i,/^(?:\{\{)/i,/^(?:\()/i,/^(?:\[)/i,/^(?:[\s]+)/i,/^(?:[^\(\[\n\)\{\}]+)/i,/^(?:$)/i,/^(?:["][`])/i,/^(?:[^`"]+)/i,/^(?:[`]["])/i,/^(?:["])/i,/^(?:[^"]+)/i,/^(?:["])/i,/^(?:[\)]\))/i,/^(?:[\)])/i,/^(?:[\]])/i,/^(?:\}\})/i,/^(?:\(-)/i,/^(?:-\))/i,/^(?:\(\()/i,/^(?:\()/i,/^(?:[^\)\]\(\}]+)/i,/^(?:.+(?!\(\())/i],conditions:{CLASS:{rules:[3,4],inclusive:!1},ICON:{rules:[8,9],inclusive:!1},NSTR2:{rules:[22,23],inclusive:!1},NSTR:{rules:[25,26],inclusive:!1},NODE:{rules:[21,24,27,28,29,30,31,32,33,34,35,36],inclusive:!1},INITIAL:{rules:[0,1,2,5,6,7,10,11,12,13,14,15,16,17,18,19,20],inclusive:!0}}};return _})();C.lexer=S;function E(){this.yy={}}return s(E,"Parser"),E.prototype=C,C.Parser=E,new E})(),UI.parser=UI,mgt=UI});function ygt(e,t=0){return(Ds[e[t+0]]+Ds[e[t+1]]+Ds[e[t+2]]+Ds[e[t+3]]+"-"+Ds[e[t+4]]+Ds[e[t+5]]+"-"+Ds[e[t+6]]+Ds[e[t+7]]+"-"+Ds[e[t+8]]+Ds[e[t+9]]+"-"+Ds[e[t+10]]+Ds[e[t+11]]+Ds[e[t+12]]+Ds[e[t+13]]+Ds[e[t+14]]+Ds[e[t+15]]).toLowerCase()}var Ds,QRt=G(()=>{"use strict";Ds=[];for(let e=0;e<256;++e)Ds.push((e+256).toString(16).slice(1));s(ygt,"unsafeStringify")});function vgt(){return crypto.getRandomValues(bgt)}var bgt,ZRt=G(()=>{"use strict";bgt=new Uint8Array(16),s(vgt,"rng")});function xgt(e,t,r){return!t&&!e&&crypto.randomUUID?crypto.randomUUID():Tgt(e,t,r)}function Tgt(e,t,r){e=e||{};let i=e.random??e.rng?.()??vgt();if(i.length<16)throw new Error("Random bytes length must be >= 16");if(i[6]=i[6]&15|64,i[8]=i[8]&63|128,t){if(r=r||0,r<0||r+16>t.length)throw new RangeError(`UUID byte range ${r}:${r+15} is out of buffer bounds`);for(let n=0;n<16;++n)t[r+n]=i[n];return t}return ygt(i)}var kgt,JRt=G(()=>{"use strict";ZRt(),QRt(),s(xgt,"v4"),s(Tgt,"_v4"),kgt=xgt}),e$t=G(()=>{"use strict";JRt()}),Cgt,t$t=G(()=>{"use strict";ec(),Ar(),Cgt=12}),Oh,wgt,r$t=G(()=>{"use strict";var e;Er(),e$t(),mi(),Nt(),Za(),Gr(),t$t(),Oh={DEFAULT:0,NO_BORDER:0,ROUNDED_RECT:1,RECT:2,CIRCLE:3,CLOUD:4,BANG:5,HEXAGON:6},wgt=(e=class{constructor(){this.nodes=[],this.count=0,this.elements={},this.getLogger=this.getLogger.bind(this),this.nodeType=Oh,this.clear(),this.getType=this.getType.bind(this),this.getElementById=this.getElementById.bind(this),this.getParent=this.getParent.bind(this),this.getMindmap=this.getMindmap.bind(this),this.addNode=this.addNode.bind(this),this.decorateNode=this.decorateNode.bind(this)}clear(){this.nodes=[],this.count=0,this.elements={},this.baseLevel=void 0}getParent(r){for(let i=this.nodes.length-1;i>=0;i--)if(this.nodes[i].level0?this.nodes[0]:null}addNode(r,i,n,a){Ee.info("addNode",r,i,n,a);let o=!1;this.nodes.length===0?(this.baseLevel=r,r=0,o=!0):this.baseLevel!==void 0&&(r=r-this.baseLevel,o=!1);let l=it(),u=l.mindmap?.padding??vi.mindmap.padding;switch(a){case this.nodeType.ROUNDED_RECT:case this.nodeType.RECT:case this.nodeType.HEXAGON:u*=2;break}let h={id:this.count++,nodeId:Si(i,l),level:r,descr:Si(n,l),type:a,children:[],width:l.mindmap?.maxNodeWidth??vi.mindmap.maxNodeWidth,padding:u,isRoot:o},d=this.getParent(r);if(d)d.children.push(h),this.nodes.push(h);else if(o)this.nodes.push(h);else throw new Error(`There can be only one root. No parent could be found for ("${h.descr}")`)}getType(r,i){switch(Ee.debug("In get type",r,i),r){case"[":return this.nodeType.RECT;case"(":return i===")"?this.nodeType.ROUNDED_RECT:this.nodeType.CLOUD;case"((":return this.nodeType.CIRCLE;case")":return this.nodeType.CLOUD;case"))":return this.nodeType.BANG;case"{{":return this.nodeType.HEXAGON;default:return this.nodeType.DEFAULT}}setElementForId(r,i){this.elements[r]=i}getElementById(r){return this.elements[r]}decorateNode(r){if(!r)return;let i=it(),n=this.nodes[this.nodes.length-1];r.icon&&(n.icon=Si(r.icon,i)),r.class&&(n.class=Si(r.class,i))}type2Str(r){switch(r){case this.nodeType.DEFAULT:return"no-border";case this.nodeType.RECT:return"rect";case this.nodeType.ROUNDED_RECT:return"rounded-rect";case this.nodeType.CIRCLE:return"circle";case this.nodeType.CLOUD:return"cloud";case this.nodeType.BANG:return"bang";case this.nodeType.HEXAGON:return"hexgon";default:return"no-border"}}assignSections(r,i){if(r.level===0?r.section=void 0:r.section=i,r.children)for(let[n,a]of r.children.entries()){let o=r.level===0?n%(Cgt-1):i;this.assignSections(a,o)}}flattenNodes(r,i){let n=it(),a=["mindmap-node"];r.isRoot===!0?a.push("section-root","section--1"):r.section!==void 0&&a.push(`section-${r.section}`),r.class&&a.push(r.class);let o=a.join(" "),l=s(h=>{let d=(n.theme?.toLowerCase()??"").includes("redux");switch(h){case Oh.CIRCLE:return"mindmapCircle";case Oh.RECT:return"rect";case Oh.ROUNDED_RECT:return"rounded";case Oh.CLOUD:return"cloud";case Oh.BANG:return"bang";case Oh.HEXAGON:return"hexagon";case Oh.DEFAULT:return d?"rounded":"defaultMindmapNode";case Oh.NO_BORDER:default:return"rect"}},"getShapeFromType"),u={id:r.id.toString(),domId:"node_"+r.id.toString(),label:r.descr,labelType:"markdown",isGroup:!1,shape:l(r.type),width:r.width,height:r.height??0,padding:r.padding,cssClasses:o,cssStyles:[],look:n.look,icon:r.icon,x:r.x,y:r.y,level:r.level,nodeId:r.nodeId,type:r.type,section:r.section};if(i.push(u),r.children)for(let h of r.children)this.flattenNodes(h,i)}generateEdges(r,i){if(!r.children)return;let n=it();for(let a of r.children){let o="edge";a.section!==void 0&&(o+=` section-edge-${a.section}`);let l=r.level+1;o+=` edge-depth-${l}`;let u={id:`edge_${r.id}_${a.id}`,start:r.id.toString(),end:a.id.toString(),type:"normal",curve:"basis",thickness:"normal",look:n.look,classes:o,depth:r.level,section:a.section};i.push(u),this.generateEdges(a,i)}}getData(){let r=this.getMindmap(),i=it(),n=xD().layout!==void 0,a=i;if(n||(a.layout="cose-bilkent"),!r)return{nodes:[],edges:[],config:a};Ee.debug("getData: mindmapRoot",r,i),this.assignSections(r);let o=[],l=[];this.flattenNodes(r,o),this.generateEdges(r,l),Ee.debug(`getData: processed ${o.length} nodes and ${l.length} edges`);let u=new Map;for(let h of o)u.set(h.id,{shape:h.shape,width:h.width,height:h.height,padding:h.padding});return{nodes:o,edges:l,config:a,rootNode:r,markers:["point"],direction:"TB",nodeSpacing:50,rankSpacing:50,shapes:Object.fromEntries(u),type:"mindmap",diagramId:"mindmap-"+kgt()}}getLogger(){return Ee}},s(e,"MindmapDB"),e)}),Egt,Sgt,i$t=G(()=>{"use strict";Nt(),l2(),xm(),Tm(),Za(),Gr(),Egt=s(async(e,t,r,i)=>{Ee.debug(`Rendering mindmap diagram +`+e);let n=i.db,a=n.getData(),o=bg(t,a.config.securityLevel);if(a.type=i.type,a.layoutAlgorithm=mv(a.config.layout,{fallback:"cose-bilkent"}),a.diagramId=t,!n.getMindmap())return;a.nodes.forEach(p=>{p.shape==="rounded"?(p.radius=15,p.taper=15,p.stroke="none",p.width=0,p.padding=15):p.shape==="circle"?p.padding=10:p.shape==="rect"?(p.width=0,p.padding=10):p.shape==="hexagon"&&(p.width=0,p.height=0)}),await bm(a,o);let{themeVariables:l}=Rr(),{useGradient:u,gradientStart:h,gradientStop:d}=l;if(u&&h&&d){let p=o.attr("id"),f=o.append("defs").append("linearGradient").attr("id",`${p}-gradient`).attr("gradientUnits","objectBoundingBox").attr("x1","0%").attr("y1","0%").attr("x2","100%").attr("y2","0%");f.append("stop").attr("offset","0%").attr("stop-color",h).attr("stop-opacity",1),f.append("stop").attr("offset","100%").attr("stop-color",d).attr("stop-opacity",1)}kf(o,a.config.mindmap?.padding??vi.mindmap.padding,"mindmapDiagram",a.config.mindmap?.useMaxWidth??vi.mindmap.useMaxWidth)},"draw"),Sgt={draw:Egt}}),_gt,Agt,Rgt,$gt,n$t=G(()=>{"use strict";Qa(),_gt=s(e=>{let{theme:t,look:r}=e,i="";for(let n=0;n{let i="";for(let n=0;n{let{theme:t}=e,r=e.svgId,i=e.dropShadow?e.dropShadow.replace("url(#drop-shadow)",`url(${r}-drop-shadow)`):"none";return` + .edge { + stroke-width: 3; + } + ${_gt(e)} + .section-root rect, .section-root path, .section-root circle, .section-root polygon { + fill: ${e.git0}; + } + .section-root text { + fill: ${e.gitBranchLabel0}; + } + .section-root span { + color: ${t?.includes("redux")?e.nodeBorder:e.gitBranchLabel0}; + } + .icon-container { + height:100%; + display: flex; + justify-content: center; + align-items: center; + } + .edge { + fill: none; + } + .mindmap-node-label { + dy: 1em; + alignment-baseline: middle; + text-anchor: middle; + dominant-baseline: middle; + text-align: center; + } + [data-look="neo"].mindmap-node { + filter: ${i}; + } + [data-look="neo"].mindmap-node.section-root rect, [data-look="neo"].mindmap-node.section-root path, [data-look="neo"].mindmap-node.section-root circle, [data-look="neo"].mindmap-node.section-root polygon { + fill: ${t?.includes("redux")?e.mainBkg:e.git0}; + } + [data-look="neo"].mindmap-node.section-root .text-inner-tspan { + fill: ${t?.includes("redux")?e.nodeBorder:e["cScaleLabel"+(t==="neutral"?1:0)]}; + } + ${e.useGradient&&r&&e.mainBkg?Agt(e.THEME_COLOR_LIMIT,r,e.mainBkg):""} +`},"getStyles"),$gt=Rgt}),Lgt={};Mr(Lgt,{diagram:()=>Igt});var Igt,a$t=G(()=>{"use strict";KRt(),r$t(),i$t(),n$t(),Igt={get db(){return new wgt},renderer:Sgt,parser:mgt,styles:$gt}}),YI,Mgt,s$t=G(()=>{"use strict";YI=(function(){var e=s(function(x,w,R,$){for(R=R||{},$=x.length;$--;R[x[$]]=w);return R},"o"),t=[1,4],r=[1,13],i=[1,12],n=[1,15],a=[1,16],o=[1,20],l=[1,19],u=[6,7,8],h=[1,26],d=[1,24],p=[1,25],f=[6,7,11],g=[1,31],m=[6,7,11,24],y=[1,6,13,16,17,20,23],k=[1,35],C=[1,36],S=[1,6,7,11,13,16,17,20,23],E=[1,38],_={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mindMap:4,spaceLines:5,SPACELINE:6,NL:7,KANBAN:8,document:9,stop:10,EOF:11,statement:12,SPACELIST:13,node:14,shapeData:15,ICON:16,CLASS:17,nodeWithId:18,nodeWithoutId:19,NODE_DSTART:20,NODE_DESCR:21,NODE_DEND:22,NODE_ID:23,SHAPE_DATA:24,$accept:0,$end:1},terminals_:{2:"error",6:"SPACELINE",7:"NL",8:"KANBAN",11:"EOF",13:"SPACELIST",16:"ICON",17:"CLASS",20:"NODE_DSTART",21:"NODE_DESCR",22:"NODE_DEND",23:"NODE_ID",24:"SHAPE_DATA"},productions_:[0,[3,1],[3,2],[5,1],[5,2],[5,2],[4,2],[4,3],[10,1],[10,1],[10,1],[10,2],[10,2],[9,3],[9,2],[12,3],[12,2],[12,2],[12,2],[12,1],[12,2],[12,1],[12,1],[12,1],[12,1],[14,1],[14,1],[19,3],[18,1],[18,4],[15,2],[15,1]],performAction:s(function(x,w,R,$,M,N,j){var I=N.length-1;switch(M){case 6:case 7:return $;case 8:$.getLogger().trace("Stop NL ");break;case 9:$.getLogger().trace("Stop EOF ");break;case 11:$.getLogger().trace("Stop NL2 ");break;case 12:$.getLogger().trace("Stop EOF2 ");break;case 15:$.getLogger().info("Node: ",N[I-1].id),$.addNode(N[I-2].length,N[I-1].id,N[I-1].descr,N[I-1].type,N[I]);break;case 16:$.getLogger().info("Node: ",N[I].id),$.addNode(N[I-1].length,N[I].id,N[I].descr,N[I].type);break;case 17:$.getLogger().trace("Icon: ",N[I]),$.decorateNode({icon:N[I]});break;case 18:case 23:$.decorateNode({class:N[I]});break;case 19:$.getLogger().trace("SPACELIST");break;case 20:$.getLogger().trace("Node: ",N[I-1].id),$.addNode(0,N[I-1].id,N[I-1].descr,N[I-1].type,N[I]);break;case 21:$.getLogger().trace("Node: ",N[I].id),$.addNode(0,N[I].id,N[I].descr,N[I].type);break;case 22:$.decorateNode({icon:N[I]});break;case 27:$.getLogger().trace("node found ..",N[I-2]),this.$={id:N[I-1],descr:N[I-1],type:$.getType(N[I-2],N[I])};break;case 28:this.$={id:N[I],descr:N[I],type:0};break;case 29:$.getLogger().trace("node found ..",N[I-3]),this.$={id:N[I-3],descr:N[I-1],type:$.getType(N[I-2],N[I])};break;case 30:this.$=N[I-1]+N[I];break;case 31:this.$=N[I];break}},"anonymous"),table:[{3:1,4:2,5:3,6:[1,5],8:t},{1:[3]},{1:[2,1]},{4:6,6:[1,7],7:[1,8],8:t},{6:r,7:[1,10],9:9,12:11,13:i,14:14,16:n,17:a,18:17,19:18,20:o,23:l},e(u,[2,3]),{1:[2,2]},e(u,[2,4]),e(u,[2,5]),{1:[2,6],6:r,12:21,13:i,14:14,16:n,17:a,18:17,19:18,20:o,23:l},{6:r,9:22,12:11,13:i,14:14,16:n,17:a,18:17,19:18,20:o,23:l},{6:h,7:d,10:23,11:p},e(f,[2,24],{18:17,19:18,14:27,16:[1,28],17:[1,29],20:o,23:l}),e(f,[2,19]),e(f,[2,21],{15:30,24:g}),e(f,[2,22]),e(f,[2,23]),e(m,[2,25]),e(m,[2,26]),e(m,[2,28],{20:[1,32]}),{21:[1,33]},{6:h,7:d,10:34,11:p},{1:[2,7],6:r,12:21,13:i,14:14,16:n,17:a,18:17,19:18,20:o,23:l},e(y,[2,14],{7:k,11:C}),e(S,[2,8]),e(S,[2,9]),e(S,[2,10]),e(f,[2,16],{15:37,24:g}),e(f,[2,17]),e(f,[2,18]),e(f,[2,20],{24:E}),e(m,[2,31]),{21:[1,39]},{22:[1,40]},e(y,[2,13],{7:k,11:C}),e(S,[2,11]),e(S,[2,12]),e(f,[2,15],{24:E}),e(m,[2,30]),{22:[1,41]},e(m,[2,27]),e(m,[2,29])],defaultActions:{2:[2,1],6:[2,2]},parseError:s(function(x,w){if(w.recoverable)this.trace(x);else{var R=new Error(x);throw R.hash=w,R}},"parseError"),parse:s(function(x){var w=this,R=[0],$=[],M=[null],N=[],j=this.table,I="",z=0,F=0,O=0,B=2,Y=1,U=N.slice.call(arguments,1),H=Object.create(this.lexer),J={yy:{}};for(var te in this.yy)Object.prototype.hasOwnProperty.call(this.yy,te)&&(J.yy[te]=this.yy[te]);H.setInput(x,J.yy),J.yy.lexer=H,J.yy.parser=this,typeof H.yylloc>"u"&&(H.yylloc={});var ie=H.yylloc;N.push(ie);var ue=H.options&&H.options.ranges;typeof J.yy.parseError=="function"?this.parseError=J.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function se(Ve){R.length=R.length-2*Ve,M.length=M.length-Ve,N.length=N.length-Ve}s(se,"popStack");function le(){var Ve;return Ve=$.pop()||H.lex()||Y,typeof Ve!="number"&&(Ve instanceof Array&&($=Ve,Ve=$.pop()),Ve=w.symbols_[Ve]||Ve),Ve}s(le,"lex");for(var Te,oe,fe,ge,ke,Ne,xe={},$e,Me,He,Ge;;){if(fe=R[R.length-1],this.defaultActions[fe]?ge=this.defaultActions[fe]:((Te===null||typeof Te>"u")&&(Te=le()),ge=j[fe]&&j[fe][Te]),typeof ge>"u"||!ge.length||!ge[0]){var ze="";Ge=[];for($e in j[fe])this.terminals_[$e]&&$e>B&&Ge.push("'"+this.terminals_[$e]+"'");H.showPosition?ze="Parse error on line "+(z+1)+`: +`+H.showPosition()+` +Expecting `+Ge.join(", ")+", got '"+(this.terminals_[Te]||Te)+"'":ze="Parse error on line "+(z+1)+": Unexpected "+(Te==Y?"end of input":"'"+(this.terminals_[Te]||Te)+"'"),this.parseError(ze,{text:H.match,token:this.terminals_[Te]||Te,line:H.yylineno,loc:ie,expected:Ge})}if(ge[0]instanceof Array&&ge.length>1)throw new Error("Parse Error: multiple actions possible at state: "+fe+", token: "+Te);switch(ge[0]){case 1:R.push(Te),M.push(H.yytext),N.push(H.yylloc),R.push(ge[1]),Te=null,oe?(Te=oe,oe=null):(F=H.yyleng,I=H.yytext,z=H.yylineno,ie=H.yylloc,O>0&&O--);break;case 2:if(Me=this.productions_[ge[1]][1],xe.$=M[M.length-Me],xe._$={first_line:N[N.length-(Me||1)].first_line,last_line:N[N.length-1].last_line,first_column:N[N.length-(Me||1)].first_column,last_column:N[N.length-1].last_column},ue&&(xe._$.range=[N[N.length-(Me||1)].range[0],N[N.length-1].range[1]]),Ne=this.performAction.apply(xe,[I,F,z,J.yy,ge[1],M,N].concat(U)),typeof Ne<"u")return Ne;Me&&(R=R.slice(0,-1*Me*2),M=M.slice(0,-1*Me),N=N.slice(0,-1*Me)),R.push(this.productions_[ge[1]][0]),M.push(xe.$),N.push(xe._$),He=j[R[R.length-2]][R[R.length-1]],R.push(He);break;case 3:return!0}}return!0},"parse")},b=(function(){var x={EOF:1,parseError:s(function(w,R){if(this.yy.parser)this.yy.parser.parseError(w,R);else throw new Error(w)},"parseError"),setInput:s(function(w,R){return this.yy=R||this.yy||{},this._input=w,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var w=this._input[0];this.yytext+=w,this.yyleng++,this.offset++,this.match+=w,this.matched+=w;var R=w.match(/(?:\r\n?|\n).*/g);return R?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),w},"input"),unput:s(function(w){var R=w.length,$=w.split(/(?:\r\n?|\n)/g);this._input=w+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-R),this.offset-=R;var M=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),$.length-1&&(this.yylineno-=$.length-1);var N=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:$?($.length===M.length?this.yylloc.first_column:0)+M[M.length-$.length].length-$[0].length:this.yylloc.first_column-R},this.options.ranges&&(this.yylloc.range=[N[0],N[0]+this.yyleng-R]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(w){this.unput(this.match.slice(w))},"less"),pastInput:s(function(){var w=this.matched.substr(0,this.matched.length-this.match.length);return(w.length>20?"...":"")+w.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var w=this.match;return w.length<20&&(w+=this._input.substr(0,20-w.length)),(w.substr(0,20)+(w.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var w=this.pastInput(),R=new Array(w.length+1).join("-");return w+this.upcomingInput()+` +`+R+"^"},"showPosition"),test_match:s(function(w,R){var $,M,N;if(this.options.backtrack_lexer&&(N={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(N.yylloc.range=this.yylloc.range.slice(0))),M=w[0].match(/(?:\r\n?|\n).*/g),M&&(this.yylineno+=M.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:M?M[M.length-1].length-M[M.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+w[0].length},this.yytext+=w[0],this.match+=w[0],this.matches=w,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(w[0].length),this.matched+=w[0],$=this.performAction.call(this,this.yy,this,R,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),$)return $;if(this._backtrack){for(var j in N)this[j]=N[j];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var w,R,$,M;this._more||(this.yytext="",this.match="");for(var N=this._currentRules(),j=0;jR[0].length)){if(R=$,M=j,this.options.backtrack_lexer){if(w=this.test_match($,N[j]),w!==!1)return w;if(this._backtrack){R=!1;continue}else return!1}else if(!this.options.flex)break}return R?(w=this.test_match(R,N[M]),w!==!1?w:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var w=this.next();return w||this.lex()},"lex"),begin:s(function(w){this.conditionStack.push(w)},"begin"),popState:s(function(){var w=this.conditionStack.length-1;return w>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(w){return w=this.conditionStack.length-1-Math.abs(w||0),w>=0?this.conditionStack[w]:"INITIAL"},"topState"),pushState:s(function(w){this.begin(w)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(w,R,$,M){var N=M;switch($){case 0:return this.pushState("shapeData"),R.yytext="",24;case 1:return this.pushState("shapeDataStr"),24;case 2:return this.popState(),24;case 3:let j=/\n\s*/g;return R.yytext=R.yytext.replace(j,"
    "),24;case 4:return 24;case 5:this.popState();break;case 6:return w.getLogger().trace("Found comment",R.yytext),6;case 7:return 8;case 8:this.begin("CLASS");break;case 9:return this.popState(),17;case 10:this.popState();break;case 11:w.getLogger().trace("Begin icon"),this.begin("ICON");break;case 12:return w.getLogger().trace("SPACELINE"),6;case 13:return 7;case 14:return 16;case 15:w.getLogger().trace("end icon"),this.popState();break;case 16:return w.getLogger().trace("Exploding node"),this.begin("NODE"),20;case 17:return w.getLogger().trace("Cloud"),this.begin("NODE"),20;case 18:return w.getLogger().trace("Explosion Bang"),this.begin("NODE"),20;case 19:return w.getLogger().trace("Cloud Bang"),this.begin("NODE"),20;case 20:return this.begin("NODE"),20;case 21:return this.begin("NODE"),20;case 22:return this.begin("NODE"),20;case 23:return this.begin("NODE"),20;case 24:return 13;case 25:return 23;case 26:return 11;case 27:this.begin("NSTR2");break;case 28:return"NODE_DESCR";case 29:this.popState();break;case 30:w.getLogger().trace("Starting NSTR"),this.begin("NSTR");break;case 31:return w.getLogger().trace("description:",R.yytext),"NODE_DESCR";case 32:this.popState();break;case 33:return this.popState(),w.getLogger().trace("node end ))"),"NODE_DEND";case 34:return this.popState(),w.getLogger().trace("node end )"),"NODE_DEND";case 35:return this.popState(),w.getLogger().trace("node end ...",R.yytext),"NODE_DEND";case 36:return this.popState(),w.getLogger().trace("node end (("),"NODE_DEND";case 37:return this.popState(),w.getLogger().trace("node end (-"),"NODE_DEND";case 38:return this.popState(),w.getLogger().trace("node end (-"),"NODE_DEND";case 39:return this.popState(),w.getLogger().trace("node end (("),"NODE_DEND";case 40:return this.popState(),w.getLogger().trace("node end (("),"NODE_DEND";case 41:return w.getLogger().trace("Long description:",R.yytext),21;case 42:return w.getLogger().trace("Long description:",R.yytext),21}},"anonymous"),rules:[/^(?:@\{)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^\"]+)/i,/^(?:[^}^"]+)/i,/^(?:\})/i,/^(?:\s*%%.*)/i,/^(?:kanban\b)/i,/^(?::::)/i,/^(?:.+)/i,/^(?:\n)/i,/^(?:::icon\()/i,/^(?:[\s]+[\n])/i,/^(?:[\n]+)/i,/^(?:[^\)]+)/i,/^(?:\))/i,/^(?:-\))/i,/^(?:\(-)/i,/^(?:\)\))/i,/^(?:\))/i,/^(?:\(\()/i,/^(?:\{\{)/i,/^(?:\()/i,/^(?:\[)/i,/^(?:[\s]+)/i,/^(?:[^\(\[\n\)\{\}@]+)/i,/^(?:$)/i,/^(?:["][`])/i,/^(?:[^`"]+)/i,/^(?:[`]["])/i,/^(?:["])/i,/^(?:[^"]+)/i,/^(?:["])/i,/^(?:[\)]\))/i,/^(?:[\)])/i,/^(?:[\]])/i,/^(?:\}\})/i,/^(?:\(-)/i,/^(?:-\))/i,/^(?:\(\()/i,/^(?:\()/i,/^(?:[^\)\]\(\}]+)/i,/^(?:.+(?!\(\())/i],conditions:{shapeDataEndBracket:{rules:[],inclusive:!1},shapeDataStr:{rules:[2,3],inclusive:!1},shapeData:{rules:[1,4,5],inclusive:!1},CLASS:{rules:[9,10],inclusive:!1},ICON:{rules:[14,15],inclusive:!1},NSTR2:{rules:[28,29],inclusive:!1},NSTR:{rules:[31,32],inclusive:!1},NODE:{rules:[27,30,33,34,35,36,37,38,39,40,41,42],inclusive:!1},INITIAL:{rules:[0,6,7,8,11,12,13,16,17,18,19,20,21,22,23,24,25,26],inclusive:!0}}};return x})();_.lexer=b;function v(){this.yy={}}return s(v,"Parser"),v.prototype=_,_.Parser=v,new v})(),YI.parser=YI,Mgt=YI}),Wl,HI,GI,XI,Dgt,Ngt,fpe,Ogt,Bgt,Xa,Pgt,Fgt,zgt,qgt,Wgt,jgt,Vgt,Ugt,o$t=G(()=>{"use strict";Er(),mi(),Nt(),Za(),X_(),Wl=[],HI=[],GI=0,XI={},Dgt=s(()=>{Wl=[],HI=[],GI=0,XI={}},"clear"),Ngt=s(e=>{if(Wl.length===0)return null;let t=Wl[0].level,r=null;for(let i=Wl.length-1;i>=0;i--)if(Wl[i].level===t&&!r&&(r=Wl[i]),Wl[i].levell.parentId===n.id);for(let l of o){let u={id:l.id,parentId:n.id,label:Si(l.label??"",i),labelType:"markdown",isGroup:!1,ticket:l?.ticket,priority:l?.priority,assigned:l?.assigned,icon:l?.icon,shape:"kanbanItem",level:l.level,rx:5,ry:5,cssStyles:["text-align: left"]};t.push(u)}}return{nodes:t,edges:e,other:{},config:it()}},"getData"),Bgt=s((e,t,r,i,n)=>{let a=it(),o=a.mindmap?.padding??vi.mindmap.padding;switch(i){case Xa.ROUNDED_RECT:case Xa.RECT:case Xa.HEXAGON:o*=2}let l={id:Si(t,a)||"kbn"+GI++,level:e,label:Si(r,a),width:a.mindmap?.maxNodeWidth??vi.mindmap.maxNodeWidth,padding:o,isGroup:!1};if(n!==void 0){let h;n.includes(` +`)?h=n+` +`:h=`{ +`+n+` +}`;let d=B4(h,{schema:O4});if(d.shape&&(d.shape!==d.shape.toLowerCase()||d.shape.includes("_")))throw new Error(`No such shape: ${d.shape}. Shape names should be lowercase.`);d?.shape&&d.shape==="kanbanItem"&&(l.shape=d?.shape),d?.label&&(l.label=d?.label),d?.icon&&(l.icon=d?.icon.toString()),d?.assigned&&(l.assigned=d?.assigned.toString()),d?.ticket&&(l.ticket=d?.ticket.toString()),d?.priority&&(l.priority=d?.priority)}let u=Ngt(e);u?l.parentId=u.id||"kbn"+GI++:HI.push(l),Wl.push(l)},"addNode"),Xa={DEFAULT:0,NO_BORDER:0,ROUNDED_RECT:1,RECT:2,CIRCLE:3,CLOUD:4,BANG:5,HEXAGON:6},Pgt=s((e,t)=>{switch(Ee.debug("In get type",e,t),e){case"[":return Xa.RECT;case"(":return t===")"?Xa.ROUNDED_RECT:Xa.CLOUD;case"((":return Xa.CIRCLE;case")":return Xa.CLOUD;case"))":return Xa.BANG;case"{{":return Xa.HEXAGON;default:return Xa.DEFAULT}},"getType"),Fgt=s((e,t)=>{XI[e]=t},"setElementForId"),zgt=s(e=>{if(!e)return;let t=it(),r=Wl[Wl.length-1];e.icon&&(r.icon=Si(e.icon,t)),e.class&&(r.cssClasses=Si(e.class,t))},"decorateNode"),qgt=s(e=>{switch(e){case Xa.DEFAULT:return"no-border";case Xa.RECT:return"rect";case Xa.ROUNDED_RECT:return"rounded-rect";case Xa.CIRCLE:return"circle";case Xa.CLOUD:return"cloud";case Xa.BANG:return"bang";case Xa.HEXAGON:return"hexgon";default:return"no-border"}},"type2Str"),Wgt=s(()=>Ee,"getLogger"),jgt=s(e=>XI[e],"getElementById"),Vgt={clear:Dgt,addNode:Bgt,getSections:fpe,getData:Ogt,nodeType:Xa,getType:Pgt,setElementForId:Fgt,decorateNode:zgt,type2Str:qgt,getLogger:Wgt,getElementById:jgt},Ugt=Vgt}),Ygt,Hgt,l$t=G(()=>{"use strict";Er(),Nt(),wo(),vn(),Za(),Q_(),p2(),Ygt=s(async(e,t,r,i)=>{Ee.debug(`Rendering kanban diagram +`+e);let n=i.db.getData(),a=it();a.htmlLabels=!1;let o=Ks(t);for(let y of n.nodes)y.domId=`${t}-${y.id}`;let l=o.append("g");l.attr("class","sections");let u=o.append("g");u.attr("class","items");let h=n.nodes.filter(y=>y.isGroup),d=0,p=10,f=[],g=25;for(let y of h){let k=a?.kanban?.sectionWidth||200;d=d+1,y.x=k*d+(d-1)*p/2,y.width=k,y.y=0,y.height=k*3,y.rx=5,y.ry=5,y.cssClasses=y.cssClasses+" section-"+d;let C=await P4(l,y);g=Math.max(g,C?.labelBBox?.height),f.push(C)}let m=0;for(let y of h){let k=f[m];m=m+1;let C=a?.kanban?.sectionWidth||200,S=-C*3/2+g,E=S,_=n.nodes.filter(x=>x.parentId===y.id);for(let x of _){if(x.isGroup)throw new Error("Groups within groups are not allowed in Kanban diagrams");x.x=y.x,x.width=C-1.5*p;let w=(await d2(u,x,{config:a})).node().getBBox();x.y=E+w.height/2,await kg(x),E=x.y+w.height/2+p/2}let b=k.cluster.select("rect"),v=Math.max(E-S+3*p,50)+(g-25);b.attr("height",v)}Q0(void 0,o,a.mindmap?.padding??vi.kanban.padding,a.mindmap?.useMaxWidth??vi.kanban.useMaxWidth)},"draw"),Hgt={draw:Ygt}}),Ggt,Xgt,Kgt,c$t=G(()=>{"use strict";Qa(),Lk(),Ggt=s(e=>{let t="";for(let i=0;ie.darkMode?xt(i,n):bt(i,n),"adjuster");for(let i=0;i` + .edge { + stroke-width: 3; + } + ${Ggt(e)} + .section-root rect, .section-root path, .section-root circle, .section-root polygon { + fill: ${e.git0}; + } + .section-root text { + fill: ${e.gitBranchLabel0}; + } + .icon-container { + height:100%; + display: flex; + justify-content: center; + align-items: center; + } + .edge { + fill: none; + } + .cluster-label, .label { + color: ${e.textColor}; + fill: ${e.textColor}; + } + .kanban-label { + dy: 1em; + alignment-baseline: middle; + text-anchor: middle; + dominant-baseline: middle; + text-align: center; + } + ${yv()} +`,"getStyles"),Kgt=Xgt}),Qgt={};Mr(Qgt,{diagram:()=>Zgt});var Zgt,u$t=G(()=>{"use strict";s$t(),o$t(),l$t(),c$t(),Zgt={db:Ugt,renderer:Hgt,parser:Mgt,styles:Kgt}}),KI,z5,h$t=G(()=>{"use strict";KI=(function(){var e=s(function(l,u,h,d){for(h=h||{},d=l.length;d--;h[l[d]]=u);return h},"o"),t=[1,9],r=[1,10],i=[1,5,10,12],n={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,SANKEY:4,NEWLINE:5,csv:6,opt_eof:7,record:8,csv_tail:9,EOF:10,"field[source]":11,COMMA:12,"field[target]":13,"field[value]":14,field:15,escaped:16,non_escaped:17,DQUOTE:18,ESCAPED_TEXT:19,NON_ESCAPED_TEXT:20,$accept:0,$end:1},terminals_:{2:"error",4:"SANKEY",5:"NEWLINE",10:"EOF",11:"field[source]",12:"COMMA",13:"field[target]",14:"field[value]",18:"DQUOTE",19:"ESCAPED_TEXT",20:"NON_ESCAPED_TEXT"},productions_:[0,[3,4],[6,2],[9,2],[9,0],[7,1],[7,0],[8,5],[15,1],[15,1],[16,3],[17,1]],performAction:s(function(l,u,h,d,p,f,g){var m=f.length-1;switch(p){case 7:let y=d.findOrCreateNode(f[m-4].trim().replaceAll('""','"')),k=d.findOrCreateNode(f[m-2].trim().replaceAll('""','"')),C=parseFloat(f[m].trim());d.addLink(y,k,C);break;case 8:case 9:case 11:this.$=f[m];break;case 10:this.$=f[m-1];break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},{5:[1,3]},{6:4,8:5,15:6,16:7,17:8,18:t,20:r},{1:[2,6],7:11,10:[1,12]},e(r,[2,4],{9:13,5:[1,14]}),{12:[1,15]},e(i,[2,8]),e(i,[2,9]),{19:[1,16]},e(i,[2,11]),{1:[2,1]},{1:[2,5]},e(r,[2,2]),{6:17,8:5,15:6,16:7,17:8,18:t,20:r},{15:18,16:7,17:8,18:t,20:r},{18:[1,19]},e(r,[2,3]),{12:[1,20]},e(i,[2,10]),{15:21,16:7,17:8,18:t,20:r},e([1,5,10],[2,7])],defaultActions:{11:[2,1],12:[2,5]},parseError:s(function(l,u){if(u.recoverable)this.trace(l);else{var h=new Error(l);throw h.hash=u,h}},"parseError"),parse:s(function(l){var u=this,h=[0],d=[],p=[null],f=[],g=this.table,m="",y=0,k=0,C=0,S=2,E=1,_=f.slice.call(arguments,1),b=Object.create(this.lexer),v={yy:{}};for(var x in this.yy)Object.prototype.hasOwnProperty.call(this.yy,x)&&(v.yy[x]=this.yy[x]);b.setInput(l,v.yy),v.yy.lexer=b,v.yy.parser=this,typeof b.yylloc>"u"&&(b.yylloc={});var w=b.yylloc;f.push(w);var R=b.options&&b.options.ranges;typeof v.yy.parseError=="function"?this.parseError=v.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function $(ie){h.length=h.length-2*ie,p.length=p.length-ie,f.length=f.length-ie}s($,"popStack");function M(){var ie;return ie=d.pop()||b.lex()||E,typeof ie!="number"&&(ie instanceof Array&&(d=ie,ie=d.pop()),ie=u.symbols_[ie]||ie),ie}s(M,"lex");for(var N,j,I,z,F,O,B={},Y,U,H,J;;){if(I=h[h.length-1],this.defaultActions[I]?z=this.defaultActions[I]:((N===null||typeof N>"u")&&(N=M()),z=g[I]&&g[I][N]),typeof z>"u"||!z.length||!z[0]){var te="";J=[];for(Y in g[I])this.terminals_[Y]&&Y>S&&J.push("'"+this.terminals_[Y]+"'");b.showPosition?te="Parse error on line "+(y+1)+`: +`+b.showPosition()+` +Expecting `+J.join(", ")+", got '"+(this.terminals_[N]||N)+"'":te="Parse error on line "+(y+1)+": Unexpected "+(N==E?"end of input":"'"+(this.terminals_[N]||N)+"'"),this.parseError(te,{text:b.match,token:this.terminals_[N]||N,line:b.yylineno,loc:w,expected:J})}if(z[0]instanceof Array&&z.length>1)throw new Error("Parse Error: multiple actions possible at state: "+I+", token: "+N);switch(z[0]){case 1:h.push(N),p.push(b.yytext),f.push(b.yylloc),h.push(z[1]),N=null,j?(N=j,j=null):(k=b.yyleng,m=b.yytext,y=b.yylineno,w=b.yylloc,C>0&&C--);break;case 2:if(U=this.productions_[z[1]][1],B.$=p[p.length-U],B._$={first_line:f[f.length-(U||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(U||1)].first_column,last_column:f[f.length-1].last_column},R&&(B._$.range=[f[f.length-(U||1)].range[0],f[f.length-1].range[1]]),O=this.performAction.apply(B,[m,k,y,v.yy,z[1],p,f].concat(_)),typeof O<"u")return O;U&&(h=h.slice(0,-1*U*2),p=p.slice(0,-1*U),f=f.slice(0,-1*U)),h.push(this.productions_[z[1]][0]),p.push(B.$),f.push(B._$),H=g[h[h.length-2]][h[h.length-1]],h.push(H);break;case 3:return!0}}return!0},"parse")},a=(function(){var l={EOF:1,parseError:s(function(u,h){if(this.yy.parser)this.yy.parser.parseError(u,h);else throw new Error(u)},"parseError"),setInput:s(function(u,h){return this.yy=h||this.yy||{},this._input=u,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var u=this._input[0];this.yytext+=u,this.yyleng++,this.offset++,this.match+=u,this.matched+=u;var h=u.match(/(?:\r\n?|\n).*/g);return h?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),u},"input"),unput:s(function(u){var h=u.length,d=u.split(/(?:\r\n?|\n)/g);this._input=u+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-h),this.offset-=h;var p=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),d.length-1&&(this.yylineno-=d.length-1);var f=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:d?(d.length===p.length?this.yylloc.first_column:0)+p[p.length-d.length].length-d[0].length:this.yylloc.first_column-h},this.options.ranges&&(this.yylloc.range=[f[0],f[0]+this.yyleng-h]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(u){this.unput(this.match.slice(u))},"less"),pastInput:s(function(){var u=this.matched.substr(0,this.matched.length-this.match.length);return(u.length>20?"...":"")+u.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var u=this.match;return u.length<20&&(u+=this._input.substr(0,20-u.length)),(u.substr(0,20)+(u.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var u=this.pastInput(),h=new Array(u.length+1).join("-");return u+this.upcomingInput()+` +`+h+"^"},"showPosition"),test_match:s(function(u,h){var d,p,f;if(this.options.backtrack_lexer&&(f={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(f.yylloc.range=this.yylloc.range.slice(0))),p=u[0].match(/(?:\r\n?|\n).*/g),p&&(this.yylineno+=p.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:p?p[p.length-1].length-p[p.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+u[0].length},this.yytext+=u[0],this.match+=u[0],this.matches=u,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(u[0].length),this.matched+=u[0],d=this.performAction.call(this,this.yy,this,h,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),d)return d;if(this._backtrack){for(var g in f)this[g]=f[g];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var u,h,d,p;this._more||(this.yytext="",this.match="");for(var f=this._currentRules(),g=0;gh[0].length)){if(h=d,p=g,this.options.backtrack_lexer){if(u=this.test_match(d,f[g]),u!==!1)return u;if(this._backtrack){h=!1;continue}else return!1}else if(!this.options.flex)break}return h?(u=this.test_match(h,f[p]),u!==!1?u:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var u=this.next();return u||this.lex()},"lex"),begin:s(function(u){this.conditionStack.push(u)},"begin"),popState:s(function(){var u=this.conditionStack.length-1;return u>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(u){return u=this.conditionStack.length-1-Math.abs(u||0),u>=0?this.conditionStack[u]:"INITIAL"},"topState"),pushState:s(function(u){this.begin(u)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(u,h,d,p){var f=p;switch(d){case 0:return this.pushState("csv"),4;case 1:return this.pushState("csv"),4;case 2:return 10;case 3:return 5;case 4:return 12;case 5:return this.pushState("escaped_text"),18;case 6:return 20;case 7:return this.popState("escaped_text"),18;case 8:return 19}},"anonymous"),rules:[/^(?:sankey-beta\b)/i,/^(?:sankey\b)/i,/^(?:$)/i,/^(?:((\u000D\u000A)|(\u000A)))/i,/^(?:(\u002C))/i,/^(?:(\u0022))/i,/^(?:([\u0020-\u0021\u0023-\u002B\u002D-\u007E])*)/i,/^(?:(\u0022)(?!(\u0022)))/i,/^(?:(([\u0020-\u0021\u0023-\u002B\u002D-\u007E])|(\u002C)|(\u000D)|(\u000A)|(\u0022)(\u0022))*)/i],conditions:{csv:{rules:[2,3,4,5,6,7,8],inclusive:!1},escaped_text:{rules:[7,8],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,8],inclusive:!0}}};return l})();n.lexer=a;function o(){this.yy={}}return s(o,"Parser"),o.prototype=n,n.Parser=o,new o})(),KI.parser=KI,z5=KI}),q5,W5,j5,Jgt,emt,tmt,rmt,imt,nmt,amt,smt,omt,d$t=G(()=>{"use strict";var e,t;Er(),mi(),fn(),q5=[],W5=[],j5=new Map,Jgt=s(()=>{q5=[],W5=[],j5=new Map,pn()},"clear"),emt=(e=class{constructor(i,n,a=0){this.source=i,this.target=n,this.value=a}},s(e,"SankeyLink"),e),tmt=s((r,i,n)=>{q5.push(new emt(r,i,n))},"addLink"),rmt=(t=class{constructor(i){this.ID=i}},s(t,"SankeyNode"),t),imt=s(r=>{r=Vt.sanitizeText(r,it());let i=j5.get(r);return i===void 0&&(i=new rmt(r),j5.set(r,i),W5.push(i)),i},"findOrCreateNode"),nmt=s(()=>W5,"getNodes"),amt=s(()=>q5,"getLinks"),smt=s(()=>({nodes:W5.map(r=>({id:r.ID})),links:q5.map(r=>({source:r.source.ID,target:r.target.ID,value:r.value}))}),"getGraph"),omt={nodesMap:j5,getConfig:s(()=>it().sankey,"getConfig"),getNodes:nmt,getLinks:amt,getGraph:smt,addLink:tmt,findOrCreateNode:imt,getAccTitle:Ln,setAccTitle:bn,getAccDescription:Mn,setAccDescription:In,getDiagramTitle:xn,setDiagramTitle:Dn,clear:Jgt}});function gpe(e,t){let r;if(t===void 0)for(let i of e)i!=null&&(r=i)&&(r=i);else{let i=-1;for(let n of e)(n=t(n,++i,e))!=null&&(r=n)&&(r=n)}return r}var p$t=G(()=>{"use strict";s(gpe,"max")});function mpe(e,t){let r;if(t===void 0)for(let i of e)i!=null&&(r>i||r===void 0&&i>=i)&&(r=i);else{let i=-1;for(let n of e)(n=t(n,++i,e))!=null&&(r>n||r===void 0&&n>=n)&&(r=n)}return r}var f$t=G(()=>{"use strict";s(mpe,"min")});function QI(e,t){let r=0;if(t===void 0)for(let i of e)(i=+i)&&(r+=i);else{let i=-1;for(let n of e)(n=+t(n,++i,e))&&(r+=n)}return r}var g$t=G(()=>{"use strict";s(QI,"sum")}),lmt=G(()=>{"use strict";p$t(),f$t(),g$t()});function cmt(e){return e.target.depth}function umt(e){return e.depth}function hmt(e,t){return t-1-e.height}function ype(e,t){return e.sourceLinks.length?e.depth:t-1}function dmt(e){return e.targetLinks.length?e.depth:e.sourceLinks.length?mpe(e.sourceLinks,cmt)-1:0}var pmt=G(()=>{"use strict";lmt(),s(cmt,"targetDepth"),s(umt,"left"),s(hmt,"right"),s(ype,"justify"),s(dmt,"center")});function V5(e){return function(){return e}}var m$t=G(()=>{"use strict";s(V5,"constant")});function vpe(e,t){return U5(e.source,t.source)||e.index-t.index}function bpe(e,t){return U5(e.target,t.target)||e.index-t.index}function U5(e,t){return e.y0-t.y0}function ZI(e){return e.value}function fmt(e){return e.index}function gmt(e){return e.nodes}function mmt(e){return e.links}function xpe(e,t){let r=e.get(t);if(!r)throw new Error("missing: "+t);return r}function Tpe({nodes:e}){for(let t of e){let r=t.y0,i=r;for(let n of t.sourceLinks)n.y0=r+n.width/2,r+=n.width;for(let n of t.targetLinks)n.y1=i+n.width/2,i+=n.width}}function ymt(){let e=0,t=0,r=1,i=1,n=24,a=8,o,l=fmt,u=ype,h,d,p=gmt,f=mmt,g=6;function m(){let z={nodes:p.apply(null,arguments),links:f.apply(null,arguments)};return y(z),k(z),C(z),S(z),b(z),Tpe(z),z}s(m,"sankey"),m.update=function(z){return Tpe(z),z},m.nodeId=function(z){return arguments.length?(l=typeof z=="function"?z:V5(z),m):l},m.nodeAlign=function(z){return arguments.length?(u=typeof z=="function"?z:V5(z),m):u},m.nodeSort=function(z){return arguments.length?(h=z,m):h},m.nodeWidth=function(z){return arguments.length?(n=+z,m):n},m.nodePadding=function(z){return arguments.length?(a=o=+z,m):a},m.nodes=function(z){return arguments.length?(p=typeof z=="function"?z:V5(z),m):p},m.links=function(z){return arguments.length?(f=typeof z=="function"?z:V5(z),m):f},m.linkSort=function(z){return arguments.length?(d=z,m):d},m.size=function(z){return arguments.length?(e=t=0,r=+z[0],i=+z[1],m):[r-e,i-t]},m.extent=function(z){return arguments.length?(e=+z[0][0],r=+z[1][0],t=+z[0][1],i=+z[1][1],m):[[e,t],[r,i]]},m.iterations=function(z){return arguments.length?(g=+z,m):g};function y({nodes:z,links:F}){for(let[B,Y]of z.entries())Y.index=B,Y.sourceLinks=[],Y.targetLinks=[];let O=new Map(z.map((B,Y)=>[l(B,Y,z),B]));for(let[B,Y]of F.entries()){Y.index=B;let{source:U,target:H}=Y;typeof U!="object"&&(U=Y.source=xpe(O,U)),typeof H!="object"&&(H=Y.target=xpe(O,H)),U.sourceLinks.push(Y),H.targetLinks.push(Y)}if(d!=null)for(let{sourceLinks:B,targetLinks:Y}of z)B.sort(d),Y.sort(d)}s(y,"computeNodeLinks");function k({nodes:z}){for(let F of z)F.value=F.fixedValue===void 0?Math.max(QI(F.sourceLinks,ZI),QI(F.targetLinks,ZI)):F.fixedValue}s(k,"computeNodeValues");function C({nodes:z}){let F=z.length,O=new Set(z),B=new Set,Y=0;for(;O.size;){for(let U of O){U.depth=Y;for(let{target:H}of U.sourceLinks)B.add(H)}if(++Y>F)throw new Error("circular link");O=B,B=new Set}}s(C,"computeNodeDepths");function S({nodes:z}){let F=z.length,O=new Set(z),B=new Set,Y=0;for(;O.size;){for(let U of O){U.height=Y;for(let{source:H}of U.targetLinks)B.add(H)}if(++Y>F)throw new Error("circular link");O=B,B=new Set}}s(S,"computeNodeHeights");function E({nodes:z}){let F=gpe(z,Y=>Y.depth)+1,O=(r-e-n)/(F-1),B=new Array(F);for(let Y of z){let U=Math.max(0,Math.min(F-1,Math.floor(u.call(null,Y,F))));Y.layer=U,Y.x0=e+U*O,Y.x1=Y.x0+n,B[U]?B[U].push(Y):B[U]=[Y]}if(h)for(let Y of B)Y.sort(h);return B}s(E,"computeNodeLayers");function _(z){let F=mpe(z,O=>(i-t-(O.length-1)*o)/QI(O,ZI));for(let O of z){let B=t;for(let Y of O){Y.y0=B,Y.y1=B+Y.value*F,B=Y.y1+o;for(let U of Y.sourceLinks)U.width=U.value*F}B=(i-B+o)/(O.length+1);for(let Y=0;YO.length)-1)),_(F);for(let O=0;O0))continue;let ie=(J/te-H.y0)*F;H.y0+=ie,H.y1+=ie,M(H)}h===void 0&&U.sort(U5),w(U,O)}}s(v,"relaxLeftToRight");function x(z,F,O){for(let B=z.length,Y=B-2;Y>=0;--Y){let U=z[Y];for(let H of U){let J=0,te=0;for(let{target:ue,value:se}of H.sourceLinks){let le=se*(ue.layer-H.layer);J+=I(H,ue)*le,te+=le}if(!(te>0))continue;let ie=(J/te-H.y0)*F;H.y0+=ie,H.y1+=ie,M(H)}h===void 0&&U.sort(U5),w(U,O)}}s(x,"relaxRightToLeft");function w(z,F){let O=z.length>>1,B=z[O];$(z,B.y0-o,O-1,F),R(z,B.y1+o,O+1,F),$(z,i,z.length-1,F),R(z,t,0,F)}s(w,"resolveCollisions");function R(z,F,O,B){for(;O1e-6&&(Y.y0+=U,Y.y1+=U),F=Y.y1+o}}s(R,"resolveCollisionsTopToBottom");function $(z,F,O,B){for(;O>=0;--O){let Y=z[O],U=(Y.y1-F)*B;U>1e-6&&(Y.y0-=U,Y.y1-=U),F=Y.y0-o}}s($,"resolveCollisionsBottomToTop");function M({sourceLinks:z,targetLinks:F}){if(d===void 0){for(let{source:{sourceLinks:O}}of F)O.sort(bpe);for(let{target:{targetLinks:O}}of z)O.sort(vpe)}}s(M,"reorderNodeLinks");function N(z){if(d===void 0)for(let{sourceLinks:F,targetLinks:O}of z)F.sort(bpe),O.sort(vpe)}s(N,"reorderLinks");function j(z,F){let O=z.y0-(z.sourceLinks.length-1)*o/2;for(let{target:B,width:Y}of z.sourceLinks){if(B===F)break;O+=Y+o}for(let{source:B,width:Y}of F.targetLinks){if(B===z)break;O-=Y}return O}s(j,"targetTop");function I(z,F){let O=F.y0-(F.targetLinks.length-1)*o/2;for(let{source:B,width:Y}of F.targetLinks){if(B===z)break;O+=Y+o}for(let{target:B,width:Y}of z.sourceLinks){if(B===F)break;O-=Y}return O}return s(I,"sourceTop"),m}var y$t=G(()=>{"use strict";lmt(),pmt(),m$t(),s(vpe,"ascendingSourceBreadth"),s(bpe,"ascendingTargetBreadth"),s(U5,"ascendingBreadth"),s(ZI,"value"),s(fmt,"defaultId"),s(gmt,"defaultNodes"),s(mmt,"defaultLinks"),s(xpe,"find"),s(Tpe,"computeLinkBreadths"),s(ymt,"Sankey")});function JI(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function kpe(){return new JI}var eM,tM,L0,vmt,bmt,v$t=G(()=>{"use strict";eM=Math.PI,tM=2*eM,L0=1e-6,vmt=tM-L0,s(JI,"Path"),s(kpe,"path"),JI.prototype=kpe.prototype={constructor:JI,moveTo:s(function(e,t){this._+="M"+(this._x0=this._x1=+e)+","+(this._y0=this._y1=+t)},"moveTo"),closePath:s(function(){this._x1!==null&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},"closePath"),lineTo:s(function(e,t){this._+="L"+(this._x1=+e)+","+(this._y1=+t)},"lineTo"),quadraticCurveTo:s(function(e,t,r,i){this._+="Q"+ +e+","+ +t+","+(this._x1=+r)+","+(this._y1=+i)},"quadraticCurveTo"),bezierCurveTo:s(function(e,t,r,i,n,a){this._+="C"+ +e+","+ +t+","+ +r+","+ +i+","+(this._x1=+n)+","+(this._y1=+a)},"bezierCurveTo"),arcTo:s(function(e,t,r,i,n){e=+e,t=+t,r=+r,i=+i,n=+n;var a=this._x1,o=this._y1,l=r-e,u=i-t,h=a-e,d=o-t,p=h*h+d*d;if(n<0)throw new Error("negative radius: "+n);if(this._x1===null)this._+="M"+(this._x1=e)+","+(this._y1=t);else if(p>L0)if(!(Math.abs(d*l-u*h)>L0)||!n)this._+="L"+(this._x1=e)+","+(this._y1=t);else{var f=r-a,g=i-o,m=l*l+u*u,y=f*f+g*g,k=Math.sqrt(m),C=Math.sqrt(p),S=n*Math.tan((eM-Math.acos((m+p-y)/(2*k*C)))/2),E=S/C,_=S/k;Math.abs(E-1)>L0&&(this._+="L"+(e+E*h)+","+(t+E*d)),this._+="A"+n+","+n+",0,0,"+ +(d*f>h*g)+","+(this._x1=e+_*l)+","+(this._y1=t+_*u)}},"arcTo"),arc:s(function(e,t,r,i,n,a){e=+e,t=+t,r=+r,a=!!a;var o=r*Math.cos(i),l=r*Math.sin(i),u=e+o,h=t+l,d=1^a,p=a?i-n:n-i;if(r<0)throw new Error("negative radius: "+r);this._x1===null?this._+="M"+u+","+h:(Math.abs(this._x1-u)>L0||Math.abs(this._y1-h)>L0)&&(this._+="L"+u+","+h),r&&(p<0&&(p=p%tM+tM),p>vmt?this._+="A"+r+","+r+",0,1,"+d+","+(e-o)+","+(t-l)+"A"+r+","+r+",0,1,"+d+","+(this._x1=u)+","+(this._y1=h):p>L0&&(this._+="A"+r+","+r+",0,"+ +(p>=eM)+","+d+","+(this._x1=e+r*Math.cos(n))+","+(this._y1=t+r*Math.sin(n))))},"arc"),rect:s(function(e,t,r,i){this._+="M"+(this._x0=this._x1=+e)+","+(this._y0=this._y1=+t)+"h"+ +r+"v"+ +i+"h"+-r+"Z"},"rect"),toString:s(function(){return this._},"toString")},bmt=kpe}),b$t=G(()=>{"use strict";v$t()});function Cpe(e){return s(function(){return e},"constant")}var x$t=G(()=>{"use strict";s(Cpe,"default")});function xmt(e){return e[0]}function Tmt(e){return e[1]}var T$t=G(()=>{"use strict";s(xmt,"x"),s(Tmt,"y")}),kmt,k$t=G(()=>{"use strict";kmt=Array.prototype.slice});function Cmt(e){return e.source}function wmt(e){return e.target}function Emt(e){var t=Cmt,r=wmt,i=xmt,n=Tmt,a=null;function o(){var l,u=kmt.call(arguments),h=t.apply(this,u),d=r.apply(this,u);if(a||(a=l=bmt()),e(a,+i.apply(this,(u[0]=h,u)),+n.apply(this,u),+i.apply(this,(u[0]=d,u)),+n.apply(this,u)),l)return a=null,l+""||null}return s(o,"link"),o.source=function(l){return arguments.length?(t=l,o):t},o.target=function(l){return arguments.length?(r=l,o):r},o.x=function(l){return arguments.length?(i=typeof l=="function"?l:Cpe(+l),o):i},o.y=function(l){return arguments.length?(n=typeof l=="function"?l:Cpe(+l),o):n},o.context=function(l){return arguments.length?(a=l??null,o):a},o}function Smt(e,t,r,i,n){e.moveTo(t,r),e.bezierCurveTo(t=(t+i)/2,r,t,n,i,n)}function _mt(){return Emt(Smt)}var C$t=G(()=>{"use strict";b$t(),k$t(),x$t(),T$t(),s(Cmt,"linkSource"),s(wmt,"linkTarget"),s(Emt,"link"),s(Smt,"curveHorizontal"),s(_mt,"linkHorizontal")}),w$t=G(()=>{"use strict";C$t()});function Amt(e){return[e.source.x1,e.y0]}function Rmt(e){return[e.target.x0,e.y1]}function $mt(){return _mt().source(Amt).target(Rmt)}var E$t=G(()=>{"use strict";w$t(),s(Amt,"horizontalSource"),s(Rmt,"horizontalTarget"),s($mt,"default")}),S$t=G(()=>{"use strict";y$t(),pmt(),E$t()}),wpe,_$t=G(()=>{"use strict";var e;wpe=(e=class{static next(r){return new e(r+ ++e.count)}constructor(r){this.id=r,this.href=`#${r}`}toString(){return"url("+this.href+")"}},s(e,"Uid"),e.count=0,e)}),Lmt,Imt,Mmt,Dmt,A$t=G(()=>{"use strict";Er(),pi(),S$t(),vn(),_$t(),Lmt={left:umt,right:hmt,center:dmt,justify:ype},Imt=s(e=>{let t=0,r=0;for(let i of e){let n=i.value??0;n>t&&(t=n,r=i.layer??0)}return r},"findCentralNodeLayer"),Mmt=s(function(e,t,r,i){let{securityLevel:n,sankey:a}=it(),o=qO.sankey,l;n==="sandbox"&&(l=zt("#i"+t));let u=zt(n==="sandbox"?l.nodes()[0].contentDocument.body:"body"),h=n==="sandbox"?u.select(`[id="${t}"]`):zt(`[id="${t}"]`),d=a?.width??o.width,p=a?.height??o.width,f=a?.useMaxWidth??o.useMaxWidth,g=a?.nodeAlignment??o.nodeAlignment,m=a?.prefix??o.prefix,y=a?.suffix??o.suffix,k=a?.showValues??o.showValues,C=a?.nodeWidth??o.nodeWidth??10,S=a?.nodePadding??o.nodePadding??12,E=a?.labelStyle??o.labelStyle??"legacy",_=a?.nodeColors??{},b=i.db.getGraph(),v=Lmt[g];ymt().nodeId(O=>O.id).nodeWidth(C).nodePadding(S+(k?15:0)).nodeAlign(v).extent([[0,0],[d,p]])(b);let x=Imt(b.nodes),w=Lp(eke),R=s(O=>_[O]??w(O),"getNodeColor");h.append("g").attr("class","nodes").selectAll(".node").data(b.nodes).join("g").attr("class","node").attr("id",O=>(O.uid=wpe.next("node-")).id).attr("transform",function(O){return"translate("+O.x0+","+O.y0+")"}).attr("x",O=>O.x0).attr("y",O=>O.y0).append("rect").attr("height",O=>O.y1-O.y0).attr("width",O=>O.x1-O.x0).attr("fill",O=>R(O.id));let $=s(({id:O,value:B})=>k?`${O} +${m}${Math.round(B*100)/100}${y}`:O,"getText"),M=s(O=>E==="outlined"?(O.layer??0)N.selectAll(O?`.${O}`:"text").data(b.nodes).join("text").attr("class",O??null).attr("x",B=>M(B).x).attr("y",B=>(B.y1+B.y0)/2).attr("dy",`${k?"0":"0.35"}em`).attr("text-anchor",B=>M(B).anchor).text($),"appendLabel");E==="outlined"?(j("sankey-label-bg"),j("sankey-label-fg")):j();let I=h.append("g").attr("class","links").attr("fill","none").attr("stroke-opacity",.5).selectAll(".link").data(b.links).join("g").attr("class","link").style("mix-blend-mode","multiply"),z=a?.linkColor??"gradient";if(z==="gradient"){let O=I.append("linearGradient").attr("id",B=>(B.uid=wpe.next("linearGradient-")).id).attr("gradientUnits","userSpaceOnUse").attr("x1",B=>B.source.x1).attr("x2",B=>B.target.x0);O.append("stop").attr("offset","0%").attr("stop-color",B=>R(B.source.id)),O.append("stop").attr("offset","100%").attr("stop-color",B=>R(B.target.id))}let F;switch(z){case"gradient":F=s(O=>O.uid,"coloring");break;case"source":F=s(O=>R(O.source.id),"coloring");break;case"target":F=s(O=>R(O.target.id),"coloring");break;default:F=z}I.append("path").attr("d",$mt()).attr("stroke",F).attr("stroke-width",O=>Math.max(1,O.width)),Q0(void 0,h,0,f)},"draw"),Dmt={draw:Mmt}}),Nmt,R$t=G(()=>{"use strict";Nmt=s(e=>e.replaceAll(/^[^\S\n\r]+|[^\S\n\r]+$/g,"").replaceAll(/([\n\r])+/g,` +`).trim(),"prepareTextForParsing")}),Omt,Bmt,$$t=G(()=>{"use strict";Omt=s(e=>`.label { + font-family: ${e.fontFamily}; + } + + .node-labels { + font-family: ${e.fontFamily}; + } + + /* Outlined label style - background stroke for better readability */ + .sankey-label-bg { + stroke: ${e.mainBkg||e.background||"#fff"}; + stroke-width: 4px; + stroke-linejoin: round; + paint-order: stroke; + } + + /* Foreground label text */ + .sankey-label-fg { + fill: ${e.textColor}; + } + + /* Node styling */ + .node rect { + shape-rendering: crispEdges; + } + + /* Link styling */ + .link { + fill: none; + stroke-opacity: 0.5; + mix-blend-mode: multiply; + } +`,"getStyles"),Bmt=Omt}),Pmt={};Mr(Pmt,{diagram:()=>zmt});var Fmt,zmt,L$t=G(()=>{"use strict";h$t(),d$t(),A$t(),R$t(),$$t(),Fmt=z5.parse.bind(z5),z5.parse=e=>Fmt(Nmt(e)),zmt={styles:Bmt,parser:z5,db:omt,renderer:Dmt}}),qmt,Epe,Wmt=G(()=>{"use strict";var e;Gr(),Za(),Ar(),fn(),qmt=vi.packet,Epe=(e=class{constructor(){this.packet=[],this.setAccTitle=bn,this.getAccTitle=Ln,this.setDiagramTitle=Dn,this.getDiagramTitle=xn,this.getAccDescription=Mn,this.setAccDescription=In}getConfig(){let r=Gn({...qmt,...Rr().packet});return r.showBits&&(r.paddingY+=10),r}getPacket(){return this.packet}pushWord(r){r.length>0&&this.packet.push(r)}clear(){pn(),this.packet=[]}},s(e,"PacketDB"),e)}),jmt,Vmt,Umt,Spe,I$t=G(()=>{"use strict";Co(),Nt(),vl(),Wmt(),jmt=1e4,Vmt=s((e,t)=>{Yo(e,t);let r=-1,i=[],n=1,{bitsPerRow:a}=t.getConfig();for(let{start:o,end:l,bits:u,label:h}of e.blocks){if(o!==void 0&&l!==void 0&&l{if(e.start===void 0)throw new Error("start should have been set during first phase");if(e.end===void 0)throw new Error("end should have been set during first phase");if(e.start>e.end)throw new Error(`Block start ${e.start} is greater than block end ${e.end}.`);if(e.end+1<=t*r)return[e,void 0];let i=t*r-1,n=t*r;return[{start:e.start,end:i,label:e.label,bits:i-e.start},{start:n,end:e.end,label:e.label,bits:e.end-n}]},"getNextFittingBlock"),Spe={parser:{yy:void 0},parse:s(async e=>{let t=await Pl("packet",e),r=Spe.parser?.yy;if(!(r instanceof Epe))throw new Error("parser.parser?.yy was not a PacketDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.");Ee.debug(t),Vmt(t,r)},"parse")}}),Ymt,Hmt,Gmt,M$t=G(()=>{"use strict";wo(),vn(),Ymt=s((e,t,r,i)=>{let n=i.db,a=n.getConfig(),{rowHeight:o,paddingY:l,bitWidth:u,bitsPerRow:h}=a,d=n.getPacket(),p=n.getDiagramTitle(),f=o+l,g=f*(d.length+1)-(p?0:o),m=u*h+2,y=Ks(t);y.attr("viewBox",`0 0 ${m} ${g}`),aa(y,g,m,a.useMaxWidth);for(let[k,C]of d.entries())Hmt(y,C,k,a);y.append("text").text(p).attr("x",m/2).attr("y",g-f/2).attr("dominant-baseline","middle").attr("text-anchor","middle").attr("class","packetTitle")},"draw"),Hmt=s((e,t,r,{rowHeight:i,paddingX:n,paddingY:a,bitWidth:o,bitsPerRow:l,showBits:u})=>{let h=e.append("g"),d=r*(i+a)+a;for(let p of t){let f=p.start%l*o+1,g=(p.end-p.start+1)*o-n;if(h.append("rect").attr("x",f).attr("y",d).attr("width",g).attr("height",i).attr("class","packetBlock"),h.append("text").attr("x",f+g/2).attr("y",d+i/2).attr("class","packetLabel").attr("dominant-baseline","middle").attr("text-anchor","middle").text(p.label),!u)continue;let m=p.end===p.start,y=d-2;h.append("text").attr("x",f+(m?g/2:0)).attr("y",y).attr("class","packetByte start").attr("dominant-baseline","auto").attr("text-anchor",m?"middle":"start").text(p.start),m||h.append("text").attr("x",f+g).attr("y",y).attr("class","packetByte end").attr("dominant-baseline","auto").attr("text-anchor","end").text(p.end)}},"drawWord"),Gmt={draw:Ymt}}),Xmt,Kmt,D$t=G(()=>{"use strict";Ar(),Xmt={byteFontSize:"10px",startByteColor:"black",endByteColor:"black",labelColor:"black",labelFontSize:"12px",titleColor:"black",titleFontSize:"14px",blockStrokeColor:"black",blockStrokeWidth:"1",blockFillColor:"#efefef"},Kmt=s(({packet:e}={})=>{let t=Gn(Xmt,e);return` + .packetByte { + font-size: ${t.byteFontSize}; + } + .packetByte.start { + fill: ${t.startByteColor}; + } + .packetByte.end { + fill: ${t.endByteColor}; + } + .packetLabel { + fill: ${t.labelColor}; + font-size: ${t.labelFontSize}; + } + .packetTitle { + fill: ${t.titleColor}; + font-size: ${t.titleFontSize}; + } + .packetBlock { + stroke: ${t.blockStrokeColor}; + stroke-width: ${t.blockStrokeWidth}; + fill: ${t.blockFillColor}; + } + `},"styles")}),Qmt={};Mr(Qmt,{diagram:()=>Zmt});var Zmt,N$t=G(()=>{"use strict";Wmt(),I$t(),M$t(),D$t(),Zmt={parser:Spe,get db(){return new Epe},renderer:Gmt,styles:Kmt}}),O1,Y5,_pe,fu,Jmt,e1t,Ape,t1t,r1t,i1t,n1t,a1t,s1t,o1t,tx,l1t=G(()=>{"use strict";Gr(),Za(),Ar(),fn(),Nt(),O1={showLegend:!0,ticks:5,max:null,min:0,graticule:"circle"},Y5=32,_pe={axes:[],curves:[],options:O1},fu=structuredClone(_pe),Jmt=vi.radar,e1t=s(()=>Gn({...Jmt,...Rr().radar}),"getConfig"),Ape=s(()=>fu.axes,"getAxes"),t1t=s(()=>fu.curves,"getCurves"),r1t=s(()=>fu.options,"getOptions"),i1t=s(e=>{fu.axes=e.map(t=>({name:t.name,label:t.label??t.name}))},"setAxes"),n1t=s(e=>{fu.curves=e.map(t=>({name:t.name,label:t.label??t.name,entries:a1t(t.entries)}))},"setCurves"),a1t=s(e=>{if(e[0].axis==null)return e.map(r=>r.value);let t=Ape();if(t.length===0)throw new Error("Axes must be populated before curves for reference entries");return t.map(r=>{let i=e.find(n=>n.axis?.$refText===r.name);if(i===void 0)throw new Error("Missing entry for axis "+r.label);return i.value})},"computeCurveEntries"),s1t=s(e=>{let t=e.reduce((r,i)=>(r[i.name]=i,r),{});fu.options={showLegend:t.showLegend?.value??O1.showLegend,ticks:t.ticks?.value??O1.ticks,max:t.max?.value??O1.max,min:t.min?.value??O1.min,graticule:t.graticule?.value??O1.graticule},fu.options.ticks>Y5&&(Ee.warn(`Radar diagram ticks (${fu.options.ticks}) exceeds maximum allowed (${Y5}). Using ${Y5} instead.`),fu.options.ticks=Y5)},"setOptions"),o1t=s(()=>{pn(),fu=structuredClone(_pe)},"clear"),tx={getAxes:Ape,getCurves:t1t,getOptions:r1t,setAxes:i1t,setCurves:n1t,setOptions:s1t,getConfig:e1t,clear:o1t,setAccTitle:bn,getAccTitle:Ln,setDiagramTitle:Dn,getDiagramTitle:xn,getAccDescription:Mn,setAccDescription:In}}),c1t,u1t,O$t=G(()=>{"use strict";Co(),Nt(),vl(),l1t(),c1t=s(e=>{Yo(e,tx);let{axes:t,curves:r,options:i}=e;tx.setAxes(t),tx.setCurves(r),tx.setOptions(i)},"populate"),u1t={parse:s(async e=>{let t=await Pl("radar",e);Ee.debug(t),c1t(t)},"parse")}});function h1t(e,t,r,i,n,a,o){let l=t.length,u=Math.min(o.width,o.height)/2;r.forEach((h,d)=>{if(h.entries.length!==l)return;let p=h.entries.map((f,g)=>{let m=2*Math.PI*g/l-Math.PI/2,y=d1t(f,i,n,u),k=y*Math.cos(m),C=y*Math.sin(m);return{x:k,y:C}});a==="circle"?e.append("path").attr("d",p1t(p,o.curveTension)).attr("class",`radarCurve-${d}`):a==="polygon"&&e.append("polygon").attr("points",p.map(f=>`${f.x},${f.y}`).join(" ")).attr("class",`radarCurve-${d}`)})}function d1t(e,t,r,i){let n=Math.min(Math.max(e,t),r);return i*(n-t)/(r-t)}function p1t(e,t){let r=e.length,i=`M${e[0].x},${e[0].y}`;for(let n=0;n{let h=e.append("g").attr("transform",`translate(${n}, ${a+u*o})`);h.append("rect").attr("width",12).attr("height",12).attr("class",`radarLegendBox-${u}`),h.append("text").attr("x",16).attr("y",0).attr("class","radarLegendText").text(l.label)})}var g1t,m1t,y1t,v1t,b1t,B$t=G(()=>{"use strict";wo(),vn(),g1t=s((e,t,r,i)=>{let n=i.db,a=n.getAxes(),o=n.getCurves(),l=n.getOptions(),u=n.getConfig(),h=n.getDiagramTitle(),d=Ks(t),p=m1t(d,u),f=l.max??Math.max(...o.map(y=>Math.max(...y.entries))),g=l.min,m=Math.min(u.width,u.height)/2;y1t(p,a,m,l.ticks,l.graticule),v1t(p,a,m,u),h1t(p,a,o,g,f,l.graticule,u),f1t(p,o,l.showLegend,u),p.append("text").attr("class","radarTitle").text(h).attr("x",0).attr("y",-u.height/2-u.marginTop)},"draw"),m1t=s((e,t)=>{let r=t.width+t.marginLeft+t.marginRight,i=t.height+t.marginTop+t.marginBottom,n={x:t.marginLeft+t.width/2,y:t.marginTop+t.height/2};return aa(e,i,r,t.useMaxWidth??!0),e.attr("viewBox",`0 0 ${r} ${i}`).attr("overflow","visible"),e.append("g").attr("transform",`translate(${n.x}, ${n.y})`)},"drawFrame"),y1t=s((e,t,r,i,n)=>{if(n==="circle")for(let a=0;a{let p=2*d*Math.PI/a-Math.PI/2,f=l*Math.cos(p),g=l*Math.sin(p);return`${f},${g}`}).join(" ");e.append("polygon").attr("points",u).attr("class","radarGraticule")}}},"drawGraticule"),v1t=s((e,t,r,i)=>{let n=t.length;for(let a=0;a.01?"start":u<-.01?"end":"middle",p=h>.01?"hanging":h<-.01?"auto":"central",f=4;e.append("text").text(o).attr("x",r*i.axisLabelFactor*u+f*u).attr("y",r*i.axisLabelFactor*h+f*h).attr("text-anchor",d).attr("dominant-baseline",p).attr("class","radarAxisLabel")}},"drawAxes"),s(h1t,"drawCurves"),s(d1t,"relativeRadius"),s(p1t,"closedRoundCurve"),s(f1t,"drawLegend"),b1t={draw:g1t}}),x1t,T1t,k1t,P$t=G(()=>{"use strict";Ar(),Wh(),Gr(),x1t=s((e,t)=>{let r="";for(let i=0;i{let t=ku(),r=Rr(),i=Gn(t,r.themeVariables),n=Gn(i.radar,e);return{themeVariables:i,radarOptions:n}},"buildRadarStyleOptions"),k1t=s(({radar:e}={})=>{let{themeVariables:t,radarOptions:r}=T1t(e);return` + .radarTitle { + font-size: ${t.fontSize}; + color: ${t.titleColor}; + dominant-baseline: hanging; + text-anchor: middle; + } + .radarAxisLine { + stroke: ${r.axisColor}; + stroke-width: ${r.axisStrokeWidth}; + } + .radarAxisLabel { + font-size: ${r.axisLabelFontSize}px; + color: ${r.axisColor}; + } + .radarGraticule { + fill: ${r.graticuleColor}; + fill-opacity: ${r.graticuleOpacity}; + stroke: ${r.graticuleColor}; + stroke-width: ${r.graticuleStrokeWidth}; + } + .radarLegendText { + text-anchor: start; + font-size: ${r.legendFontSize}px; + dominant-baseline: hanging; + } + ${x1t(t,r)} + `},"styles")}),C1t={};Mr(C1t,{diagram:()=>w1t});var w1t,F$t=G(()=>{"use strict";l1t(),O$t(),B$t(),P$t(),w1t={parser:u1t,db:tx,renderer:b1t,styles:k1t}}),rM,E1t,z$t=G(()=>{"use strict";rM=(function(){var e=s(function(E,_,b,v){for(b=b||{},v=E.length;v--;b[E[v]]=_);return b},"o"),t=[1,15],r=[1,7],i=[1,13],n=[1,14],a=[1,19],o=[1,16],l=[1,17],u=[1,18],h=[8,30],d=[8,10,21,28,29,30,31,39,43,46],p=[1,23],f=[1,24],g=[8,10,15,16,21,28,29,30,31,39,43,46],m=[8,10,15,16,21,27,28,29,30,31,39,43,46],y=[1,49],k={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,spaceLines:3,SPACELINE:4,NL:5,separator:6,SPACE:7,EOF:8,start:9,BLOCK_DIAGRAM_KEY:10,document:11,stop:12,statement:13,link:14,LINK:15,START_LINK:16,LINK_LABEL:17,STR:18,nodeStatement:19,columnsStatement:20,SPACE_BLOCK:21,blockStatement:22,classDefStatement:23,cssClassStatement:24,styleStatement:25,node:26,SIZE:27,COLUMNS:28,"id-block":29,end:30,NODE_ID:31,nodeShapeNLabel:32,dirList:33,DIR:34,NODE_DSTART:35,NODE_DEND:36,BLOCK_ARROW_START:37,BLOCK_ARROW_END:38,classDef:39,CLASSDEF_ID:40,CLASSDEF_STYLEOPTS:41,DEFAULT:42,class:43,CLASSENTITY_IDS:44,STYLECLASS:45,style:46,STYLE_ENTITY_IDS:47,STYLE_DEFINITION_DATA:48,$accept:0,$end:1},terminals_:{2:"error",4:"SPACELINE",5:"NL",7:"SPACE",8:"EOF",10:"BLOCK_DIAGRAM_KEY",15:"LINK",16:"START_LINK",17:"LINK_LABEL",18:"STR",21:"SPACE_BLOCK",27:"SIZE",28:"COLUMNS",29:"id-block",30:"end",31:"NODE_ID",34:"DIR",35:"NODE_DSTART",36:"NODE_DEND",37:"BLOCK_ARROW_START",38:"BLOCK_ARROW_END",39:"classDef",40:"CLASSDEF_ID",41:"CLASSDEF_STYLEOPTS",42:"DEFAULT",43:"class",44:"CLASSENTITY_IDS",45:"STYLECLASS",46:"style",47:"STYLE_ENTITY_IDS",48:"STYLE_DEFINITION_DATA"},productions_:[0,[3,1],[3,2],[3,2],[6,1],[6,1],[6,1],[9,3],[12,1],[12,1],[12,2],[12,2],[11,1],[11,2],[14,1],[14,4],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[19,3],[19,2],[19,1],[20,1],[22,4],[22,3],[26,1],[26,2],[33,1],[33,2],[32,3],[32,4],[23,3],[23,3],[24,3],[25,3]],performAction:s(function(E,_,b,v,x,w,R){var $=w.length-1;switch(x){case 4:v.getLogger().debug("Rule: separator (NL) ");break;case 5:v.getLogger().debug("Rule: separator (Space) ");break;case 6:v.getLogger().debug("Rule: separator (EOF) ");break;case 7:v.getLogger().debug("Rule: hierarchy: ",w[$-1]),v.setHierarchy(w[$-1]);break;case 8:v.getLogger().debug("Stop NL ");break;case 9:v.getLogger().debug("Stop EOF ");break;case 10:v.getLogger().debug("Stop NL2 ");break;case 11:v.getLogger().debug("Stop EOF2 ");break;case 12:v.getLogger().debug("Rule: statement: ",w[$]),typeof w[$].length=="number"?this.$=w[$]:this.$=[w[$]];break;case 13:v.getLogger().debug("Rule: statement #2: ",w[$-1]),this.$=[w[$-1]].concat(w[$]);break;case 14:v.getLogger().debug("Rule: link: ",w[$],E),this.$={edgeTypeStr:w[$],label:""};break;case 15:v.getLogger().debug("Rule: LABEL link: ",w[$-3],w[$-1],w[$]),this.$={edgeTypeStr:w[$],label:w[$-1]};break;case 18:let M=parseInt(w[$]),N=v.generateId();this.$={id:N,type:"space",label:"",width:M,children:[]};break;case 23:v.getLogger().debug("Rule: (nodeStatement link node) ",w[$-2],w[$-1],w[$]," typestr: ",w[$-1].edgeTypeStr);let j=v.edgeStrToEdgeData(w[$-1].edgeTypeStr),I=v.edgeStrToEdgeStartData(w[$-1].edgeTypeStr),z=v.edgeStrToThickness(w[$-1].edgeTypeStr),F=v.edgeStrToPattern(w[$-1].edgeTypeStr);this.$=[{id:w[$-2].id,label:w[$-2].label,type:w[$-2].type,directions:w[$-2].directions},{id:w[$-2].id+"-"+w[$].id,start:w[$-2].id,end:w[$].id,label:w[$-1].label,type:"edge",thickness:z,pattern:F,directions:w[$].directions,arrowTypeEnd:j,arrowTypeStart:I},{id:w[$].id,label:w[$].label,type:v.typeStr2Type(w[$].typeStr),directions:w[$].directions}];break;case 24:v.getLogger().debug("Rule: nodeStatement (abc88 node size) ",w[$-1],w[$]),this.$={id:w[$-1].id,label:w[$-1].label,type:v.typeStr2Type(w[$-1].typeStr),directions:w[$-1].directions,widthInColumns:parseInt(w[$],10)};break;case 25:v.getLogger().debug("Rule: nodeStatement (node) ",w[$]),this.$={id:w[$].id,label:w[$].label,type:v.typeStr2Type(w[$].typeStr),directions:w[$].directions,widthInColumns:1};break;case 26:v.getLogger().debug("APA123",this?this:"na"),v.getLogger().debug("COLUMNS: ",w[$]),this.$={type:"column-setting",columns:w[$]==="auto"?-1:parseInt(w[$])};break;case 27:v.getLogger().debug("Rule: id-block statement : ",w[$-2],w[$-1]);let O=v.generateId();this.$={...w[$-2],type:"composite",children:w[$-1]};break;case 28:v.getLogger().debug("Rule: blockStatement : ",w[$-2],w[$-1],w[$]);let B=v.generateId();this.$={id:B,type:"composite",label:"",children:w[$-1]};break;case 29:v.getLogger().debug("Rule: node (NODE_ID separator): ",w[$]),this.$={id:w[$]};break;case 30:v.getLogger().debug("Rule: node (NODE_ID nodeShapeNLabel separator): ",w[$-1],w[$]),this.$={id:w[$-1],label:w[$].label,typeStr:w[$].typeStr,directions:w[$].directions};break;case 31:v.getLogger().debug("Rule: dirList: ",w[$]),this.$=[w[$]];break;case 32:v.getLogger().debug("Rule: dirList: ",w[$-1],w[$]),this.$=[w[$-1]].concat(w[$]);break;case 33:v.getLogger().debug("Rule: nodeShapeNLabel: ",w[$-2],w[$-1],w[$]),this.$={typeStr:w[$-2]+w[$],label:w[$-1]};break;case 34:v.getLogger().debug("Rule: BLOCK_ARROW nodeShapeNLabel: ",w[$-3],w[$-2]," #3:",w[$-1],w[$]),this.$={typeStr:w[$-3]+w[$],label:w[$-2],directions:w[$-1]};break;case 35:case 36:this.$={type:"classDef",id:w[$-1].trim(),css:w[$].trim()};break;case 37:this.$={type:"applyClass",id:w[$-1].trim(),styleClass:w[$].trim()};break;case 38:this.$={type:"applyStyles",id:w[$-1].trim(),stylesStr:w[$].trim()};break}},"anonymous"),table:[{9:1,10:[1,2]},{1:[3]},{10:t,11:3,13:4,19:5,20:6,21:r,22:8,23:9,24:10,25:11,26:12,28:i,29:n,31:a,39:o,43:l,46:u},{8:[1,20]},e(h,[2,12],{13:4,19:5,20:6,22:8,23:9,24:10,25:11,26:12,11:21,10:t,21:r,28:i,29:n,31:a,39:o,43:l,46:u}),e(d,[2,16],{14:22,15:p,16:f}),e(d,[2,17]),e(d,[2,18]),e(d,[2,19]),e(d,[2,20]),e(d,[2,21]),e(d,[2,22]),e(g,[2,25],{27:[1,25]}),e(d,[2,26]),{19:26,26:12,31:a},{10:t,11:27,13:4,19:5,20:6,21:r,22:8,23:9,24:10,25:11,26:12,28:i,29:n,31:a,39:o,43:l,46:u},{40:[1,28],42:[1,29]},{44:[1,30]},{47:[1,31]},e(m,[2,29],{32:32,35:[1,33],37:[1,34]}),{1:[2,7]},e(h,[2,13]),{26:35,31:a},{31:[2,14]},{17:[1,36]},e(g,[2,24]),{10:t,11:37,13:4,14:22,15:p,16:f,19:5,20:6,21:r,22:8,23:9,24:10,25:11,26:12,28:i,29:n,31:a,39:o,43:l,46:u},{30:[1,38]},{41:[1,39]},{41:[1,40]},{45:[1,41]},{48:[1,42]},e(m,[2,30]),{18:[1,43]},{18:[1,44]},e(g,[2,23]),{18:[1,45]},{30:[1,46]},e(d,[2,28]),e(d,[2,35]),e(d,[2,36]),e(d,[2,37]),e(d,[2,38]),{36:[1,47]},{33:48,34:y},{15:[1,50]},e(d,[2,27]),e(m,[2,33]),{38:[1,51]},{33:52,34:y,38:[2,31]},{31:[2,15]},e(m,[2,34]),{38:[2,32]}],defaultActions:{20:[2,7],23:[2,14],50:[2,15],52:[2,32]},parseError:s(function(E,_){if(_.recoverable)this.trace(E);else{var b=new Error(E);throw b.hash=_,b}},"parseError"),parse:s(function(E){var _=this,b=[0],v=[],x=[null],w=[],R=this.table,$="",M=0,N=0,j=0,I=2,z=1,F=w.slice.call(arguments,1),O=Object.create(this.lexer),B={yy:{}};for(var Y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,Y)&&(B.yy[Y]=this.yy[Y]);O.setInput(E,B.yy),B.yy.lexer=O,B.yy.parser=this,typeof O.yylloc>"u"&&(O.yylloc={});var U=O.yylloc;w.push(U);var H=O.options&&O.options.ranges;typeof B.yy.parseError=="function"?this.parseError=B.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function J(Me){b.length=b.length-2*Me,x.length=x.length-Me,w.length=w.length-Me}s(J,"popStack");function te(){var Me;return Me=v.pop()||O.lex()||z,typeof Me!="number"&&(Me instanceof Array&&(v=Me,Me=v.pop()),Me=_.symbols_[Me]||Me),Me}s(te,"lex");for(var ie,ue,se,le,Te,oe,fe={},ge,ke,Ne,xe;;){if(se=b[b.length-1],this.defaultActions[se]?le=this.defaultActions[se]:((ie===null||typeof ie>"u")&&(ie=te()),le=R[se]&&R[se][ie]),typeof le>"u"||!le.length||!le[0]){var $e="";xe=[];for(ge in R[se])this.terminals_[ge]&&ge>I&&xe.push("'"+this.terminals_[ge]+"'");O.showPosition?$e="Parse error on line "+(M+1)+`: +`+O.showPosition()+` +Expecting `+xe.join(", ")+", got '"+(this.terminals_[ie]||ie)+"'":$e="Parse error on line "+(M+1)+": Unexpected "+(ie==z?"end of input":"'"+(this.terminals_[ie]||ie)+"'"),this.parseError($e,{text:O.match,token:this.terminals_[ie]||ie,line:O.yylineno,loc:U,expected:xe})}if(le[0]instanceof Array&&le.length>1)throw new Error("Parse Error: multiple actions possible at state: "+se+", token: "+ie);switch(le[0]){case 1:b.push(ie),x.push(O.yytext),w.push(O.yylloc),b.push(le[1]),ie=null,ue?(ie=ue,ue=null):(N=O.yyleng,$=O.yytext,M=O.yylineno,U=O.yylloc,j>0&&j--);break;case 2:if(ke=this.productions_[le[1]][1],fe.$=x[x.length-ke],fe._$={first_line:w[w.length-(ke||1)].first_line,last_line:w[w.length-1].last_line,first_column:w[w.length-(ke||1)].first_column,last_column:w[w.length-1].last_column},H&&(fe._$.range=[w[w.length-(ke||1)].range[0],w[w.length-1].range[1]]),oe=this.performAction.apply(fe,[$,N,M,B.yy,le[1],x,w].concat(F)),typeof oe<"u")return oe;ke&&(b=b.slice(0,-1*ke*2),x=x.slice(0,-1*ke),w=w.slice(0,-1*ke)),b.push(this.productions_[le[1]][0]),x.push(fe.$),w.push(fe._$),Ne=R[b[b.length-2]][b[b.length-1]],b.push(Ne);break;case 3:return!0}}return!0},"parse")},C=(function(){var E={EOF:1,parseError:s(function(_,b){if(this.yy.parser)this.yy.parser.parseError(_,b);else throw new Error(_)},"parseError"),setInput:s(function(_,b){return this.yy=b||this.yy||{},this._input=_,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var _=this._input[0];this.yytext+=_,this.yyleng++,this.offset++,this.match+=_,this.matched+=_;var b=_.match(/(?:\r\n?|\n).*/g);return b?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),_},"input"),unput:s(function(_){var b=_.length,v=_.split(/(?:\r\n?|\n)/g);this._input=_+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-b),this.offset-=b;var x=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),v.length-1&&(this.yylineno-=v.length-1);var w=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:v?(v.length===x.length?this.yylloc.first_column:0)+x[x.length-v.length].length-v[0].length:this.yylloc.first_column-b},this.options.ranges&&(this.yylloc.range=[w[0],w[0]+this.yyleng-b]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(_){this.unput(this.match.slice(_))},"less"),pastInput:s(function(){var _=this.matched.substr(0,this.matched.length-this.match.length);return(_.length>20?"...":"")+_.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var _=this.match;return _.length<20&&(_+=this._input.substr(0,20-_.length)),(_.substr(0,20)+(_.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var _=this.pastInput(),b=new Array(_.length+1).join("-");return _+this.upcomingInput()+` +`+b+"^"},"showPosition"),test_match:s(function(_,b){var v,x,w;if(this.options.backtrack_lexer&&(w={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(w.yylloc.range=this.yylloc.range.slice(0))),x=_[0].match(/(?:\r\n?|\n).*/g),x&&(this.yylineno+=x.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:x?x[x.length-1].length-x[x.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+_[0].length},this.yytext+=_[0],this.match+=_[0],this.matches=_,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(_[0].length),this.matched+=_[0],v=this.performAction.call(this,this.yy,this,b,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),v)return v;if(this._backtrack){for(var R in w)this[R]=w[R];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var _,b,v,x;this._more||(this.yytext="",this.match="");for(var w=this._currentRules(),R=0;Rb[0].length)){if(b=v,x=R,this.options.backtrack_lexer){if(_=this.test_match(v,w[R]),_!==!1)return _;if(this._backtrack){b=!1;continue}else return!1}else if(!this.options.flex)break}return b?(_=this.test_match(b,w[x]),_!==!1?_:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var _=this.next();return _||this.lex()},"lex"),begin:s(function(_){this.conditionStack.push(_)},"begin"),popState:s(function(){var _=this.conditionStack.length-1;return _>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(_){return _=this.conditionStack.length-1-Math.abs(_||0),_>=0?this.conditionStack[_]:"INITIAL"},"topState"),pushState:s(function(_){this.begin(_)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:s(function(_,b,v,x){var w=x;switch(v){case 0:return _.getLogger().debug("Found block-beta"),10;case 1:return _.getLogger().debug("Found id-block"),29;case 2:return _.getLogger().debug("Found block"),10;case 3:_.getLogger().debug(".",b.yytext);break;case 4:_.getLogger().debug("_",b.yytext);break;case 5:return 5;case 6:return b.yytext=-1,28;case 7:return b.yytext=b.yytext.replace(/columns\s+/,""),_.getLogger().debug("COLUMNS (LEX)",b.yytext),28;case 8:this.pushState("md_string");break;case 9:return"MD_STR";case 10:this.popState();break;case 11:this.pushState("string");break;case 12:_.getLogger().debug("LEX: POPPING STR:",b.yytext),this.popState();break;case 13:return _.getLogger().debug("LEX: STR end:",b.yytext),"STR";case 14:return b.yytext=b.yytext.replace(/space\:/,""),_.getLogger().debug("SPACE NUM (LEX)",b.yytext),21;case 15:return b.yytext="1",_.getLogger().debug("COLUMNS (LEX)",b.yytext),21;case 16:return 42;case 17:return"LINKSTYLE";case 18:return"INTERPOLATE";case 19:return this.pushState("CLASSDEF"),39;case 20:return this.popState(),this.pushState("CLASSDEFID"),"DEFAULT_CLASSDEF_ID";case 21:return this.popState(),this.pushState("CLASSDEFID"),40;case 22:return this.popState(),41;case 23:return this.pushState("CLASS"),43;case 24:return this.popState(),this.pushState("CLASS_STYLE"),44;case 25:return this.popState(),45;case 26:return this.pushState("STYLE_STMNT"),46;case 27:return this.popState(),this.pushState("STYLE_DEFINITION"),47;case 28:return this.popState(),48;case 29:return this.pushState("acc_title"),"acc_title";case 30:return this.popState(),"acc_title_value";case 31:return this.pushState("acc_descr"),"acc_descr";case 32:return this.popState(),"acc_descr_value";case 33:this.pushState("acc_descr_multiline");break;case 34:this.popState();break;case 35:return"acc_descr_multiline_value";case 36:return 30;case 37:return this.popState(),_.getLogger().debug("Lex: (("),"NODE_DEND";case 38:return this.popState(),_.getLogger().debug("Lex: (("),"NODE_DEND";case 39:return this.popState(),_.getLogger().debug("Lex: ))"),"NODE_DEND";case 40:return this.popState(),_.getLogger().debug("Lex: (("),"NODE_DEND";case 41:return this.popState(),_.getLogger().debug("Lex: (("),"NODE_DEND";case 42:return this.popState(),_.getLogger().debug("Lex: (-"),"NODE_DEND";case 43:return this.popState(),_.getLogger().debug("Lex: -)"),"NODE_DEND";case 44:return this.popState(),_.getLogger().debug("Lex: (("),"NODE_DEND";case 45:return this.popState(),_.getLogger().debug("Lex: ]]"),"NODE_DEND";case 46:return this.popState(),_.getLogger().debug("Lex: ("),"NODE_DEND";case 47:return this.popState(),_.getLogger().debug("Lex: ])"),"NODE_DEND";case 48:return this.popState(),_.getLogger().debug("Lex: /]"),"NODE_DEND";case 49:return this.popState(),_.getLogger().debug("Lex: /]"),"NODE_DEND";case 50:return this.popState(),_.getLogger().debug("Lex: )]"),"NODE_DEND";case 51:return this.popState(),_.getLogger().debug("Lex: )"),"NODE_DEND";case 52:return this.popState(),_.getLogger().debug("Lex: ]>"),"NODE_DEND";case 53:return this.popState(),_.getLogger().debug("Lex: ]"),"NODE_DEND";case 54:return _.getLogger().debug("Lexa: -)"),this.pushState("NODE"),35;case 55:return _.getLogger().debug("Lexa: (-"),this.pushState("NODE"),35;case 56:return _.getLogger().debug("Lexa: ))"),this.pushState("NODE"),35;case 57:return _.getLogger().debug("Lexa: )"),this.pushState("NODE"),35;case 58:return _.getLogger().debug("Lex: ((("),this.pushState("NODE"),35;case 59:return _.getLogger().debug("Lexa: )"),this.pushState("NODE"),35;case 60:return _.getLogger().debug("Lexa: )"),this.pushState("NODE"),35;case 61:return _.getLogger().debug("Lexa: )"),this.pushState("NODE"),35;case 62:return _.getLogger().debug("Lexc: >"),this.pushState("NODE"),35;case 63:return _.getLogger().debug("Lexa: (["),this.pushState("NODE"),35;case 64:return _.getLogger().debug("Lexa: )"),this.pushState("NODE"),35;case 65:return this.pushState("NODE"),35;case 66:return this.pushState("NODE"),35;case 67:return this.pushState("NODE"),35;case 68:return this.pushState("NODE"),35;case 69:return this.pushState("NODE"),35;case 70:return this.pushState("NODE"),35;case 71:return this.pushState("NODE"),35;case 72:return _.getLogger().debug("Lexa: ["),this.pushState("NODE"),35;case 73:return this.pushState("BLOCK_ARROW"),_.getLogger().debug("LEX ARR START"),37;case 74:return _.getLogger().debug("Lex: NODE_ID",b.yytext),31;case 75:return _.getLogger().debug("Lex: EOF",b.yytext),8;case 76:this.pushState("md_string");break;case 77:this.pushState("md_string");break;case 78:return"NODE_DESCR";case 79:this.popState();break;case 80:_.getLogger().debug("Lex: Starting string"),this.pushState("string");break;case 81:_.getLogger().debug("LEX ARR: Starting string"),this.pushState("string");break;case 82:return _.getLogger().debug("LEX: NODE_DESCR:",b.yytext),"NODE_DESCR";case 83:_.getLogger().debug("LEX POPPING"),this.popState();break;case 84:_.getLogger().debug("Lex: =>BAE"),this.pushState("ARROW_DIR");break;case 85:return b.yytext=b.yytext.replace(/^,\s*/,""),_.getLogger().debug("Lex (right): dir:",b.yytext),"DIR";case 86:return b.yytext=b.yytext.replace(/^,\s*/,""),_.getLogger().debug("Lex (left):",b.yytext),"DIR";case 87:return b.yytext=b.yytext.replace(/^,\s*/,""),_.getLogger().debug("Lex (x):",b.yytext),"DIR";case 88:return b.yytext=b.yytext.replace(/^,\s*/,""),_.getLogger().debug("Lex (y):",b.yytext),"DIR";case 89:return b.yytext=b.yytext.replace(/^,\s*/,""),_.getLogger().debug("Lex (up):",b.yytext),"DIR";case 90:return b.yytext=b.yytext.replace(/^,\s*/,""),_.getLogger().debug("Lex (down):",b.yytext),"DIR";case 91:return b.yytext="]>",_.getLogger().debug("Lex (ARROW_DIR end):",b.yytext),this.popState(),this.popState(),"BLOCK_ARROW_END";case 92:return _.getLogger().debug("Lex: LINK","#"+b.yytext+"#"),15;case 93:return _.getLogger().debug("Lex: LINK",b.yytext),15;case 94:return _.getLogger().debug("Lex: LINK",b.yytext),15;case 95:return _.getLogger().debug("Lex: LINK",b.yytext),15;case 96:return _.getLogger().debug("Lex: START_LINK",b.yytext),this.pushState("LLABEL"),16;case 97:return _.getLogger().debug("Lex: START_LINK",b.yytext),this.pushState("LLABEL"),16;case 98:return _.getLogger().debug("Lex: START_LINK",b.yytext),this.pushState("LLABEL"),16;case 99:this.pushState("md_string");break;case 100:return _.getLogger().debug("Lex: Starting string"),this.pushState("string"),"LINK_LABEL";case 101:return this.popState(),_.getLogger().debug("Lex: LINK","#"+b.yytext+"#"),15;case 102:return this.popState(),_.getLogger().debug("Lex: LINK",b.yytext),15;case 103:return this.popState(),_.getLogger().debug("Lex: LINK",b.yytext),15;case 104:return _.getLogger().debug("Lex: COLON",b.yytext),b.yytext=b.yytext.slice(1),27}},"anonymous"),rules:[/^(?:block-beta\b)/,/^(?:block:)/,/^(?:block\b)/,/^(?:[\s]+)/,/^(?:[\n]+)/,/^(?:((\u000D\u000A)|(\u000A)))/,/^(?:columns\s+auto\b)/,/^(?:columns\s+[\d]+)/,/^(?:["][`])/,/^(?:[^`"]+)/,/^(?:[`]["])/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:space[:]\d+)/,/^(?:space\b)/,/^(?:default\b)/,/^(?:linkStyle\b)/,/^(?:interpolate\b)/,/^(?:classDef\s+)/,/^(?:DEFAULT\s+)/,/^(?:\w+\s+)/,/^(?:[^\n]*)/,/^(?:class\s+)/,/^(?:(\w+)+((,\s*\w+)*))/,/^(?:[^\n]*)/,/^(?:style\s+)/,/^(?:(\w+)+((,\s*\w+)*))/,/^(?:[^\n]*)/,/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:end\b\s*)/,/^(?:\(\(\()/,/^(?:\)\)\))/,/^(?:[\)]\))/,/^(?:\}\})/,/^(?:\})/,/^(?:\(-)/,/^(?:-\))/,/^(?:\(\()/,/^(?:\]\])/,/^(?:\()/,/^(?:\]\))/,/^(?:\\\])/,/^(?:\/\])/,/^(?:\)\])/,/^(?:[\)])/,/^(?:\]>)/,/^(?:[\]])/,/^(?:-\))/,/^(?:\(-)/,/^(?:\)\))/,/^(?:\))/,/^(?:\(\(\()/,/^(?:\(\()/,/^(?:\{\{)/,/^(?:\{)/,/^(?:>)/,/^(?:\(\[)/,/^(?:\()/,/^(?:\[\[)/,/^(?:\[\|)/,/^(?:\[\()/,/^(?:\)\)\))/,/^(?:\[\\)/,/^(?:\[\/)/,/^(?:\[\\)/,/^(?:\[)/,/^(?:<\[)/,/^(?:[^\(\[\n\-\)\{\}\s\<\>:=]+)/,/^(?:$)/,/^(?:["][`])/,/^(?:["][`])/,/^(?:[^`"]+)/,/^(?:[`]["])/,/^(?:["])/,/^(?:["])/,/^(?:[^"]+)/,/^(?:["])/,/^(?:\]>\s*\()/,/^(?:,?\s*right\s*)/,/^(?:,?\s*left\s*)/,/^(?:,?\s*x\s*)/,/^(?:,?\s*y\s*)/,/^(?:,?\s*up\s*)/,/^(?:,?\s*down\s*)/,/^(?:\)\s*)/,/^(?:\s*[xo<]?--+[-xo>]\s*)/,/^(?:\s*[xo<]?==+[=xo>]\s*)/,/^(?:\s*[xo<]?-?\.+-[xo>]?\s*)/,/^(?:\s*~~[\~]+\s*)/,/^(?:\s*[xo<]?--\s*)/,/^(?:\s*[xo<]?==\s*)/,/^(?:\s*[xo<]?-\.\s*)/,/^(?:["][`])/,/^(?:["])/,/^(?:\s*[xo<]?--+[-xo>]\s*)/,/^(?:\s*[xo<]?==+[=xo>]\s*)/,/^(?:\s*[xo<]?-?\.+-[xo>]?\s*)/,/^(?::\d+)/],conditions:{STYLE_DEFINITION:{rules:[28],inclusive:!1},STYLE_STMNT:{rules:[27],inclusive:!1},CLASSDEFID:{rules:[22],inclusive:!1},CLASSDEF:{rules:[20,21],inclusive:!1},CLASS_STYLE:{rules:[25],inclusive:!1},CLASS:{rules:[24],inclusive:!1},LLABEL:{rules:[99,100,101,102,103],inclusive:!1},ARROW_DIR:{rules:[85,86,87,88,89,90,91],inclusive:!1},BLOCK_ARROW:{rules:[76,81,84],inclusive:!1},NODE:{rules:[37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,77,80],inclusive:!1},md_string:{rules:[9,10,78,79],inclusive:!1},space:{rules:[],inclusive:!1},string:{rules:[12,13,82,83],inclusive:!1},acc_descr_multiline:{rules:[34,35],inclusive:!1},acc_descr:{rules:[32],inclusive:!1},acc_title:{rules:[30],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,8,11,14,15,16,17,18,19,23,26,29,31,33,36,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,92,93,94,95,96,97,98,104],inclusive:!0}}};return E})();k.lexer=C;function S(){this.yy={}}return s(S,"Parser"),S.prototype=k,k.Parser=S,new S})(),rM.parser=rM,E1t=rM});function S1t(e){switch(Ee.debug("typeStr2Type",e),e){case"[]":return"square";case"()":return Ee.debug("we have a round"),"round";case"(())":return"circle";case">]":return"rect_left_inv_arrow";case"{}":return"diamond";case"{{}}":return"hexagon";case"([])":return"stadium";case"[[]]":return"subroutine";case"[()]":return"cylinder";case"((()))":return"doublecircle";case"[//]":return"lean_right";case"[\\\\]":return"lean_left";case"[/\\]":return"trapezoid";case"[\\/]":return"inv_trapezoid";case"<[]>":return"block_arrow";default:return"na"}}function _1t(e){return(Ee.debug("typeStr2Type",e),e)==="=="?"thick":"normal"}function A1t(e){switch(e.trim().slice(-1)){case"x":return"arrow_cross";case"o":return"arrow_circle";case">":return"arrow_point";default:return""}}function R1t(e){switch(e.trim().charAt(0)){case"x":return"arrow_cross";case"o":return"arrow_circle";case"<":return"arrow_point";default:return"arrow_open"}}function $1t(e){return e.includes("==")?"thick":"normal"}function L1t(e){return e.includes(".-")?"dotted":"solid"}var xc,iM,nM,Rpe,$pe,I1t,Lpe,H5,aM,M1t,D1t,N1t,O1t,Ipe,sM,rx,B1t,Mpe,P1t,F1t,z1t,q1t,W1t,j1t,V1t,U1t,Y1t,H1t,G1t,X1t,K1t,Q1t,q$t=G(()=>{"use strict";NF(),Gr(),Er(),Nt(),mi(),fn(),xc=new Map,iM=[],nM=new Map,Rpe="color",$pe="fill",I1t="bgFill",Lpe=",",H5=new Map,aM="",M1t=s(e=>Vt.sanitizeText(e,it()),"sanitizeText"),D1t=s(function(e,t=""){let r=H5.get(e);r||(r={id:e,styles:[],textStyles:[]},H5.set(e,r)),t?.split(Lpe).forEach(i=>{let n=i.replace(/([^;]*);/,"$1").trim();if(RegExp(Rpe).exec(i)){let a=n.replace($pe,I1t).replace(Rpe,$pe);r.textStyles.push(a)}r.styles.push(n)})},"addStyleClass"),N1t=s(function(e,t=""){let r=xc.get(e);t!=null&&(r.styles=t.split(Lpe))},"addStyle2Node"),O1t=s(function(e,t){e.split(",").forEach(function(r){let i=xc.get(r);if(i===void 0){let n=r.trim();i={id:n,type:"na",children:[]},xc.set(n,i)}i.classes||(i.classes=[]),i.classes.push(t)})},"setCssClass"),Ipe=s((e,t)=>{let r=e.flat(),i=[],n=r.find(a=>a?.type==="column-setting")?.columns??-1;for(let a of r){if(typeof n=="number"&&n>0&&a.type!=="column-setting"&&typeof a.widthInColumns=="number"&&a.widthInColumns>n&&Ee.warn(`Block ${a.id} width ${a.widthInColumns} exceeds configured column width ${n}`),a.label&&(a.label=M1t(a.label)),a.type==="classDef"){D1t(a.id,a.css);continue}if(a.type==="applyClass"){O1t(a.id,a?.styleClass??"");continue}if(a.type==="applyStyles"){a?.stylesStr&&N1t(a.id,a?.stylesStr);continue}if(a.type==="column-setting")t.columns=a.columns??-1;else if(a.type==="edge"){let o=(nM.get(a.id)??0)+1;nM.set(a.id,o),a.id=o+"-"+a.id,iM.push(a)}else{a.label||(a.type==="composite"?a.label="":a.label=a.id);let o=xc.get(a.id);if(o===void 0?xc.set(a.id,a):(a.type!=="na"&&(o.type=a.type),a.label!==a.id&&(o.label=a.label)),a.children&&Ipe(a.children,a),a.type==="space"){let l=a.width??1;for(let u=0;u{Ee.debug("Clear called"),pn(),rx={id:"root",type:"composite",children:[],columns:-1},xc=new Map([["root",rx]]),sM=[],H5=new Map,iM=[],nM=new Map,aM=""},"clear"),s(S1t,"typeStr2Type"),s(_1t,"edgeTypeStr2Type"),s(A1t,"edgeStrToEdgeData"),s(R1t,"edgeStrToEdgeStartData"),s($1t,"edgeStrToThickness"),s(L1t,"edgeStrToPattern"),Mpe=0,P1t=s(()=>(Mpe++,"id-"+Math.random().toString(36).substr(2,12)+"-"+Mpe),"generateId"),F1t=s(e=>{rx.children=e,Ipe(e,rx),sM=rx.children},"setHierarchy"),z1t=s(e=>{let t=xc.get(e);return t?t.columns?t.columns:t.children?t.children.length:-1:-1},"getColumns"),q1t=s(()=>[...xc.values()],"getBlocksFlat"),W1t=s(()=>sM||[],"getBlocks"),j1t=s(()=>iM,"getEdges"),V1t=s(e=>xc.get(e),"getBlock"),U1t=s(e=>{xc.set(e.id,e)},"setBlock"),Y1t=s(e=>{aM=e},"setDiagramId"),H1t=s(()=>aM,"getDiagramId"),G1t=s(()=>Ee,"getLogger"),X1t=s(function(){return H5},"getClasses"),K1t={getConfig:s(()=>Rr().block,"getConfig"),typeStr2Type:S1t,edgeTypeStr2Type:_1t,edgeStrToEdgeData:A1t,edgeStrToEdgeStartData:R1t,edgeStrToThickness:$1t,edgeStrToPattern:L1t,getLogger:G1t,getBlocksFlat:q1t,getBlocks:W1t,getEdges:j1t,setHierarchy:F1t,getBlock:V1t,setBlock:U1t,getColumns:z1t,getClasses:X1t,clear:B1t,generateId:P1t,setDiagramId:Y1t,getDiagramId:H1t},Q1t=K1t}),oM,Z1t,J1t,W$t=G(()=>{"use strict";Qa(),Lk(),oM=s((e,t)=>{let r=mE,i=r(e,"r"),n=r(e,"g"),a=r(e,"b");return Yl(i,n,a,t)},"fade"),Z1t=s(e=>`.label { + font-family: ${e.fontFamily}; + color: ${e.nodeTextColor||e.textColor}; + } + .cluster-label text { + fill: ${e.titleColor}; + } + .cluster-label span { + color: ${e.titleColor}; + } + + + + .label text,span { + fill: ${e.nodeTextColor||e.textColor}; + color: ${e.nodeTextColor||e.textColor}; + } + + .node rect, + .node circle, + .node ellipse, + .node polygon, + .node path { + fill: ${e.mainBkg}; + stroke: ${e.nodeBorder}; + stroke-width: 1px; + } + .flowchart-label text { + text-anchor: middle; + } + // .flowchart-label .text-outer-tspan { + // text-anchor: middle; + // } + // .flowchart-label .text-inner-tspan { + // text-anchor: start; + // } + + .node .label { + text-align: center; + } + .node.clickable { + cursor: pointer; + } + + .arrowheadPath { + fill: ${e.arrowheadColor}; + } + + .edgePaths .path { + stroke: ${e.lineColor}; + stroke-width: 2.0px; + } + + .flowchart-link { + stroke: ${e.lineColor}; + fill: none; + } + + .edgeLabel { + background-color: ${e.edgeLabelBackground}; + /* + * This is for backward compatibility with existing code that didn't + * add a \`

    \` around edge labels. + * + * TODO: We should probably remove this in a future release. + */ + p { + margin: 0; + padding: 0; + display: inline; + } + rect { + opacity: 0.5; + background-color: ${e.edgeLabelBackground}; + fill: ${e.edgeLabelBackground}; + } + text-align: center; + } + + /* For html labels only */ + .labelBkg { + background-color: ${e.edgeLabelBackground}; + } + + .node .cluster { + // fill: ${oM(e.mainBkg,.5)}; + fill: ${oM(e.clusterBkg,.5)}; + stroke: ${oM(e.clusterBorder,.2)}; + box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px; + stroke-width: 1px; + } + + .cluster text { + fill: ${e.titleColor}; + } + + .cluster span { + color: ${e.titleColor}; + } + /* .cluster div { + color: ${e.titleColor}; + } */ + + div.mermaidTooltip { + position: absolute; + text-align: center; + max-width: 200px; + padding: 2px; + font-family: ${e.fontFamily}; + font-size: 12px; + background: ${e.tertiaryColor}; + border: 1px solid ${e.border2}; + border-radius: 2px; + pointer-events: none; + z-index: 100; + } + + .flowchartTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${e.textColor}; + } + ${yv()} +`,"getStyles"),J1t=Z1t});function Dpe(e,t){if(e===0||!Number.isInteger(e))throw new Error("Columns must be an integer !== 0.");if(t<0||!Number.isInteger(t))throw new Error("Position must be a non-negative integer."+t);if(e<0)return{px:t,py:0};if(e===1)return{px:0,py:t};let r=t%e,i=Math.floor(t/e);return{px:r,py:i}}function lM(e,t,r=0,i=0,n=8){Ee.debug("setBlockSizes abc95 (start)",e.id,e?.size?.x,"block width =",e?.size,"siblingWidth",r),e?.size?.width||(e.size={width:r,height:i,x:0,y:0});let a=0,o=0;if(e.children?.length>0){for(let m of e.children)lM(m,t,0,0,n);let l=tyt(e);a=l.width,o=l.height,Ee.debug("setBlockSizes abc95 maxWidth of",e.id,":s children is ",a,o);for(let m of e.children)m.size&&(Ee.debug(`abc95 Setting size of children of ${e.id} id=${m.id} ${a} ${o} ${JSON.stringify(m.size)}`),m.size.width=a*(m.widthInColumns??1)+n*((m.widthInColumns??1)-1),m.size.height=o,m.size.x=0,m.size.y=0,Ee.debug(`abc95 updating size of ${e.id} children child:${m.id} maxWidth:${a} maxHeight:${o}`));for(let m of e.children)lM(m,t,a,o,n);let u=e.columns??-1,h=0;for(let m of e.children)h+=m.widthInColumns??1;let d=e.children.length;u>0&&u0?Math.min(e.children.length,u):e.children.length;if(m>0){let y=(f-m*n-n)/m;Ee.debug("abc95 (growing to fit) width",e.id,f,e.size?.width,y);for(let k of e.children)k.size&&(k.size.width=y)}}e.size={width:f,height:g,x:0,y:0}}Ee.debug("setBlockSizes abc94 (done)",e.id,e?.size?.x,e?.size?.width,e?.size?.y,e?.size?.height)}function Npe(e,t,r=8){Ee.debug(`abc85 layout blocks (=>layoutBlocks) ${e.id} x: ${e?.size?.x} y: ${e?.size?.y} width: ${e?.size?.width}`);let i=e.columns??-1;if(Ee.debug("layoutBlocks columns abc95",e.id,"=>",i,e),e.children&&e.children.length>0){let n=e?.children[0]?.size?.width??0,a=e.children.length*n+(e.children.length-1)*r;Ee.debug("widthOfChildren 88",a,"posX");let o=new Map;{let p=0;for(let f of e.children){if(!f.size)continue;let{py:g}=Dpe(i,p),m=o.get(g)??0;f.size.height>m&&o.set(g,f.size.height);let y=f?.widthInColumns??1;i>0&&(y=Math.min(y,i-p%i)),p+=y}}let l=new Map;{let p=0,f=[...o.keys()].sort((g,m)=>g-m);for(let g of f)l.set(g,p),p+=(o.get(g)??0)+r}let u=0;Ee.debug("abc91 block?.size?.x",e.id,e?.size?.x);let h=e?.size?.x?e?.size?.x+(-e?.size?.width/2||0):-r,d=0;for(let p of e.children){let f=e;if(!p.size)continue;let{width:g,height:m}=p.size,{px:y,py:k}=Dpe(i,u);if(k!=d&&(d=k,h=e?.size?.x?e?.size?.x+(-e?.size?.width/2||0):-r,Ee.debug("New row in layout for block",e.id," and child ",p.id,d)),Ee.debug(`abc89 layout blocks (child) id: ${p.id} Pos: ${u} (px, py) ${y},${k} (${f?.size?.x},${f?.size?.y}) parent: ${f.id} width: ${g}${r}`),f.size){let S=g/2;p.size.x=h+r+S,Ee.debug(`abc91 layout blocks (calc) px, pyid:${p.id} startingPos=X${h} new startingPosX${p.size.x} ${S} padding=${r} width=${g} halfWidth=${S} => x:${p.size.x} y:${p.size.y} ${p.widthInColumns} (width * (child?.w || 1)) / 2 ${g*(p?.widthInColumns??1)/2}`),h=p.size.x+S;let E=l.get(k)??0,_=o.get(k)??m;p.size.y=f.size.y-f.size.height/2+E+_/2+r,Ee.debug(`abc88 layout blocks (calc) px, pyid:${p.id}startingPosX${h}${r}${S}=>x:${p.size.x}y:${p.size.y}${p.widthInColumns}(width * (child?.w || 1)) / 2${g*(p?.widthInColumns??1)/2}`)}p.children&&Npe(p,t,r);let C=p?.widthInColumns??1;i>0&&(C=Math.min(C,i-u%i)),u+=C,Ee.debug("abc88 columnsPos",p,u)}}Ee.debug(`layout blocks (<==layoutBlocks) ${e.id} x: ${e?.size?.x} y: ${e?.size?.y} width: ${e?.size?.width}`)}function Ope(e,{minX:t,minY:r,maxX:i,maxY:n}={minX:0,minY:0,maxX:0,maxY:0}){if(e.size&&e.id!=="root"){let{x:a,y:o,width:l,height:u}=e.size;a-l/2i&&(i=a+l/2),o+u/2>n&&(n=o+u/2)}if(e.children)for(let a of e.children)({minX:t,minY:r,maxX:i,maxY:n}=Ope(a,{minX:t,minY:r,maxX:i,maxY:n}));return{minX:t,minY:r,maxX:i,maxY:n}}function eyt(e){let t=e.getBlock("root");if(!t)return;let r=it()?.block?.padding??8;lM(t,e,0,0,r),Npe(t,e,r),Ee.debug("getBlocks",JSON.stringify(t,null,2));let{minX:i,minY:n,maxX:a,maxY:o}=Ope(t),l=o-n,u=a-i;return{x:i,y:n,width:u,height:l}}var tyt,j$t=G(()=>{"use strict";Nt(),Er(),s(Dpe,"calculateBlockPosition"),tyt=s(e=>{let t=0,r=0;for(let i of e.children){let{width:n,height:a,x:o,y:l}=i.size??{width:0,height:0,x:0,y:0};if(Ee.debug("getMaxChildSize abc95 child:",i.id,"width:",n,"height:",a,"x:",o,"y:",l,i.type),i.type==="space")continue;let u=n/(i.widthInColumns??1);u>t&&(t=u),a>r&&(r=a)}return{width:t,height:r}},"getMaxChildSize"),s(lM,"setBlockSizes"),s(Npe,"layoutBlocks"),s(Ope,"findBounds"),s(eyt,"layout")});function Bpe(e,t,r=!1){let i=e,n="default";(i?.classes?.length||0)>0&&(n=(i?.classes??[]).join(" ")),n=n+" flowchart-label";let a=(i?.classes??[]).flatMap(g=>t.getClasses().get(g)?.styles??[]),o=0,l="rect",u;switch(i.type){case"round":o=5,l="rect";break;case"composite":o=0,l="composite",u=0;break;case"square":l="rect";break;case"diamond":l="question";break;case"hexagon":l="hexagon";break;case"block_arrow":l="block_arrow";break;case"odd":l="rect_left_inv_arrow";break;case"lean_right":l="lean_right";break;case"lean_left":l="lean_left";break;case"trapezoid":l="trapezoid";break;case"inv_trapezoid":l="inv_trapezoid";break;case"rect_left_inv_arrow":l="rect_left_inv_arrow";break;case"circle":l="circle";break;case"ellipse":l="ellipse";break;case"stadium":l="stadium";break;case"subroutine":l="subroutine";break;case"cylinder":l="cylinder";break;case"group":l="rect";break;case"doublecircle":l="doublecircle";break;default:l="rect"}let h=PF(i?.styles??[]),d=i.label,p=i.size??{width:0,height:0,x:0,y:0},f=t.getDiagramId();return{labelStyle:h.labelStyle,shape:l,label:d,labelText:d,rx:o,ry:o,class:n,cssClasses:n,cssStyles:i?.styles??[],cssCompiledStyles:a,style:h.style,id:i.id,domId:f?`${f}-${i.id}`:i.id,isGroup:!1,directions:i.directions,width:p.width||void 0,height:p.height||void 0,wrappingWidth:p.width||Number.POSITIVE_INFINITY,x:p.x,y:p.y,positioned:r,intersect:void 0,padding:u??Rr()?.block?.padding??0,widthInColumns:i.widthInColumns??1}}async function ryt(e,t,r){let i=Bpe(t,r,!1);if(t.type==="group")return;let n=Rr(),a=await d2(e,i,{config:n}),o=a.node()?.getBBox()??{width:0,height:0},l=r.getBlock(i.id);l.size={width:o.width,height:o.height,x:0,y:0,node:a},r.setBlock(l),a.remove()}async function iyt(e,t,r){let i=Bpe(t,r,!0);if(r.getBlock(i.id).type!=="space"){let n=Rr();await d2(e,i,{config:n}),t.intersect=i?.intersect,kg(i)}}async function cM(e,t,r,i){for(let n of t)await i(e,n,r),n.children&&await cM(e,n.children,r,i)}async function nyt(e,t,r){await cM(e,t,r,ryt)}async function ayt(e,t,r){await cM(e,t,r,iyt)}async function syt(e,t,r,i,n){let a=new lo({multigraph:!0,compound:!0});a.setGraph({rankdir:"TB",nodesep:10,ranksep:10,marginx:8,marginy:8});for(let o of r)o.size&&a.setNode(o.id,{width:o.size.width,height:o.size.height,intersect:o.intersect});for(let o of t)if(o.start&&o.end){let l=i.getBlock(o.start),u=i.getBlock(o.end);if(l?.size&&u?.size){let h=l.size,d=u.size,p=[{x:h.x,y:h.y},{x:h.x+(d.x-h.x)/2,y:h.y+(d.y-h.y)/2},{x:d.x,y:d.y}],f=n?`${n}-${o.id}`:o.id,g=o.thickness==="thick"?"edge-thickness-thick":"edge-thickness-normal",m=o.pattern==="dotted"?"edge-pattern-dotted":"edge-pattern-solid",y=`${g} ${m} flowchart-link LS-a1 LE-b1`;q4(e,{...o,id:f,arrowTypeEnd:o.arrowTypeEnd,arrowTypeStart:o.arrowTypeStart,points:p,classes:y},{},"block",a.node(o.start),a.node(o.end),n),o.label&&(await Tg(e,{...o,label:o.label,labelStyle:"stroke: #333; stroke-width: 1.5px;fill:none;",arrowTypeEnd:o.arrowTypeEnd,arrowTypeStart:o.arrowTypeStart,points:p,classes:y}),Z_({...o,x:p[1].x,y:p[1].y},{originalPath:p}))}}}var V$t=G(()=>{"use strict";ic(),Gr(),W4(),p2(),Ar(),s(Bpe,"getNodeFromBlock"),s(ryt,"calculateBlockSize"),s(iyt,"insertBlockPositioned"),s(cM,"performOperations"),s(nyt,"calculateBlockSizes"),s(ayt,"insertBlocks"),s(syt,"insertEdges")}),oyt,lyt,cyt,U$t=G(()=>{"use strict";pi(),Gr(),Nt(),Kq(),vn(),j$t(),V$t(),oyt=s(function(e,t){return t.db.getClasses()},"getClasses"),lyt=s(async function(e,t,r,i){let{securityLevel:n,block:a}=Rr(),o=i.db;o.setDiagramId(t);let l;n==="sandbox"&&(l=zt("#i"+t));let u=zt(n==="sandbox"?l.nodes()[0].contentDocument.body:"body"),h=n==="sandbox"?u.select(`[id="${t}"]`):zt(`[id="${t}"]`);J_(h,["point","circle","cross"],i.type,t);let d=o.getBlocks(),p=o.getBlocksFlat(),f=o.getEdges(),g=h.insert("g").attr("class","block");await nyt(g,d,o);let m=eyt(o);await ayt(g,d,o),await syt(g,f,p,o,t);let y=g.node()?.getBBox(),k=y&&Number.isFinite(y.width)&&Number.isFinite(y.height)?y:m;if(k){let C=Math.max(1,Math.round(.125*(k.width/k.height))),S=k.height+C+10,E=k.width+10,{useMaxWidth:_}=a;aa(h,S,E,!!_),Ee.debug("Here Bounds",m,k),h.attr("viewBox",`${k.x-5} ${k.y-5} ${k.width+10} ${k.height+10}`)}},"draw"),cyt={draw:lyt,getClasses:oyt}}),uyt={};Mr(uyt,{diagram:()=>hyt});var hyt,Y$t=G(()=>{"use strict";z$t(),q$t(),W$t(),U$t(),hyt={parser:E1t,db:Q1t,renderer:cyt,styles:J1t}});function dyt(e){return e.some(t=>Ppe.test(t))}function pyt(e){for(let t of e){let r=Fpe.exec(t);if(r?.index&&r.index>0)return r.index}return 4}function fyt(e,t){return e.replace(/\bline\s+(\d+)\b/gi,(r,i)=>{let n=parseInt(i,10),a=t.get(n);return a?`line ${a}`:r})}function gyt(e){let t=e.split(` +`),r=new Map,i=-1;for(let[u,h]of t.entries())if(h.trim()==="treeView-beta"){i=u;break}if(i===-1)return{text:e,lineMap:r};let n=[];for(let u=i+1;u{"use strict";Ppe=/[─━│┃└┗├┣]/,Fpe=/[└┗├┣]/,myt=/[─━]/,zpe=/^[\s│┃]+$/,qpe=/^\s*(title[\t ]|accTitle[\t ]*:|accDescr[\t ]*[:{])/,Wpe=/^\s*%%/,yyt=" ",s(dyt,"isBoxDrawingFormat"),s(pyt,"inferSegmentWidth"),s(fyt,"remapErrorLines"),s(gyt,"preprocessBoxDrawing")}),gu,vyt,byt,xyt,Tyt,kyt,Cyt,wyt,uM,Eyt=G(()=>{"use strict";Gr(),Za(),Ar(),fhe(),fn(),gu=new H9(()=>({cnt:1,stack:[{id:0,level:-1,name:"/",nodeType:"directory",children:[]}]})),vyt=s(()=>{gu.reset(),pn()},"clear"),byt=s(()=>gu.records.stack[0],"getRoot"),xyt=s(()=>gu.records.cnt,"getCount"),Tyt=vi.treeView,kyt=s(()=>Gn(Tyt,Rr().treeView),"getConfig"),Cyt=s((e,t,r,i,n,a)=>{for(;e<=gu.records.stack[gu.records.stack.length-1].level;)gu.records.stack.pop();let o={id:gu.records.cnt++,level:e,name:t,nodeType:r,icon:n,cssClass:i,description:a,children:[]};gu.records.stack[gu.records.stack.length-1].children.push(o),gu.records.stack.push(o)},"addNode"),wyt={clear:vyt,addNode:Cyt,getRoot:byt,getCount:xyt,getConfig:kyt,getAccTitle:Ln,getAccDescription:Mn,getDiagramTitle:xn,setAccDescription:In,setAccTitle:bn,setDiagramTitle:Dn},uM=wyt}),Syt,_yt,G$t=G(()=>{"use strict";Co(),Gr(),Nt(),mi(),vl(),H$t(),Eyt(),Syt=s(e=>{Yo(e,uM);for(let t of e.nodes){let r=typeof t.indent=="number"?t.indent:0,i=t.name,n=i.endsWith("/");n&&(i=i.slice(0,-1));let a=n?"directory":"file",o=t.classAnnotation||void 0,l=t.iconAnnotation,u=l!==void 0?l||"none":void 0,h=t.descAnnotation||void 0,d=h?Si(h,Rr()):void 0;uM.addNode(r,i,a,o,u,d)}},"populate"),_yt={parse:s(async e=>{let{text:t,lineMap:r}=gyt(e);try{let i=await Pl("treeView",t);Ee.debug(i),Syt(i)}catch(i){throw r.size>0&&i instanceof Error&&(i.message=fyt(i.message,r)),i}},"parse")}});function Ayt(e,t){let r=t?.filenameIcons?.[e];if(r)return r;let i=e.lastIndexOf(".");if(i>0){let n=e.substring(i).toLowerCase(),a=t?.extensionIcons;return a?.[n]??a?.[n.slice(1)]}}function jpe(e,t){return e.includes(":")?e:e in ix.icons||!t?`${ix.prefix}:${e}`:`${t}:${e}`}function Vpe(e,t){if(e.icon!=="none"){if(e.icon)return jpe(e.icon,t.defaultIconPack);if(t.showIcons){if(e.nodeType==="file"){let r=Ayt(e.name,t);if(r==="none")return;if(r)return jpe(r,t.defaultIconPack)}return`${ix.prefix}:${e.nodeType==="directory"?"folder":"file"}`}}}var ix,X$t=G(()=>{"use strict";ix={prefix:"mermaid-treeview",height:24,width:24,icons:{folder:{body:''},file:{body:''}}},s(Ayt,"detectIcon"),s(jpe,"qualifyIcon"),s(Vpe,"getNodeIcon")}),hM,Ryt,$yt,Lyt,Iyt,Upe,Myt,Dyt,Nyt,Oyt,K$t=G(()=>{"use strict";Nt(),Iu(),wo(),vn(),X$t(),SS([{name:ix.prefix,icons:ix}]),hM=14,Ryt=4,$yt=16,Lyt=s(async(e,t)=>{let r=[],i=s(a=>{let o=Vpe(a,t);o&&r.push({icon:o,node:a}),a.children.forEach(i)},"collect");i(e);let n=await Promise.all(r.map(async({icon:a,node:o})=>({id:o.id,svg:await Lu(a,{height:hM,width:hM})})));return new Map(n.map(({id:a,svg:o})=>[a,o]))},"resolveNodeIcons"),Iyt=s((e,t,r,i,n,a)=>{let o=i.append("g"),l="treeView-node-label";r.nodeType==="directory"&&(l+=" treeView-node-dir"),r.cssClass&&(l+=` ${r.cssClass}`);let u=hM+Ryt,h=Vpe(r,n),d=h!==void 0;h&&o.append("g").attr("class","treeView-node-icon").attr("transform",`translate(${e+n.paddingX}, ${t+n.paddingY})`).html(a.get(r.id)??"");let p=o.append("text").text(r.name).attr("dominant-baseline","middle").attr("class",l),{height:f,width:g}=p.node().getBBox(),m=f+n.paddingY*2,y=e+n.paddingX+(d?u:0);p.attr("x",y),p.attr("y",t+m/2);let k=y+g,C=g+n.paddingX*2+(d?u:0);return r.BBox={x:e,y:t,width:C,height:m},r.cssClass?.split(/\s+/).includes("highlight")&&o.insert("rect",":first-child").attr("x",e).attr("y",t+1).attr("width",0).attr("height",m-2).attr("rx",3).attr("class","treeView-highlight-bg"),{node:r,nodeGroup:o,labelRightEdge:k,centerY:t+m/2}},"positionLabel"),Upe=s((e,t,r,i,n,a)=>e.append("line").attr("x1",t).attr("y1",r).attr("x2",i).attr("y2",n).attr("stroke-width",a).attr("class","treeView-node-line"),"positionLine"),Myt=s((e,t,r,i)=>{let n=0,a=0,o=[],l=s((d,p,f,g)=>{let m=g*(f.rowIndent+f.paddingX),y=Iyt(m,n,p,d,f,i);o.push(y);let{height:k,width:C}=p.BBox;Upe(d,m-f.rowIndent,n+k/2,m,n+k/2,f.lineThickness),a=Math.max(a,m+C),n+=k},"drawNode"),u=s((d,p=0)=>{l(e,d,r,p),d.children.forEach(y=>{u(y,p+1)});let{x:f,y:g,height:m}=d.BBox;if(d.children.length){let{y,height:k}=d.children[d.children.length-1].BBox;Upe(e,f+r.paddingX,g+m,f+r.paddingX,y+k/2+r.lineThickness/2,r.lineThickness)}},"processNode");u(t);let h=o.filter(d=>d.node.description);if(h.length>0){let d=Math.max(...o.map(p=>p.labelRightEdge))+$yt;for(let p of h){let f=p.nodeGroup.append("text").text(p.node.description).attr("dominant-baseline","middle").attr("class","treeView-node-description").attr("x",d).attr("y",p.centerY).node().getBBox();a=Math.max(a,d+f.width+r.paddingX)}}for(let d of o)if(d.node.cssClass?.split(/\s+/).includes("highlight")){let p=d.nodeGroup.select(".treeView-highlight-bg");if(!p.empty()){let f=a-d.node.BBox.x+8;p.attr("width",f),a=Math.max(a,d.node.BBox.x+f+2)}}return{totalHeight:n,totalWidth:a}},"drawTree"),Dyt=s(async(e,t,r,i)=>{Ee.debug(`Rendering treeView diagram +`+e);let n=i.db,a=n.getRoot(),o=n.getConfig(),l=Ks(t),u=l.append("g");u.attr("class","tree-view");let h=await Lyt(a,o),{totalHeight:d,totalWidth:p}=Myt(u,a,o,h);l.attr("viewBox",`-${o.lineThickness/2} 0 ${p} ${d}`),aa(l,d,p,o.useMaxWidth)},"draw"),Nyt={draw:Dyt},Oyt=Nyt}),Byt,Pyt,Fyt,Q$t=G(()=>{"use strict";Ar(),Byt={labelFontSize:"16px",labelColor:"black",lineColor:"black",iconColor:"#546e7a",descriptionColor:"#6a9955",highlightBg:"rgba(255, 193, 7, 0.15)",highlightStroke:"#ffc107"},Pyt=s(({treeView:e})=>{let{labelFontSize:t,labelColor:r,lineColor:i,iconColor:n,descriptionColor:a,highlightBg:o,highlightStroke:l}=Gn(Byt,e);return` + .treeView-node-label { + font-size: ${t}; + fill: ${r}; + white-space: pre; + } + .treeView-node-dir { + font-weight: bold; + } + .treeView-node-line { + stroke: ${i}; + } + .treeView-node-icon { + color: ${n}; + } + .treeView-node-description { + font-size: ${t}; + fill: ${a}; + font-style: italic; + white-space: pre; + } + .treeView-highlight-bg { + fill: ${o}; + stroke: ${l}; + stroke-width: 1; + } + `},"styles"),Fyt=Pyt}),zyt={};Mr(zyt,{diagram:()=>qyt});var qyt,Z$t=G(()=>{"use strict";G$t(),Eyt(),K$t(),Q$t(),qyt={db:uM,renderer:Oyt,parser:_yt,styles:Fyt}}),Ype,Hpe,G5,Wyt,Gpe,_o,up,dM,jyt,Vyt,pM,Uyt,Yyt,Hyt,Gyt,Xyt,Xpe,Kpe,B1,Qpe=G(()=>{"use strict";Ype={L:"left",R:"right",T:"top",B:"bottom"},Hpe={L:s(e=>`${e},${e/2} 0,${e} 0,0`,"L"),R:s(e=>`0,${e/2} ${e},0 ${e},${e}`,"R"),T:s(e=>`0,0 ${e},0 ${e/2},${e}`,"T"),B:s(e=>`${e/2},0 ${e},${e} 0,${e}`,"B")},G5={L:s((e,t)=>e-t+2,"L"),R:s((e,t)=>e-2,"R"),T:s((e,t)=>e-t+2,"T"),B:s((e,t)=>e-2,"B")},Wyt=s(function(e){return _o(e)?e==="L"?"R":"L":e==="T"?"B":"T"},"getOppositeArchitectureDirection"),Gpe=s(function(e){let t=e;return t==="L"||t==="R"||t==="T"||t==="B"},"isArchitectureDirection"),_o=s(function(e){let t=e;return t==="L"||t==="R"},"isArchitectureDirectionX"),up=s(function(e){let t=e;return t==="T"||t==="B"},"isArchitectureDirectionY"),dM=s(function(e,t){let r=_o(e)&&up(t),i=up(e)&&_o(t);return r||i},"isArchitectureDirectionXY"),jyt=s(function(e){let t=e[0],r=e[1],i=_o(t)&&up(r),n=up(t)&&_o(r);return i||n},"isArchitecturePairXY"),Vyt=s(function(e){return e!=="LL"&&e!=="RR"&&e!=="TT"&&e!=="BB"},"isValidArchitectureDirectionPair"),pM=s(function(e,t){let r=`${e}${t}`;return Vyt(r)?r:void 0},"getArchitectureDirectionPair"),Uyt=s(function([e,t],r){let i=r[0],n=r[1];return _o(i)?up(n)?[e+(i==="L"?-1:1),t+(n==="T"?1:-1)]:[e+(i==="L"?-1:1),t]:_o(n)?[e+(n==="L"?1:-1),t+(i==="T"?1:-1)]:[e,t+(i==="T"?1:-1)]},"shiftPositionByArchitectureDirectionPair"),Yyt=s(function(e){return e==="LT"||e==="TL"?[1,1]:e==="BL"||e==="LB"?[1,-1]:e==="BR"||e==="RB"?[-1,-1]:[-1,1]},"getArchitectureDirectionXYFactors"),Hyt=s(function(e,t){return dM(e,t)?"bend":_o(e)?"horizontal":"vertical"},"getArchitectureDirectionAlignment"),Gyt=s(function(e){return e.type==="service"},"isArchitectureService"),Xyt=s(function(e){return e.type==="junction"},"isArchitectureJunction"),Xpe=s((e,t)=>{let[r,i]=[e,t].sort();return`${JSON.stringify(r)}-${JSON.stringify(i)}`},"architectureGroupAlignmentKey"),Kpe=s(e=>e.data(),"edgeData"),B1=s(e=>e.data(),"nodeData")}),Kyt,Zpe,Qyt=G(()=>{"use strict";var e;Gr(),Za(),Ar(),fn(),Qpe(),Kyt=vi.architecture,Zpe=(e=class{constructor(){this.nodes=new Map,this.groups=new Map,this.edges=[],this.layoutHints=[],this.registeredIds=new Map,this.elements=new Map,this.diagramId="",this.setAccTitle=bn,this.getAccTitle=Ln,this.setDiagramTitle=Dn,this.getDiagramTitle=xn,this.getAccDescription=Mn,this.setAccDescription=In,this.clear()}setDiagramId(r){this.diagramId=r}getDiagramId(){return this.diagramId}clear(){this.nodes=new Map,this.groups=new Map,this.edges=[],this.layoutHints=[],this.registeredIds=new Map,this.dataStructures=void 0,this.elements=new Map,this.diagramId="",pn()}addService({id:r,icon:i,in:n,title:a,iconText:o}){if(this.registeredIds.has(r))throw new Error(`The service id [${r}] is already in use by another ${this.registeredIds.get(r)}`);if(n!==void 0){if(r===n)throw new Error(`The service [${r}] cannot be placed within itself`);if(!this.registeredIds.has(n))throw new Error(`The service [${r}]'s parent does not exist. Please make sure the parent is created before this service`);if(this.registeredIds.get(n)==="node")throw new Error(`The service [${r}]'s parent is not a group`)}this.registeredIds.set(r,"node"),this.nodes.set(r,{id:r,type:"service",icon:i,iconText:o,title:a,edges:[],in:n})}getServices(){return[...this.nodes.values()].filter(Gyt)}addJunction({id:r,in:i}){if(this.registeredIds.has(r))throw new Error(`The junction id [${r}] is already in use by another ${this.registeredIds.get(r)}`);if(i!==void 0){if(r===i)throw new Error(`The junction [${r}] cannot be placed within itself`);if(!this.registeredIds.has(i))throw new Error(`The junction [${r}]'s parent does not exist. Please make sure the parent is created before this junction`);if(this.registeredIds.get(i)==="node")throw new Error(`The junction [${r}]'s parent is not a group`)}this.registeredIds.set(r,"node"),this.nodes.set(r,{id:r,type:"junction",edges:[],in:i})}getJunctions(){return[...this.nodes.values()].filter(Xyt)}getNodes(){return[...this.nodes.values()]}getNode(r){return this.nodes.get(r)??null}addGroup({id:r,icon:i,in:n,title:a}){if(this.registeredIds.has(r))throw new Error(`The group id [${r}] is already in use by another ${this.registeredIds.get(r)}`);if(n!==void 0){if(r===n)throw new Error(`The group [${r}] cannot be placed within itself`);if(!this.registeredIds.has(n))throw new Error(`The group [${r}]'s parent does not exist. Please make sure the parent is created before this group`);if(this.registeredIds.get(n)==="node")throw new Error(`The group [${r}]'s parent is not a group`)}this.registeredIds.set(r,"group"),this.groups.set(r,{id:r,icon:i,title:a,in:n})}getGroups(){return[...this.groups.values()]}addEdge({lhsId:r,rhsId:i,lhsDir:n,rhsDir:a,lhsInto:o,rhsInto:l,lhsGroup:u,rhsGroup:h,title:d}){if(!Gpe(n))throw new Error(`Invalid direction given for left hand side of edge ${r}--${i}. Expected (L,R,T,B) got ${String(n)}`);if(!Gpe(a))throw new Error(`Invalid direction given for right hand side of edge ${r}--${i}. Expected (L,R,T,B) got ${String(a)}`);if(!this.nodes.has(r)&&!this.groups.has(r))throw new Error(`The left-hand id [${r}] does not yet exist. Please create the service/group before declaring an edge to it.`);if(!this.nodes.has(i)&&!this.groups.has(i))throw new Error(`The right-hand id [${i}] does not yet exist. Please create the service/group before declaring an edge to it.`);let p=this.nodes.get(r).in,f=this.nodes.get(i).in;if(u&&p&&f&&p==f)throw new Error(`The left-hand id [${r}] is modified to traverse the group boundary, but the edge does not pass through two groups.`);if(h&&p&&f&&p==f)throw new Error(`The right-hand id [${i}] is modified to traverse the group boundary, but the edge does not pass through two groups.`);let g={lhsId:r,lhsDir:n,lhsInto:o,lhsGroup:u,rhsId:i,rhsDir:a,rhsInto:l,rhsGroup:h,title:d};this.edges.push(g);let m=this.nodes.get(r),y=this.nodes.get(i);m&&y&&(m.edges.push(this.edges[this.edges.length-1]),y.edges.push(this.edges[this.edges.length-1]))}getEdges(){return this.edges}addLayoutHint(r){if(r.members.length<2)throw new Error(`An align directive requires at least two members; got ${r.members.length}`);let i=new Set;r.members.forEach(n=>{if(this.registeredIds.get(n)!=="node")throw new Error(`align ${r.direction} references [${n}], which is not a service or junction`);if(i.has(n))throw new Error(`align ${r.direction} lists [${n}] more than once`);i.add(n)}),this.layoutHints.push(r)}getLayoutHints(){return this.layoutHints}getDataStructures(){if(this.dataStructures===void 0){let r=new Map,i=new Map;for(let[u,h]of this.nodes.entries()){let d=new Map;for(let p of h.edges){let f=this.getNode(p.lhsId)?.in,g=this.getNode(p.rhsId)?.in;if(f&&g&&f!==g){let m=Hyt(p.lhsDir,p.rhsDir);m!=="bend"&&r.set(Xpe(f,g),m)}if(p.lhsId===u){let m=pM(p.lhsDir,p.rhsDir);m&&d.set(m,p.rhsId)}else{let m=pM(p.rhsDir,p.lhsDir);m&&d.set(m,p.lhsId)}}i.set(u,d)}let n=new Set,a=new Set(i.keys()),o=s(u=>{let h=new Map([[u,[0,0]]]),d=[u];for(;d.length>0;){let p=d.shift();if(p){n.add(p),a.delete(p);let f=i.get(p);if(!f)throw new Error(`BFS error: adjacency list for id ${p} not found. Please report this as a bug.`);let g=h.get(p);if(!g)throw new Error(`BFS error: position for id ${p} not found in spatial map. Please report this as a bug.`);let[m,y]=g;f.forEach((k,C)=>{n.has(k)||(h.set(k,Uyt([m,y],C)),d.push(k))})}}return h},"BFS"),l=[];for(;a.size>0;){let u=a.values().next().value;l.push(o(u))}this.dataStructures={adjList:i,spatialMaps:l,groupAlignments:r}}return this.dataStructures}setElementForId(r,i){this.elements.set(r,i)}getElementById(r){return this.elements.get(r)}getConfig(){return Gn({...Kyt,...Rr().architecture})}getConfigField(r){return this.getConfig()[r]}},s(e,"ArchitectureDB"),e)}),Zyt,Jpe,J$t=G(()=>{"use strict";Co(),Nt(),vl(),Qyt(),Zyt=s((e,t)=>{Yo(e,t),e.groups.map(r=>t.addGroup(r)),e.services.map(r=>t.addService({...r,type:"service"})),e.junctions.map(r=>t.addJunction({...r,type:"junction"})),e.edges.map(r=>t.addEdge(r)),e.alignments?.map(r=>t.addLayoutHint({direction:r.direction,members:[...r.members]}))},"populateDb"),Jpe={parser:{yy:void 0},parse:s(async e=>{let t=await Pl("architecture",e);Ee.debug(t);let r=Jpe.parser?.yy;if(!(r instanceof Zpe))throw new Error("parser.parser?.yy was not a ArchitectureDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.");Zyt(t,r)},"parse")}}),Jyt,e2t,eLt=G(()=>{"use strict";Jyt=s(e=>` + .edge { + stroke-width: ${e.archEdgeWidth}; + stroke: ${e.archEdgeColor}; + fill: none; + } + + .arrow { + fill: ${e.archEdgeArrowColor}; + } + + .node-bkg { + fill: none; + stroke: ${e.archGroupBorderColor}; + stroke-width: ${e.archGroupBorderWidth}; + stroke-dasharray: 8; + } + .node-icon-text { + display: flex; + align-items: center; + } + + .node-icon-text > div { + color: #fff; + margin: 1px; + height: fit-content; + text-align: center; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + } +`,"getStyles"),e2t=Jyt}),t2t=Ro((e,t)=>{"use strict";s((function(r,i){typeof e=="object"&&typeof t=="object"?t.exports=i():typeof define=="function"&&define.amd?define([],i):typeof e=="object"?e.layoutBase=i():r.layoutBase=i()}),"webpackUniversalModuleDefinition")(e,function(){return(function(r){var i={};function n(a){if(i[a])return i[a].exports;var o=i[a]={i:a,l:!1,exports:{}};return r[a].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return s(n,"__webpack_require__"),n.m=r,n.c=i,n.i=function(a){return a},n.d=function(a,o,l){n.o(a,o)||Object.defineProperty(a,o,{configurable:!1,enumerable:!0,get:l})},n.n=function(a){var o=a&&a.__esModule?s(function(){return a.default},"getDefault"):s(function(){return a},"getModuleExports");return n.d(o,"a",o),o},n.o=function(a,o){return Object.prototype.hasOwnProperty.call(a,o)},n.p="",n(n.s=28)})([(function(r,i,n){"use strict";function a(){}s(a,"LayoutConstants"),a.QUALITY=1,a.DEFAULT_CREATE_BENDS_AS_NEEDED=!1,a.DEFAULT_INCREMENTAL=!1,a.DEFAULT_ANIMATION_ON_LAYOUT=!0,a.DEFAULT_ANIMATION_DURING_LAYOUT=!1,a.DEFAULT_ANIMATION_PERIOD=50,a.DEFAULT_UNIFORM_LEAF_NODE_SIZES=!1,a.DEFAULT_GRAPH_MARGIN=15,a.NODE_DIMENSIONS_INCLUDE_LABELS=!1,a.SIMPLE_NODE_SIZE=40,a.SIMPLE_NODE_HALF_SIZE=a.SIMPLE_NODE_SIZE/2,a.EMPTY_COMPOUND_NODE_SIZE=40,a.MIN_EDGE_LENGTH=1,a.WORLD_BOUNDARY=1e6,a.INITIAL_WORLD_BOUNDARY=a.WORLD_BOUNDARY/1e3,a.WORLD_CENTER_X=1200,a.WORLD_CENTER_Y=900,r.exports=a}),(function(r,i,n){"use strict";var a=n(2),o=n(8),l=n(9);function u(d,p,f){a.call(this,f),this.isOverlapingSourceAndTarget=!1,this.vGraphObject=f,this.bendpoints=[],this.source=d,this.target=p}s(u,"LEdge"),u.prototype=Object.create(a.prototype);for(var h in a)u[h]=a[h];u.prototype.getSource=function(){return this.source},u.prototype.getTarget=function(){return this.target},u.prototype.isInterGraph=function(){return this.isInterGraph},u.prototype.getLength=function(){return this.length},u.prototype.isOverlapingSourceAndTarget=function(){return this.isOverlapingSourceAndTarget},u.prototype.getBendpoints=function(){return this.bendpoints},u.prototype.getLca=function(){return this.lca},u.prototype.getSourceInLca=function(){return this.sourceInLca},u.prototype.getTargetInLca=function(){return this.targetInLca},u.prototype.getOtherEnd=function(d){if(this.source===d)return this.target;if(this.target===d)return this.source;throw"Node is not incident with this edge"},u.prototype.getOtherEndInGraph=function(d,p){for(var f=this.getOtherEnd(d),g=p.getGraphManager().getRoot();;){if(f.getOwner()==p)return f;if(f.getOwner()==g)break;f=f.getOwner().getParent()}return null},u.prototype.updateLength=function(){var d=new Array(4);this.isOverlapingSourceAndTarget=o.getIntersection(this.target.getRect(),this.source.getRect(),d),this.isOverlapingSourceAndTarget||(this.lengthX=d[0]-d[2],this.lengthY=d[1]-d[3],Math.abs(this.lengthX)<1&&(this.lengthX=l.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=l.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY))},u.prototype.updateLengthSimple=function(){this.lengthX=this.target.getCenterX()-this.source.getCenterX(),this.lengthY=this.target.getCenterY()-this.source.getCenterY(),Math.abs(this.lengthX)<1&&(this.lengthX=l.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=l.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY)},r.exports=u}),(function(r,i,n){"use strict";function a(o){this.vGraphObject=o}s(a,"LGraphObject"),r.exports=a}),(function(r,i,n){"use strict";var a=n(2),o=n(10),l=n(13),u=n(0),h=n(16),d=n(5);function p(g,m,y,k){y==null&&k==null&&(k=m),a.call(this,k),g.graphManager!=null&&(g=g.graphManager),this.estimatedSize=o.MIN_VALUE,this.inclusionTreeDepth=o.MAX_VALUE,this.vGraphObject=k,this.edges=[],this.graphManager=g,y!=null&&m!=null?this.rect=new l(m.x,m.y,y.width,y.height):this.rect=new l}s(p,"LNode"),p.prototype=Object.create(a.prototype);for(var f in a)p[f]=a[f];p.prototype.getEdges=function(){return this.edges},p.prototype.getChild=function(){return this.child},p.prototype.getOwner=function(){return this.owner},p.prototype.getWidth=function(){return this.rect.width},p.prototype.setWidth=function(g){this.rect.width=g},p.prototype.getHeight=function(){return this.rect.height},p.prototype.setHeight=function(g){this.rect.height=g},p.prototype.getCenterX=function(){return this.rect.x+this.rect.width/2},p.prototype.getCenterY=function(){return this.rect.y+this.rect.height/2},p.prototype.getCenter=function(){return new d(this.rect.x+this.rect.width/2,this.rect.y+this.rect.height/2)},p.prototype.getLocation=function(){return new d(this.rect.x,this.rect.y)},p.prototype.getRect=function(){return this.rect},p.prototype.getDiagonal=function(){return Math.sqrt(this.rect.width*this.rect.width+this.rect.height*this.rect.height)},p.prototype.getHalfTheDiagonal=function(){return Math.sqrt(this.rect.height*this.rect.height+this.rect.width*this.rect.width)/2},p.prototype.setRect=function(g,m){this.rect.x=g.x,this.rect.y=g.y,this.rect.width=m.width,this.rect.height=m.height},p.prototype.setCenter=function(g,m){this.rect.x=g-this.rect.width/2,this.rect.y=m-this.rect.height/2},p.prototype.setLocation=function(g,m){this.rect.x=g,this.rect.y=m},p.prototype.moveBy=function(g,m){this.rect.x+=g,this.rect.y+=m},p.prototype.getEdgeListToNode=function(g){var m=[],y,k=this;return k.edges.forEach(function(C){if(C.target==g){if(C.source!=k)throw"Incorrect edge source!";m.push(C)}}),m},p.prototype.getEdgesBetween=function(g){var m=[],y,k=this;return k.edges.forEach(function(C){if(!(C.source==k||C.target==k))throw"Incorrect edge source and/or target";(C.target==g||C.source==g)&&m.push(C)}),m},p.prototype.getNeighborsList=function(){var g=new Set,m=this;return m.edges.forEach(function(y){if(y.source==m)g.add(y.target);else{if(y.target!=m)throw"Incorrect incidency!";g.add(y.source)}}),g},p.prototype.withChildren=function(){var g=new Set,m,y;if(g.add(this),this.child!=null)for(var k=this.child.getNodes(),C=0;Cm?(this.rect.x-=(this.labelWidth-m)/2,this.setWidth(this.labelWidth)):this.labelPosHorizontal=="right"&&this.setWidth(m+this.labelWidth)),this.labelHeight&&(this.labelPosVertical=="top"?(this.rect.y-=this.labelHeight,this.setHeight(y+this.labelHeight)):this.labelPosVertical=="center"&&this.labelHeight>y?(this.rect.y-=(this.labelHeight-y)/2,this.setHeight(this.labelHeight)):this.labelPosVertical=="bottom"&&this.setHeight(y+this.labelHeight))}}},p.prototype.getInclusionTreeDepth=function(){if(this.inclusionTreeDepth==o.MAX_VALUE)throw"assert failed";return this.inclusionTreeDepth},p.prototype.transform=function(g){var m=this.rect.x;m>u.WORLD_BOUNDARY?m=u.WORLD_BOUNDARY:m<-u.WORLD_BOUNDARY&&(m=-u.WORLD_BOUNDARY);var y=this.rect.y;y>u.WORLD_BOUNDARY?y=u.WORLD_BOUNDARY:y<-u.WORLD_BOUNDARY&&(y=-u.WORLD_BOUNDARY);var k=new d(m,y),C=g.inverseTransformPoint(k);this.setLocation(C.x,C.y)},p.prototype.getLeft=function(){return this.rect.x},p.prototype.getRight=function(){return this.rect.x+this.rect.width},p.prototype.getTop=function(){return this.rect.y},p.prototype.getBottom=function(){return this.rect.y+this.rect.height},p.prototype.getParent=function(){return this.owner==null?null:this.owner.getParent()},r.exports=p}),(function(r,i,n){"use strict";var a=n(0);function o(){}s(o,"FDLayoutConstants");for(var l in a)o[l]=a[l];o.MAX_ITERATIONS=2500,o.DEFAULT_EDGE_LENGTH=50,o.DEFAULT_SPRING_STRENGTH=.45,o.DEFAULT_REPULSION_STRENGTH=4500,o.DEFAULT_GRAVITY_STRENGTH=.4,o.DEFAULT_COMPOUND_GRAVITY_STRENGTH=1,o.DEFAULT_GRAVITY_RANGE_FACTOR=3.8,o.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR=1.5,o.DEFAULT_USE_SMART_IDEAL_EDGE_LENGTH_CALCULATION=!0,o.DEFAULT_USE_SMART_REPULSION_RANGE_CALCULATION=!0,o.DEFAULT_COOLING_FACTOR_INCREMENTAL=.3,o.COOLING_ADAPTATION_FACTOR=.33,o.ADAPTATION_LOWER_NODE_LIMIT=1e3,o.ADAPTATION_UPPER_NODE_LIMIT=5e3,o.MAX_NODE_DISPLACEMENT_INCREMENTAL=100,o.MAX_NODE_DISPLACEMENT=o.MAX_NODE_DISPLACEMENT_INCREMENTAL*3,o.MIN_REPULSION_DIST=o.DEFAULT_EDGE_LENGTH/10,o.CONVERGENCE_CHECK_PERIOD=100,o.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR=.1,o.MIN_EDGE_LENGTH=1,o.GRID_CALCULATION_CHECK_PERIOD=10,r.exports=o}),(function(r,i,n){"use strict";function a(o,l){o==null&&l==null?(this.x=0,this.y=0):(this.x=o,this.y=l)}s(a,"PointD"),a.prototype.getX=function(){return this.x},a.prototype.getY=function(){return this.y},a.prototype.setX=function(o){this.x=o},a.prototype.setY=function(o){this.y=o},a.prototype.getDifference=function(o){return new DimensionD(this.x-o.x,this.y-o.y)},a.prototype.getCopy=function(){return new a(this.x,this.y)},a.prototype.translate=function(o){return this.x+=o.width,this.y+=o.height,this},r.exports=a}),(function(r,i,n){"use strict";var a=n(2),o=n(10),l=n(0),u=n(7),h=n(3),d=n(1),p=n(13),f=n(12),g=n(11);function m(k,C,S){a.call(this,S),this.estimatedSize=o.MIN_VALUE,this.margin=l.DEFAULT_GRAPH_MARGIN,this.edges=[],this.nodes=[],this.isConnected=!1,this.parent=k,C!=null&&C instanceof u?this.graphManager=C:C!=null&&C instanceof Layout&&(this.graphManager=C.graphManager)}s(m,"LGraph"),m.prototype=Object.create(a.prototype);for(var y in a)m[y]=a[y];m.prototype.getNodes=function(){return this.nodes},m.prototype.getEdges=function(){return this.edges},m.prototype.getGraphManager=function(){return this.graphManager},m.prototype.getParent=function(){return this.parent},m.prototype.getLeft=function(){return this.left},m.prototype.getRight=function(){return this.right},m.prototype.getTop=function(){return this.top},m.prototype.getBottom=function(){return this.bottom},m.prototype.isConnected=function(){return this.isConnected},m.prototype.add=function(k,C,S){if(C==null&&S==null){var E=k;if(this.graphManager==null)throw"Graph has no graph mgr!";if(this.getNodes().indexOf(E)>-1)throw"Node already in graph!";return E.owner=this,this.getNodes().push(E),E}else{var _=k;if(!(this.getNodes().indexOf(C)>-1&&this.getNodes().indexOf(S)>-1))throw"Source or target not in graph!";if(!(C.owner==S.owner&&C.owner==this))throw"Both owners must be this graph!";return C.owner!=S.owner?null:(_.source=C,_.target=S,_.isInterGraph=!1,this.getEdges().push(_),C.edges.push(_),S!=C&&S.edges.push(_),_)}},m.prototype.remove=function(k){var C=k;if(k instanceof h){if(C==null)throw"Node is null!";if(!(C.owner!=null&&C.owner==this))throw"Owner graph is invalid!";if(this.graphManager==null)throw"Owner graph manager is invalid!";for(var S=C.edges.slice(),E,_=S.length,b=0;b<_;b++)E=S[b],E.isInterGraph?this.graphManager.remove(E):E.source.owner.remove(E);var v=this.nodes.indexOf(C);if(v==-1)throw"Node not in owner node list!";this.nodes.splice(v,1)}else if(k instanceof d){var E=k;if(E==null)throw"Edge is null!";if(!(E.source!=null&&E.target!=null))throw"Source and/or target is null!";if(!(E.source.owner!=null&&E.target.owner!=null&&E.source.owner==this&&E.target.owner==this))throw"Source and/or target owner is invalid!";var x=E.source.edges.indexOf(E),w=E.target.edges.indexOf(E);if(!(x>-1&&w>-1))throw"Source and/or target doesn't know this edge!";E.source.edges.splice(x,1),E.target!=E.source&&E.target.edges.splice(w,1);var v=E.source.owner.getEdges().indexOf(E);if(v==-1)throw"Not in owner's edge list!";E.source.owner.getEdges().splice(v,1)}},m.prototype.updateLeftTop=function(){for(var k=o.MAX_VALUE,C=o.MAX_VALUE,S,E,_,b=this.getNodes(),v=b.length,x=0;xS&&(k=S),C>E&&(C=E)}return k==o.MAX_VALUE?null:(b[0].getParent().paddingLeft!=null?_=b[0].getParent().paddingLeft:_=this.margin,this.left=C-_,this.top=k-_,new f(this.left,this.top))},m.prototype.updateBounds=function(k){for(var C=o.MAX_VALUE,S=-o.MAX_VALUE,E=o.MAX_VALUE,_=-o.MAX_VALUE,b,v,x,w,R,$=this.nodes,M=$.length,N=0;Nb&&(C=b),Sx&&(E=x),_b&&(C=b),Sx&&(E=x),_=this.nodes.length){var M=0;S.forEach(function(N){N.owner==k&&M++}),M==this.nodes.length&&(this.isConnected=!0)}},r.exports=m}),(function(r,i,n){"use strict";var a,o=n(1);function l(u){a=n(6),this.layout=u,this.graphs=[],this.edges=[]}s(l,"LGraphManager"),l.prototype.addRoot=function(){var u=this.layout.newGraph(),h=this.layout.newNode(null),d=this.add(u,h);return this.setRootGraph(d),this.rootGraph},l.prototype.add=function(u,h,d,p,f){if(d==null&&p==null&&f==null){if(u==null)throw"Graph is null!";if(h==null)throw"Parent node is null!";if(this.graphs.indexOf(u)>-1)throw"Graph already in this graph mgr!";if(this.graphs.push(u),u.parent!=null)throw"Already has a parent!";if(h.child!=null)throw"Already has a child!";return u.parent=h,h.child=u,u}else{f=d,p=h,d=u;var g=p.getOwner(),m=f.getOwner();if(!(g!=null&&g.getGraphManager()==this))throw"Source not in this graph mgr!";if(!(m!=null&&m.getGraphManager()==this))throw"Target not in this graph mgr!";if(g==m)return d.isInterGraph=!1,g.add(d,p,f);if(d.isInterGraph=!0,d.source=p,d.target=f,this.edges.indexOf(d)>-1)throw"Edge already in inter-graph edge list!";if(this.edges.push(d),!(d.source!=null&&d.target!=null))throw"Edge source and/or target is null!";if(!(d.source.edges.indexOf(d)==-1&&d.target.edges.indexOf(d)==-1))throw"Edge already in source and/or target incidency list!";return d.source.edges.push(d),d.target.edges.push(d),d}},l.prototype.remove=function(u){if(u instanceof a){var h=u;if(h.getGraphManager()!=this)throw"Graph not in this graph mgr";if(!(h==this.rootGraph||h.parent!=null&&h.parent.graphManager==this))throw"Invalid parent node!";var d=[];d=d.concat(h.getEdges());for(var p,f=d.length,g=0;g=u.getRight()?h[0]+=Math.min(u.getX()-l.getX(),l.getRight()-u.getRight()):u.getX()<=l.getX()&&u.getRight()>=l.getRight()&&(h[0]+=Math.min(l.getX()-u.getX(),u.getRight()-l.getRight())),l.getY()<=u.getY()&&l.getBottom()>=u.getBottom()?h[1]+=Math.min(u.getY()-l.getY(),l.getBottom()-u.getBottom()):u.getY()<=l.getY()&&u.getBottom()>=l.getBottom()&&(h[1]+=Math.min(l.getY()-u.getY(),u.getBottom()-l.getBottom()));var f=Math.abs((u.getCenterY()-l.getCenterY())/(u.getCenterX()-l.getCenterX()));u.getCenterY()===l.getCenterY()&&u.getCenterX()===l.getCenterX()&&(f=1);var g=f*h[0],m=h[1]/f;h[0]g)return h[0]=d,h[1]=y,h[2]=f,h[3]=$,!1;if(pf)return h[0]=m,h[1]=p,h[2]=w,h[3]=g,!1;if(df?(h[0]=C,h[1]=S,I=!0):(h[0]=k,h[1]=y,I=!0):F===B&&(d>f?(h[0]=m,h[1]=y,I=!0):(h[0]=E,h[1]=S,I=!0)),-O===B?f>d?(h[2]=R,h[3]=$,z=!0):(h[2]=w,h[3]=x,z=!0):O===B&&(f>d?(h[2]=v,h[3]=x,z=!0):(h[2]=M,h[3]=$,z=!0)),I&&z)return!1;if(d>f?p>g?(Y=this.getCardinalDirection(F,B,4),U=this.getCardinalDirection(O,B,2)):(Y=this.getCardinalDirection(-F,B,3),U=this.getCardinalDirection(-O,B,1)):p>g?(Y=this.getCardinalDirection(-F,B,1),U=this.getCardinalDirection(-O,B,3)):(Y=this.getCardinalDirection(F,B,2),U=this.getCardinalDirection(O,B,4)),!I)switch(Y){case 1:J=y,H=d+-b/B,h[0]=H,h[1]=J;break;case 2:H=E,J=p+_*B,h[0]=H,h[1]=J;break;case 3:J=S,H=d+b/B,h[0]=H,h[1]=J;break;case 4:H=C,J=p+-_*B,h[0]=H,h[1]=J;break}if(!z)switch(U){case 1:ie=x,te=f+-j/B,h[2]=te,h[3]=ie;break;case 2:te=M,ie=g+N*B,h[2]=te,h[3]=ie;break;case 3:ie=$,te=f+j/B,h[2]=te,h[3]=ie;break;case 4:te=R,ie=g+-N*B,h[2]=te,h[3]=ie;break}}return!1},o.getCardinalDirection=function(l,u,h){return l>u?h:1+h%4},o.getIntersection=function(l,u,h,d){if(d==null)return this.getIntersection2(l,u,h);var p=l.x,f=l.y,g=u.x,m=u.y,y=h.x,k=h.y,C=d.x,S=d.y,E=void 0,_=void 0,b=void 0,v=void 0,x=void 0,w=void 0,R=void 0,$=void 0,M=void 0;return b=m-f,x=p-g,R=g*f-p*m,v=S-k,w=y-C,$=C*k-y*S,M=b*w-v*x,M===0?null:(E=(x*$-w*R)/M,_=(v*R-b*$)/M,new a(E,_))},o.angleOfVector=function(l,u,h,d){var p=void 0;return l!==h?(p=Math.atan((d-u)/(h-l)),h=0){var S=(-y+Math.sqrt(y*y-4*m*k))/(2*m),E=(-y-Math.sqrt(y*y-4*m*k))/(2*m),_=null;return S>=0&&S<=1?[S]:E>=0&&E<=1?[E]:_}else return null},o.HALF_PI=.5*Math.PI,o.ONE_AND_HALF_PI=1.5*Math.PI,o.TWO_PI=2*Math.PI,o.THREE_PI=3*Math.PI,r.exports=o}),(function(r,i,n){"use strict";function a(){}s(a,"IMath"),a.sign=function(o){return o>0?1:o<0?-1:0},a.floor=function(o){return o<0?Math.ceil(o):Math.floor(o)},a.ceil=function(o){return o<0?Math.floor(o):Math.ceil(o)},r.exports=a}),(function(r,i,n){"use strict";function a(){}s(a,"Integer"),a.MAX_VALUE=2147483647,a.MIN_VALUE=-2147483648,r.exports=a}),(function(r,i,n){"use strict";var a=(function(){function p(f,g){for(var m=0;m"u"?"undefined":a(l);return l==null||u!="object"&&u!="function"},r.exports=o}),(function(r,i,n){"use strict";function a(y){if(Array.isArray(y)){for(var k=0,C=Array(y.length);k0&&k;){for(b.push(x[0]);b.length>0&&k;){var w=b[0];b.splice(0,1),_.add(w);for(var R=w.getEdges(),E=0;E-1&&x.splice(j,1)}_=new Set,v=new Map}}return y},m.prototype.createDummyNodesForBendpoints=function(y){for(var k=[],C=y.source,S=this.graphManager.calcLowestCommonAncestor(y.source,y.target),E=0;E0){for(var S=this.edgeToDummyNodes.get(C),E=0;E=0&&k.splice($,1);var M=v.getNeighborsList();M.forEach(function(I){if(C.indexOf(I)<0){var z=S.get(I),F=z-1;F==1&&w.push(I),S.set(I,F)}})}C=C.concat(w),(k.length==1||k.length==2)&&(E=!0,_=k[0])}return _},m.prototype.setGraphManager=function(y){this.graphManager=y},r.exports=m}),(function(r,i,n){"use strict";function a(){}s(a,"RandomSeed"),a.seed=1,a.x=0,a.nextDouble=function(){return a.x=Math.sin(a.seed++)*1e4,a.x-Math.floor(a.x)},r.exports=a}),(function(r,i,n){"use strict";var a=n(5);function o(l,u){this.lworldOrgX=0,this.lworldOrgY=0,this.ldeviceOrgX=0,this.ldeviceOrgY=0,this.lworldExtX=1,this.lworldExtY=1,this.ldeviceExtX=1,this.ldeviceExtY=1}s(o,"Transform"),o.prototype.getWorldOrgX=function(){return this.lworldOrgX},o.prototype.setWorldOrgX=function(l){this.lworldOrgX=l},o.prototype.getWorldOrgY=function(){return this.lworldOrgY},o.prototype.setWorldOrgY=function(l){this.lworldOrgY=l},o.prototype.getWorldExtX=function(){return this.lworldExtX},o.prototype.setWorldExtX=function(l){this.lworldExtX=l},o.prototype.getWorldExtY=function(){return this.lworldExtY},o.prototype.setWorldExtY=function(l){this.lworldExtY=l},o.prototype.getDeviceOrgX=function(){return this.ldeviceOrgX},o.prototype.setDeviceOrgX=function(l){this.ldeviceOrgX=l},o.prototype.getDeviceOrgY=function(){return this.ldeviceOrgY},o.prototype.setDeviceOrgY=function(l){this.ldeviceOrgY=l},o.prototype.getDeviceExtX=function(){return this.ldeviceExtX},o.prototype.setDeviceExtX=function(l){this.ldeviceExtX=l},o.prototype.getDeviceExtY=function(){return this.ldeviceExtY},o.prototype.setDeviceExtY=function(l){this.ldeviceExtY=l},o.prototype.transformX=function(l){var u=0,h=this.lworldExtX;return h!=0&&(u=this.ldeviceOrgX+(l-this.lworldOrgX)*this.ldeviceExtX/h),u},o.prototype.transformY=function(l){var u=0,h=this.lworldExtY;return h!=0&&(u=this.ldeviceOrgY+(l-this.lworldOrgY)*this.ldeviceExtY/h),u},o.prototype.inverseTransformX=function(l){var u=0,h=this.ldeviceExtX;return h!=0&&(u=this.lworldOrgX+(l-this.ldeviceOrgX)*this.lworldExtX/h),u},o.prototype.inverseTransformY=function(l){var u=0,h=this.ldeviceExtY;return h!=0&&(u=this.lworldOrgY+(l-this.ldeviceOrgY)*this.lworldExtY/h),u},o.prototype.inverseTransformPoint=function(l){var u=new a(this.inverseTransformX(l.x),this.inverseTransformY(l.y));return u},r.exports=o}),(function(r,i,n){"use strict";function a(g){if(Array.isArray(g)){for(var m=0,y=Array(g.length);ml.ADAPTATION_LOWER_NODE_LIMIT&&(this.coolingFactor=Math.max(this.coolingFactor*l.COOLING_ADAPTATION_FACTOR,this.coolingFactor-(g-l.ADAPTATION_LOWER_NODE_LIMIT)/(l.ADAPTATION_UPPER_NODE_LIMIT-l.ADAPTATION_LOWER_NODE_LIMIT)*this.coolingFactor*(1-l.COOLING_ADAPTATION_FACTOR))),this.maxNodeDisplacement=l.MAX_NODE_DISPLACEMENT_INCREMENTAL):(g>l.ADAPTATION_LOWER_NODE_LIMIT?this.coolingFactor=Math.max(l.COOLING_ADAPTATION_FACTOR,1-(g-l.ADAPTATION_LOWER_NODE_LIMIT)/(l.ADAPTATION_UPPER_NODE_LIMIT-l.ADAPTATION_LOWER_NODE_LIMIT)*(1-l.COOLING_ADAPTATION_FACTOR)):this.coolingFactor=1,this.initialCoolingFactor=this.coolingFactor,this.maxNodeDisplacement=l.MAX_NODE_DISPLACEMENT),this.maxIterations=Math.max(this.getAllNodes().length*5,this.maxIterations),this.displacementThresholdPerNode=3*l.DEFAULT_EDGE_LENGTH/100,this.totalDisplacementThreshold=this.displacementThresholdPerNode*this.getAllNodes().length,this.repulsionRange=this.calcRepulsionRange()},p.prototype.calcSpringForces=function(){for(var g=this.getAllEdges(),m,y=0;y0&&arguments[0]!==void 0?arguments[0]:!0,m=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!1,y,k,C,S,E=this.getAllNodes(),_;if(this.useFRGridVariant)for(this.totalIterations%l.GRID_CALCULATION_CHECK_PERIOD==1&&g&&this.updateGrid(),_=new Set,y=0;yb||_>b)&&(g.gravitationForceX=-this.gravityConstant*C,g.gravitationForceY=-this.gravityConstant*S)):(b=m.getEstimatedSize()*this.compoundGravityRangeFactor,(E>b||_>b)&&(g.gravitationForceX=-this.gravityConstant*C*this.compoundGravityConstant,g.gravitationForceY=-this.gravityConstant*S*this.compoundGravityConstant))},p.prototype.isConverged=function(){var g,m=!1;return this.totalIterations>this.maxIterations/3&&(m=Math.abs(this.totalDisplacement-this.oldTotalDisplacement)<2),g=this.totalDisplacement=E.length||b>=E[0].length)){for(var v=0;vd},"_defaultCompareFunction")}]),h})();r.exports=u}),(function(r,i,n){"use strict";function a(){}s(a,"SVD"),a.svd=function(o){this.U=null,this.V=null,this.s=null,this.m=0,this.n=0,this.m=o.length,this.n=o[0].length;var l=Math.min(this.m,this.n);this.s=(function(_t){for(var Kt=[];_t-- >0;)Kt.push(0);return Kt})(Math.min(this.m+1,this.n)),this.U=(function(_t){var Kt=s(function $t(Ot){if(Ot.length==0)return 0;for(var Jt=[],Br=0;Br0;)Kt.push(0);return Kt})(this.n),h=(function(_t){for(var Kt=[];_t-- >0;)Kt.push(0);return Kt})(this.m),d=!0,p=!0,f=Math.min(this.m-1,this.n),g=Math.max(0,Math.min(this.n-2,this.m)),m=0;m=0;B--)if(this.s[B]!==0){for(var Y=B+1;Y=0;se--){if((function(_t,Kt){return _t&&Kt})(se0;){var Me=void 0,He=void 0;for(Me=z-2;Me>=-1&&Me!==-1;Me--)if(Math.abs(u[Me])<=$e+xe*(Math.abs(this.s[Me])+Math.abs(this.s[Me+1]))){u[Me]=0;break}if(Me===z-2)He=4;else{var Ge=void 0;for(Ge=z-1;Ge>=Me&&Ge!==Me;Ge--){var ze=(Ge!==z?Math.abs(u[Ge]):0)+(Ge!==Me+1?Math.abs(u[Ge-1]):0);if(Math.abs(this.s[Ge])<=$e+xe*ze){this.s[Ge]=0;break}}Ge===Me?He=3:Ge===z-1?He=1:(He=2,Me=Ge)}switch(Me++,He){case 1:{var Ve=u[z-2];u[z-2]=0;for(var et=z-2;et>=Me;et--){var he=a.hypot(this.s[et],Ve),ye=this.s[et]/he,be=Ve/he;if(this.s[et]=he,et!==Me&&(Ve=-be*u[et-1],u[et-1]=ye*u[et-1]),p)for(var Ie=0;Ie=this.s[Me+1]);){var Bt=this.s[Me];if(this.s[Me]=this.s[Me+1],this.s[Me+1]=Bt,p&&MeMath.abs(l)?(u=l/o,u=Math.abs(o)*Math.sqrt(1+u*u)):l!=0?(u=o/l,u=Math.abs(l)*Math.sqrt(1+u*u)):u=0,u},r.exports=a}),(function(r,i,n){"use strict";var a=(function(){function u(h,d){for(var p=0;p2&&arguments[2]!==void 0?arguments[2]:1,f=arguments.length>3&&arguments[3]!==void 0?arguments[3]:-1,g=arguments.length>4&&arguments[4]!==void 0?arguments[4]:-1;o(this,u),this.sequence1=h,this.sequence2=d,this.match_score=p,this.mismatch_penalty=f,this.gap_penalty=g,this.iMax=h.length+1,this.jMax=d.length+1,this.grid=new Array(this.iMax);for(var m=0;m=0;h--){var d=this.listeners[h];d.event===l&&d.callback===u&&this.listeners.splice(h,1)}},o.emit=function(l,u){for(var h=0;h{"use strict";s((function(r,i){typeof e=="object"&&typeof t=="object"?t.exports=i(t2t()):typeof define=="function"&&define.amd?define(["layout-base"],i):typeof e=="object"?e.coseBase=i(t2t()):r.coseBase=i(r.layoutBase)}),"webpackUniversalModuleDefinition")(e,function(r){return(()=>{"use strict";var i={45:((l,u,h)=>{var d={};d.layoutBase=h(551),d.CoSEConstants=h(806),d.CoSEEdge=h(767),d.CoSEGraph=h(880),d.CoSEGraphManager=h(578),d.CoSELayout=h(765),d.CoSENode=h(991),d.ConstraintHandler=h(902),l.exports=d}),806:((l,u,h)=>{var d=h(551).FDLayoutConstants;function p(){}s(p,"CoSEConstants");for(var f in d)p[f]=d[f];p.DEFAULT_USE_MULTI_LEVEL_SCALING=!1,p.DEFAULT_RADIAL_SEPARATION=d.DEFAULT_EDGE_LENGTH,p.DEFAULT_COMPONENT_SEPERATION=60,p.TILE=!0,p.TILING_PADDING_VERTICAL=10,p.TILING_PADDING_HORIZONTAL=10,p.TRANSFORM_ON_CONSTRAINT_HANDLING=!0,p.ENFORCE_CONSTRAINTS=!0,p.APPLY_LAYOUT=!0,p.RELAX_MOVEMENT_ON_CONSTRAINTS=!0,p.TREE_REDUCTION_ON_INCREMENTAL=!0,p.PURE_INCREMENTAL=p.DEFAULT_INCREMENTAL,l.exports=p}),767:((l,u,h)=>{var d=h(551).FDLayoutEdge;function p(g,m,y){d.call(this,g,m,y)}s(p,"CoSEEdge"),p.prototype=Object.create(d.prototype);for(var f in d)p[f]=d[f];l.exports=p}),880:((l,u,h)=>{var d=h(551).LGraph;function p(g,m,y){d.call(this,g,m,y)}s(p,"CoSEGraph"),p.prototype=Object.create(d.prototype);for(var f in d)p[f]=d[f];l.exports=p}),578:((l,u,h)=>{var d=h(551).LGraphManager;function p(g){d.call(this,g)}s(p,"CoSEGraphManager"),p.prototype=Object.create(d.prototype);for(var f in d)p[f]=d[f];l.exports=p}),765:((l,u,h)=>{var d=h(551).FDLayout,p=h(578),f=h(880),g=h(991),m=h(767),y=h(806),k=h(902),C=h(551).FDLayoutConstants,S=h(551).LayoutConstants,E=h(551).Point,_=h(551).PointD,b=h(551).DimensionD,v=h(551).Layout,x=h(551).Integer,w=h(551).IGeometry,R=h(551).LGraph,$=h(551).Transform,M=h(551).LinkedList;function N(){d.call(this),this.toBeTiled={},this.constraints={}}s(N,"CoSELayout"),N.prototype=Object.create(d.prototype);for(var j in d)N[j]=d[j];N.prototype.newGraphManager=function(){var I=new p(this);return this.graphManager=I,I},N.prototype.newGraph=function(I){return new f(null,this.graphManager,I)},N.prototype.newNode=function(I){return new g(this.graphManager,I)},N.prototype.newEdge=function(I){return new m(null,null,I)},N.prototype.initParameters=function(){d.prototype.initParameters.call(this,arguments),this.isSubLayout||(y.DEFAULT_EDGE_LENGTH<10?this.idealEdgeLength=10:this.idealEdgeLength=y.DEFAULT_EDGE_LENGTH,this.useSmartIdealEdgeLengthCalculation=y.DEFAULT_USE_SMART_IDEAL_EDGE_LENGTH_CALCULATION,this.gravityConstant=C.DEFAULT_GRAVITY_STRENGTH,this.compoundGravityConstant=C.DEFAULT_COMPOUND_GRAVITY_STRENGTH,this.gravityRangeFactor=C.DEFAULT_GRAVITY_RANGE_FACTOR,this.compoundGravityRangeFactor=C.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR,this.prunedNodesAll=[],this.growTreeIterations=0,this.afterGrowthIterations=0,this.isTreeGrowing=!1,this.isGrowthFinished=!1)},N.prototype.initSpringEmbedder=function(){d.prototype.initSpringEmbedder.call(this),this.coolingCycle=0,this.maxCoolingCycle=this.maxIterations/C.CONVERGENCE_CHECK_PERIOD,this.finalTemperature=.04,this.coolingAdjuster=1},N.prototype.layout=function(){var I=S.DEFAULT_CREATE_BENDS_AS_NEEDED;return I&&(this.createBendpoints(),this.graphManager.resetAllEdges()),this.level=0,this.classicLayout()},N.prototype.classicLayout=function(){if(this.nodesWithGravity=this.calculateNodesToApplyGravitationTo(),this.graphManager.setAllNodesToApplyGravitation(this.nodesWithGravity),this.calcNoOfChildrenForAllNodes(),this.graphManager.calcLowestCommonAncestors(),this.graphManager.calcInclusionTreeDepths(),this.graphManager.getRoot().calcEstimatedSize(),this.calcIdealEdgeLengths(),this.incremental){if(y.TREE_REDUCTION_ON_INCREMENTAL){this.reduceTrees(),this.graphManager.resetAllNodesToApplyGravitation();var I=new Set(this.getAllNodes()),z=this.nodesWithGravity.filter(function(O){return I.has(O)});this.graphManager.setAllNodesToApplyGravitation(z)}}else{var F=this.getFlatForest();if(F.length>0)this.positionNodesRadially(F);else{this.reduceTrees(),this.graphManager.resetAllNodesToApplyGravitation();var I=new Set(this.getAllNodes()),z=this.nodesWithGravity.filter(function(Y){return I.has(Y)});this.graphManager.setAllNodesToApplyGravitation(z),this.positionNodesRandomly()}}return Object.keys(this.constraints).length>0&&(k.handleConstraints(this),this.initConstraintVariables()),this.initSpringEmbedder(),y.APPLY_LAYOUT&&this.runSpringEmbedder(),!0},N.prototype.tick=function(){if(this.totalIterations++,this.totalIterations===this.maxIterations&&!this.isTreeGrowing&&!this.isGrowthFinished)if(this.prunedNodesAll.length>0)this.isTreeGrowing=!0;else return!0;if(this.totalIterations%C.CONVERGENCE_CHECK_PERIOD==0&&!this.isTreeGrowing&&!this.isGrowthFinished){if(this.isConverged())if(this.prunedNodesAll.length>0)this.isTreeGrowing=!0;else return!0;this.coolingCycle++,this.layoutQuality==0?this.coolingAdjuster=this.coolingCycle:this.layoutQuality==1&&(this.coolingAdjuster=this.coolingCycle/3),this.coolingFactor=Math.max(this.initialCoolingFactor-Math.pow(this.coolingCycle,Math.log(100*(this.initialCoolingFactor-this.finalTemperature))/Math.log(this.maxCoolingCycle))/100*this.coolingAdjuster,this.finalTemperature),this.animationPeriod=Math.ceil(this.initialAnimationPeriod*Math.sqrt(this.coolingFactor))}if(this.isTreeGrowing){if(this.growTreeIterations%10==0)if(this.prunedNodesAll.length>0){this.graphManager.updateBounds(),this.updateGrid(),this.growTree(this.prunedNodesAll),this.graphManager.resetAllNodesToApplyGravitation();var I=new Set(this.getAllNodes()),z=this.nodesWithGravity.filter(function(B){return I.has(B)});this.graphManager.setAllNodesToApplyGravitation(z),this.graphManager.updateBounds(),this.updateGrid(),y.PURE_INCREMENTAL?this.coolingFactor=C.DEFAULT_COOLING_FACTOR_INCREMENTAL/2:this.coolingFactor=C.DEFAULT_COOLING_FACTOR_INCREMENTAL}else this.isTreeGrowing=!1,this.isGrowthFinished=!0;this.growTreeIterations++}if(this.isGrowthFinished){if(this.isConverged())return!0;this.afterGrowthIterations%10==0&&(this.graphManager.updateBounds(),this.updateGrid()),y.PURE_INCREMENTAL?this.coolingFactor=C.DEFAULT_COOLING_FACTOR_INCREMENTAL/2*((100-this.afterGrowthIterations)/100):this.coolingFactor=C.DEFAULT_COOLING_FACTOR_INCREMENTAL*((100-this.afterGrowthIterations)/100),this.afterGrowthIterations++}var F=!this.isTreeGrowing&&!this.isGrowthFinished,O=this.growTreeIterations%10==1&&this.isTreeGrowing||this.afterGrowthIterations%10==1&&this.isGrowthFinished;return this.totalDisplacement=0,this.graphManager.updateBounds(),this.calcSpringForces(),this.calcRepulsionForces(F,O),this.calcGravitationalForces(),this.moveNodes(),this.animate(),!1},N.prototype.getPositionsData=function(){for(var I=this.graphManager.getAllNodes(),z={},F=0;F0&&this.updateDisplacements();for(var F=0;F0&&(O.fixedNodeWeight=Y)}}if(this.constraints.relativePlacementConstraint){var U=new Map,H=new Map;if(this.dummyToNodeForVerticalAlignment=new Map,this.dummyToNodeForHorizontalAlignment=new Map,this.fixedNodesOnHorizontal=new Set,this.fixedNodesOnVertical=new Set,this.fixedNodeSet.forEach(function(oe){I.fixedNodesOnHorizontal.add(oe),I.fixedNodesOnVertical.add(oe)}),this.constraints.alignmentConstraint){if(this.constraints.alignmentConstraint.vertical)for(var J=this.constraints.alignmentConstraint.vertical,F=0;F=2*oe.length/3;ke--)fe=Math.floor(Math.random()*(ke+1)),ge=oe[ke],oe[ke]=oe[fe],oe[fe]=ge;return oe},this.nodesInRelativeHorizontal=[],this.nodesInRelativeVertical=[],this.nodeToRelativeConstraintMapHorizontal=new Map,this.nodeToRelativeConstraintMapVertical=new Map,this.nodeToTempPositionMapHorizontal=new Map,this.nodeToTempPositionMapVertical=new Map,this.constraints.relativePlacementConstraint.forEach(function(oe){if(oe.left){var fe=U.has(oe.left)?U.get(oe.left):oe.left,ge=U.has(oe.right)?U.get(oe.right):oe.right;I.nodesInRelativeHorizontal.includes(fe)||(I.nodesInRelativeHorizontal.push(fe),I.nodeToRelativeConstraintMapHorizontal.set(fe,[]),I.dummyToNodeForVerticalAlignment.has(fe)?I.nodeToTempPositionMapHorizontal.set(fe,I.idToNodeMap.get(I.dummyToNodeForVerticalAlignment.get(fe)[0]).getCenterX()):I.nodeToTempPositionMapHorizontal.set(fe,I.idToNodeMap.get(fe).getCenterX())),I.nodesInRelativeHorizontal.includes(ge)||(I.nodesInRelativeHorizontal.push(ge),I.nodeToRelativeConstraintMapHorizontal.set(ge,[]),I.dummyToNodeForVerticalAlignment.has(ge)?I.nodeToTempPositionMapHorizontal.set(ge,I.idToNodeMap.get(I.dummyToNodeForVerticalAlignment.get(ge)[0]).getCenterX()):I.nodeToTempPositionMapHorizontal.set(ge,I.idToNodeMap.get(ge).getCenterX())),I.nodeToRelativeConstraintMapHorizontal.get(fe).push({right:ge,gap:oe.gap}),I.nodeToRelativeConstraintMapHorizontal.get(ge).push({left:fe,gap:oe.gap})}else{var ke=H.has(oe.top)?H.get(oe.top):oe.top,Ne=H.has(oe.bottom)?H.get(oe.bottom):oe.bottom;I.nodesInRelativeVertical.includes(ke)||(I.nodesInRelativeVertical.push(ke),I.nodeToRelativeConstraintMapVertical.set(ke,[]),I.dummyToNodeForHorizontalAlignment.has(ke)?I.nodeToTempPositionMapVertical.set(ke,I.idToNodeMap.get(I.dummyToNodeForHorizontalAlignment.get(ke)[0]).getCenterY()):I.nodeToTempPositionMapVertical.set(ke,I.idToNodeMap.get(ke).getCenterY())),I.nodesInRelativeVertical.includes(Ne)||(I.nodesInRelativeVertical.push(Ne),I.nodeToRelativeConstraintMapVertical.set(Ne,[]),I.dummyToNodeForHorizontalAlignment.has(Ne)?I.nodeToTempPositionMapVertical.set(Ne,I.idToNodeMap.get(I.dummyToNodeForHorizontalAlignment.get(Ne)[0]).getCenterY()):I.nodeToTempPositionMapVertical.set(Ne,I.idToNodeMap.get(Ne).getCenterY())),I.nodeToRelativeConstraintMapVertical.get(ke).push({bottom:Ne,gap:oe.gap}),I.nodeToRelativeConstraintMapVertical.get(Ne).push({top:ke,gap:oe.gap})}});else{var ie=new Map,ue=new Map;this.constraints.relativePlacementConstraint.forEach(function(oe){if(oe.left){var fe=U.has(oe.left)?U.get(oe.left):oe.left,ge=U.has(oe.right)?U.get(oe.right):oe.right;ie.has(fe)?ie.get(fe).push(ge):ie.set(fe,[ge]),ie.has(ge)?ie.get(ge).push(fe):ie.set(ge,[fe])}else{var ke=H.has(oe.top)?H.get(oe.top):oe.top,Ne=H.has(oe.bottom)?H.get(oe.bottom):oe.bottom;ue.has(ke)?ue.get(ke).push(Ne):ue.set(ke,[Ne]),ue.has(Ne)?ue.get(Ne).push(ke):ue.set(Ne,[ke])}});var se=s(function(oe,fe){var ge=[],ke=[],Ne=new M,xe=new Set,$e=0;return oe.forEach(function(Me,He){if(!xe.has(He)){ge[$e]=[],ke[$e]=!1;var Ge=He;for(Ne.push(Ge),xe.add(Ge),ge[$e].push(Ge);Ne.length!=0;){Ge=Ne.shift(),fe.has(Ge)&&(ke[$e]=!0);var ze=oe.get(Ge);ze.forEach(function(Ve){xe.has(Ve)||(Ne.push(Ve),xe.add(Ve),ge[$e].push(Ve))})}$e++}}),{components:ge,isFixed:ke}},"constructComponents"),le=se(ie,I.fixedNodesOnHorizontal);this.componentsOnHorizontal=le.components,this.fixedComponentsOnHorizontal=le.isFixed;var Te=se(ue,I.fixedNodesOnVertical);this.componentsOnVertical=Te.components,this.fixedComponentsOnVertical=Te.isFixed}}},N.prototype.updateDisplacements=function(){var I=this;if(this.constraints.fixedNodeConstraint&&this.constraints.fixedNodeConstraint.forEach(function(Te){var oe=I.idToNodeMap.get(Te.nodeId);oe.displacementX=0,oe.displacementY=0}),this.constraints.alignmentConstraint){if(this.constraints.alignmentConstraint.vertical)for(var z=this.constraints.alignmentConstraint.vertical,F=0;F1){var H;for(H=0;HO&&(O=Math.floor(U.y)),Y=Math.floor(U.x+y.DEFAULT_COMPONENT_SEPERATION)}this.transform(new _(S.WORLD_CENTER_X-U.x/2,S.WORLD_CENTER_Y-U.y/2))},N.radialLayout=function(I,z,F){var O=Math.max(this.maxDiagonalInTree(I),y.DEFAULT_RADIAL_SEPARATION);N.branchRadialLayout(z,null,0,359,0,O);var B=R.calculateBounds(I),Y=new $;Y.setDeviceOrgX(B.getMinX()),Y.setDeviceOrgY(B.getMinY()),Y.setWorldOrgX(F.x),Y.setWorldOrgY(F.y);for(var U=0;U1;){var ke=ge[0];ge.splice(0,1);var Ne=se.indexOf(ke);Ne>=0&&se.splice(Ne,1),oe--,le--}z!=null?fe=(se.indexOf(ge[0])+1)%oe:fe=0;for(var xe=Math.abs(O-F)/le,$e=fe;Te!=le;$e=++$e%oe){var Me=se[$e].getOtherEnd(I);if(Me!=z){var He=(F+Te*xe)%360,Ge=(He+xe)%360;N.branchRadialLayout(Me,I,He,Ge,B+Y,Y),Te++}}},N.maxDiagonalInTree=function(I){for(var z=x.MIN_VALUE,F=0;Fz&&(z=B)}return z},N.prototype.calcRepulsionRange=function(){return 2*(this.level+1)*this.idealEdgeLength},N.prototype.groupZeroDegreeMembers=function(){var I=this,z={};this.memberGroups={},this.idToDummyNode={};for(var F=[],O=this.graphManager.getAllNodes(),B=0;B"u"&&(z[H]=[]),z[H]=z[H].concat(Y)}Object.keys(z).forEach(function(J){if(z[J].length>1){var te="DummyCompound_"+J;I.memberGroups[te]=z[J];var ie=z[J][0].getParent(),ue=new g(I.graphManager);ue.id=te,ue.paddingLeft=ie.paddingLeft||0,ue.paddingRight=ie.paddingRight||0,ue.paddingBottom=ie.paddingBottom||0,ue.paddingTop=ie.paddingTop||0,I.idToDummyNode[te]=ue;var se=I.getGraphManager().add(I.newGraph(),ue),le=ie.getChild();le.add(ue);for(var Te=0;TeB?(O.rect.x-=(O.labelWidth-B)/2,O.setWidth(O.labelWidth),O.labelMarginLeft=(O.labelWidth-B)/2):O.labelPosHorizontal=="right"&&O.setWidth(B+O.labelWidth)),O.labelHeight&&(O.labelPosVertical=="top"?(O.rect.y-=O.labelHeight,O.setHeight(Y+O.labelHeight),O.labelMarginTop=O.labelHeight):O.labelPosVertical=="center"&&O.labelHeight>Y?(O.rect.y-=(O.labelHeight-Y)/2,O.setHeight(O.labelHeight),O.labelMarginTop=(O.labelHeight-Y)/2):O.labelPosVertical=="bottom"&&O.setHeight(Y+O.labelHeight))}})},N.prototype.repopulateCompounds=function(){for(var I=this.compoundOrder.length-1;I>=0;I--){var z=this.compoundOrder[I],F=z.id,O=z.paddingLeft,B=z.paddingTop,Y=z.labelMarginLeft,U=z.labelMarginTop;this.adjustLocations(this.tiledMemberPack[F],z.rect.x,z.rect.y,O,B,Y,U)}},N.prototype.repopulateZeroDegreeMembers=function(){var I=this,z=this.tiledZeroDegreePack;Object.keys(z).forEach(function(F){var O=I.idToDummyNode[F],B=O.paddingLeft,Y=O.paddingTop,U=O.labelMarginLeft,H=O.labelMarginTop;I.adjustLocations(z[F],O.rect.x,O.rect.y,B,Y,U,H)})},N.prototype.getToBeTiled=function(I){var z=I.id;if(this.toBeTiled[z]!=null)return this.toBeTiled[z];var F=I.getChild();if(F==null)return this.toBeTiled[z]=!1,!1;for(var O=F.getNodes(),B=0;B0)return this.toBeTiled[z]=!1,!1;if(Y.getChild()==null){this.toBeTiled[Y.id]=!1;continue}if(!this.getToBeTiled(Y))return this.toBeTiled[z]=!1,!1}return this.toBeTiled[z]=!0,!0},N.prototype.getNodeDegree=function(I){for(var z=I.id,F=I.getEdges(),O=0,B=0;Bie&&(ie=se.rect.height)}F+=ie+I.verticalPadding}},N.prototype.tileCompoundMembers=function(I,z){var F=this;this.tiledMemberPack=[],Object.keys(I).forEach(function(O){var B=z[O];if(F.tiledMemberPack[O]=F.tileNodes(I[O],B.paddingLeft+B.paddingRight),B.rect.width=F.tiledMemberPack[O].width,B.rect.height=F.tiledMemberPack[O].height,B.setCenter(F.tiledMemberPack[O].centerX,F.tiledMemberPack[O].centerY),B.labelMarginLeft=0,B.labelMarginTop=0,y.NODE_DIMENSIONS_INCLUDE_LABELS){var Y=B.rect.width,U=B.rect.height;B.labelWidth&&(B.labelPosHorizontal=="left"?(B.rect.x-=B.labelWidth,B.setWidth(Y+B.labelWidth),B.labelMarginLeft=B.labelWidth):B.labelPosHorizontal=="center"&&B.labelWidth>Y?(B.rect.x-=(B.labelWidth-Y)/2,B.setWidth(B.labelWidth),B.labelMarginLeft=(B.labelWidth-Y)/2):B.labelPosHorizontal=="right"&&B.setWidth(Y+B.labelWidth)),B.labelHeight&&(B.labelPosVertical=="top"?(B.rect.y-=B.labelHeight,B.setHeight(U+B.labelHeight),B.labelMarginTop=B.labelHeight):B.labelPosVertical=="center"&&B.labelHeight>U?(B.rect.y-=(B.labelHeight-U)/2,B.setHeight(B.labelHeight),B.labelMarginTop=(B.labelHeight-U)/2):B.labelPosVertical=="bottom"&&B.setHeight(U+B.labelHeight))}})},N.prototype.tileNodes=function(I,z){var F=this.tileNodesByFavoringDim(I,z,!0),O=this.tileNodesByFavoringDim(I,z,!1),B=this.getOrgRatio(F),Y=this.getOrgRatio(O),U;return YH&&(H=Te.getWidth())});var J=Y/B,te=U/B,ie=Math.pow(F-O,2)+4*(J+O)*(te+F)*B,ue=(O-F+Math.sqrt(ie))/(2*(J+O)),se;z?(se=Math.ceil(ue),se==ue&&se++):se=Math.floor(ue);var le=se*(J+O)-O;return H>le&&(le=H),le+=O*2,le},N.prototype.tileNodesByFavoringDim=function(I,z,F){var O=y.TILING_PADDING_VERTICAL,B=y.TILING_PADDING_HORIZONTAL,Y=y.TILING_COMPARE_BY,U={rows:[],rowWidth:[],rowHeight:[],width:0,height:z,verticalPadding:O,horizontalPadding:B,centerX:0,centerY:0};Y&&(U.idealRowWidth=this.calcIdealRowWidth(I,F));var H=s(function(Te){return Te.rect.width*Te.rect.height},"getNodeArea"),J=s(function(Te,oe){return H(oe)-H(Te)},"areaCompareFcn");I.sort(function(Te,oe){var fe=J;return U.idealRowWidth?(fe=Y,fe(Te.id,oe.id)):fe(Te,oe)});for(var te=0,ie=0,ue=0;ue0&&(U+=I.horizontalPadding),I.rowWidth[F]=U,I.width0&&(H+=I.verticalPadding);var J=0;H>I.rowHeight[F]&&(J=I.rowHeight[F],I.rowHeight[F]=H,J=I.rowHeight[F]-J),I.height+=J,I.rows[F].push(z)},N.prototype.getShortestRowIndex=function(I){for(var z=-1,F=Number.MAX_VALUE,O=0;OF&&(z=O,F=I.rowWidth[O]);return z},N.prototype.canAddHorizontal=function(I,z,F){if(I.idealRowWidth){var O=I.rows.length-1,B=I.rowWidth[O];return B+z+I.horizontalPadding<=I.idealRowWidth}var Y=this.getShortestRowIndex(I);if(Y<0)return!0;var U=I.rowWidth[Y];if(U+I.horizontalPadding+z<=I.width)return!0;var H=0;I.rowHeight[Y]0&&(H=F+I.verticalPadding-I.rowHeight[Y]);var J;I.width-U>=z+I.horizontalPadding?J=(I.height+H)/(U+z+I.horizontalPadding):J=(I.height+H)/I.width,H=F+I.verticalPadding;var te;return I.widthY&&z!=F){O.splice(-1,1),I.rows[F].push(B),I.rowWidth[z]=I.rowWidth[z]-Y,I.rowWidth[F]=I.rowWidth[F]+Y,I.width=I.rowWidth[instance.getLongestRowIndex(I)];for(var U=Number.MIN_VALUE,H=0;HU&&(U=O[H].height);z>0&&(U+=I.verticalPadding);var J=I.rowHeight[z]+I.rowHeight[F];I.rowHeight[z]=U,I.rowHeight[F]0)for(var le=B;le<=Y;le++)se[0]+=this.grid[le][U-1].length+this.grid[le][U].length-1;if(Y0)for(var le=U;le<=H;le++)se[3]+=this.grid[B-1][le].length+this.grid[B][le].length-1;for(var Te=x.MAX_VALUE,oe,fe,ge=0;ge{var d=h(551).FDLayoutNode,p=h(551).IMath;function f(m,y,k,C){d.call(this,m,y,k,C)}s(f,"CoSENode"),f.prototype=Object.create(d.prototype);for(var g in d)f[g]=d[g];f.prototype.calculateDisplacement=function(){var m=this.graphManager.getLayout();this.getChild()!=null&&this.fixedNodeWeight?(this.displacementX+=m.coolingFactor*(this.springForceX+this.repulsionForceX+this.gravitationForceX)/this.fixedNodeWeight,this.displacementY+=m.coolingFactor*(this.springForceY+this.repulsionForceY+this.gravitationForceY)/this.fixedNodeWeight):(this.displacementX+=m.coolingFactor*(this.springForceX+this.repulsionForceX+this.gravitationForceX)/this.noOfChildren,this.displacementY+=m.coolingFactor*(this.springForceY+this.repulsionForceY+this.gravitationForceY)/this.noOfChildren),Math.abs(this.displacementX)>m.coolingFactor*m.maxNodeDisplacement&&(this.displacementX=m.coolingFactor*m.maxNodeDisplacement*p.sign(this.displacementX)),Math.abs(this.displacementY)>m.coolingFactor*m.maxNodeDisplacement&&(this.displacementY=m.coolingFactor*m.maxNodeDisplacement*p.sign(this.displacementY)),this.child&&this.child.getNodes().length>0&&this.propogateDisplacementToChildren(this.displacementX,this.displacementY)},f.prototype.propogateDisplacementToChildren=function(m,y){for(var k=this.getChild().getNodes(),C,S=0;S{function d(k){if(Array.isArray(k)){for(var C=0,S=Array(k.length);C0){var mt=0;qe.forEach(function(Lt){ve=="horizontal"?(Ke.set(Lt,E.has(Lt)?_[E.get(Lt)]:me.get(Lt)),mt+=Ke.get(Lt)):(Ke.set(Lt,E.has(Lt)?b[E.get(Lt)]:me.get(Lt)),mt+=Ke.get(Lt))}),mt=mt/qe.length,ct.forEach(function(Lt){X.has(Lt)||Ke.set(Lt,mt)})}else{var St=0;ct.forEach(function(Lt){ve=="horizontal"?St+=E.has(Lt)?_[E.get(Lt)]:me.get(Lt):St+=E.has(Lt)?b[E.get(Lt)]:me.get(Lt)}),St=St/ct.length,ct.forEach(function(Lt){Ke.set(Lt,St)})}});for(var Fe=s(function(){var ct=Ze.shift(),qe=ee.get(ct);qe.forEach(function(mt){if(Ke.get(mt.id)Lt&&(Lt=Ot),JtBt&&(Bt=Jt)}}catch(Cr){tr=!0,Et=Cr}finally{try{!qt&&_t.return&&_t.return()}finally{if(tr)throw Et}}var Br=(mt+Lt)/2-(St+Bt)/2,At=!0,jr=!1,br=void 0;try{for(var Hr=ct[Symbol.iterator](),Ei;!(At=(Ei=Hr.next()).done);At=!0){var Li=Ei.value;Ke.set(Li,Ke.get(Li)+Br)}}catch(Cr){jr=!0,br=Cr}finally{try{!At&&Hr.return&&Hr.return()}finally{if(jr)throw br}}})}return Ke},"findAppropriatePositionForRelativePlacement"),j=s(function(ee){var ve=0,X=0,me=0,Ce=0;if(ee.forEach(function(De){De.left?_[E.get(De.left)]-_[E.get(De.right)]>=0?ve++:X++:b[E.get(De.top)]-b[E.get(De.bottom)]>=0?me++:Ce++}),ve>X&&me>Ce)for(var Be=0;BeX)for(var Re=0;ReCe)for(var Ke=0;Ke1)C.fixedNodeConstraint.forEach(function(ee,ve){O[ve]=[ee.position.x,ee.position.y],B[ve]=[_[E.get(ee.nodeId)],b[E.get(ee.nodeId)]]}),Y=!0;else if(C.alignmentConstraint)(function(){var ee=0;if(C.alignmentConstraint.vertical){for(var ve=C.alignmentConstraint.vertical,X=s(function(Ke){var De=new Set;ve[Ke].forEach(function(Je){De.add(Je)});var Ze=new Set([].concat(d(De)).filter(function(Je){return H.has(Je)})),Fe=void 0;Ze.size>0?Fe=_[E.get(Ze.values().next().value)]:Fe=M(De).x,ve[Ke].forEach(function(Je){O[ee]=[Fe,b[E.get(Je)]],B[ee]=[_[E.get(Je)],b[E.get(Je)]],ee++})},"_loop2"),me=0;me0?Fe=_[E.get(Ze.values().next().value)]:Fe=M(De).y,Ce[Ke].forEach(function(Je){O[ee]=[_[E.get(Je)],Fe],B[ee]=[_[E.get(Je)],b[E.get(Je)]],ee++})},"_loop3"),Re=0;Reue&&(ue=ie[le].length,se=le);if(ue0){var ye={x:0,y:0};C.fixedNodeConstraint.forEach(function(ee,ve){var X={x:_[E.get(ee.nodeId)],y:b[E.get(ee.nodeId)]},me=ee.position,Ce=$(me,X);ye.x+=Ce.x,ye.y+=Ce.y}),ye.x/=C.fixedNodeConstraint.length,ye.y/=C.fixedNodeConstraint.length,_.forEach(function(ee,ve){_[ve]+=ye.x}),b.forEach(function(ee,ve){b[ve]+=ye.y}),C.fixedNodeConstraint.forEach(function(ee){_[E.get(ee.nodeId)]=ee.position.x,b[E.get(ee.nodeId)]=ee.position.y})}if(C.alignmentConstraint){if(C.alignmentConstraint.vertical)for(var be=C.alignmentConstraint.vertical,Ie=s(function(ee){var ve=new Set;be[ee].forEach(function(Ce){ve.add(Ce)});var X=new Set([].concat(d(ve)).filter(function(Ce){return H.has(Ce)})),me=void 0;X.size>0?me=_[E.get(X.values().next().value)]:me=M(ve).x,ve.forEach(function(Ce){H.has(Ce)||(_[E.get(Ce)]=me)})},"_loop4"),pe=0;pe0?me=b[E.get(X.values().next().value)]:me=M(ve).y,ve.forEach(function(Ce){H.has(Ce)||(b[E.get(Ce)]=me)})},"_loop5"),Ae=0;Ae{l.exports=r})},n={};function a(l){var u=n[l];if(u!==void 0)return u.exports;var h=n[l]={exports:{}};return i[l](h,h.exports,a),h.exports}s(a,"__webpack_require__");var o=a(45);return o})()})}),tLt=Ro((e,t)=>{"use strict";s((function(r,i){typeof e=="object"&&typeof t=="object"?t.exports=i(r2t()):typeof define=="function"&&define.amd?define(["cose-base"],i):typeof e=="object"?e.cytoscapeFcose=i(r2t()):r.cytoscapeFcose=i(r.coseBase)}),"webpackUniversalModuleDefinition")(e,function(r){return(()=>{"use strict";var i={658:(l=>{l.exports=Object.assign!=null?Object.assign.bind(Object):function(u){for(var h=arguments.length,d=Array(h>1?h-1:0),p=1;p{var d=(function(){function g(m,y){var k=[],C=!0,S=!1,E=void 0;try{for(var _=m[Symbol.iterator](),b;!(C=(b=_.next()).done)&&(k.push(b.value),!(y&&k.length===y));C=!0);}catch(v){S=!0,E=v}finally{try{!C&&_.return&&_.return()}finally{if(S)throw E}}return k}return s(g,"sliceIterator"),function(m,y){if(Array.isArray(m))return m;if(Symbol.iterator in Object(m))return g(m,y);throw new TypeError("Invalid attempt to destructure non-iterable instance")}})(),p=h(140).layoutBase.LinkedList,f={};f.getTopMostNodes=function(g){for(var m={},y=0;y0&&O.merge(H)});for(var B=0;B1){b=E[0],v=b.connectedEdges().length,E.forEach(function(O){O.connectedEdges().length0&&k.set("dummy"+(k.size+1),R),$},f.relocateComponent=function(g,m,y){if(!y.fixedNodeConstraint){var k=Number.POSITIVE_INFINITY,C=Number.NEGATIVE_INFINITY,S=Number.POSITIVE_INFINITY,E=Number.NEGATIVE_INFINITY;if(y.quality=="draft"){var _=!0,b=!1,v=void 0;try{for(var x=m.nodeIndexes[Symbol.iterator](),w;!(_=(w=x.next()).done);_=!0){var R=w.value,$=d(R,2),M=$[0],N=$[1],j=y.cy.getElementById(M);if(j){var I=j.boundingBox(),z=m.xCoords[N]-I.w/2,F=m.xCoords[N]+I.w/2,O=m.yCoords[N]-I.h/2,B=m.yCoords[N]+I.h/2;zC&&(C=F),OE&&(E=B)}}}catch(te){b=!0,v=te}finally{try{!_&&x.return&&x.return()}finally{if(b)throw v}}var Y=g.x-(C+k)/2,U=g.y-(E+S)/2;m.xCoords=m.xCoords.map(function(te){return te+Y}),m.yCoords=m.yCoords.map(function(te){return te+U})}else{Object.keys(m).forEach(function(te){var ie=m[te],ue=ie.getRect().x,se=ie.getRect().x+ie.getRect().width,le=ie.getRect().y,Te=ie.getRect().y+ie.getRect().height;ueC&&(C=se),leE&&(E=Te)});var H=g.x-(C+k)/2,J=g.y-(E+S)/2;Object.keys(m).forEach(function(te){var ie=m[te];ie.setCenter(ie.getCenterX()+H,ie.getCenterY()+J)})}}},f.calcBoundingBox=function(g,m,y,k){for(var C=Number.MAX_SAFE_INTEGER,S=Number.MIN_SAFE_INTEGER,E=Number.MAX_SAFE_INTEGER,_=Number.MIN_SAFE_INTEGER,b=void 0,v=void 0,x=void 0,w=void 0,R=g.descendants().not(":parent"),$=R.length,M=0;M<$;M++){var N=R[M];b=m[k.get(N.id())]-N.width()/2,v=m[k.get(N.id())]+N.width()/2,x=y[k.get(N.id())]-N.height()/2,w=y[k.get(N.id())]+N.height()/2,C>b&&(C=b),Sx&&(E=x),_{var d=h(548),p=h(140).CoSELayout,f=h(140).CoSENode,g=h(140).layoutBase.PointD,m=h(140).layoutBase.DimensionD,y=h(140).layoutBase.LayoutConstants,k=h(140).layoutBase.FDLayoutConstants,C=h(140).CoSEConstants,S=s(function(E,_){var b=E.cy,v=E.eles,x=v.nodes(),w=v.edges(),R=void 0,$=void 0,M=void 0,N={};E.randomize&&(R=_.nodeIndexes,$=_.xCoords,M=_.yCoords);var j=s(function(H){return typeof H=="function"},"isFn"),I=s(function(H,J){return j(H)?H(J):H},"optFn"),z=d.calcParentsWithoutChildren(b,v),F=s(function H(J,te,ie,ue){for(var se=te.length,le=0;le0){var Ne=void 0;Ne=ie.getGraphManager().add(ie.newGraph(),fe),H(Ne,oe,ie,ue)}}},"processChildrenList"),O=s(function(H,J,te){for(var ie=0,ue=0,se=0;se0?C.DEFAULT_EDGE_LENGTH=k.DEFAULT_EDGE_LENGTH=ie/ue:j(E.idealEdgeLength)?C.DEFAULT_EDGE_LENGTH=k.DEFAULT_EDGE_LENGTH=50:C.DEFAULT_EDGE_LENGTH=k.DEFAULT_EDGE_LENGTH=E.idealEdgeLength,C.MIN_REPULSION_DIST=k.MIN_REPULSION_DIST=k.DEFAULT_EDGE_LENGTH/10,C.DEFAULT_RADIAL_SEPARATION=k.DEFAULT_EDGE_LENGTH)},"processEdges"),B=s(function(H,J){J.fixedNodeConstraint&&(H.constraints.fixedNodeConstraint=J.fixedNodeConstraint),J.alignmentConstraint&&(H.constraints.alignmentConstraint=J.alignmentConstraint),J.relativePlacementConstraint&&(H.constraints.relativePlacementConstraint=J.relativePlacementConstraint)},"processConstraints");E.nestingFactor!=null&&(C.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR=k.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR=E.nestingFactor),E.gravity!=null&&(C.DEFAULT_GRAVITY_STRENGTH=k.DEFAULT_GRAVITY_STRENGTH=E.gravity),E.numIter!=null&&(C.MAX_ITERATIONS=k.MAX_ITERATIONS=E.numIter),E.gravityRange!=null&&(C.DEFAULT_GRAVITY_RANGE_FACTOR=k.DEFAULT_GRAVITY_RANGE_FACTOR=E.gravityRange),E.gravityCompound!=null&&(C.DEFAULT_COMPOUND_GRAVITY_STRENGTH=k.DEFAULT_COMPOUND_GRAVITY_STRENGTH=E.gravityCompound),E.gravityRangeCompound!=null&&(C.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR=k.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR=E.gravityRangeCompound),E.initialEnergyOnIncremental!=null&&(C.DEFAULT_COOLING_FACTOR_INCREMENTAL=k.DEFAULT_COOLING_FACTOR_INCREMENTAL=E.initialEnergyOnIncremental),E.tilingCompareBy!=null&&(C.TILING_COMPARE_BY=E.tilingCompareBy),E.quality=="proof"?y.QUALITY=2:y.QUALITY=0,C.NODE_DIMENSIONS_INCLUDE_LABELS=k.NODE_DIMENSIONS_INCLUDE_LABELS=y.NODE_DIMENSIONS_INCLUDE_LABELS=E.nodeDimensionsIncludeLabels,C.DEFAULT_INCREMENTAL=k.DEFAULT_INCREMENTAL=y.DEFAULT_INCREMENTAL=!E.randomize,C.ANIMATE=k.ANIMATE=y.ANIMATE=E.animate,C.TILE=E.tile,C.TILING_PADDING_VERTICAL=typeof E.tilingPaddingVertical=="function"?E.tilingPaddingVertical.call():E.tilingPaddingVertical,C.TILING_PADDING_HORIZONTAL=typeof E.tilingPaddingHorizontal=="function"?E.tilingPaddingHorizontal.call():E.tilingPaddingHorizontal,C.DEFAULT_INCREMENTAL=k.DEFAULT_INCREMENTAL=y.DEFAULT_INCREMENTAL=!0,C.PURE_INCREMENTAL=!E.randomize,y.DEFAULT_UNIFORM_LEAF_NODE_SIZES=E.uniformNodeDimensions,E.step=="transformed"&&(C.TRANSFORM_ON_CONSTRAINT_HANDLING=!0,C.ENFORCE_CONSTRAINTS=!1,C.APPLY_LAYOUT=!1),E.step=="enforced"&&(C.TRANSFORM_ON_CONSTRAINT_HANDLING=!1,C.ENFORCE_CONSTRAINTS=!0,C.APPLY_LAYOUT=!1),E.step=="cose"&&(C.TRANSFORM_ON_CONSTRAINT_HANDLING=!1,C.ENFORCE_CONSTRAINTS=!1,C.APPLY_LAYOUT=!0),E.step=="all"&&(E.randomize?C.TRANSFORM_ON_CONSTRAINT_HANDLING=!0:C.TRANSFORM_ON_CONSTRAINT_HANDLING=!1,C.ENFORCE_CONSTRAINTS=!0,C.APPLY_LAYOUT=!0),E.fixedNodeConstraint||E.alignmentConstraint||E.relativePlacementConstraint?C.TREE_REDUCTION_ON_INCREMENTAL=!1:C.TREE_REDUCTION_ON_INCREMENTAL=!0;var Y=new p,U=Y.newGraphManager();return F(U.addRoot(),d.getTopMostNodes(x),Y,E),O(Y,U,w),B(Y,E),Y.runLayout(),N},"coseLayout");l.exports={coseLayout:S}}),212:((l,u,h)=>{var d=(function(){function _(b,v){for(var x=0;x0)if(O){var B=g.getTopMostNodes(v.eles.nodes());if(j=g.connectComponents(x,v.eles,B),j.forEach(function(Ge){var ze=Ge.boundingBox();I.push({x:ze.x1+ze.w/2,y:ze.y1+ze.h/2})}),v.randomize&&j.forEach(function(Ge){v.eles=Ge,R.push(y(v))}),v.quality=="default"||v.quality=="proof"){var Y=x.collection();if(v.tile){var U=new Map,H=[],J=[],te=0,ie={nodeIndexes:U,xCoords:H,yCoords:J},ue=[];if(j.forEach(function(Ge,ze){Ge.edges().length==0&&(Ge.nodes().forEach(function(Ve,et){Y.merge(Ge.nodes()[et]),Ve.isParent()||(ie.nodeIndexes.set(Ge.nodes()[et].id(),te++),ie.xCoords.push(Ge.nodes()[0].position().x),ie.yCoords.push(Ge.nodes()[0].position().y))}),ue.push(ze))}),Y.length>1){var se=Y.boundingBox();I.push({x:se.x1+se.w/2,y:se.y1+se.h/2}),j.push(Y),R.push(ie);for(var le=ue.length-1;le>=0;le--)j.splice(ue[le],1),R.splice(ue[le],1),I.splice(ue[le],1)}}j.forEach(function(Ge,ze){v.eles=Ge,N.push(C(v,R[ze])),g.relocateComponent(I[ze],N[ze],v)})}else j.forEach(function(Ge,ze){g.relocateComponent(I[ze],R[ze],v)});var Te=new Set;if(j.length>1){var oe=[],fe=w.filter(function(Ge){return Ge.css("display")=="none"});j.forEach(function(Ge,ze){var Ve=void 0;if(v.quality=="draft"&&(Ve=R[ze].nodeIndexes),Ge.nodes().not(fe).length>0){var et={};et.edges=[],et.nodes=[];var he=void 0;Ge.nodes().not(fe).forEach(function(ye){if(v.quality=="draft")if(!ye.isParent())he=Ve.get(ye.id()),et.nodes.push({x:R[ze].xCoords[he]-ye.boundingbox().w/2,y:R[ze].yCoords[he]-ye.boundingbox().h/2,width:ye.boundingbox().w,height:ye.boundingbox().h});else{var be=g.calcBoundingBox(ye,R[ze].xCoords,R[ze].yCoords,Ve);et.nodes.push({x:be.topLeftX,y:be.topLeftY,width:be.width,height:be.height})}else N[ze][ye.id()]&&et.nodes.push({x:N[ze][ye.id()].getLeft(),y:N[ze][ye.id()].getTop(),width:N[ze][ye.id()].getWidth(),height:N[ze][ye.id()].getHeight()})}),Ge.edges().forEach(function(ye){var be=ye.source(),Ie=ye.target();if(be.css("display")!="none"&&Ie.css("display")!="none")if(v.quality=="draft"){var pe=Ve.get(be.id()),Qe=Ve.get(Ie.id()),W=[],Ae=[];if(be.isParent()){var K=g.calcBoundingBox(be,R[ze].xCoords,R[ze].yCoords,Ve);W.push(K.topLeftX+K.width/2),W.push(K.topLeftY+K.height/2)}else W.push(R[ze].xCoords[pe]),W.push(R[ze].yCoords[pe]);if(Ie.isParent()){var de=g.calcBoundingBox(Ie,R[ze].xCoords,R[ze].yCoords,Ve);Ae.push(de.topLeftX+de.width/2),Ae.push(de.topLeftY+de.height/2)}else Ae.push(R[ze].xCoords[Qe]),Ae.push(R[ze].yCoords[Qe]);et.edges.push({startX:W[0],startY:W[1],endX:Ae[0],endY:Ae[1]})}else N[ze][be.id()]&&N[ze][Ie.id()]&&et.edges.push({startX:N[ze][be.id()].getCenterX(),startY:N[ze][be.id()].getCenterY(),endX:N[ze][Ie.id()].getCenterX(),endY:N[ze][Ie.id()].getCenterY()})}),et.nodes.length>0&&(oe.push(et),Te.add(ze))}});var ge=F.packComponents(oe,v.randomize).shifts;if(v.quality=="draft")R.forEach(function(Ge,ze){var Ve=Ge.xCoords.map(function(he){return he+ge[ze].dx}),et=Ge.yCoords.map(function(he){return he+ge[ze].dy});Ge.xCoords=Ve,Ge.yCoords=et});else{var ke=0;Te.forEach(function(Ge){Object.keys(N[Ge]).forEach(function(ze){var Ve=N[Ge][ze];Ve.setCenter(Ve.getCenterX()+ge[ke].dx,Ve.getCenterY()+ge[ke].dy)}),ke++})}}}else{var Ne=v.eles.boundingBox();if(I.push({x:Ne.x1+Ne.w/2,y:Ne.y1+Ne.h/2}),v.randomize){var xe=y(v);R.push(xe)}v.quality=="default"||v.quality=="proof"?(N.push(C(v,R[0])),g.relocateComponent(I[0],N[0],v)):g.relocateComponent(I[0],R[0],v)}var $e=s(function(Ge,ze){if(v.quality=="default"||v.quality=="proof"){typeof Ge=="number"&&(Ge=ze);var Ve=void 0,et=void 0,he=Ge.data("id");return N.forEach(function(be){he in be&&(Ve={x:be[he].getRect().getCenterX(),y:be[he].getRect().getCenterY()},et=be[he])}),v.nodeDimensionsIncludeLabels&&(et.labelWidth&&(et.labelPosHorizontal=="left"?Ve.x+=et.labelWidth/2:et.labelPosHorizontal=="right"&&(Ve.x-=et.labelWidth/2)),et.labelHeight&&(et.labelPosVertical=="top"?Ve.y+=et.labelHeight/2:et.labelPosVertical=="bottom"&&(Ve.y-=et.labelHeight/2))),Ve==null&&(Ve={x:Ge.position("x"),y:Ge.position("y")}),{x:Ve.x,y:Ve.y}}else{var ye=void 0;return R.forEach(function(be){var Ie=be.nodeIndexes.get(Ge.id());Ie!=null&&(ye={x:be.xCoords[Ie],y:be.yCoords[Ie]})}),ye==null&&(ye={x:Ge.position("x"),y:Ge.position("y")}),{x:ye.x,y:ye.y}}},"getPositions");if(v.quality=="default"||v.quality=="proof"||v.randomize){var Me=g.calcParentsWithoutChildren(x,w),He=w.filter(function(Ge){return Ge.css("display")=="none"});v.eles=w.not(He),w.nodes().not(":parent").not(He).layoutPositions(b,v,$e),Me.length>0&&Me.forEach(function(Ge){Ge.position($e(Ge))})}else console.log("If randomize option is set to false, then quality option must be 'default' or 'proof'.")},"run")}]),_})();l.exports=E}),657:((l,u,h)=>{var d=h(548),p=h(140).layoutBase.Matrix,f=h(140).layoutBase.SVD,g=s(function(m){var y=m.cy,k=m.eles,C=k.nodes(),S=k.nodes(":parent"),E=new Map,_=new Map,b=new Map,v=[],x=[],w=[],R=[],$=[],M=[],N=[],j=[],I=void 0,z=void 0,F=1e8,O=1e-9,B=m.piTol,Y=m.samplingType,U=m.nodeSeparation,H=void 0,J=s(function(){for(var ee=0,ve=0,X=!1;ve=Ce;){Re=me[Ce++];for(var Le=v[Re],ct=0;ctZe&&(Ze=$[mt],Fe=mt)}return Fe},"BFS"),ie=s(function(ee){var ve=void 0;if(ee){ve=Math.floor(Math.random()*z),I=ve;for(var X=0;X=1)break;Ze=De}for(var Le=0;Le=1)break;Ze=De}for(var qe=0;qe0&&(X.isParent()?v[ve].push(b.get(X.id())):v[ve].push(X.id()))})});var Me=s(function(ee){var ve=_.get(ee),X=void 0;E.get(ee).forEach(function(me){y.getElementById(me).isParent()?X=b.get(me):X=me,v[ve].push(X),v[_.get(X)].push(ee)})},"_loop"),He=!0,Ge=!1,ze=void 0;try{for(var Ve=E.keys()[Symbol.iterator](),et;!(He=(et=Ve.next()).done);He=!0){var he=et.value;Me(he)}}catch(ee){Ge=!0,ze=ee}finally{try{!He&&Ve.return&&Ve.return()}finally{if(Ge)throw ze}}z=_.size;var ye=void 0;if(z>2){H=z{var d=h(212),p=s(function(f){f&&f("layout","fcose",d)},"register");typeof cytoscape<"u"&&p(cytoscape),l.exports=p}),140:(l=>{l.exports=r})},n={};function a(l){var u=n[l];if(u!==void 0)return u.exports;var h=n[l]={exports:{}};return i[l](h,h.exports,a),h.exports}s(a,"__webpack_require__");var o=a(579);return o})()})});function efe(e,t){if(e===0)return t();let r=Math.random,i=e>>>0;Math.random=function(){i=i+1831565813>>>0;let n=i;return n=Math.imul(n^n>>>15,n|1),n^=n+Math.imul(n^n>>>7,n|61),((n^n>>>14)>>>0)/4294967296};try{return t()}finally{Math.random=r}}var rLt=G(()=>{"use strict";s(efe,"withSeededRandom")}),P1,nx,i2t=G(()=>{"use strict";Iu(),P1=s(e=>`${e}`,"wrapIcon"),nx={prefix:"mermaid-architecture",height:80,width:80,icons:{database:{body:P1('')},server:{body:P1('')},disk:{body:P1('')},internet:{body:P1('')},cloud:{body:P1('')},unknown:$O,blank:{body:P1("")}}}}),n2t,a2t,s2t,o2t,iLt=G(()=>{"use strict";Er(),ec(),Iu(),mi(),i2t(),Qpe(),Ar(),n2t=s(async function(e,t,r,i){let n=r.getConfigField("padding"),a=r.getConfigField("iconSize"),o=a/2,l=a/6,u=l/2;await Promise.all(t.edges().map(async h=>{let{source:d,sourceDir:p,sourceArrow:f,sourceGroup:g,target:m,targetDir:y,targetArrow:k,targetGroup:C,label:S}=Kpe(h),{x:E,y:_}=h[0].sourceEndpoint(),{x:b,y:v}=h[0].midpoint(),{x,y:w}=h[0].targetEndpoint(),R=n+4;if(g&&(_o(p)?E+=p==="L"?-R:R:_+=p==="T"?-R:R+18),C&&(_o(y)?x+=y==="L"?-R:R:w+=y==="T"?-R:R+18),!g&&r.getNode(d)?.type==="junction"&&(_o(p)?E+=p==="L"?o:-o:_+=p==="T"?o:-o),!C&&r.getNode(m)?.type==="junction"&&(_o(y)?x+=y==="L"?o:-o:w+=y==="T"?o:-o),h[0]._private.rscratch){let $=e.insert("g");if($.insert("path").attr("d",`M ${E},${_} L ${b},${v} L${x},${w} `).attr("class","edge").attr("id",`${i}-${gg(d,m,{prefix:"L"})}`),f){let M=_o(p)?G5[p](E,l):E-u,N=up(p)?G5[p](_,l):_-u;$.insert("polygon").attr("points",Hpe[p](l)).attr("transform",`translate(${M},${N})`).attr("class","arrow")}if(k){let M=_o(y)?G5[y](x,l):x-u,N=up(y)?G5[y](w,l):w-u;$.insert("polygon").attr("points",Hpe[y](l)).attr("transform",`translate(${M},${N})`).attr("class","arrow")}if(S){let M=dM(p,y)?"XY":_o(p)?"X":"Y",N=0;M==="X"?N=Math.abs(E-x):M==="Y"?N=Math.abs(_-w)/1.5:N=Math.abs(E-x)/2;let j=$.append("g");if(await Io(j,S,{useHtmlLabels:!1,width:N,classes:"architecture-service-label"},it()),j.attr("dy","1em").attr("alignment-baseline","middle").attr("dominant-baseline","middle").attr("text-anchor","middle"),M==="X")j.attr("transform","translate("+b+", "+v+")");else if(M==="Y")j.attr("transform","translate("+b+", "+v+") rotate(-90)");else if(M==="XY"){let I=pM(p,y);if(I&&jyt(I)){let z=j.node().getBoundingClientRect(),[F,O]=Yyt(I);j.attr("dominant-baseline","auto").attr("transform",`rotate(${-1*F*O*45})`);let B=j.node().getBoundingClientRect();j.attr("transform",` + translate(${b}, ${v-z.height/2}) + translate(${F*B.width/2}, ${O*B.height/2}) + rotate(${-1*F*O*45}, 0, ${z.height/2}) + `)}}}}}))},"drawEdges"),a2t=s(async function(e,t,r,i){let n=r.getConfigField("padding")*.75,a=r.getConfigField("fontSize"),o=r.getConfigField("iconSize")/2;await Promise.all(t.nodes().map(async l=>{let u=B1(l);if(u.type==="group"){let{h,w:d,x1:p,y1:f}=l.boundingBox(),g=e.append("rect");g.attr("id",`${i}-group-${u.id}`).attr("x",p+o).attr("y",f+o).attr("width",d).attr("height",h).attr("class","node-bkg");let m=e.append("g"),y=p,k=f;if(u.icon){let C=m.append("g");C.html(`${await Lu(u.icon,{height:n,width:n,fallbackPrefix:nx.prefix})}`),C.attr("transform","translate("+(y+o+1)+", "+(k+o+1)+")"),y+=n,k+=a/2-1-2}if(u.label){let C=m.append("g");await Io(C,u.label,{useHtmlLabels:!1,width:d,classes:"architecture-service-label"},it()),C.attr("dy","1em").attr("alignment-baseline","middle").attr("dominant-baseline","start").attr("text-anchor","start"),C.attr("transform","translate("+(y+o+4)+", "+(k+o+2)+")")}r.setElementForId(u.id,g)}}))},"drawGroups"),s2t=s(async function(e,t,r,i){let n=it();for(let a of r){let o=t.append("g"),l=e.getConfigField("iconSize");if(a.title){let p=o.append("g");await Io(p,a.title,{useHtmlLabels:!1,width:l*1.5,classes:"architecture-service-label"},n),p.attr("dy","1em").attr("alignment-baseline","middle").attr("dominant-baseline","middle").attr("text-anchor","middle"),p.attr("transform","translate("+l/2+", "+l+")")}let u=o.append("g");if(a.icon)u.html(`${await Lu(a.icon,{height:l,width:l,fallbackPrefix:nx.prefix})}`);else if(a.iconText){u.html(`${await Lu("blank",{height:l,width:l,fallbackPrefix:nx.prefix})}`);let p=u.append("g").append("foreignObject").attr("width",l).attr("height",l).append("div").attr("class","node-icon-text").attr("style",`height: ${l}px;`).append("div").html(Si(a.iconText,n)),f=parseInt(window.getComputedStyle(p.node(),null).getPropertyValue("font-size").replace(/\D/g,""))??16;p.attr("style",`-webkit-line-clamp: ${Math.floor((l-2)/f)};`)}else u.append("path").attr("class","node-bkg").attr("id",`${i}-node-${a.id}`).attr("d",`M0,${l} V5 Q0,0 5,0 H${l-5} Q${l},0 ${l},5 V${l} Z`);o.attr("id",`${i}-service-${a.id}`).attr("class","architecture-service");let{width:h,height:d}=o.node().getBBox();a.width=h,a.height=d,e.setElementForId(a.id,o)}return 0},"drawServices"),o2t=s(function(e,t,r,i){r.forEach(n=>{let a=t.append("g"),o=e.getConfigField("iconSize");a.append("g").append("rect").attr("id",`${i}-node-${n.id}`).attr("fill-opacity","0").attr("width",o).attr("height",o),a.attr("class","architecture-junction");let{width:l,height:u}=a._groups[0][0].getBBox();a.width=l,a.height=u,e.setElementForId(n.id,a)})},"drawJunctions")});function l2t(e,t,r){e.forEach(i=>{t.add({group:"nodes",data:{type:"service",id:i.id,icon:i.icon,label:i.title,parent:i.in,width:r.getConfigField("iconSize"),height:r.getConfigField("iconSize")},classes:"node-service"})})}function c2t(e,t,r){e.forEach(i=>{t.add({group:"nodes",data:{type:"junction",id:i.id,parent:i.in,width:r.getConfigField("iconSize"),height:r.getConfigField("iconSize")},classes:"node-junction"})})}function u2t(e,t){t.nodes().map(r=>{let i=B1(r);i.type!=="group"&&(i.x=r.position().x,i.y=r.position().y,e.getElementById(i.id).attr("transform","translate("+(i.x||0)+","+(i.y||0)+")"))})}function h2t(e,t){e.forEach(r=>{t.add({group:"nodes",data:{type:"group",id:r.id,icon:r.icon,label:r.title,parent:r.in},classes:"node-group"})})}function d2t(e,t){e.forEach(r=>{let{lhsId:i,rhsId:n,lhsInto:a,lhsGroup:o,rhsInto:l,lhsDir:u,rhsDir:h,rhsGroup:d,title:p}=r,f=dM(r.lhsDir,r.rhsDir)?"segments":"straight",g={id:`${i}-${n}`,label:p,source:i,sourceDir:u,sourceArrow:a,sourceGroup:o,sourceEndpoint:u==="L"?"0 50%":u==="R"?"100% 50%":u==="T"?"50% 0":"50% 100%",target:n,targetDir:h,targetArrow:l,targetGroup:d,targetEndpoint:h==="L"?"0 50%":h==="R"?"100% 50%":h==="T"?"50% 0":"50% 100%"};t.add({group:"edges",data:g,classes:f})})}function p2t(e,t,r,i=[]){let n=s((f,g)=>{let m=new Map;for(let[y,k]of f.entries()){let C=`${y}`,S=0,E=[...k.entries()];if(E.length===1){m.set(C,E[0][1]);continue}for(let _=0;_{let g=new Map,m=new Map;return f.forEach(([y,k],C)=>{let S=e.getNode(C)?.in??"default",E=g.get(k)??new Map;g.has(k)||g.set(k,E);let _=m.get(y)??new Map;m.has(y)||m.set(y,_);for(let b of[E,_]){let v=b.get(S)??[];b.has(S)||b.set(S,v),v.push(C)}}),{horiz:[...n(g,"horizontal").values()].filter(y=>y.length>1),vert:[...n(m,"vertical").values()].filter(y=>y.length>1)}}),[o,l]=a.reduce(([f,g],{horiz:m,vert:y})=>[[...f,...m],[...g,...y]],[[],[]]),u=new Set;i.forEach(f=>f.members.forEach(g=>u.add(g)));let h=s(f=>f.filter(g=>!g.some(m=>u.has(m))),"dropOverlapping"),d=h(o),p=h(l);return i.forEach(f=>{f.members.length<2||(f.direction==="row"?d.push([...f.members]):p.push([...f.members]))}),{horizontal:d,vertical:p}}function f2t(e,t,r=[]){let i=[],n=t.getConfigField("iconSize"),a=t.getConfigField("idealEdgeLengthMultiplier"),o=a*n,l=new Set;r.forEach(d=>{for(let p=0;p`${d[0]},${d[1]}`,"posToStr"),h=s(d=>d.split(",").map(p=>parseInt(p)),"strToPos");return e.forEach(d=>{let p=new Map([...d.entries()].map(([y,k])=>[u(k),y])),f=[u([0,0])],g={},m={L:[-1,0],R:[1,0],T:[0,1],B:[0,-1]};for(;f.length>0;){let y=f.shift();if(y){g[y]=1;let k=p.get(y);if(k){let C=h(y);Object.entries(m).forEach(([S,E])=>{let _=u([C[0]+E[0],C[1]+E[1]]),b=p.get(_);if(b&&!g[_]){if(f.push(_),l.has(`${k}|${b}`))return;i.push({[Ype[S]]:b,[Ype[Wyt(S)]]:k,gap:a*n})}})}}}}),i}function g2t(e,t,r,i,n,{spatialMaps:a,groupAlignments:o}){return new Promise(l=>{let u=zt("body").append("div").attr("id","cy").attr("style","display:none"),h=Qc({container:document.getElementById("cy"),style:[{selector:"edge",style:{"curve-style":"straight","source-endpoint":"data(sourceEndpoint)","target-endpoint":"data(targetEndpoint)"}},{selector:"edge[label]",style:{label:"data(label)"}},{selector:"edge.segments",style:{"curve-style":"segments","segment-weights":"0","segment-distances":[.5],"edge-distances":"endpoints","source-endpoint":"data(sourceEndpoint)","target-endpoint":"data(targetEndpoint)"}},{selector:"node",style:{"compound-sizing-wrt-labels":"include"}},{selector:"node[label]",style:{"text-valign":"bottom","text-halign":"center","font-size":`${n.getConfigField("fontSize")}px`}},{selector:".node-service",style:{label:"data(label)",width:"data(width)",height:"data(height)"}},{selector:".node-junction",style:{width:"data(width)",height:"data(height)"}},{selector:".node-group",style:{padding:`${n.getConfigField("padding")}px`}}],layout:{name:"grid",boundingBox:{x1:0,x2:100,y1:0,y2:100}}});u.remove(),h2t(r,h),l2t(e,h,n),c2t(t,h,n),d2t(i,h);let d=n.getLayoutHints(),p=p2t(n,a,o,d),f=f2t(a,n,d),g=n.getConfigField("iconSize"),m=n.getConfigField("idealEdgeLengthMultiplier")*g,y=.5*g,k=n.getConfigField("edgeElasticity"),C=n.getConfigField("seed"),S=h.layout({name:"fcose",quality:"proof",randomize:n.getConfigField("randomize"),nodeSeparation:n.getConfigField("nodeSeparation"),numIter:n.getConfigField("numIter"),styleEnabled:!1,animate:!1,nodeDimensionsIncludeLabels:!1,idealEdgeLength(E){let[_,b]=E.connectedNodes(),{parent:v}=B1(_),{parent:x}=B1(b);return v===x?m:y},edgeElasticity(E){let[_,b]=E.connectedNodes(),{parent:v}=B1(_),{parent:x}=B1(b);return v===x?k:.001},alignmentConstraint:p,relativePlacementConstraint:f});S.one("layoutstop",()=>{function E(_,b,v,x){let w,R,{x:$,y:M}=_,{x:N,y:j}=b;R=(x-M+($-v)*(M-j)/($-N))/Math.sqrt(1+Math.pow((M-j)/($-N),2)),w=Math.sqrt(Math.pow(x-M,2)+Math.pow(v-$,2)-Math.pow(R,2));let I=Math.sqrt(Math.pow(N-$,2)+Math.pow(j-M,2));w=w/I;let z=(N-$)*(x-M)-(j-M)*(v-$);switch(!0){case z>=0:z=1;break;case z<0:z=-1;break}let F=(N-$)*(v-$)+(j-M)*(x-M);switch(!0){case F>=0:F=1;break;case F<0:F=-1;break}return R=Math.abs(R)*z,w=w*F,{distances:R,weights:w}}s(E,"getSegmentWeights"),h.startBatch();for(let _ of Object.values(h.edges()))if(_.data?.()){let{x:b,y:v}=_.source().position(),{x,y:w}=_.target().position();if(b!==x&&v!==w){let R=_.sourceEndpoint(),$=_.targetEndpoint(),{sourceDir:M}=Kpe(_),[N,j]=up(M)?[R.x,$.y]:[$.x,R.y],{weights:I,distances:z}=E(R,$,N,j);_.style("segment-distances",z),_.style("segment-weights",I)}}h.endBatch(),efe(C,()=>S.run())});try{efe(C,()=>S.run())}catch(E){throw E instanceof RangeError&&E.message.includes("Invalid array length")?new Error("Architecture layout failed: a declared `align row|column` directive likely contradicts the edge directions, or two declared alignments overlap on a shared node. Check that the order of members in each `align` chain is consistent with the edges between them, and that no node appears in two `align` directives along the same axis."):E}h.ready(E=>{Ee.info("Ready",E),l(h)})})}var m2t,y2t,v2t,nLt=G(()=>{"use strict";DHe(),m2t=$o(tLt(),1),rLt(),pi(),Nt(),Iu(),wo(),vn(),i2t(),Qpe(),iLt(),SS([{name:nx.prefix,icons:nx}]),Qc.use(m2t.default),s(l2t,"addServices"),s(c2t,"addJunctions"),s(u2t,"positionNodes"),s(h2t,"addGroups"),s(d2t,"addEdges"),s(p2t,"getAlignments"),s(f2t,"getRelativeConstraints"),s(g2t,"layoutArchitecture"),y2t=s(async(e,t,r,i)=>{let n=i.db;n.setDiagramId(t);let a=n.getServices(),o=n.getJunctions(),l=n.getGroups(),u=n.getEdges(),h=n.getDataStructures(),d=Ks(t),p=d.append("g");p.attr("class","architecture-edges");let f=d.append("g");f.attr("class","architecture-services");let g=d.append("g");g.attr("class","architecture-groups"),await s2t(n,f,a,t),o2t(n,f,o,t);let m=await g2t(a,o,l,u,n,h);await n2t(p,m,n,t),await a2t(g,m,n,t),u2t(n,m),Q0(void 0,d,n.getConfigField("padding"),n.getConfigField("useMaxWidth"))},"draw"),v2t={draw:y2t}}),b2t={};Mr(b2t,{diagram:()=>x2t});var x2t,aLt=G(()=>{"use strict";J$t(),Qyt(),eLt(),nLt(),x2t={parser:Jpe,get db(){return new Zpe},renderer:v2t,styles:e2t}}),tfe,rfe,fM,ife,sLt=G(()=>{"use strict";tfe="position frame",rfe="frame positioned",fM="position relation",ife="relation positioned"});function T2t(){yM={}}function k2t(){let e=Y2t,{ast:t}=yM,r=nfe();if(!t)throw new Error("No data for EventModel");return t.frames.forEach((i,n)=>{let a=R2t(i,t.dataEntities,r);e=mM(e,{$kind:tfe,index:n,frame:i,textProps:a});let o;N2t(i)?(Ee.debug("source frame",i.sourceFrames),o=t.frames.filter(l=>i.sourceFrames.some(u=>u.$refText===l.name)),o.forEach(l=>{e=mM(e,{$kind:fM,index:n,frame:i,sourceFrame:l})})):e=mM(e,{$kind:fM,index:n,frame:i})}),e={...e,sortedSwimlanesArray:afe(e.swimlanes)},e}function C2t(e){yM.ast=e}function nfe(){return Hi}function w2t(e){let t=e.split(".");if(t.length===2)return t[0]}function E2t(e){let t=e.split(".");return t.length===2?t[1]:e}function S2t(e,t){if(!(!t||t.length===0))return Object.values(e).find(r=>r.namespace===t)}function gM(e,t,r){return Math.max(t,...Object.keys(e).filter(i=>{let n=Number.parseInt(i);return n>t&&nNumber.parseInt(i)))+1}function _2t(e,t){let r=w2t(e.entityIdentifier),i=S2t(t,r);switch(e.modelEntityType){case"ui":case"pcr":case"processor":return i?{index:i.index,label:i.namespace||Hi.labelUiAutomation}:r?{index:gM(t,0,100),label:Hi.labelUiAutomationPrefix+r}:{index:0,label:Hi.labelUiAutomation};case"rmo":case"readmodel":case"cmd":case"command":return i?{index:i.index,label:i.namespace||Hi.labelCommandReadModel}:r?{index:gM(t,100,200),label:Hi.labelCommandReadModelPrefix+r}:{index:100,label:Hi.labelCommandReadModel};default:return i?{index:i.index,label:i.namespace||Hi.labelEvents}:r?{index:gM(t,200,300),label:Hi.labelEventsPrefix+r}:{index:200,label:Hi.labelEvents}}}function A2t(e){let{themeVariables:t}=Rr();switch(e.modelEntityType){case"ui":return{fill:t.emUiFill??"white",stroke:t.emUiStroke??"#dbdada"};case"pcr":case"processor":return{fill:t.emProcessorFill??"#edb3f6",stroke:t.emProcessorStroke??"#b88cbf"};case"rmo":case"readmodel":return{fill:t.emReadModelFill??"#d3f1a2",stroke:t.emReadModelStroke??"#a3b732"};case"cmd":case"command":return{fill:t.emCommandFill??"#bcd6fe",stroke:t.emCommandStroke??"#679ac3"};case"evt":case"event":return{fill:t.emEventFill??"#ffb778",stroke:t.emEventStroke??"#c19a0f"};default:return{fill:"red",stroke:"black"}}}function R2t(e,t,r){let i=Rr(),n=Si(E2t(e.entityIdentifier)??"",i),a,o={fontSize:16,fontWeight:700,fontFamily:'"trebuchet ms", verdana, arial, sans-serif',joinWith:"
    "},l=`${Uy(n,r.textMaxWidth,o)}`;if(e.dataInlineValue&&(a=e.dataInlineValue,a=a.substring(a.indexOf("{")+1),a=a.substring(0,a.lastIndexOf("}")-1),a=Si(a,i),a=Uy(a,r.textMaxWidth,o),a=a.replaceAll(" "," ")),e.dataReference){let g=t.find(m=>m.name===e.dataReference?.$refText);g&&(a=g.dataBlockValue,a=a.substring(a.indexOf(`{ +`)+2),a=a.substring(0,a.lastIndexOf("}")-1),a=Si(a,i),a=Uy(a,r.textMaxWidth,o),a=a.replaceAll(" "," "),a+="
    ")}let u=a!==void 0;u&&(l+=`

    ${a}`);let h={fontSize:o.fontSize,fontWeight:o.fontWeight,fontFamily:o.fontFamily},d=f4(l,h),p=u?d.width/3:d.width,f={content:l,width:p,height:d.height};return Ee.debug(`[${e.name}] ${e.entityIdentifier} text`,f),f}function $2t(e,t){let r=t,i=A2t(r.frame),n={width:r.textProps.width+2*Hi.boxTextPadding,height:r.textProps.height+2*Hi.boxTextPadding};return[{$kind:rfe,frame:r.frame,index:r.index,visual:i,dimension:n,textProps:r.textProps}]}function L2t(e,t,r){return t===void 0?Hi.contentStartX:t.index===e.index&&e.r?e.r+Hi.boxPadding:r===void 0?Hi.contentStartX:r.r-Hi.boxOverlap+Hi.boxPadding}function I2t(e,t){let r=[...e.map(i=>i.r),t];return Math.max(...r)}function afe(e){return Object.values(e).sort((t,r)=>t.index-r.index)}function M2t(e,t){let r=t,i=_2t(r.frame,e.swimlanes),n;i.index in e.swimlanes?n=e.swimlanes[i.index]:n={index:i.index,label:i.label,r:0,y:i.index*Hi.swimlaneMinHeight+Hi.swimlaneGap,height:Hi.swimlaneMinHeight,maxHeight:Hi.swimlaneMinHeight};let a=e.boxes.length>0?e.boxes[e.boxes.length-1]:void 0,o=e.previousSwimlaneNumber!==void 0?e.swimlanes[e.previousSwimlaneNumber]:void 0,l={width:Math.max(Hi.boxMinWidth,Math.min(Hi.boxMaxWidth,r.dimension.width))+2*Hi.boxPadding,height:Math.max(Hi.boxMinHeight,Math.min(Hi.boxMaxHeight,r.dimension.height))+2*Hi.boxPadding},u=L2t(n,o,a),h=u+l.width+Hi.boxPadding,d=I2t(Object.values(e.swimlanes),h);n.r=u+l.width,n.maxHeight=Math.max(n.maxHeight,l.height),n.height=Math.max(Hi.swimlaneMinHeight,n.maxHeight)+2*Hi.swimlanePadding;let p={x:u,y:Hi.swimlanePadding+n.y,r:h,dimension:l,leftSibling:!1,swimlane:n,visual:r.visual,text:r.textProps.content,frame:r.frame,index:r.index},f={...e,boxes:[...e.boxes,p],swimlanes:{...e.swimlanes,[`${n.index}`]:n},previousSwimlaneNumber:i.index,previousFrame:r.frame,maxR:d},g=afe(f.swimlanes);g.length>0&&(g[0].y=0);for(let m=1;m0}function sfe(e,t){if(t!=null)return e.find(r=>r.frame.name===t.name)}function O2t(e,t,r){if(!(r<0))for(let i=r;i>=0;i--){let n=e[i];if(n.swimlane.index!==t)return n}}function B2t(e,t){let r=t;if(Une(r.frame)||D2t(r.index,r.frame))return[];let i=sfe(e.boxes,r.frame);if(i===void 0)throw new Error(`Target box not found for frame ${r.frame.name}`);let n;return r.sourceFrame?n=sfe(e.boxes,r.sourceFrame):n=O2t(e.boxes,i.swimlane.index,r.index-1),n===void 0?[]:[{$kind:ife,frame:r.frame,index:r.index,sourceBox:n,targetBox:i}]}function P2t(e,t){let r=t,i={visual:{fill:"none",stroke:"#000"},source:{x:r.sourceBox.x,y:r.sourceBox.y},target:{x:r.targetBox.x,y:r.targetBox.y},sourceBox:r.sourceBox,targetBox:r.targetBox};return{...e,relations:[...e.relations,i]}}function F2t(e,t){let r=H2t[t.$kind];if(r==null)return[];let i=r(e,t);return Ee.debug("decided events",i),i}function z2t(e,t){let r=t.reduce((i,n)=>{let a=G2t[n.$kind];return a==null?i:a(i,n)},e);return Ee.debug("evolve events",{state:e,newState:r,events:t}),r}function mM(e,t){let r=F2t(e,t);return z2t(e,r)}var q2t,W2t,j2t,V2t,U2t,yM,Hi,Y2t,H2t,G2t,vM,X2t=G(()=>{"use strict";Nt(),Ar(),Ar(),Gr(),fn(),mi(),Za(),Co(),sLt(),q2t=s(function(e){Ee.debug("options str",e)},"setOptions"),W2t=s(function(){return{}},"getOptions"),j2t=s(function(){T2t(),pn()},"clear"),s(T2t,"reset"),V2t=vi.eventmodeling,U2t=s(()=>Gn({...V2t,...Rr().eventmodeling}),"getConfig"),yM={},s(k2t,"getState"),s(C2t,"setAst"),Hi={swimlaneMinHeight:70,swimlanePadding:15,swimlaneGap:10,boxPadding:10,boxOverlap:90,boxDefaultY:0,boxMinWidth:80,boxMaxWidth:450,boxMinHeight:80,boxMaxHeight:750,contentStartX:250,textMaxWidth:430,boxTextFontWeight:"bold",boxTextPadding:10,swimlaneTextFontWeight:"bold",labelUiAutomation:"UI/Automation",labelUiAutomationPrefix:"UI/A: ",labelCommandReadModel:"Command/Read Model",labelCommandReadModelPrefix:"C/RM: ",labelEvents:"Events",labelEventsPrefix:"Stream: "},s(nfe,"getDiagramProps"),Y2t={boxes:[],swimlanes:{},relations:[],maxR:0,sortedSwimlanesArray:[]},s(w2t,"extractNamespace"),s(E2t,"extractName"),s(S2t,"findSwimlaneByNamespace"),s(gM,"findNextAvailableIndex"),s(_2t,"calculateSwimlaneProps"),s(A2t,"calculateEntityVisualProps"),s(R2t,"calculateTextProps"),s($2t,"decidePositionFrame"),s(L2t,"calculateX"),s(I2t,"calculateMaxRight"),s(afe,"sortedSwimlanesArray"),s(M2t,"evolveFramePositioned"),s(D2t,"isFirstFrame"),s(N2t,"hasSourceFrame"),s(sfe,"findBoxByFrame"),s(O2t,"findBoxByLineIndex"),s(B2t,"decidePositionRelation"),s(P2t,"evolveRelationPositioned"),H2t={[tfe]:$2t,[fM]:B2t},G2t={[rfe]:M2t,[ife]:P2t},s(F2t,"decide"),s(z2t,"evolve"),s(mM,"dispatch"),vM={getConfig:U2t,setOptions:q2t,getOptions:W2t,clear:j2t,setAccTitle:bn,getAccTitle:Ln,getAccDescription:Mn,setAccDescription:In,setDiagramTitle:Dn,getDiagramTitle:xn,setAst:C2t,getDiagramProps:nfe,getState:k2t}}),K2t,oLt=G(()=>{"use strict";Co(),Nt(),vl(),X2t(),K2t={parse:s(async e=>{let t=await Pl("eventmodeling",e);Ee.debug(t),vM.setAst(t),Yo(t,vM)},"parse")}});function Q2t(e,t){return r=>{let i=r.swimlane.y+t.swimlanePadding,n=e.append("g").attr("class","em-box");n.append("rect").attr("x",r.x).attr("y",i).attr("rx","3").attr("width",r.dimension.width).attr("height",r.dimension.height).attr("stroke",r.visual.stroke).attr("fill",r.visual.fill),n.append("foreignObject").attr("x",r.x+t.boxPadding).attr("y",i+10).attr("width",r.dimension.width-2*t.boxPadding).attr("height",r.dimension.height-2*t.boxPadding).append("xhtml:div").style("display","table").style("height","100%").style("width","100%").append("span").style("display","table-cell").style("text-align","center").style("vertical-align","middle").html(r.text)}}function Z2t(e,t){return e>t}function J2t(e,t,r,i){return n=>{let a=n.sourceBox.swimlane.y+t.swimlanePadding,o=n.targetBox.swimlane.y+t.swimlanePadding,l=Z2t(a,o),u=n.sourceBox.x+n.sourceBox.dimension.width*2/3,h=n.targetBox.x+n.targetBox.dimension.width/3,d,p;Ee.debug(`rendering relation up=${l} for `,{sourceBox:n.sourceBox,targetBox:n.targetBox}),l?(d=a,p=o+n.targetBox.dimension.height):(d=a+n.sourceBox.dimension.height,p=o);let f=i.emRelationStroke??n.visual.stroke;e.append("path").attr("class","em-relation").attr("fill",n.visual.fill).attr("stroke",f).attr("stroke-width","1").attr("marker-end",`url(#${r})`).attr("d",`M${u} ${d} L${h} ${p}`)}}function evt(e,t,r,i){return n=>{let a=e.append("g").attr("class","em-swimlane"),o=i.emSwimlaneBackgroundOdd??"rgb(250,250,250)",l=i.emSwimlaneBackgroundStroke??"rgb(240,240,240)";a.append("rect").attr("x",0).attr("y",n.y).attr("rx","3").attr("width",t+r.swimlanePadding).attr("height",n.height).attr("fill",o).attr("stroke",l),a.append("text").attr("font-weight",r.swimlaneTextFontWeight).attr("x",30).attr("y",n.y+30).text(n.label)}}var tvt,rvt,ivt,nvt,lLt=G(()=>{"use strict";pi(),Er(),Nt(),tvt=it(),rvt=tvt?.eventmodeling,s(Q2t,"renderD3Box"),s(Z2t,"dirUpwards"),s(J2t,"renderD3Relation"),s(evt,"renderD3Swimlane"),ivt=s(function(e,t,r,i){if(Ee.debug("in eventmodeling renderer",e+` +`,"id:",t,r),!rvt)throw new Error("EventModeling config not found");let n=i.db,{themeVariables:a,eventmodeling:o}=it(),l=zt(`[id="${t}"]`),u=n.getDiagramProps(),h=n.getState(),d=`em-arrowhead-${t}`,p=a.emArrowhead??"#000000";h.sortedSwimlanesArray.forEach(evt(l,h.maxR,u,a)),h.boxes.forEach(Q2t(l,u)),h.relations.forEach(J2t(l,u,d,a)),l.append("defs").append("marker").attr("id",d).attr("markerWidth","10").attr("markerHeight","7").attr("refX","10").attr("refY","3.5").attr("orient","auto").append("polygon").attr("points","0 0, 10 3.5, 0 7").attr("fill",p),NS(void 0,l,o?.padding??30,o?.useMaxWidth)},"draw"),nvt={draw:ivt}}),avt,svt,cLt=G(()=>{"use strict";avt=s(e=>"","getStyles"),svt=avt}),ovt={};Mr(ovt,{diagram:()=>lvt});var lvt,uLt=G(()=>{"use strict";oLt(),X2t(),lLt(),cLt(),lvt={parser:K2t,db:vM,renderer:nvt,styles:svt}}),bM,cvt,hLt=G(()=>{"use strict";bM=(function(){var e=s(function(C,S,E,_){for(E=E||{},_=C.length;_--;E[C[_]]=S);return E},"o"),t=[1,4],r=[1,14],i=[1,12],n=[1,13],a=[6,7,8],o=[1,20],l=[1,18],u=[1,19],h=[6,7,11],d=[1,6,13,14],p=[1,23],f=[1,24],g=[1,6,7,11,13,14],m={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,ishikawa:4,spaceLines:5,SPACELINE:6,NL:7,ISHIKAWA:8,document:9,stop:10,EOF:11,statement:12,SPACELIST:13,TEXT:14,$accept:0,$end:1},terminals_:{2:"error",6:"SPACELINE",7:"NL",8:"ISHIKAWA",11:"EOF",13:"SPACELIST",14:"TEXT"},productions_:[0,[3,1],[3,2],[5,1],[5,2],[5,2],[4,2],[4,3],[10,1],[10,1],[10,1],[10,2],[10,2],[9,3],[9,2],[12,2],[12,1],[12,1],[12,1]],performAction:s(function(C,S,E,_,b,v,x){var w=v.length-1;switch(b){case 6:case 7:return _;case 15:_.addNode(v[w-1].length,v[w].trim());break;case 16:_.addNode(0,v[w].trim());break}},"anonymous"),table:[{3:1,4:2,5:3,6:[1,5],8:t},{1:[3]},{1:[2,1]},{4:6,6:[1,7],7:[1,8],8:t},{6:r,7:[1,10],9:9,12:11,13:i,14:n},e(a,[2,3]),{1:[2,2]},e(a,[2,4]),e(a,[2,5]),{1:[2,6],6:r,12:15,13:i,14:n},{6:r,9:16,12:11,13:i,14:n},{6:o,7:l,10:17,11:u},e(h,[2,18],{14:[1,21]}),e(h,[2,16]),e(h,[2,17]),{6:o,7:l,10:22,11:u},{1:[2,7],6:r,12:15,13:i,14:n},e(d,[2,14],{7:p,11:f}),e(g,[2,8]),e(g,[2,9]),e(g,[2,10]),e(h,[2,15]),e(d,[2,13],{7:p,11:f}),e(g,[2,11]),e(g,[2,12])],defaultActions:{2:[2,1],6:[2,2]},parseError:s(function(C,S){if(S.recoverable)this.trace(C);else{var E=new Error(C);throw E.hash=S,E}},"parseError"),parse:s(function(C){var S=this,E=[0],_=[],b=[null],v=[],x=this.table,w="",R=0,$=0,M=0,N=2,j=1,I=v.slice.call(arguments,1),z=Object.create(this.lexer),F={yy:{}};for(var O in this.yy)Object.prototype.hasOwnProperty.call(this.yy,O)&&(F.yy[O]=this.yy[O]);z.setInput(C,F.yy),F.yy.lexer=z,F.yy.parser=this,typeof z.yylloc>"u"&&(z.yylloc={});var B=z.yylloc;v.push(B);var Y=z.options&&z.options.ranges;typeof F.yy.parseError=="function"?this.parseError=F.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function U(xe){E.length=E.length-2*xe,b.length=b.length-xe,v.length=v.length-xe}s(U,"popStack");function H(){var xe;return xe=_.pop()||z.lex()||j,typeof xe!="number"&&(xe instanceof Array&&(_=xe,xe=_.pop()),xe=S.symbols_[xe]||xe),xe}s(H,"lex");for(var J,te,ie,ue,se,le,Te={},oe,fe,ge,ke;;){if(ie=E[E.length-1],this.defaultActions[ie]?ue=this.defaultActions[ie]:((J===null||typeof J>"u")&&(J=H()),ue=x[ie]&&x[ie][J]),typeof ue>"u"||!ue.length||!ue[0]){var Ne="";ke=[];for(oe in x[ie])this.terminals_[oe]&&oe>N&&ke.push("'"+this.terminals_[oe]+"'");z.showPosition?Ne="Parse error on line "+(R+1)+`: +`+z.showPosition()+` +Expecting `+ke.join(", ")+", got '"+(this.terminals_[J]||J)+"'":Ne="Parse error on line "+(R+1)+": Unexpected "+(J==j?"end of input":"'"+(this.terminals_[J]||J)+"'"),this.parseError(Ne,{text:z.match,token:this.terminals_[J]||J,line:z.yylineno,loc:B,expected:ke})}if(ue[0]instanceof Array&&ue.length>1)throw new Error("Parse Error: multiple actions possible at state: "+ie+", token: "+J);switch(ue[0]){case 1:E.push(J),b.push(z.yytext),v.push(z.yylloc),E.push(ue[1]),J=null,te?(J=te,te=null):($=z.yyleng,w=z.yytext,R=z.yylineno,B=z.yylloc,M>0&&M--);break;case 2:if(fe=this.productions_[ue[1]][1],Te.$=b[b.length-fe],Te._$={first_line:v[v.length-(fe||1)].first_line,last_line:v[v.length-1].last_line,first_column:v[v.length-(fe||1)].first_column,last_column:v[v.length-1].last_column},Y&&(Te._$.range=[v[v.length-(fe||1)].range[0],v[v.length-1].range[1]]),le=this.performAction.apply(Te,[w,$,R,F.yy,ue[1],b,v].concat(I)),typeof le<"u")return le;fe&&(E=E.slice(0,-1*fe*2),b=b.slice(0,-1*fe),v=v.slice(0,-1*fe)),E.push(this.productions_[ue[1]][0]),b.push(Te.$),v.push(Te._$),ge=x[E[E.length-2]][E[E.length-1]],E.push(ge);break;case 3:return!0}}return!0},"parse")},y=(function(){var C={EOF:1,parseError:s(function(S,E){if(this.yy.parser)this.yy.parser.parseError(S,E);else throw new Error(S)},"parseError"),setInput:s(function(S,E){return this.yy=E||this.yy||{},this._input=S,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var S=this._input[0];this.yytext+=S,this.yyleng++,this.offset++,this.match+=S,this.matched+=S;var E=S.match(/(?:\r\n?|\n).*/g);return E?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),S},"input"),unput:s(function(S){var E=S.length,_=S.split(/(?:\r\n?|\n)/g);this._input=S+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-E),this.offset-=E;var b=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),_.length-1&&(this.yylineno-=_.length-1);var v=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:_?(_.length===b.length?this.yylloc.first_column:0)+b[b.length-_.length].length-_[0].length:this.yylloc.first_column-E},this.options.ranges&&(this.yylloc.range=[v[0],v[0]+this.yyleng-E]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(S){this.unput(this.match.slice(S))},"less"),pastInput:s(function(){var S=this.matched.substr(0,this.matched.length-this.match.length);return(S.length>20?"...":"")+S.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var S=this.match;return S.length<20&&(S+=this._input.substr(0,20-S.length)),(S.substr(0,20)+(S.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var S=this.pastInput(),E=new Array(S.length+1).join("-");return S+this.upcomingInput()+` +`+E+"^"},"showPosition"),test_match:s(function(S,E){var _,b,v;if(this.options.backtrack_lexer&&(v={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(v.yylloc.range=this.yylloc.range.slice(0))),b=S[0].match(/(?:\r\n?|\n).*/g),b&&(this.yylineno+=b.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:b?b[b.length-1].length-b[b.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+S[0].length},this.yytext+=S[0],this.match+=S[0],this.matches=S,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(S[0].length),this.matched+=S[0],_=this.performAction.call(this,this.yy,this,E,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),_)return _;if(this._backtrack){for(var x in v)this[x]=v[x];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var S,E,_,b;this._more||(this.yytext="",this.match="");for(var v=this._currentRules(),x=0;xE[0].length)){if(E=_,b=x,this.options.backtrack_lexer){if(S=this.test_match(_,v[x]),S!==!1)return S;if(this._backtrack){E=!1;continue}else return!1}else if(!this.options.flex)break}return E?(S=this.test_match(E,v[b]),S!==!1?S:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var S=this.next();return S||this.lex()},"lex"),begin:s(function(S){this.conditionStack.push(S)},"begin"),popState:s(function(){var S=this.conditionStack.length-1;return S>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(S){return S=this.conditionStack.length-1-Math.abs(S||0),S>=0?this.conditionStack[S]:"INITIAL"},"topState"),pushState:s(function(S){this.begin(S)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(S,E,_,b){var v=b;switch(_){case 0:return 6;case 1:return 8;case 2:return 8;case 3:return 6;case 4:return 7;case 5:return 13;case 6:return 14;case 7:return 11}},"anonymous"),rules:[/^(?:\s*%%.*)/i,/^(?:ishikawa-beta\b)/i,/^(?:ishikawa\b)/i,/^(?:[\s]+[\n])/i,/^(?:[\n]+)/i,/^(?:[\s]+)/i,/^(?:[^\n]+)/i,/^(?:$)/i],conditions:{INITIAL:{rules:[0,1,2,3,4,5,6,7],inclusive:!0}}};return C})();m.lexer=y;function k(){this.yy={}}return s(k,"Parser"),k.prototype=m,m.Parser=k,new k})(),bM.parser=bM,cvt=bM}),uvt,dLt=G(()=>{"use strict";var e;Er(),mi(),fn(),uvt=(e=class{constructor(){this.stack=[],this.clear=this.clear.bind(this),this.addNode=this.addNode.bind(this),this.getRoot=this.getRoot.bind(this)}clear(){this.root=void 0,this.stack=[],this.baseLevel=void 0,pn()}getRoot(){return this.root}addNode(r,i){let n=Vt.sanitizeText(i,it());if(!this.root){this.root={text:n,children:[]},this.stack=[{level:0,node:this.root}],Dn(n);return}this.baseLevel??=r;let a=r-this.baseLevel+1;for(a<=0&&(a=1);this.stack.length>1&&this.stack[this.stack.length-1].level>=a;)this.stack.pop();let o=this.stack[this.stack.length-1].node,l={text:n,children:[]};o.children.push(l),this.stack.push({level:a,node:l})}getAccTitle(){return Ln()}setAccTitle(r){bn(r)}getAccDescription(){return Mn()}setAccDescription(r){In(r)}getDiagramTitle(){return xn()}setDiagramTitle(r){Dn(r)}},s(e,"IshikawaDB"),e)}),hvt,F1,dvt,pvt,fvt,ofe,lfe,cfe,ufe,gvt,hfe,mvt,yvt,vvt,xM,bvt,xvt,dfe,X5,pfe,z1,Tvt,pLt=G(()=>{"use strict";Er(),wo(),vn(),Ar(),vr(),hvt=14,F1=250,dvt=30,pvt=60,fvt=5,ofe=82*Math.PI/180,lfe=Math.cos(ofe),cfe=Math.sin(ofe),ufe=s((e,t,r)=>{let i=e.node().getBBox(),n=i.width+t*2,a=i.height+t*2;aa(e,a,n,r),e.attr("viewBox",`${i.x-t} ${i.y-t} ${n} ${a}`)},"applyPaddedViewBox"),gvt=s((e,t,r,i)=>{let n=i.db.getRoot();if(!n)return;let a=it(),{look:o,handDrawnSeed:l,themeVariables:u}=a,h=zu(a.fontSize)[0]??hvt,d=o==="handDrawn",p=n.children??[],f=a.ishikawa?.diagramPadding??20,g=a.ishikawa?.useMaxWidth??!1,m=Ks(t),y=m.append("g").attr("class","ishikawa"),k=d?ur.svg(m.node()):void 0,C=k?{roughSvg:k,seed:l??0,lineColor:u?.lineColor??"#333",fillColor:u?.mainBkg??"#fff"}:void 0,S=`ishikawa-arrow-${t}`;d||y.append("defs").append("marker").attr("id",S).attr("viewBox","0 0 10 10").attr("refX",0).attr("refY",5).attr("markerWidth",6).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 10 0 L 0 5 L 10 10 Z").attr("class","ishikawa-arrow");let E=0,_=F1,b=d?void 0:z1(y,E,_,E,_,"ishikawa-spine");if(mvt(y,E,_,n.text,h,C),!p.length){d&&z1(y,E,_,E,_,"ishikawa-spine",C),ufe(m,f,g);return}E-=20;let v=p.filter((z,F)=>F%2===0),x=p.filter((z,F)=>F%2===1),w=hfe(v),R=hfe(x),$=w.total+R.total,M=F1,N=F1;if($>0){let z=F1*2,F=F1*.3;M=Math.max(F,z*(w.total/$)),N=Math.max(F,z*(R.total/$))}let j=h*2;M=Math.max(M,w.max*j),N=Math.max(N,R.max*j),_=Math.max(M,F1),b&&b.attr("y1",_).attr("y2",_),y.select(".ishikawa-head-group").attr("transform",`translate(0,${_})`);let I=Math.ceil(p.length/2);for(let z=0;zMath.min(O,B.getBBox().x),1/0)}if(d)z1(y,E,_,0,_,"ishikawa-spine",C);else{b.attr("x1",E);let z=`url(#${S})`;y.selectAll("line.ishikawa-branch, line.ishikawa-sub-branch").attr("marker-start",z)}ufe(m,f,g)},"draw"),hfe=s(e=>{let t=s(r=>r.children.reduce((i,n)=>i+1+t(n),0),"countDescendants");return e.reduce((r,i)=>{let n=t(i);return r.total+=n,r.max=Math.max(r.max,n),r},{total:0,max:0})},"sideStats"),mvt=s((e,t,r,i,n,a)=>{let o=Math.max(6,Math.floor(110/(n*.6))),l=e.append("g").attr("class","ishikawa-head-group").attr("transform",`translate(${t},${r})`),u=X5(l,dfe(i,o),0,0,"ishikawa-head-label","start",n),h=u.node().getBBox(),d=Math.max(60,h.width+6),p=Math.max(40,h.height*2+40),f=`M 0 ${-p/2} L 0 ${p/2} Q ${d*2.4} 0 0 ${-p/2} Z`;if(a){let g=a.roughSvg.path(f,{roughness:1.5,seed:a.seed,fill:a.fillColor,fillStyle:"hachure",fillWeight:2.5,hachureGap:5,stroke:a.lineColor,strokeWidth:2});l.insert(()=>g,":first-child").attr("class","ishikawa-head")}else l.insert("path",":first-child").attr("class","ishikawa-head").attr("d",f);u.attr("transform",`translate(${(d-h.width)/2-h.x+3},${-h.y-h.height/2})`)},"drawHead"),yvt=s((e,t)=>{let r=[],i=[],n=s((a,o,l)=>{let u=t===-1?[...a].reverse():a;for(let h of u){let d=r.length,p=h.children??[];r.push({depth:l,text:dfe(h.text,15),parentIndex:o,childCount:p.length}),l%2===0?(i.push(d),p.length&&n(p,d,l+1)):(p.length&&n(p,d,l+1),i.push(d))}},"walk");return n(e,-1,2),{entries:r,yOrder:i}},"flattenTree"),vvt=s((e,t,r,i,n,a,o)=>{let l=e.append("g").attr("class","ishikawa-label-group"),u=X5(l,t,r,i+11*n,"ishikawa-label cause","middle",a).node().getBBox();if(o){let h=o.roughSvg.rectangle(u.x-20,u.y-2,u.width+40,u.height+4,{roughness:1.5,seed:o.seed,fill:o.fillColor,fillStyle:"hachure",fillWeight:2.5,hachureGap:5,stroke:o.lineColor,strokeWidth:2});l.insert(()=>h,":first-child").attr("class","ishikawa-label-box")}else l.insert("rect",":first-child").attr("class","ishikawa-label-box").attr("x",u.x-20).attr("y",u.y-2).attr("width",u.width+40).attr("height",u.height+4)},"drawCauseLabel"),xM=s((e,t,r,i,n,a)=>{let o=Math.sqrt(i*i+n*n);if(o===0)return;let l=i/o,u=n/o,h=6,d=-u*h,p=l*h,f=t,g=r,m=`M ${f} ${g} L ${f-l*h*2+d} ${g-u*h*2+p} L ${f-l*h*2-d} ${g-u*h*2-p} Z`,y=a.roughSvg.path(m,{roughness:1,seed:a.seed,fill:a.lineColor,fillStyle:"solid",stroke:a.lineColor,strokeWidth:1});e.append(()=>y)},"drawArrowMarker"),bvt=s((e,t,r,i,n,a,o,l)=>{let u=t.children??[],h=a*(u.length?1:.2),d=-lfe*h,p=cfe*h*n,f=r+d,g=i+p;if(z1(e,r,i,f,g,"ishikawa-branch",l),l&&xM(e,r,i,r-f,i-g,l),vvt(e,t.text,f,g,n,o,l),!u.length)return;let{entries:m,yOrder:y}=yvt(u,n),k=m.length,C=new Array(k);for(let[v,x]of y.entries())C[x]=i+p*((v+1)/(k+1));let S=new Map;S.set(-1,{x0:r,y0:i,x1:f,y1:g,childCount:u.length,childrenDrawn:0});let E=-lfe,_=cfe*n,b=n<0?"ishikawa-label up":"ishikawa-label down";for(let[v,x]of m.entries()){let w=C[v],R=S.get(x.parentIndex),$=e.append("g").attr("class","ishikawa-sub-group"),M=0,N=0,j=0;if(x.depth%2===0){let I=R.y1-R.y0;M=pfe(R.x0,R.x1,I?(w-R.y0)/I:.5),N=w,j=M-(x.childCount>0?pvt+x.childCount*fvt:dvt),z1($,M,w,j,w,"ishikawa-sub-branch",l),l&&xM($,M,w,1,0,l),X5($,x.text,j,w,"ishikawa-label align","end",o)}else{let I=R.childrenDrawn++;M=pfe(R.x0,R.x1,(R.childCount-I)/(R.childCount+1)),N=R.y0,j=M+E*((w-N)/_),z1($,M,N,j,w,"ishikawa-sub-branch",l),l&&xM($,M,N,M-j,N-w,l),X5($,x.text,j,w,b,"end",o)}x.childCount>0&&S.set(v,{x0:M,y0:N,x1:j,y1:w,childCount:x.childCount,childrenDrawn:0})}},"drawBranch"),xvt=s(e=>e.split(/|\n/),"splitLines"),dfe=s((e,t)=>{if(e.length<=t)return e;let r=[];for(let i of e.split(/\s+/)){let n=r.length-1;n>=0&&r[n].length+1+i.length<=t?r[n]+=" "+i:r.push(i)}return r.join(` +`)},"wrapText"),X5=s((e,t,r,i,n,a,o)=>{let l=xvt(t),u=o*1.05,h=e.append("text").attr("class",n).attr("text-anchor",a).attr("x",r).attr("y",i-(l.length-1)*u/2);for(let[d,p]of l.entries())h.append("tspan").attr("x",r).attr("dy",d===0?0:u).text(p);return h},"drawMultilineText"),pfe=s((e,t,r)=>e+(t-e)*r,"lerp"),z1=s((e,t,r,i,n,a,o)=>{if(o){let l=o.roughSvg.line(t,r,i,n,{roughness:1.5,seed:o.seed,stroke:o.lineColor,strokeWidth:2});e.append(()=>l).attr("class",a);return}return e.append("line").attr("class",a).attr("x1",t).attr("y1",r).attr("x2",i).attr("y2",n)},"drawLine"),Tvt={draw:gvt}}),kvt,Cvt,fLt=G(()=>{"use strict";kvt=s(e=>` +.ishikawa .ishikawa-spine, +.ishikawa .ishikawa-branch, +.ishikawa .ishikawa-sub-branch { + stroke: ${e.lineColor}; + stroke-width: 2; + fill: none; +} + +.ishikawa .ishikawa-sub-branch { + stroke-width: 1; +} + +.ishikawa .ishikawa-arrow { + fill: ${e.lineColor}; +} + +.ishikawa .ishikawa-head { + fill: ${e.mainBkg}; + stroke: ${e.lineColor}; + stroke-width: 2; +} + +.ishikawa .ishikawa-label-box { + fill: ${e.mainBkg}; + stroke: ${e.lineColor}; + stroke-width: 2; +} + +.ishikawa text { + font-family: ${e.fontFamily}; + font-size: ${e.fontSize}; + fill: ${e.textColor}; +} + +.ishikawa .ishikawa-head-label { + font-weight: 600; + text-anchor: middle; + dominant-baseline: middle; + font-size: 14px; +} + +.ishikawa .ishikawa-label { + text-anchor: end; +} + +.ishikawa .ishikawa-label.cause { + text-anchor: middle; + dominant-baseline: middle; +} + +.ishikawa .ishikawa-label.align { + text-anchor: end; + dominant-baseline: middle; +} + +.ishikawa .ishikawa-label.up { + dominant-baseline: baseline; +} + +.ishikawa .ishikawa-label.down { + dominant-baseline: hanging; +} +`,"getStyles"),Cvt=kvt}),wvt={};Mr(wvt,{diagram:()=>Evt});var Evt,gLt=G(()=>{"use strict";hLt(),dLt(),pLt(),fLt(),Evt={parser:cvt,get db(){return new uvt},renderer:Tvt,styles:Cvt}}),TM,Svt,mLt=G(()=>{"use strict";TM=(function(){var e=s(function(S,E,_,b){for(_=_||{},b=S.length;b--;_[S[b]]=E);return _},"o"),t=[5,8],r=[7,8,11,12,17,19,22,24],i=[1,17],n=[1,18],a=[7,8,11,12,14,15,16,17,19,20,21,22,24,27],o=[1,31],l=[1,39],u=[7,8,11,12,17,19,22,24,27],h=[1,57],d=[1,56],p=[1,58],f=[1,59],g=[1,60],m=[7,8,11,12,16,17,19,20,22,24,27,31,32,33],y={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,optNewlines:4,VENN:5,document:6,EOF:7,NEWLINE:8,line:9,statement:10,TITLE:11,SET:12,identifier:13,BRACKET_LABEL:14,COLON:15,NUMERIC:16,UNION:17,identifierList:18,TEXT:19,IDENTIFIER:20,STRING:21,INDENT_TEXT:22,indentedTextTail:23,STYLE:24,stylesOpt:25,styleField:26,COMMA:27,styleValue:28,valueTokens:29,valueToken:30,HEXCOLOR:31,RGBCOLOR:32,RGBACOLOR:33,$accept:0,$end:1},terminals_:{2:"error",5:"VENN",7:"EOF",8:"NEWLINE",11:"TITLE",12:"SET",14:"BRACKET_LABEL",15:"COLON",16:"NUMERIC",17:"UNION",19:"TEXT",20:"IDENTIFIER",21:"STRING",22:"INDENT_TEXT",24:"STYLE",27:"COMMA",31:"HEXCOLOR",32:"RGBCOLOR",33:"RGBACOLOR"},productions_:[0,[3,4],[4,0],[4,2],[6,0],[6,2],[9,1],[9,1],[10,1],[10,2],[10,3],[10,4],[10,5],[10,2],[10,3],[10,4],[10,5],[10,3],[10,3],[10,3],[10,4],[10,4],[10,2],[10,3],[23,1],[23,1],[23,1],[23,2],[23,2],[25,1],[25,3],[26,3],[28,1],[28,1],[29,1],[29,2],[30,1],[30,1],[30,1],[30,1],[30,1],[18,1],[18,3],[13,1],[13,1]],performAction:s(function(S,E,_,b,v,x,w){var R=x.length-1;switch(v){case 1:return x[R-1];case 2:case 3:case 4:this.$=[];break;case 5:x[R-1].push(x[R]),this.$=x[R-1];break;case 6:this.$=[];break;case 7:case 22:case 32:case 36:case 37:case 38:case 39:case 40:this.$=x[R];break;case 8:b.setDiagramTitle(x[R].substr(6)),this.$=x[R].substr(6);break;case 9:b.addSubsetData([x[R]],void 0,void 0),b.setIndentMode&&b.setIndentMode(!0);break;case 10:b.addSubsetData([x[R-1]],x[R],void 0),b.setIndentMode&&b.setIndentMode(!0);break;case 11:b.addSubsetData([x[R-2]],void 0,parseFloat(x[R])),b.setIndentMode&&b.setIndentMode(!0);break;case 12:b.addSubsetData([x[R-3]],x[R-2],parseFloat(x[R])),b.setIndentMode&&b.setIndentMode(!0);break;case 13:if(x[R].length<2)throw new Error("union requires multiple identifiers");b.validateUnionIdentifiers&&b.validateUnionIdentifiers(x[R]),b.addSubsetData(x[R],void 0,void 0),b.setIndentMode&&b.setIndentMode(!0);break;case 14:if(x[R-1].length<2)throw new Error("union requires multiple identifiers");b.validateUnionIdentifiers&&b.validateUnionIdentifiers(x[R-1]),b.addSubsetData(x[R-1],x[R],void 0),b.setIndentMode&&b.setIndentMode(!0);break;case 15:if(x[R-2].length<2)throw new Error("union requires multiple identifiers");b.validateUnionIdentifiers&&b.validateUnionIdentifiers(x[R-2]),b.addSubsetData(x[R-2],void 0,parseFloat(x[R])),b.setIndentMode&&b.setIndentMode(!0);break;case 16:if(x[R-3].length<2)throw new Error("union requires multiple identifiers");b.validateUnionIdentifiers&&b.validateUnionIdentifiers(x[R-3]),b.addSubsetData(x[R-3],x[R-2],parseFloat(x[R])),b.setIndentMode&&b.setIndentMode(!0);break;case 17:case 18:case 19:b.addTextData(x[R-1],x[R],void 0);break;case 20:case 21:b.addTextData(x[R-2],x[R-1],x[R]);break;case 23:b.addStyleData(x[R-1],x[R]);break;case 24:case 25:case 26:var $=b.getCurrentSets();if(!$)throw new Error("text requires set");b.addTextData($,x[R],void 0);break;case 27:case 28:var $=b.getCurrentSets();if(!$)throw new Error("text requires set");b.addTextData($,x[R-1],x[R]);break;case 29:case 41:this.$=[x[R]];break;case 30:case 42:this.$=[...x[R-2],x[R]];break;case 31:this.$=[x[R-2],x[R]];break;case 33:this.$=x[R].join(" ");break;case 34:this.$=[x[R]];break;case 35:x[R-1].push(x[R]),this.$=x[R-1];break;case 43:case 44:this.$=x[R];break}},"anonymous"),table:[e(t,[2,2],{3:1,4:2}),{1:[3]},{5:[1,3],8:[1,4]},e(r,[2,4],{6:5}),e(t,[2,3]),{7:[1,6],8:[1,8],9:7,10:9,11:[1,10],12:[1,11],17:[1,12],19:[1,13],22:[1,14],24:[1,15]},{1:[2,1]},e(r,[2,5]),e(r,[2,6]),e(r,[2,7]),e(r,[2,8]),{13:16,20:i,21:n},{13:20,18:19,20:i,21:n},{13:20,18:21,20:i,21:n},{16:[1,25],20:[1,23],21:[1,24],23:22},{13:20,18:26,20:i,21:n},e(r,[2,9],{14:[1,27],15:[1,28]}),e(a,[2,43]),e(a,[2,44]),e(r,[2,13],{14:[1,29],15:[1,30],27:o}),e(a,[2,41]),{16:[1,34],20:[1,32],21:[1,33],27:o},e(r,[2,22]),e(r,[2,24],{14:[1,35]}),e(r,[2,25],{14:[1,36]}),e(r,[2,26]),{20:l,25:37,26:38,27:o},e(r,[2,10],{15:[1,40]}),{16:[1,41]},e(r,[2,14],{15:[1,42]}),{16:[1,43]},{13:44,20:i,21:n},e(r,[2,17],{14:[1,45]}),e(r,[2,18],{14:[1,46]}),e(r,[2,19]),e(r,[2,27]),e(r,[2,28]),e(r,[2,23],{27:[1,47]}),e(u,[2,29]),{15:[1,48]},{16:[1,49]},e(r,[2,11]),{16:[1,50]},e(r,[2,15]),e(a,[2,42]),e(r,[2,20]),e(r,[2,21]),{20:l,26:51},{16:h,20:d,21:[1,53],28:52,29:54,30:55,31:p,32:f,33:g},e(r,[2,12]),e(r,[2,16]),e(u,[2,30]),e(u,[2,31]),e(u,[2,32]),e(u,[2,33],{30:61,16:h,20:d,31:p,32:f,33:g}),e(m,[2,34]),e(m,[2,36]),e(m,[2,37]),e(m,[2,38]),e(m,[2,39]),e(m,[2,40]),e(m,[2,35])],defaultActions:{6:[2,1]},parseError:s(function(S,E){if(E.recoverable)this.trace(S);else{var _=new Error(S);throw _.hash=E,_}},"parseError"),parse:s(function(S){var E=this,_=[0],b=[],v=[null],x=[],w=this.table,R="",$=0,M=0,N=0,j=2,I=1,z=x.slice.call(arguments,1),F=Object.create(this.lexer),O={yy:{}};for(var B in this.yy)Object.prototype.hasOwnProperty.call(this.yy,B)&&(O.yy[B]=this.yy[B]);F.setInput(S,O.yy),O.yy.lexer=F,O.yy.parser=this,typeof F.yylloc>"u"&&(F.yylloc={});var Y=F.yylloc;x.push(Y);var U=F.options&&F.options.ranges;typeof O.yy.parseError=="function"?this.parseError=O.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function H($e){_.length=_.length-2*$e,v.length=v.length-$e,x.length=x.length-$e}s(H,"popStack");function J(){var $e;return $e=b.pop()||F.lex()||I,typeof $e!="number"&&($e instanceof Array&&(b=$e,$e=b.pop()),$e=E.symbols_[$e]||$e),$e}s(J,"lex");for(var te,ie,ue,se,le,Te,oe={},fe,ge,ke,Ne;;){if(ue=_[_.length-1],this.defaultActions[ue]?se=this.defaultActions[ue]:((te===null||typeof te>"u")&&(te=J()),se=w[ue]&&w[ue][te]),typeof se>"u"||!se.length||!se[0]){var xe="";Ne=[];for(fe in w[ue])this.terminals_[fe]&&fe>j&&Ne.push("'"+this.terminals_[fe]+"'");F.showPosition?xe="Parse error on line "+($+1)+`: +`+F.showPosition()+` +Expecting `+Ne.join(", ")+", got '"+(this.terminals_[te]||te)+"'":xe="Parse error on line "+($+1)+": Unexpected "+(te==I?"end of input":"'"+(this.terminals_[te]||te)+"'"),this.parseError(xe,{text:F.match,token:this.terminals_[te]||te,line:F.yylineno,loc:Y,expected:Ne})}if(se[0]instanceof Array&&se.length>1)throw new Error("Parse Error: multiple actions possible at state: "+ue+", token: "+te);switch(se[0]){case 1:_.push(te),v.push(F.yytext),x.push(F.yylloc),_.push(se[1]),te=null,ie?(te=ie,ie=null):(M=F.yyleng,R=F.yytext,$=F.yylineno,Y=F.yylloc,N>0&&N--);break;case 2:if(ge=this.productions_[se[1]][1],oe.$=v[v.length-ge],oe._$={first_line:x[x.length-(ge||1)].first_line,last_line:x[x.length-1].last_line,first_column:x[x.length-(ge||1)].first_column,last_column:x[x.length-1].last_column},U&&(oe._$.range=[x[x.length-(ge||1)].range[0],x[x.length-1].range[1]]),Te=this.performAction.apply(oe,[R,M,$,O.yy,se[1],v,x].concat(z)),typeof Te<"u")return Te;ge&&(_=_.slice(0,-1*ge*2),v=v.slice(0,-1*ge),x=x.slice(0,-1*ge)),_.push(this.productions_[se[1]][0]),v.push(oe.$),x.push(oe._$),ke=w[_[_.length-2]][_[_.length-1]],_.push(ke);break;case 3:return!0}}return!0},"parse")},k=(function(){var S={EOF:1,parseError:s(function(E,_){if(this.yy.parser)this.yy.parser.parseError(E,_);else throw new Error(E)},"parseError"),setInput:s(function(E,_){return this.yy=_||this.yy||{},this._input=E,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var E=this._input[0];this.yytext+=E,this.yyleng++,this.offset++,this.match+=E,this.matched+=E;var _=E.match(/(?:\r\n?|\n).*/g);return _?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),E},"input"),unput:s(function(E){var _=E.length,b=E.split(/(?:\r\n?|\n)/g);this._input=E+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-_),this.offset-=_;var v=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),b.length-1&&(this.yylineno-=b.length-1);var x=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:b?(b.length===v.length?this.yylloc.first_column:0)+v[v.length-b.length].length-b[0].length:this.yylloc.first_column-_},this.options.ranges&&(this.yylloc.range=[x[0],x[0]+this.yyleng-_]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(E){this.unput(this.match.slice(E))},"less"),pastInput:s(function(){var E=this.matched.substr(0,this.matched.length-this.match.length);return(E.length>20?"...":"")+E.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var E=this.match;return E.length<20&&(E+=this._input.substr(0,20-E.length)),(E.substr(0,20)+(E.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var E=this.pastInput(),_=new Array(E.length+1).join("-");return E+this.upcomingInput()+` +`+_+"^"},"showPosition"),test_match:s(function(E,_){var b,v,x;if(this.options.backtrack_lexer&&(x={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(x.yylloc.range=this.yylloc.range.slice(0))),v=E[0].match(/(?:\r\n?|\n).*/g),v&&(this.yylineno+=v.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:v?v[v.length-1].length-v[v.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+E[0].length},this.yytext+=E[0],this.match+=E[0],this.matches=E,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(E[0].length),this.matched+=E[0],b=this.performAction.call(this,this.yy,this,_,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),b)return b;if(this._backtrack){for(var w in x)this[w]=x[w];return!1}return!1},"test_match"),next:s(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var E,_,b,v;this._more||(this.yytext="",this.match="");for(var x=this._currentRules(),w=0;w_[0].length)){if(_=b,v=w,this.options.backtrack_lexer){if(E=this.test_match(b,x[w]),E!==!1)return E;if(this._backtrack){_=!1;continue}else return!1}else if(!this.options.flex)break}return _?(E=this.test_match(_,x[v]),E!==!1?E:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:s(function(){var E=this.next();return E||this.lex()},"lex"),begin:s(function(E){this.conditionStack.push(E)},"begin"),popState:s(function(){var E=this.conditionStack.length-1;return E>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:s(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:s(function(E){return E=this.conditionStack.length-1-Math.abs(E||0),E>=0?this.conditionStack[E]:"INITIAL"},"topState"),pushState:s(function(E){this.begin(E)},"pushState"),stateStackSize:s(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:s(function(E,_,b,v){var x=v;switch(b){case 0:break;case 1:break;case 2:break;case 3:if(E.getIndentMode&&E.getIndentMode())return E.consumeIndentText=!0,this.begin("INITIAL"),22;break;case 4:break;case 5:E.setIndentMode&&E.setIndentMode(!1),this.begin("INITIAL"),this.unput(_.yytext);break;case 6:return this.begin("bol"),8;case 7:break;case 8:break;case 9:return 7;case 10:return 11;case 11:return 5;case 12:return 12;case 13:return 17;case 14:if(E.consumeIndentText)E.consumeIndentText=!1;else return 19;break;case 15:return 24;case 16:return _.yytext=_.yytext.slice(2,-2),14;case 17:return _.yytext=_.yytext.slice(1,-1).trim(),14;case 18:return 16;case 19:return 31;case 20:return 33;case 21:return 32;case 22:return 20;case 23:return 21;case 24:return 27;case 25:return 15}},"anonymous"),rules:[/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[ \t]+(?=[\n\r]))/i,/^(?:[ \t]+(?=text\b))/i,/^(?:[ \t]+)/i,/^(?:[^ \t\n\r])/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:[ \t]+)/i,/^(?:$)/i,/^(?:title\s[^#\n;]+)/i,/^(?:venn-beta\b)/i,/^(?:set\b)/i,/^(?:union\b)/i,/^(?:text\b)/i,/^(?:style\b)/i,/^(?:\["[^\"]*"\])/i,/^(?:\[[^\]\"]+\])/i,/^(?:[+-]?(\d+(\.\d+)?|\.\d+))/i,/^(?:#[0-9a-fA-F]{3,8})/i,/^(?:rgba\(\s*[0-9.]+\s*[,]\s*[0-9.]+\s*[,]\s*[0-9.]+\s*[,]\s*[0-9.]+\s*\))/i,/^(?:rgb\(\s*[0-9.]+\s*[,]\s*[0-9.]+\s*[,]\s*[0-9.]+\s*\))/i,/^(?:[A-Za-z_][A-Za-z0-9\-_]*)/i,/^(?:"[^\"]*")/i,/^(?:,)/i,/^(?::)/i],conditions:{bol:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],inclusive:!0},INITIAL:{rules:[0,1,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],inclusive:!0}}};return S})();y.lexer=k;function C(){this.yy={}}return s(C,"Parser"),C.prototype=y,y.Parser=C,new C})(),TM.parser=TM,Svt=TM});function _vt(){return Gn(Fvt,Rr().venn)}var kM,CM,wM,EM,ffe,SM,Avt,Rvt,ax,$vt,Lvt,Ivt,Mvt,K5,Dvt,Nvt,Ovt,Bvt,Pvt,Fvt,zvt,qvt,yLt=G(()=>{"use strict";Ar(),Gr(),fn(),Za(),kM=[],CM=[],wM=[],EM=new Set,SM=!1,Avt=s((e,t,r)=>{let i=K5(e).sort(),n=r??10/Math.pow(e.length,2);ffe=i,i.length===1&&EM.add(i[0]),kM.push({sets:i,size:n,label:t?ax(t):void 0})},"addSubsetData"),Rvt=s(()=>kM,"getSubsetData"),ax=s(e=>{let t=e.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?t.slice(1,-1):t},"normalizeText"),$vt=s(e=>e&&ax(e),"normalizeStyleValue"),Lvt=s((e,t,r)=>{let i=ax(t);CM.push({sets:K5(e).sort(),id:i,label:r?ax(r):void 0})},"addTextData"),Ivt=s((e,t)=>{let r=K5(e).sort(),i={};for(let[n,a]of t)i[n]=$vt(a)??a;wM.push({targets:r,styles:i})},"addStyleData"),Mvt=s(()=>wM,"getStyleData"),K5=s(e=>e.map(t=>ax(t)),"normalizeIdentifierList"),Dvt=s(e=>{let t=K5(e).filter(r=>!EM.has(r));if(t.length>0)throw new Error(`unknown set identifier: ${t.join(", ")}`)},"validateUnionIdentifiers"),Nvt=s(()=>CM,"getTextData"),Ovt=s(()=>ffe,"getCurrentSets"),Bvt=s(()=>SM,"getIndentMode"),Pvt=s(e=>{SM=e},"setIndentMode"),Fvt=vi.venn,s(_vt,"getConfig"),zvt=s(()=>{pn(),kM.length=0,CM.length=0,wM.length=0,EM.clear(),ffe=void 0,SM=!1},"customClear"),qvt={getConfig:_vt,clear:zvt,setAccTitle:bn,getAccTitle:Ln,setDiagramTitle:Dn,getDiagramTitle:xn,getAccDescription:Mn,setAccDescription:In,addSubsetData:Avt,getSubsetData:Rvt,addTextData:Lvt,addStyleData:Ivt,validateUnionIdentifiers:Dvt,getTextData:Nvt,getStyleData:Mvt,getCurrentSets:Ovt,getIndentMode:Bvt,setIndentMode:Pvt}}),Wvt,jvt,vLt=G(()=>{"use strict";Wvt=s(e=>` + .venn-title { + font-size: 32px; + fill: ${e.vennTitleTextColor}; + font-family: ${e.fontFamily}; + } + + .venn-circle text { + font-size: 48px; + font-family: ${e.fontFamily}; + } + + .venn-intersection text { + font-size: 48px; + fill: ${e.vennSetTextColor}; + font-family: ${e.fontFamily}; + } + + .venn-text-node { + font-family: ${e.fontFamily}; + color: ${e.vennSetTextColor}; + } +`,"getStyles"),jvt=Wvt});function Q5(e,t){let r=Uvt(e),i=r.filter(l=>Vvt(l,e)),n=0,a=0,o=[];if(i.length>1){let l=mfe(i);for(let h=0;hd.angle-h.angle);let u=i[i.length-1];for(let h=0;hm.radius*2&&(E=m.radius*2),(f==null||f.width>E)&&(f={circle:m,width:E,p1:d,p2:u,large:E>m.radius,sweep:!0})}f!=null&&(o.push(f),n+=_M(f.circle.radius,f.width),u=d)}}else{let l=e[0];for(let h=1;hMath.abs(l.radius-e[h].radius)){u=!0;break}u?n=a=0:(n=l.radius*l.radius*Math.PI,o.push({circle:l,p1:{x:l.x,y:l.y+l.radius},p2:{x:l.x-1e-10,y:l.y+l.radius},width:l.radius*2,large:!0,sweep:!0}))}return a/=2,t&&(t.area=n+a,t.arcArea=n,t.polygonArea=a,t.arcs=o,t.innerPoints=i,t.intersectionPoints=r),n+a}function Vvt(e,t){return t.every(r=>Ko(e,r)=e+t)return 0;if(r<=Math.abs(e-t))return Math.PI*Math.min(e,t)*Math.min(e,t);let i=e-(r*r-t*t+e*e)/(2*r),n=t-(r*r-e*e+t*t)/(2*r);return _M(e,i)+_M(t,n)}function gfe(e,t){let r=Ko(e,t),i=e.radius,n=t.radius;if(r>=i+n||r<=Math.abs(i-n))return[];let a=(i*i-n*n+r*r)/(2*r),o=Math.sqrt(i*i-a*a),l=e.x+a*(t.x-e.x)/r,u=e.y+a*(t.y-e.y)/r,h=-(t.y-e.y)*(o/r),d=-(t.x-e.x)*(o/r);return[{x:l+h,y:u-d},{x:l-h,y:u+d}]}function mfe(e){let t={x:0,y:0};for(let r of e)t.x+=r.x,t.y+=r.y;return t.x/=e.length,t.y/=e.length,t}function Yvt(e,t,r,i){i=i||{};let n=i.maxIterations||100,a=i.tolerance||1e-10,o=e(t),l=e(r),u=r-t;if(o*l>0)throw"Initial bisect points must have opposite signs";if(o===0)return t;if(l===0)return r;for(let h=0;h=0&&(t=d),Math.abs(u)RM(t))}function q1(e,t){let r=0;for(let i=0;ib.fx-v.fx,"sortOrder"),C=t.slice(),S=t.slice(),E=t.slice(),_=t.slice();for(let b=0;b{let R=w.slice();return R.fx=w.fx,R.id=w.id,R});x.sort((w,R)=>w.id-R.id),r.history.push({x:m[0].slice(),fx:m[0].fx,simplex:x})}f=0;for(let x=0;x=m[g-1].fx){let x=!1;if(S.fx>v.fx?(Bh(E,1+d,C,-d,v),E.fx=e(E),E.fx=1)break;for(let w=1;wl+a*n*u||h>=k)y=n;else{if(Math.abs(p)<=-o*u)return n;p*(y-m)>=0&&(y=m),m=n,k=h}return 0}s(g,"zoom");for(let m=0;m<10;++m){if(Bh(i.x,1,r.x,n,t),h=i.fx=e(i.x,i.fxprime),p=q1(i.fxprime,t),h>l+a*n*u||m&&h>=d)return g(f,n,d);if(Math.abs(p)<=-o*u)return n;if(p>=0)return g(n,f,h);d=h,f=n,n*=2}return n}function Gvt(e,t,r){let i={x:t.slice(),fx:0,fxprime:t.slice()},n={x:t.slice(),fx:0,fxprime:t.slice()},a=t.slice(),o,l,u=1,h;r=r||{},h=r.maxIterations||t.length*20,i.fx=e(i.x,i.fxprime),o=i.fxprime.slice(),LM(o,i.fxprime,-1);for(let d=0;d{let d={};for(let p=0;pAM(e,t,i)-r,0,e+t)}function Xvt(e,t={}){let r=t.distinct,i=e.map(l=>Object.assign({},l));function n(l){return l.join(";")}if(s(n,"toKey"),r){let l=new Map;for(let u of i)for(let h=0;hl===u?0:la.sets.length===2).forEach(a=>{let o=r[a.sets[0]],l=r[a.sets[1]],u=Math.sqrt(t[o].size/Math.PI),h=Math.sqrt(t[l].size/Math.PI),d=IM(u,h,a.size);i[o][l]=i[l][o]=d;let p=0;a.size+1e-10>=Math.min(t[o].size,t[l].size)?p=1:a.size<=1e-10&&(p=-1),n[o][l]=n[l][o]=p}),{distances:i,constraints:n}}function Qvt(e,t,r,i){for(let a=0;a0&&m<=p||f<0&&m>=p||(n+=2*y*y,t[2*a]+=4*y*(o-h),t[2*a+1]+=4*y*(l-d),t[2*u]+=4*y*(h-o),t[2*u+1]+=4*y*(d-l))}}return n}function Zvt(e,t={}){let r=ebt(e,t),i=t.lossFunction||W1;if(e.length>=8){let n=Jvt(e,t),a=i(n,e),o=i(r,e);a+1e-8f.map(g=>g/l));let u=s((f,g)=>Qvt(f,g,a,o),"obj"),h=null;for(let f=0;fp.sets.length===2);for(let p of e){let f=p.weight!=null?p.weight:1,g=p.sets[0],m=p.sets[1];p.size+$fe>=Math.min(i[g].size,i[m].size)&&(f=0),n[g].push({set:m,size:p.size,weight:f}),n[m].push({set:g,size:p.size,weight:f})}let a=[];Object.keys(n).forEach(p=>{let f=0;for(let g=0;ge[o]));let a=i.weight!=null?i.weight:1;r+=a*(n-i.size)*(n-i.size)}return r}function xfe(e,t){let r=0;for(let i of t){if(i.sets.length===1)continue;let n;if(i.sets.length===2){let l=e[i.sets[0]],u=e[i.sets[1]];n=AM(l.radius,u.radius,Ko(l,u))}else n=Q5(i.sets.map(l=>e[l]));let a=i.weight!=null?i.weight:1,o=Math.log((n+1)/(i.size+1));r+=a*o*o}return r}function tbt(e,t,r){if(r==null?e.sort((n,a)=>a.radius-n.radius):e.sort(r),e.length>0){let n=e[0].x,a=e[0].y;for(let o of e)o.x-=n,o.y-=a}if(e.length===2&&Ko(e[0],e[1])1){let n=Math.atan2(e[1].x,e[1].y)-t,a=Math.cos(n),o=Math.sin(n);for(let l of e){let u=l.x,h=l.y;l.x=a*u-o*h,l.y=o*u+a*h}}if(e.length>2){let n=Math.atan2(e[2].x,e[2].y)-t;for(;n<0;)n+=2*Math.PI;for(;n>2*Math.PI;)n-=2*Math.PI;if(n>Math.PI){let a=e[1].y/(1e-10+e[1].x);for(let o of e){var i=(o.x+a*o.y)/(1+a*a);o.x=2*i-o.x,o.y=2*i*a-o.y}}}}function rbt(e){e.forEach(n=>{n.parent=n});function t(n){return n.parent!==n&&(n.parent=t(n.parent)),n.parent}s(t,"find");function r(n,a){let o=t(n),l=t(a);o.parent=l}s(r,"union");for(let n=0;n{delete n.parent}),Array.from(i.values())}function MM(e){let t=s(r=>{let i=e.reduce((a,o)=>Math.max(a,o[r]+o.radius),Number.NEGATIVE_INFINITY),n=e.reduce((a,o)=>Math.min(a,o[r]-o.radius),Number.POSITIVE_INFINITY);return{max:i,min:n}},"minMax");return{xRange:t("x"),yRange:t("y")}}function Tfe(e,t,r){t==null&&(t=Math.PI/2);let i=wfe(e).map(h=>Object.assign({},h)),n=rbt(i);for(let h of n){tbt(h,t,r);let d=MM(h);h.size=(d.xRange.max-d.xRange.min)*(d.yRange.max-d.yRange.min),h.bounds=d}n.sort((h,d)=>d.size-h.size),i=n[0];let a=i.bounds,o=(a.xRange.max-a.xRange.min)/50;function l(h,d,p){if(!h)return;let f=h.bounds,g,m;if(d)g=a.xRange.max-f.xRange.min+o;else{g=a.xRange.max-f.xRange.max;let y=(f.xRange.max-f.xRange.min)/2-(a.xRange.max-a.xRange.min)/2;y<0&&(g+=y)}if(p)m=a.yRange.max-f.yRange.min+o;else{m=a.yRange.max-f.yRange.max;let y=(f.yRange.max-f.yRange.min)/2-(a.yRange.max-a.yRange.min)/2;y<0&&(m+=y)}for(let y of h)y.x+=g,y.y+=m,i.push(y)}s(l,"addCluster");let u=1;for(;u({radius:d*g.radius,x:i+p+(g.x-o.min)*d,y:i+f+(g.y-l.min)*d,setid:g.setid})))}function Cfe(e){let t={};for(let r of e)t[r.setid]=r;return t}function wfe(e){return Object.keys(e).map(t=>Object.assign(e[t],{setid:t}))}function ibt(e={}){let t=!1,r=600,i=350,n=15,a=1e3,o=Math.PI/2,l=!0,u=null,h=!0,d=!0,p=null,f=null,g=!1,m=null,y=e&&e.symmetricalTextCentre?e.symmetricalTextCentre:!1,k={},C=e&&e.colourScheme?e.colourScheme:e&&e.colorScheme?e.colorScheme:["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"],S=0,E=s(function(x){if(x in k)return k[x];var w=k[x]=C[S];return S+=1,S>=C.length&&(S=0),w},"colours"),_=bfe,b=W1;function v(x){let w=x.datum(),R=new Set;w.forEach(le=>{le.size==0&&le.sets.length==1&&R.add(le.sets[0])}),w=w.filter(le=>!le.sets.some(Te=>R.has(Te)));let $={},M={};if(w.length>0){let le=_(w,{lossFunction:b,distinct:g});l&&(le=Tfe(le,o,f)),$=kfe(le,r,i,n,u),M=Sfe($,w,y)}let N={};w.forEach(le=>{le.label&&(N[le.sets]=le.label)});function j(le){if(le.sets in N)return N[le.sets];if(le.sets.length==1)return""+le.sets[0]}s(j,"label"),x.selectAll("svg").data([$]).enter().append("svg");let I=x.select("svg");t?I.attr("viewBox",`0 0 ${r} ${i}`):I.attr("width",r).attr("height",i);let z={},F=!1;I.selectAll(".venn-area path").each(function(le){let Te=this.getAttribute("d");le.sets.length==1&&Te&&!g&&(F=!0,z[le.sets[0]]=sbt(Te))});function O(le){return Te=>{let oe=le.sets.map(fe=>{let ge=z[fe],ke=$[fe];return ge||(ge={x:r/2,y:i/2,radius:1}),ke||(ke={x:r/2,y:i/2,radius:1}),{x:ge.x*(1-Te)+ke.x*Te,y:ge.y*(1-Te)+ke.y*Te,radius:ge.radius*(1-Te)+ke.radius*Te}});return Rfe(oe,m)}}s(O,"pathTween");let B=I.selectAll(".venn-area").data(w,le=>le.sets),Y=B.enter().append("g").attr("class",le=>`venn-area venn-${le.sets.length==1?"circle":"intersection"}${le.colour||le.color?" venn-coloured":""}`).attr("data-venn-sets",le=>le.sets.join("_")),U=Y.append("path"),H=Y.append("text").attr("class","label").text(le=>j(le)).attr("text-anchor","middle").attr("dy",".35em").attr("x",r/2).attr("y",i/2);d&&(U.style("fill-opacity","0").filter(le=>le.sets.length==1).style("fill",le=>le.colour?le.colour:le.color?le.color:E(le.sets)).style("fill-opacity",".25"),H.style("fill",le=>le.colour||le.color?"#FFF":e.textFill?e.textFill:le.sets.length==1?E(le.sets):"#444"));function J(le){return typeof le.transition=="function"?le.transition("venn").duration(a):le}s(J,"asTransition");let te=x;F&&typeof te.transition=="function"?(te=J(x),te.selectAll("path").attrTween("d",O)):te.selectAll("path").attr("d",le=>Rfe(le.sets.map(Te=>$[Te])),m);let ie=te.selectAll("text").filter(le=>le.sets in M).text(le=>j(le)).attr("x",le=>Math.floor(M[le.sets].x)).attr("y",le=>Math.floor(M[le.sets].y));h&&(F?"on"in ie?ie.on("end",DM($,j)):ie.each("end",DM($,j)):ie.each(DM($,j)));let ue=J(B.exit()).remove();typeof B.transition=="function"&&ue.selectAll("path").attrTween("d",O);let se=ue.selectAll("text").attr("x",r/2).attr("y",i/2);return p!==null&&(H.style("font-size","0px"),ie.style("font-size",p),se.style("font-size","0px")),{circles:$,textCentres:M,nodes:B,enter:Y,update:te,exit:ue}}return s(v,"chart"),v.wrap=function(x){return arguments.length?(h=x,v):h},v.useViewBox=function(){return t=!0,v},v.width=function(x){return arguments.length?(r=x,v):r},v.height=function(x){return arguments.length?(i=x,v):i},v.padding=function(x){return arguments.length?(n=x,v):n},v.distinct=function(x){return arguments.length?(g=x,v):g},v.colours=function(x){return arguments.length?(E=x,v):E},v.colors=function(x){return arguments.length?(E=x,v):E},v.fontSize=function(x){return arguments.length?(p=x,v):p},v.round=function(x){return arguments.length?(m=x,v):m},v.duration=function(x){return arguments.length?(a=x,v):a},v.layoutFunction=function(x){return arguments.length?(_=x,v):_},v.normalize=function(x){return arguments.length?(l=x,v):l},v.scaleToFit=function(x){return arguments.length?(u=x,v):u},v.styled=function(x){return arguments.length?(d=x,v):d},v.orientation=function(x){return arguments.length?(o=x,v):o},v.orientationOrder=function(x){return arguments.length?(f=x,v):f},v.lossFunction=function(x){return arguments.length?(b=x==="default"?W1:x==="logRatio"?xfe:x,v):b},v}function DM(e,t){return function(r){let i=this,n=e[r.sets[0]].radius||50,a=t(r)||"",o=a.split(/\s+/).reverse(),l=(a.length+o.length)/3,u=o.pop(),h=[u],d=0,p=1.1;i.textContent=null;let f=[];function g(S){let E=i.ownerDocument.createElementNS(i.namespaceURI,"tspan");return E.textContent=S,f.push(E),i.append(E),E}s(g,"append");let m=g(u);for(;u=o.pop(),!!u;){h.push(u);let S=h.join(" ");m.textContent=S,S.length>l&&m.getComputedTextLength()>n&&(h.pop(),m.textContent=h.join(" "),h=[u],m=g(u),d++)}let y=.35-d*p/2,k=i.getAttribute("x"),C=i.getAttribute("y");f.forEach((S,E)=>{S.setAttribute("x",k),S.setAttribute("y",C),S.setAttribute("dy",`${y+E*p}em`)})}}function NM(e,t,r){let i=t[0].radius-Ko(t[0],e);for(let n=1;n=a&&(n=i[d],a=p)}let o=vfe(d=>-1*NM({x:d[0],y:d[1]},e,t),[n.x,n.y],{maxIterations:500,minErrorDelta:1e-10}).x,l={x:r?0:o[0],y:o[1]},u=!0;for(let d of e)if(Ko(l,d)>d.radius){u=!1;break}for(let d of t)if(Ko(l,d)d.p1))}function nbt(e){let t={},r=Object.keys(e);for(let i of r)t[i]=[];for(let i=0;i0&&console.log("WARNING: area "+o+" not represented on screen")}return i}function abt(e,t,r){let i=[];return i.push(` +M`,e,t),i.push(` +m`,-r,0),i.push(` +a`,r,r,0,1,0,r*2,0),i.push(` +a`,r,r,0,1,0,-r*2,0),i.join(" ")}function sbt(e){let t=e.split(" ");return{x:Number.parseFloat(t[1]),y:Number.parseFloat(t[2]),radius:-Number.parseFloat(t[4])}}function _fe(e){if(e.length===0)return[];let t={};return Q5(e,t),t.arcs}function Afe(e,t){if(e.length===0)return"M 0 0";let r=Math.pow(10,t||0),i=t!=null?a=>Math.round(a*r)/r:a=>a;if(e.length==1){let a=e[0].circle;return abt(i(a.x),i(a.y),i(a.radius))}let n=[` +M`,i(e[0].p2.x),i(e[0].p2.y)];for(let a of e){let o=i(a.circle.radius);n.push(` +A`,o,o,0,a.large?1:0,a.sweep?1:0,i(a.p1.x),i(a.p1.y))}return n.join(" ")}function Rfe(e,t){return Afe(_fe(e),t)}function obt(e,t={}){let{lossFunction:r,layoutFunction:i=bfe,normalize:n=!0,orientation:a=Math.PI/2,orientationOrder:o,width:l=600,height:u=350,padding:h=15,scaleToFit:d=!1,symmetricalTextCentre:p=!1,distinct:f,round:g=2}=t,m=i(e,{lossFunction:r==="default"||!r?W1:r==="logRatio"?xfe:r,distinct:f});n&&(m=Tfe(m,a,o));let y=kfe(m,l,u,h,d),k=Sfe(y,e,p),C=new Map(Object.keys(y).map(_=>[_,{set:_,x:y[_].x,y:y[_].y,radius:y[_].radius}])),S=e.map(_=>{let b=_.sets.map(w=>C.get(w)),v=_fe(b),x=Afe(v,g);return{circles:b,arcs:v,path:x,area:_,has:new Set(_.sets)}});function E(_){let b="";for(let v of S)v.has.size>_.length&&_.every(x=>v.has.has(x))&&(b+=" "+v.path);return b}return s(E,"genDistinctPath"),S.map(({circles:_,arcs:b,path:v,area:x})=>({data:x,text:k[x.sets],circles:_,arcs:b,path:v,distinctPath:v+E(x.sets)}))}var $fe,bLt=G(()=>{"use strict";s(Q5,"intersectionArea"),s(Vvt,"containedInCircles"),s(Uvt,"getIntersectionPoints"),s(_M,"circleArea"),s(Ko,"distance"),s(AM,"circleOverlap"),s(gfe,"circleCircleIntersection"),s(mfe,"getCenter"),s(Yvt,"bisect"),s(RM,"zeros"),s(yfe,"zerosM"),s(q1,"dot"),s($M,"norm2"),s(LM,"scale"),s(Bh,"weightedSum"),s(vfe,"nelderMead"),s(Hvt,"wolfeLineSearch"),s(Gvt,"conjugateGradient"),s(bfe,"venn"),$fe=1e-10,s(IM,"distanceFromIntersectArea"),s(Xvt,"addMissingAreas"),s(Kvt,"getDistanceMatrices"),s(Qvt,"constrainedMDSGradient"),s(Zvt,"bestInitialLayout"),s(Jvt,"constrainedMDSLayout"),s(ebt,"greedyLayout"),s(W1,"lossFunction"),s(xfe,"logRatioLossFunction"),s(tbt,"orientateCircles"),s(rbt,"disjointCluster"),s(MM,"getBoundingBox"),s(Tfe,"normalizeSolution"),s(kfe,"scaleSolution"),s(Cfe,"toObjectNotation"),s(wfe,"fromObjectNotation"),s(ibt,"VennDiagram"),s(DM,"wrapText"),s(NM,"circleMargin"),s(Efe,"computeTextCentre"),s(nbt,"getOverlappingCircles"),s(Sfe,"computeTextCentres"),s(abt,"circlePath"),s(sbt,"circleFromPath"),s(_fe,"intersectionAreaArcs"),s(Afe,"arcsToPath"),s(Rfe,"intersectionAreaPath"),s(obt,"layout")});function lbt(e){let t=new Map;for(let r of e){let i=r.targets.join("|"),n=t.get(i);n?Object.assign(n,r.styles):t.set(i,{...r.styles})}return t}function I0(e){return e.join("|")}function cbt(e,t,r,i,n,a){let o=e?.useDebugLayout??!1,l=r.select("svg").append("g").attr("class","venn-text-nodes"),u=new Map;for(let h of i){let d=I0(h.sets),p=u.get(d);p?p.push(h):u.set(d,[h])}for(let[h,d]of u.entries()){let p=t.get(h);if(!p?.text)continue;let f=p.text.x,g=p.text.y,m=Math.min(...p.circles.map(M=>M.radius)),y=Math.min(...p.circles.map(M=>M.radius-Math.hypot(f-M.x,g-M.y))),k=Number.isFinite(y)?Math.max(0,y):0;k===0&&Number.isFinite(m)&&(k=m*.6);let C=l.append("g").attr("class","venn-text-area").attr("font-size",`${40*n}px`);o&&C.append("circle").attr("class","venn-text-debug-circle").attr("cx",f).attr("cy",g).attr("r",k).attr("fill","none").attr("stroke","purple").attr("stroke-width",1.5*n).attr("stroke-dasharray",`${6*n} ${4*n}`);let S=Math.max(80*n,k*2*.95),E=Math.max(60*n,k*2*.95),_=(p.data.label&&p.data.label.length>0?Math.min(32*n,k*.25):0)+(d.length<=2?30*n:0),b=f-S/2,v=g-E/2+_,x=Math.max(1,Math.ceil(Math.sqrt(d.length))),w=Math.max(1,Math.ceil(d.length/x)),R=S/x,$=E/w;for(let[M,N]of d.entries()){let j=M%x,I=Math.floor(M/x),z=b+R*(j+.5),F=v+$*(I+.5);o&&C.append("rect").attr("class","venn-text-debug-cell").attr("x",b+R*j).attr("y",v+$*I).attr("width",R).attr("height",$).attr("fill","none").attr("stroke","teal").attr("stroke-width",1*n).attr("stroke-dasharray",`${4*n} ${3*n}`);let O=R*.9,B=$*.9,Y=C.append("foreignObject").attr("class","venn-text-node-fo").attr("width",O).attr("height",B).attr("x",z-O/2).attr("y",F-B/2).attr("overflow","visible"),U=a.get(N.id)?.color,H=Y.append("xhtml:span").attr("class","venn-text-node").style("display","flex").style("width","100%").style("height","100%").style("white-space","normal").style("align-items","center").style("justify-content","center").style("text-align","center").style("overflow-wrap","normal").style("word-break","normal").text(N.label??N.id);U&&H.style("color",U)}}}function ubt(e){let t=new Set(e.map(n=>[...n.sets].sort().join("|"))),r=new Map(e.filter(n=>n.sets.length===1&&n.size!==void 0).map(n=>[n.sets[0],n.size])),i=[];for(let n of e){if(n.sets.length<3)continue;let a=[...n.sets].sort();for(let o=0;o0?[...e,...i]:e}var hbt,dbt,xLt=G(()=>{"use strict";pi(),Qa(),Gr(),wo(),bLt(),vn(),vr(),s(lbt,"buildStyleByKey"),hbt=s((e,t,r,i)=>{let n=i.db,a=n.getConfig?.(),{themeVariables:o,look:l,handDrawnSeed:u}=Rr(),h=l==="handDrawn",d=[o.venn1,o.venn2,o.venn3,o.venn4,o.venn5,o.venn6,o.venn7,o.venn8].filter(Boolean),p=n.getDiagramTitle?.(),f=n.getSubsetData(),g=n.getTextData(),m=lbt(n.getStyleData()),y=ubt(f),k=a?.width??800,C=a?.height??450,S=k/1600,E=p?48*S:0,_=o.primaryTextColor??o.textColor,b=Ks(t);b.attr("viewBox",`0 0 ${k} ${C}`),p&&b.append("text").text(p).attr("class","venn-title").attr("font-size",`${32*S}px`).attr("text-anchor","middle").attr("dominant-baseline","middle").attr("x","50%").attr("y",32*S).style("fill",o.vennTitleTextColor||o.titleColor);let v=zt(document.createElement("div")),x=ibt().width(k).height(C-E);v.datum(y).call(x);let w=h?ur.svg(v.select("svg").node()):void 0,R=obt(y,{width:k,height:C-E,padding:a?.padding??15}),$=new Map;for(let I of R){let z=I0([...I.data.sets].sort());$.set(z,I)}g.length>0&&cbt(a,$,v,g,S,m);let M=Js(o.background||"#f4f4f4");v.selectAll(".venn-circle").each(function(I,z){let F=zt(this),O=I0([...I.sets].sort()),B=m.get(O),Y=B?.fill||d[z%d.length]||o.primaryColor;F.classed(`venn-set-${z%8}`,!0);let U=B?.["fill-opacity"]??.1,H=B?.stroke||Y,J=B?.["stroke-width"]||`${5*S}`;if(h&&w){let ie=$.get(O);if(ie&&ie.circles.length>0){let ue=ie.circles[0],se=w.circle(ue.x,ue.y,ue.radius*2,{roughness:.7,seed:u,fill:cD(Y,.7),fillStyle:"hachure",fillWeight:2,hachureGap:8,hachureAngle:-41+z*60,stroke:H,strokeWidth:parseFloat(String(J))});F.select("path").remove(),F.node()?.insertBefore(se,F.select("text").node())}}else F.select("path").style("fill",Y).style("fill-opacity",U).style("stroke",H).style("stroke-width",J).style("stroke-opacity",.95);let te=B?.color||(M?bt(Y,30):xt(Y,30));F.select("text").style("font-size",`${48*S}px`).style("fill",te)}),h&&w?v.selectAll(".venn-intersection").each(function(I){let z=zt(this),F=I0([...I.sets].sort()),O=m.get(F),B=O?.fill;if(B){let Y=z.select("path"),U=Y.attr("d");if(U){let H=w.path(U,{roughness:.7,seed:u,fill:cD(B,.3),fillStyle:"cross-hatch",fillWeight:2,hachureGap:6,hachureAngle:60,stroke:"none"}),J=Y.node();J?.parentNode?.insertBefore(H,J),Y.remove()}}else z.select("path").style("fill-opacity",0);z.select("text").style("font-size",`${48*S}px`).style("fill",O?.color??o.vennSetTextColor??_)}):(v.selectAll(".venn-intersection text").style("font-size",`${48*S}px`).style("fill",I=>{let z=I0([...I.sets].sort());return m.get(z)?.color??o.vennSetTextColor??_}),v.selectAll(".venn-intersection path").style("fill-opacity",I=>{let z=I0([...I.sets].sort());return m.get(z)?.fill?1:0}).style("fill",I=>{let z=I0([...I.sets].sort());return m.get(z)?.fill??"transparent"}));let N=b.append("g").attr("transform",`translate(0, ${E})`),j=v.select("svg").node();if(j&&"childNodes"in j)for(let I of[...j.childNodes])N.node()?.appendChild(I);aa(b,C,k,a?.useMaxWidth??!0)},"draw"),s(I0,"stableSetsKey"),s(cbt,"renderTextNodes"),s(ubt,"ensurePairwiseSubsets"),dbt={draw:hbt}}),pbt={};Mr(pbt,{diagram:()=>fbt});var fbt,TLt=G(()=>{"use strict";mLt(),yLt(),vLt(),xLt(),fbt={parser:Svt,db:qvt,renderer:dbt,styles:jvt}}),Lfe,gbt=G(()=>{"use strict";var e;Za(),Gr(),Ar(),gr(),fn(),Lfe=(e=class{constructor(){this.nodes=[],this.levels=new Map,this.outerNodes=[],this.classes=new Map,this.setAccTitle=bn,this.getAccTitle=Ln,this.setDiagramTitle=Dn,this.getDiagramTitle=xn,this.getAccDescription=Mn,this.setAccDescription=In}getNodes(){return this.nodes}getConfig(){let r=vi,i=Rr();return Gn({...r.treemap,...i.treemap??{}})}addNode(r,i){this.nodes.push(r),this.levels.set(r,i),i===0&&(this.outerNodes.push(r),this.root??=r)}getRoot(){return{name:"",children:this.outerNodes}}addClass(r,i){let n=this.classes.get(r)??{id:r,styles:[],textStyles:[]},a=i.replace(/\\,/g,"§§§").replace(/,/g,";").replace(/§§§/g,",").split(";");a&&a.forEach(o=>{__(o)&&(n?.textStyles?n.textStyles.push(o):n.textStyles=[o]),n?.styles?n.styles.push(o):n.styles=[o]}),this.classes.set(r,n)}getClasses(){return this.classes}getStylesForClass(r){return this.classes.get(r)?.styles??[]}clear(){pn(),this.nodes=[],this.levels=new Map,this.outerNodes=[],this.classes=new Map,this.root=void 0}},s(e,"TreeMapDB"),e)});function mbt(e){if(!e.length)return[];let t=[],r=[];return e.forEach(i=>{let n={name:i.name,children:i.type==="Leaf"?void 0:[]};for(n.classSelector=i?.classSelector,i?.cssCompiledStyles&&(n.cssCompiledStyles=i.cssCompiledStyles),i.type==="Leaf"&&i.value!==void 0&&(n.value=i.value);r.length>0&&r[r.length-1].level>=i.level;)r.pop();if(r.length===0)t.push(n);else{let a=r[r.length-1].node;a.children?a.children.push(n):a.children=[n]}i.type!=="Leaf"&&r.push({node:n,level:i.level})}),t}var kLt=G(()=>{"use strict";s(mbt,"buildHierarchy")}),ybt,vbt,Ife,CLt=G(()=>{"use strict";Co(),Nt(),vl(),kLt(),gbt(),ybt=s((e,t)=>{Yo(e,t);let r=[];for(let a of e.TreemapRows??[])a.$type==="ClassDefStatement"&&t.addClass(a.className??"",a.styleText??"");for(let a of e.TreemapRows??[]){let o=a.item;if(!o)continue;let l=a.indent?parseInt(a.indent):0,u=vbt(o),h=o.classSelector?t.getStylesForClass(o.classSelector):[],d=h.length>0?h:void 0,p={level:l,name:u,type:o.$type,value:o.value,classSelector:o.classSelector,cssCompiledStyles:d};r.push(p)}let i=mbt(r),n=s((a,o)=>{for(let l of a)t.addNode(l,o),l.children&&l.children.length>0&&n(l.children,o+1)},"addNodesRecursively");n(i,0)},"populate"),vbt=s(e=>e.name?String(e.name):"","getItemName"),Ife={parser:{yy:void 0},parse:s(async e=>{try{let t=await Pl("treemap",e);Ee.debug("Treemap AST:",t);let r=Ife.parser?.yy;if(!(r instanceof Lfe))throw new Error("parser.parser?.yy was not a TreemapDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.");ybt(t,r)}catch(t){throw Ee.error("Error parsing treemap:",t),t}},"parse")}}),bbt,j1,sx,xbt,Tbt,kbt,wLt=G(()=>{"use strict";wo(),Tm(),vn(),pi(),gr(),Gr(),Nt(),bbt=10,j1=10,sx=25,xbt=s((e,t,r,i)=>{let n=i.db,a=n.getConfig(),o=a.padding??bbt,l=n.getDiagramTitle(),u=n.getRoot(),{themeVariables:h}=Rr();if(!u)return;let d=l?30:0,p=Ks(t),f=a.nodeWidth?a.nodeWidth*j1:960,g=a.nodeHeight?a.nodeHeight*j1:500,m=f,y=g+d;p.attr("viewBox",`0 0 ${m} ${y}`),aa(p,y,m,a.useMaxWidth);let k;try{let U=a.valueFormat||",";if(U==="$0,0")k=s(H=>"$"+ag(",")(H),"valueFormat");else if(U.startsWith("$")&&U.includes(",")){let H=/\.\d+/.exec(U),J=H?H[0]:"";k=s(te=>"$"+ag(","+J)(te),"valueFormat")}else if(U.startsWith("$")){let H=U.substring(1);k=s(J=>"$"+ag(H||"")(J),"valueFormat")}else k=ag(U)}catch(U){Ee.error("Error creating format function:",U),k=ag(",")}let C=Lp().range(["transparent",h.cScale0,h.cScale1,h.cScale2,h.cScale3,h.cScale4,h.cScale5,h.cScale6,h.cScale7,h.cScale8,h.cScale9,h.cScale10,h.cScale11]),S=Lp().range(["transparent",h.cScalePeer0,h.cScalePeer1,h.cScalePeer2,h.cScalePeer3,h.cScalePeer4,h.cScalePeer5,h.cScalePeer6,h.cScalePeer7,h.cScalePeer8,h.cScalePeer9,h.cScalePeer10,h.cScalePeer11]),E=Lp().range([h.cScaleLabel0,h.cScaleLabel1,h.cScaleLabel2,h.cScaleLabel3,h.cScaleLabel4,h.cScaleLabel5,h.cScaleLabel6,h.cScaleLabel7,h.cScaleLabel8,h.cScaleLabel9,h.cScaleLabel10,h.cScaleLabel11]);l&&p.append("text").attr("x",m/2).attr("y",d/2).attr("class","treemapTitle").attr("text-anchor","middle").attr("dominant-baseline","middle").text(l);let _=p.append("g").attr("transform",`translate(0, ${d})`).attr("class","treemapContainer"),b=w6(u).sum(U=>U.value??0).sort((U,H)=>(H.value??0)-(U.value??0)),v=k4e().size([f,g]).paddingTop(U=>U.children&&U.children.length>0?sx+j1:0).paddingInner(o).paddingLeft(U=>U.children&&U.children.length>0?j1:0).paddingRight(U=>U.children&&U.children.length>0?j1:0).paddingBottom(U=>U.children&&U.children.length>0?j1:0).round(!0)(b),x=v.descendants().filter(U=>U.children&&U.children.length>0),w=_.selectAll(".treemapSection").data(x).enter().append("g").attr("class","treemapSection").attr("transform",U=>`translate(${U.x0},${U.y0})`);w.append("rect").attr("width",U=>U.x1-U.x0).attr("height",sx).attr("class","treemapSectionHeader").attr("fill","none").attr("fill-opacity",.6).attr("stroke-width",.6).attr("style",U=>U.depth===0?"display: none;":""),w.append("clipPath").attr("id",(U,H)=>`clip-section-${t}-${H}`).append("rect").attr("width",U=>Math.max(0,U.x1-U.x0-12)).attr("height",sx),w.append("rect").attr("width",U=>U.x1-U.x0).attr("height",U=>U.y1-U.y0).attr("class",(U,H)=>`treemapSection section${H}`).attr("fill",U=>C(U.data.name)).attr("fill-opacity",.6).attr("stroke",U=>S(U.data.name)).attr("stroke-width",2).attr("stroke-opacity",.4).attr("style",U=>{if(U.depth===0)return"display: none;";let H=cr({cssCompiledStyles:U.data.cssCompiledStyles});return H.nodeStyles+";"+H.borderStyles.join(";")}),w.append("text").attr("class","treemapSectionLabel").attr("x",6).attr("y",sx/2).attr("dominant-baseline","middle").text(U=>U.depth===0?"":U.data.name).attr("font-weight","bold").attr("clip-path",(U,H)=>`url(#clip-section-${t}-${H})`).attr("style",U=>{if(U.depth===0)return"display: none;";let H="dominant-baseline: middle; font-size: 12px; fill:"+E(U.data.name)+"; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;",J=cr({cssCompiledStyles:U.data.cssCompiledStyles});return H+J.labelStyles.replace("color:","fill:")}).each(function(U){if(U.depth===0)return;let H=zt(this),J=U.data.name;H.text(J);let te=U.x1-U.x0,ie=6,ue;a.showValues!==!1&&U.value?ue=te-10-30-10-ie:ue=te-ie-6;let se=Math.max(15,ue),le=H.node();if(le.getComputedTextLength()>se){let Te=J;for(;Te.length>0;){if(Te=J.substring(0,Te.length-1),Te.length===0){H.text("..."),le.getComputedTextLength()>se&&H.text("");break}if(H.text(Te+"..."),le.getComputedTextLength()<=se)break}}}),a.showValues!==!1&&w.append("text").attr("class","treemapSectionValue").attr("x",U=>U.x1-U.x0-10).attr("y",sx/2).attr("text-anchor","end").attr("dominant-baseline","middle").text(U=>U.value?k(U.value):"").attr("font-style","italic").attr("style",U=>{if(U.depth===0)return"display: none;";let H="text-anchor: end; dominant-baseline: middle; font-size: 10px; fill:"+E(U.data.name)+"; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;",J=cr({cssCompiledStyles:U.data.cssCompiledStyles});return H+J.labelStyles.replace("color:","fill:")});let R=v.leaves(),$=R.length>20,M=$?16:38,N=$?14:28,j=$?4:8,I=$?4:6,z=$?2:4,F=$?8:10,O=$?1:2,B=_.selectAll(".treemapLeafGroup").data(R).enter().append("g").attr("class",(U,H)=>`treemapNode treemapLeafGroup leaf${H}${U.data.classSelector?` ${U.data.classSelector}`:""}x`).attr("transform",U=>`translate(${U.x0},${U.y0})`);B.append("rect").attr("width",U=>U.x1-U.x0).attr("height",U=>U.y1-U.y0).attr("class","treemapLeaf").attr("fill",U=>U.parent?C(U.parent.data.name):C(U.data.name)).attr("style",U=>cr({cssCompiledStyles:U.data.cssCompiledStyles}).nodeStyles).attr("fill-opacity",.3).attr("stroke",U=>U.parent?C(U.parent.data.name):C(U.data.name)).attr("stroke-width",3),B.append("clipPath").attr("id",(U,H)=>`clip-${t}-${H}`).append("rect").attr("width",U=>Math.max(0,U.x1-U.x0-4)).attr("height",U=>Math.max(0,U.y1-U.y0-4)),B.append("text").attr("class","treemapLabel").attr("x",U=>(U.x1-U.x0)/2).attr("y",U=>(U.y1-U.y0)/2).attr("style",U=>{let H=`text-anchor: middle; dominant-baseline: middle; font-size: ${M}px;fill:`+E(U.data.name)+";",J=cr({cssCompiledStyles:U.data.cssCompiledStyles});return H+J.labelStyles.replace("color:","fill:")}).attr("clip-path",(U,H)=>`url(#clip-${t}-${H})`).text(U=>U.data.name).each(function(U){let H=zt(this),J=U.x1-U.x0,te=U.y1-U.y0,ie=H.node(),ue=J-2*z,se=te-2*z;if(ueue&&le>j;)le--,H.style("font-size",`${le}px`);let oe=Math.max(I,Math.min(N,Math.round(le*Te))),fe=le+O+oe;for(;fe>se&&le>j&&(le--,oe=Math.max(I,Math.min(N,Math.round(le*Te))),!(oese;H.style("font-size",`${le}px`),$?(leue||le(U.x1-U.x0)/2).attr("y",function(U){return(U.y1-U.y0)/2}).attr("style",U=>{let H=`text-anchor: middle; dominant-baseline: hanging; font-size: ${N}px;fill:`+E(U.data.name)+";",J=cr({cssCompiledStyles:U.data.cssCompiledStyles});return H+J.labelStyles.replace("color:","fill:")}).attr("clip-path",(U,H)=>`url(#clip-${t}-${H})`).text(U=>U.value?k(U.value):"").each(function(U){let H=zt(this),J=this.parentNode;if(!J){H.style("display","none");return}let te=zt(J).select(".treemapLabel");if(te.empty()||te.style("display")==="none"){H.style("display","none");return}let ie=parseFloat(te.style("font-size")),ue=Math.max(I,Math.min(N,Math.round(ie*.6)));H.style("font-size",`${ue}px`);let se=(U.y1-U.y0)/2+ie/2+O;H.attr("y",se);let le=U.x1-U.x0,Te=U.y1-U.y0-4,oe=le-2*z;H.node().getComputedTextLength()>oe||se+ue>Te||ue{"use strict";Ar(),Wh(),Gr(),Cbt={sectionStrokeColor:"black",sectionStrokeWidth:"1",sectionFillColor:"#efefef",leafStrokeColor:"black",leafStrokeWidth:"1",leafFillColor:"#efefef",labelFontSize:"12px",valueFontSize:"10px",titleFontSize:"14px"},wbt=s(({treemap:e}={})=>{let t=ku(),r=Rr(),i=Gn(t,r.themeVariables),n=Gn(Cbt,e),a=n.titleColor??i.titleColor,o=n.labelColor??i.textColor,l=n.valueColor??i.textColor;return` + .treemapNode.section { + stroke: ${n.sectionStrokeColor}; + stroke-width: ${n.sectionStrokeWidth}; + fill: ${n.sectionFillColor}; + } + .treemapNode.leaf { + stroke: ${n.leafStrokeColor}; + stroke-width: ${n.leafStrokeWidth}; + fill: ${n.leafFillColor}; + } + .treemapLabel { + fill: ${o}; + font-size: ${n.labelFontSize}; + } + .treemapValue { + fill: ${l}; + font-size: ${n.valueFontSize}; + } + .treemapTitle { + fill: ${a}; + font-size: ${n.titleFontSize}; + } + `},"getStyles"),Ebt=wbt}),Sbt={};Mr(Sbt,{diagram:()=>_bt});var _bt,SLt=G(()=>{"use strict";gbt(),CLt(),wLt(),ELt(),_bt={parser:Ife,get db(){return new Lfe},renderer:kbt,styles:Ebt}}),Z5,M0,Mfe,Abt,Rbt,Dfe,_Lt=G(()=>{"use strict";Co(),Nt(),vl(),Z5=s((e,t)=>{let r=e<=1?e*100:e;if(r<0||r>100)throw new Error(`${t} must be between 0-1 (decimal) or 0-100 (percentage). Received: ${e}`);return r},"toPercent"),M0=s((e,t,r)=>({x:Z5(t,`${r} evolution`),y:Z5(e,`${r} visibility`)}),"toCoordinates"),Mfe=s(e=>{if(e){if(e==="+<>")return"bidirectional";if(e==="+<")return"backward";if(e==="+>")return"forward"}},"getFlowFromPort"),Abt=s(e=>{if(!e?.startsWith("+"))return{};let t=/^\+'([^']*)'/.exec(e)?.[1];return e.includes("<>")?{flow:"bidirectional",label:t}:e.includes("<")?{flow:"backward",label:t}:e.includes(">")?{flow:"forward",label:t}:{label:t}},"extractFlowFromArrow"),Rbt=s((e,t)=>{if(Yo(e,t),e.size&&t.setSize(e.size.width,e.size.height),e.evolution){let r=e.evolution.stages.map(n=>n.secondName?`${n.name.trim()} / ${n.secondName.trim()}`:n.name.trim()),i=e.evolution.stages.filter(n=>n.boundary!==void 0).map(n=>n.boundary);t.updateAxes({stages:r,stageBoundaries:i})}if(e.anchors.forEach(r=>{let i=M0(r.visibility,r.evolution,`Anchor "${r.name}"`);t.addNode(r.name,r.name,i.x,i.y,"anchor")}),e.components.forEach(r=>{let i=M0(r.visibility,r.evolution,`Component "${r.name}"`),n=r.label?(r.label.negX?-1:1)*r.label.offsetX:void 0,a=r.label?(r.label.negY?-1:1)*r.label.offsetY:void 0,o=r.decorator?.strategy;t.addNode(r.name,r.name,i.x,i.y,"component",n,a,r.inertia,o)}),e.notes.forEach(r=>{let i=M0(r.visibility,r.evolution,`Note "${r.text}"`);t.addNote(r.text,i.x,i.y)}),e.pipelines.forEach(r=>{let i=t.getNode(r.parent);if(!i||typeof i.y!="number")throw new Error(`Pipeline "${r.parent}" must reference an existing component with coordinates.`);let n=i.y;t.startPipeline(r.parent),r.components.forEach(a=>{let o=`${r.parent}_${a.name}`,l=a.label?(a.label.negX?-1:1)*a.label.offsetX:void 0,u=a.label?(a.label.negY?-1:1)*a.label.offsetY:void 0,h=Z5(a.evolution,`Pipeline component "${a.name}" evolution`);t.addNode(o,a.name,h,n,"pipeline-component",l,u),t.addPipelineComponent(r.parent,o)})}),e.links.forEach(r=>{let i=!!r.arrow&&(r.arrow.includes("-.->")||r.arrow.includes(".-.")),n=Mfe(r.fromPort)??Mfe(r.toPort),{flow:a,label:o}=Abt(r.arrow);!n&&a&&(n=a);let l=r.linkLabel,u=o??l;t.addLink(t.resolveNodeId(r.from),t.resolveNodeId(r.to),i,u,n)}),e.evolves.forEach(r=>{let i=t.getNode(r.component);if(i?.y!==void 0){let n=Z5(r.target,`Evolve target for "${r.component}"`);t.addTrend(r.component,n,i.y)}}),e.annotations.length>0){let r=e.annotations[0],i=M0(r.x,r.y,"Annotations box");t.setAnnotationsBox(i.x,i.y)}e.annotation.forEach(r=>{let i=M0(r.x,r.y,`Annotation ${r.number}`);t.addAnnotation(r.number,[{x:i.x,y:i.y}],r.text)}),e.accelerators.forEach(r=>{let i=M0(r.x,r.y,`Accelerator "${r.name}"`);t.addAccelerator(r.name,i.x,i.y)}),e.deaccelerators.forEach(r=>{let i=M0(r.x,r.y,`Deaccelerator "${r.name}"`);t.addDeaccelerator(r.name,i.x,i.y)})},"populateDb"),Dfe={parser:{yy:void 0},parse:s(async e=>{let t=await Pl("wardley",e);Ee.debug(t);let r=Dfe.parser?.yy;if(!r||typeof r.addNode!="function")throw new Error("parser.parser?.yy was not a WardleyDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.");Rbt(t,r)},"parse")}}),$bt,ALt=G(()=>{"use strict";var e;$bt=(e=class{constructor(){this.nodes=new Map,this.links=[],this.trends=new Map,this.pipelines=new Map,this.annotations=[],this.notes=[],this.accelerators=[],this.deaccelerators=[],this.axes={}}addNode(r){let i=this.nodes.get(r.id)??{id:r.id,label:r.label},n={...i,...r,className:r.className??i.className,labelOffsetX:r.labelOffsetX??i.labelOffsetX,labelOffsetY:r.labelOffsetY??i.labelOffsetY};this.nodes.set(r.id,n)}addLink(r){this.links.push(r)}addTrend(r){this.trends.set(r.nodeId,r)}startPipeline(r){this.pipelines.set(r,{nodeId:r,componentIds:[]});let i=this.nodes.get(r);i&&(i.isPipelineParent=!0)}addPipelineComponent(r,i){let n=this.pipelines.get(r);n&&n.componentIds.push(i);let a=this.nodes.get(i);a&&(a.inPipeline=!0)}addAnnotation(r){this.annotations.push(r)}addNote(r){this.notes.push(r)}addAccelerator(r){this.accelerators.push(r)}addDeaccelerator(r){this.deaccelerators.push(r)}setAnnotationsBox(r,i){this.annotationsBox={x:r,y:i}}setAxes(r){this.axes={...this.axes,...r}}setSize(r,i){this.size={width:r,height:i}}getNode(r){return this.nodes.get(r)}resolveNodeId(r){if(this.nodes.has(r))return r;for(let[i,n]of this.nodes)if(n.label===r)return i;return r}build(){let r=[];for(let i of this.nodes.values()){if(typeof i.x!="number"||typeof i.y!="number")throw new Error(`Node "${i.label}" is missing coordinates`);r.push(i)}return{nodes:r,links:[...this.links],trends:[...this.trends.values()],pipelines:[...this.pipelines.values()],annotations:[...this.annotations],notes:[...this.notes],accelerators:[...this.accelerators],deaccelerators:[...this.deaccelerators],annotationsBox:this.annotationsBox,axes:{...this.axes},size:this.size}}clear(){this.nodes.clear(),this.links=[],this.trends.clear(),this.pipelines.clear(),this.annotations=[],this.notes=[],this.accelerators=[],this.deaccelerators=[],this.annotationsBox=void 0,this.axes={},this.size=void 0}},s(e,"WardleyBuilder"),e)});function Lbt(){return it()["wardley-beta"]}function Ibt(e,t,r,i,n,a,o,l,u){Qs.addNode({id:e,label:t,x:r,y:i,className:n,labelOffsetX:a,labelOffsetY:o,inertia:l,sourceStrategy:u})}function Mbt(e,t,r=!1,i,n){Qs.addLink({source:e,target:t,dashed:r,label:i,flow:n})}function Dbt(e,t,r){Qs.addTrend({nodeId:e,targetX:t,targetY:r})}function Nbt(e,t,r){Qs.addAnnotation({number:e,coordinates:t,text:r})}function Obt(e,t,r){Qs.addNote({text:e,x:t,y:r})}function Bbt(e,t,r){Qs.addAccelerator({name:e,x:t,y:r})}function Pbt(e,t,r){Qs.addDeaccelerator({name:e,x:t,y:r})}function Fbt(e,t){Qs.setAnnotationsBox(e,t)}function zbt(e,t){Qs.setSize(e,t)}function qbt(e){Qs.startPipeline(e)}function Wbt(e,t){Qs.addPipelineComponent(e,t)}function jbt(e){Qs.setAxes(e)}function Vbt(e){return Qs.getNode(e)}function Ubt(e){return Qs.resolveNodeId(e)}function Ybt(){return Qs.build()}function Hbt(){Qs.clear(),pn()}var Qs,Gbt,RLt=G(()=>{"use strict";Er(),fn(),ALt(),Qs=new $bt,s(Lbt,"getConfig"),s(Ibt,"addNode"),s(Mbt,"addLink"),s(Dbt,"addTrend"),s(Nbt,"addAnnotation"),s(Obt,"addNote"),s(Bbt,"addAccelerator"),s(Pbt,"addDeaccelerator"),s(Fbt,"setAnnotationsBox"),s(zbt,"setSize"),s(qbt,"startPipeline"),s(Wbt,"addPipelineComponent"),s(jbt,"updateAxes"),s(Vbt,"getNode"),s(Ubt,"resolveNodeId"),s(Ybt,"getWardleyData"),s(Hbt,"clear"),Gbt={getConfig:Lbt,addNode:Ibt,addLink:Mbt,addTrend:Dbt,addAnnotation:Nbt,addNote:Obt,addAccelerator:Bbt,addDeaccelerator:Pbt,setAnnotationsBox:Fbt,setSize:zbt,startPipeline:qbt,addPipelineComponent:Wbt,updateAxes:jbt,getNode:Vbt,resolveNodeId:Ubt,getWardleyData:Ybt,clear:Hbt,setAccTitle:bn,getAccTitle:Ln,setDiagramTitle:Dn,getDiagramTitle:xn,getAccDescription:Mn,setAccDescription:In}}),Xbt,Kbt,Qbt,Zbt,Jbt,$Lt=G(()=>{"use strict";Er(),Nt(),wo(),vn(),Xbt=["Genesis","Custom Built","Product","Commodity"],Kbt=s(()=>{let{themeVariables:e}=it();return{backgroundColor:e.wardley?.backgroundColor??e.background??"#fff",axisColor:e.wardley?.axisColor??"#000",axisTextColor:e.wardley?.axisTextColor??e.primaryTextColor??"#222",gridColor:e.wardley?.gridColor??"rgba(100, 100, 100, 0.2)",componentFill:e.wardley?.componentFill??"#fff",componentStroke:e.wardley?.componentStroke??"#000",componentLabelColor:e.wardley?.componentLabelColor??e.primaryTextColor??"#222",linkStroke:e.wardley?.linkStroke??"#000",evolutionStroke:e.wardley?.evolutionStroke??"#dc3545",annotationStroke:e.wardley?.annotationStroke??"#000",annotationTextColor:e.wardley?.annotationTextColor??e.primaryTextColor??"#222",annotationFill:e.wardley?.annotationFill??e.background??"#fff"}},"getTheme"),Qbt=s(()=>{let e=it()["wardley-beta"];return{width:e?.width??900,height:e?.height??600,padding:e?.padding??48,nodeRadius:e?.nodeRadius??6,nodeLabelOffset:e?.nodeLabelOffset??8,axisFontSize:e?.axisFontSize??12,labelFontSize:e?.labelFontSize??10,showGrid:e?.showGrid??!1,useMaxWidth:e?.useMaxWidth??!0}},"getConfigValues"),Zbt=s((e,t,r,i)=>{Ee.debug(`Rendering Wardley map +`+e);let n=Qbt(),a=Kbt(),o=n.nodeRadius*1.6,l=i.db,u=l.getWardleyData(),h=l.getDiagramTitle(),d=u.size?.width??n.width,p=u.size?.height??n.height,f=Ks(t);f.selectAll("*").remove(),aa(f,p,d,n.useMaxWidth),f.attr("viewBox",`0 0 ${d} ${p}`);let g=f.append("g").attr("class","wardley-map"),m=f.append("defs");m.append("marker").attr("id",`arrow-${t}`).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerWidth",6).attr("markerHeight",6).attr("orient","auto-start-reverse").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("fill",a.evolutionStroke).attr("stroke","none"),m.append("marker").attr("id",`link-arrow-end-${t}`).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerWidth",5).attr("markerHeight",5).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("fill",a.linkStroke).attr("stroke","none"),m.append("marker").attr("id",`link-arrow-start-${t}`).attr("viewBox","0 0 10 10").attr("refX",1).attr("refY",5).attr("markerWidth",5).attr("markerHeight",5).attr("orient","auto").append("path").attr("d","M 10 0 L 0 5 L 10 10 z").attr("fill",a.linkStroke).attr("stroke","none"),g.append("rect").attr("class","wardley-background").attr("width",d).attr("height",p).attr("fill",a.backgroundColor);let y=d-n.padding*2,k=p-n.padding*2;h&&g.append("text").attr("class","wardley-title").attr("x",d/2).attr("y",n.padding/2).attr("fill",a.axisTextColor).attr("font-size",n.axisFontSize*1.05).attr("font-weight","bold").attr("text-anchor","middle").attr("dominant-baseline","middle").text(h);let C=s(O=>n.padding+O/100*y,"projectX"),S=s(O=>p-n.padding-O/100*k,"projectY"),E=g.append("g").attr("class","wardley-axes");E.append("line").attr("x1",n.padding).attr("x2",d-n.padding).attr("y1",p-n.padding).attr("y2",p-n.padding).attr("stroke",a.axisColor).attr("stroke-width",1),E.append("line").attr("x1",n.padding).attr("x2",n.padding).attr("y1",n.padding).attr("y2",p-n.padding).attr("stroke",a.axisColor).attr("stroke-width",1);let _=u.axes.xLabel??"Evolution",b=u.axes.yLabel??"Visibility";E.append("text").attr("class","wardley-axis-label wardley-axis-label-x").attr("x",n.padding+y/2).attr("y",p-n.padding/4).attr("fill",a.axisTextColor).attr("font-size",n.axisFontSize).attr("font-weight","bold").attr("text-anchor","middle").text(_),E.append("text").attr("class","wardley-axis-label wardley-axis-label-y").attr("x",n.padding/3).attr("y",n.padding+k/2).attr("fill",a.axisTextColor).attr("font-size",n.axisFontSize).attr("font-weight","bold").attr("text-anchor","middle").attr("transform",`rotate(-90 ${n.padding/3} ${n.padding+k/2})`).text(b);let v=u.axes.stages&&u.axes.stages.length>0?u.axes.stages:Xbt;if(v.length>0){let O=g.append("g").attr("class","wardley-stages"),B=u.axes.stageBoundaries,Y=[];if(B&&B.length===v.length){let U=0;B.forEach(H=>{Y.push({start:U,end:H}),U=H})}else{let U=1/v.length;v.forEach((H,J)=>{Y.push({start:J*U,end:(J+1)*U})})}v.forEach((U,H)=>{let J=Y[H],te=n.padding+J.start*y,ie=n.padding+J.end*y,ue=(te+ie)/2;H>0&&O.append("line").attr("x1",te).attr("x2",te).attr("y1",n.padding).attr("y2",p-n.padding).attr("stroke","#000").attr("stroke-width",1).attr("stroke-dasharray","5 5").attr("opacity",.8),O.append("text").attr("class","wardley-stage-label").attr("x",ue).attr("y",p-n.padding/1.5).attr("fill",a.axisTextColor).attr("font-size",n.axisFontSize-2).attr("text-anchor","middle").text(U)})}if(n.showGrid){let O=g.append("g").attr("class","wardley-grid");for(let B=1;B<4;B++){let Y=B/4,U=n.padding+y*Y;O.append("line").attr("x1",U).attr("x2",U).attr("y1",n.padding).attr("y2",p-n.padding).attr("stroke",a.gridColor).attr("stroke-dasharray","2 6"),O.append("line").attr("x1",n.padding).attr("x2",d-n.padding).attr("y1",p-n.padding-k*Y).attr("y2",p-n.padding-k*Y).attr("stroke",a.gridColor).attr("stroke-dasharray","2 6")}}let x=new Map;if(u.nodes.forEach(O=>{x.set(O.id,{x:C(O.x),y:S(O.y),node:O})}),u.pipelines.length>0){let O=g.append("g").attr("class","wardley-pipelines"),B=g.append("g").attr("class","wardley-pipeline-links");u.pipelines.forEach(Y=>{if(Y.componentIds.length===0)return;let U=Y.componentIds.map(ie=>({id:ie,pos:x.get(ie),node:u.nodes.find(ue=>ue.id===ie)})).filter(ie=>ie.pos&&ie.node).sort((ie,ue)=>ie.node.x-ue.node.x);for(let ie=0;ie{let ue=x.get(ie);ue&&(H=Math.min(H,ue.x),J=Math.max(J,ue.x),te=ue.y)}),H!==1/0&&J!==-1/0){let ie=n.nodeRadius*4,ue=te-ie/2,se=x.get(Y.nodeId);if(se){let le=(H+J)/2;se.x=le,se.y=ue-o/6}O.append("rect").attr("class","wardley-pipeline-box").attr("x",H-15).attr("y",ue).attr("width",J-H+30).attr("height",ie).attr("fill","none").attr("stroke",a.axisColor).attr("stroke-width",1.5).attr("rx",4).attr("ry",4)}})}let w=g.append("g").attr("class","wardley-links"),R=new Map;u.pipelines.forEach(O=>{R.set(O.nodeId,new Set(O.componentIds))});let $=u.links.filter(O=>!(!x.has(O.source)||!x.has(O.target)||R.get(O.target)?.has(O.source)));w.selectAll("line").data($).enter().append("line").attr("class",O=>`wardley-link${O.dashed?" wardley-link--dashed":""}`).attr("x1",O=>{let B=x.get(O.source),Y=x.get(O.target),U=u.nodes.find(ie=>ie.id===O.source).isPipelineParent?o/Math.sqrt(2):n.nodeRadius,H=Y.x-B.x,J=Y.y-B.y,te=Math.sqrt(H*H+J*J);return B.x+H/te*U}).attr("y1",O=>{let B=x.get(O.source),Y=x.get(O.target),U=u.nodes.find(ie=>ie.id===O.source).isPipelineParent?o/Math.sqrt(2):n.nodeRadius,H=Y.x-B.x,J=Y.y-B.y,te=Math.sqrt(H*H+J*J);return B.y+J/te*U}).attr("x2",O=>{let B=x.get(O.source),Y=x.get(O.target),U=u.nodes.find(ie=>ie.id===O.target).isPipelineParent?o/Math.sqrt(2):n.nodeRadius,H=B.x-Y.x,J=B.y-Y.y,te=Math.sqrt(H*H+J*J);return Y.x+H/te*U}).attr("y2",O=>{let B=x.get(O.source),Y=x.get(O.target),U=u.nodes.find(ie=>ie.id===O.target).isPipelineParent?o/Math.sqrt(2):n.nodeRadius,H=B.x-Y.x,J=B.y-Y.y,te=Math.sqrt(H*H+J*J);return Y.y+J/te*U}).attr("stroke",a.linkStroke).attr("stroke-width",1).attr("stroke-dasharray",O=>O.dashed?"6 6":null).attr("marker-end",O=>O.flow==="forward"||O.flow==="bidirectional"?`url(#link-arrow-end-${t})`:null).attr("marker-start",O=>O.flow==="backward"||O.flow==="bidirectional"?`url(#link-arrow-start-${t})`:null),w.selectAll("text").data($.filter(O=>O.label)).enter().append("text").attr("class","wardley-link-label").attr("x",O=>{let B=x.get(O.source),Y=x.get(O.target),U=(B.x+Y.x)/2,H=Y.y-B.y,J=Y.x-B.x,te=Math.sqrt(J*J+H*H),ie=8,ue=H/te;return U+ue*ie}).attr("y",O=>{let B=x.get(O.source),Y=x.get(O.target),U=(B.y+Y.y)/2,H=Y.x-B.x,J=Y.y-B.y,te=Math.sqrt(H*H+J*J),ie=8,ue=-H/te;return U+ue*ie}).attr("fill",a.axisTextColor).attr("font-size",n.labelFontSize).attr("text-anchor","middle").attr("dominant-baseline","middle").attr("transform",O=>{let B=x.get(O.source),Y=x.get(O.target),U=(B.x+Y.x)/2,H=(B.y+Y.y)/2,J=Y.x-B.x,te=Y.y-B.y,ie=Math.sqrt(J*J+te*te),ue=8,se=te/ie,le=-J/ie,Te=U+se*ue,oe=H+le*ue,fe=Math.atan2(te,J)*180/Math.PI;return(fe>90||fe<-90)&&(fe+=180),`rotate(${fe} ${Te} ${oe})`}).text(O=>O.label);let M=g.append("g").attr("class","wardley-trends"),N=u.trends.map(O=>{let B=x.get(O.nodeId);if(!B)return null;let Y=C(O.targetX),U=S(O.targetY),H=Y-B.x,J=U-B.y,te=Math.sqrt(H*H+J*J),ie=n.nodeRadius+2,ue=te>ie?Y-H/te*ie:Y,se=te>ie?U-J/te*ie:U;return{origin:B,targetX:Y,targetY:U,adjustedX2:ue,adjustedY2:se}}).filter(O=>O!==null);M.selectAll("line").data(N).enter().append("line").attr("class","wardley-trend").attr("x1",O=>O.origin.x).attr("y1",O=>O.origin.y).attr("x2",O=>O.adjustedX2).attr("y2",O=>O.adjustedY2).attr("stroke",a.evolutionStroke).attr("stroke-width",1).attr("stroke-dasharray","4 4").attr("marker-end",`url(#arrow-${t})`);let j=g.append("g").attr("class","wardley-nodes").selectAll("g").data(u.nodes).enter().append("g").attr("class",O=>["wardley-node",O.className?`wardley-node--${O.className}`:""].filter(Boolean).join(" "));j.filter(O=>O.sourceStrategy==="outsource").append("circle").attr("class","wardley-outsource-overlay").attr("cx",O=>x.get(O.id).x).attr("cy",O=>x.get(O.id).y).attr("r",n.nodeRadius*2).attr("fill","#666").attr("stroke",a.componentStroke).attr("stroke-width",1),j.filter(O=>O.sourceStrategy==="buy").append("circle").attr("class","wardley-buy-overlay").attr("cx",O=>x.get(O.id).x).attr("cy",O=>x.get(O.id).y).attr("r",n.nodeRadius*2).attr("fill","#ccc").attr("stroke",a.componentStroke).attr("stroke-width",1),j.filter(O=>O.sourceStrategy==="build").append("circle").attr("class","wardley-build-overlay").attr("cx",O=>x.get(O.id).x).attr("cy",O=>x.get(O.id).y).attr("r",n.nodeRadius*2).attr("fill","#eee").attr("stroke","#000").attr("stroke-width",1);let I=j.filter(O=>O.sourceStrategy==="market");I.append("circle").attr("class","wardley-market-overlay").attr("cx",O=>x.get(O.id).x).attr("cy",O=>x.get(O.id).y).attr("r",n.nodeRadius*2).attr("fill","white").attr("stroke",a.componentStroke).attr("stroke-width",1),j.filter(O=>!O.isPipelineParent&&O.sourceStrategy!=="market"&&O.className!=="anchor").append("circle").attr("cx",O=>x.get(O.id).x).attr("cy",O=>x.get(O.id).y).attr("r",n.nodeRadius).attr("fill",a.componentFill).attr("stroke",a.componentStroke).attr("stroke-width",1);let z=n.nodeRadius*.7,F=n.nodeRadius*1.2;if(I.append("line").attr("class","wardley-market-line").attr("x1",O=>x.get(O.id).x).attr("y1",O=>x.get(O.id).y-F).attr("x2",O=>x.get(O.id).x-F*Math.cos(Math.PI/6)).attr("y2",O=>x.get(O.id).y+F*Math.sin(Math.PI/6)).attr("stroke",a.componentStroke).attr("stroke-width",1),I.append("line").attr("class","wardley-market-line").attr("x1",O=>x.get(O.id).x-F*Math.cos(Math.PI/6)).attr("y1",O=>x.get(O.id).y+F*Math.sin(Math.PI/6)).attr("x2",O=>x.get(O.id).x+F*Math.cos(Math.PI/6)).attr("y2",O=>x.get(O.id).y+F*Math.sin(Math.PI/6)).attr("stroke",a.componentStroke).attr("stroke-width",1),I.append("line").attr("class","wardley-market-line").attr("x1",O=>x.get(O.id).x+F*Math.cos(Math.PI/6)).attr("y1",O=>x.get(O.id).y+F*Math.sin(Math.PI/6)).attr("x2",O=>x.get(O.id).x).attr("y2",O=>x.get(O.id).y-F).attr("stroke",a.componentStroke).attr("stroke-width",1),I.append("circle").attr("class","wardley-market-dot").attr("cx",O=>x.get(O.id).x).attr("cy",O=>x.get(O.id).y-F).attr("r",z).attr("fill","white").attr("stroke",a.componentStroke).attr("stroke-width",2),I.append("circle").attr("class","wardley-market-dot").attr("cx",O=>x.get(O.id).x-F*Math.cos(Math.PI/6)).attr("cy",O=>x.get(O.id).y+F*Math.sin(Math.PI/6)).attr("r",z).attr("fill","white").attr("stroke",a.componentStroke).attr("stroke-width",2),I.append("circle").attr("class","wardley-market-dot").attr("cx",O=>x.get(O.id).x+F*Math.cos(Math.PI/6)).attr("cy",O=>x.get(O.id).y+F*Math.sin(Math.PI/6)).attr("r",z).attr("fill","white").attr("stroke",a.componentStroke).attr("stroke-width",2),j.filter(O=>O.isPipelineParent===!0).append("rect").attr("x",O=>x.get(O.id).x-o/2).attr("y",O=>x.get(O.id).y-o/2).attr("width",o).attr("height",o).attr("fill",a.componentFill).attr("stroke",a.componentStroke).attr("stroke-width",1),j.filter(O=>O.inertia===!0).append("line").attr("class","wardley-inertia").attr("x1",O=>{let B=x.get(O.id),Y=O.isPipelineParent?o/2+15:n.nodeRadius+15;return O.sourceStrategy&&(Y+=n.nodeRadius+10),B.x+Y}).attr("y1",O=>{let B=x.get(O.id),Y=O.isPipelineParent?o:n.nodeRadius*2;return B.y-Y/2}).attr("x2",O=>{let B=x.get(O.id),Y=O.isPipelineParent?o/2+15:n.nodeRadius+15;return O.sourceStrategy&&(Y+=n.nodeRadius+10),B.x+Y}).attr("y2",O=>{let B=x.get(O.id),Y=O.isPipelineParent?o:n.nodeRadius*2;return B.y+Y/2}).attr("stroke",a.componentStroke).attr("stroke-width",6),j.append("text").attr("x",O=>{let B=x.get(O.id);if(O.className==="anchor")return O.labelOffsetX!==void 0?B.x+O.labelOffsetX:B.x;let Y=n.nodeLabelOffset;O.sourceStrategy&&O.labelOffsetX===void 0&&(Y+=10);let U=O.labelOffsetX??Y;return B.x+U}).attr("y",O=>{let B=x.get(O.id);if(O.className==="anchor")return O.labelOffsetY!==void 0?B.y+O.labelOffsetY:B.y-3;let Y=-n.nodeLabelOffset;O.sourceStrategy&&O.labelOffsetY===void 0&&(Y-=10);let U=O.labelOffsetY??Y;return B.y+U}).attr("class","wardley-node-label").attr("fill",O=>O.className==="evolved"?a.evolutionStroke:O.className==="anchor"?"#000":a.componentLabelColor).attr("font-size",n.labelFontSize).attr("font-weight",O=>O.className==="anchor"?"bold":"normal").attr("text-anchor",O=>O.className==="anchor"?"middle":"start").attr("dominant-baseline",O=>O.className==="anchor"?"middle":"auto").text(O=>O.label),u.annotations.length>0){let O=g.append("g").attr("class","wardley-annotations");if(u.annotations.forEach(B=>{let Y=B.coordinates.map(U=>({x:C(U.x),y:S(U.y)}));if(Y.length>1)for(let U=0;U{let H=O.append("g").attr("class","wardley-annotation");H.append("circle").attr("cx",U.x).attr("cy",U.y).attr("r",10).attr("fill","white").attr("stroke",a.axisColor).attr("stroke-width",1.5),H.append("text").attr("x",U.x).attr("y",U.y).attr("text-anchor","middle").attr("dominant-baseline","central").attr("font-size",10).attr("fill",a.axisTextColor).attr("font-weight","bold").text(B.number)})}),u.annotationsBox){let B=C(u.annotationsBox.x),Y=S(u.annotationsBox.y),U=10,H=16,J=11,te=O.append("g").attr("class","wardley-annotations-box"),ie=[...u.annotations].filter(se=>se.text).sort((se,le)=>se.number-le.number),ue=[];if(ie.forEach((se,le)=>{let Te=te.append("text").attr("x",B+U).attr("y",Y+U+(le+1)*H).attr("font-size",J).attr("fill",a.axisTextColor).attr("text-anchor","start").attr("dominant-baseline","middle").text(`${se.number}. ${se.text}`);ue.push(Te)}),ue.length>0){let se=0,le=0;ue.forEach(xe=>{let $e=xe.node(),Me=$e.getComputedTextLength();se=Math.max(se,Me);let He=$e.getBBox();le=Math.max(le,He.height)});let Te=se+U*2+105,oe=ie.length*H+U*2+le/2,fe=n.padding,ge=d-n.padding-Te,ke=n.padding,Ne=p-n.padding-oe;B=Math.max(fe,Math.min(B,ge)),Y=Math.max(ke,Math.min(Y,Ne)),ue.forEach((xe,$e)=>{xe.attr("x",B+U).attr("y",Y+U+($e+1)*H)}),te.insert("rect","text").attr("x",B).attr("y",Y).attr("width",Te).attr("height",oe).attr("fill","white").attr("stroke",a.axisColor).attr("stroke-width",1.5).attr("rx",4).attr("ry",4)}}}if(u.notes.length>0){let O=g.append("g").attr("class","wardley-notes");u.notes.forEach(B=>{let Y=C(B.x),U=S(B.y);O.append("text").attr("x",Y).attr("y",U).attr("text-anchor","start").attr("font-size",11).attr("fill",a.axisTextColor).attr("font-weight","bold").text(B.text)})}if(u.accelerators.length>0){let O=g.append("g").attr("class","wardley-accelerators");u.accelerators.forEach(B=>{let Y=C(B.x),U=S(B.y),H=60,J=30,te=20,ie=` + M ${Y} ${U-J/2} + L ${Y+H-te} ${U-J/2} + L ${Y+H-te} ${U-J/2-8} + L ${Y+H} ${U} + L ${Y+H-te} ${U+J/2+8} + L ${Y+H-te} ${U+J/2} + L ${Y} ${U+J/2} + Z + `;O.append("path").attr("d",ie).attr("fill","white").attr("stroke",a.componentStroke).attr("stroke-width",1),O.append("text").attr("x",Y+H/2).attr("y",U+J/2+15).attr("text-anchor","middle").attr("font-size",10).attr("fill",a.axisTextColor).attr("font-weight","bold").text(B.name)})}if(u.deaccelerators.length>0){let O=g.append("g").attr("class","wardley-deaccelerators");u.deaccelerators.forEach(B=>{let Y=C(B.x),U=S(B.y),H=60,J=30,te=20,ie=` + M ${Y+H} ${U-J/2} + L ${Y+te} ${U-J/2} + L ${Y+te} ${U-J/2-8} + L ${Y} ${U} + L ${Y+te} ${U+J/2+8} + L ${Y+te} ${U+J/2} + L ${Y+H} ${U+J/2} + Z + `;O.append("path").attr("d",ie).attr("fill","white").attr("stroke",a.componentStroke).attr("stroke-width",1),O.append("text").attr("x",Y+H/2).attr("y",U+J/2+15).attr("text-anchor","middle").attr("font-size",10).attr("fill",a.axisTextColor).attr("font-weight","bold").text(B.name)})}},"draw"),Jbt={draw:Zbt}}),ext,LLt=G(()=>{"use strict";Ar(),Wh(),Gr(),ext=s(({wardley:e}={})=>{let t=ku(),r=Rr(),i=Gn(t,r.themeVariables),n=Gn(i.wardley,e);return` + .wardley-background { + fill: ${n.backgroundColor}; + } + .wardley-axes line, .wardley-axes path { + stroke: ${n.axisColor}; + } + .wardley-axis-label { + fill: ${n.axisTextColor}; + } + .wardley-stage-label { + fill: ${n.axisTextColor}; + } + .wardley-grid line { + stroke: ${n.gridColor}; + } + .wardley-node circle { + fill: ${n.componentFill}; + stroke: ${n.componentStroke}; + } + .wardley-node-label { + fill: ${n.componentLabelColor}; + } + .wardley-link { + stroke: ${n.linkStroke}; + } + .wardley-link--dashed { + stroke-dasharray: 4 4; + } + .wardley-link-label { + fill: ${n.axisTextColor}; + } + .wardley-trend line { + stroke: ${n.evolutionStroke}; + } + .wardley-annotation-line { + stroke: ${n.annotationStroke}; + } + .wardley-annotation circle { + fill: ${n.annotationFill}; + stroke: ${n.annotationStroke}; + } + .wardley-annotation text { + fill: ${n.annotationTextColor}; + } + .wardley-annotations-box rect { + fill: ${n.annotationFill}; + stroke: ${n.annotationStroke}; + } + .wardley-annotations-box text { + fill: ${n.annotationTextColor}; + } + .wardley-pipeline-box { + stroke: ${n.componentStroke}; + } + .wardley-notes text { + fill: ${n.axisTextColor}; + } + `},"styles")}),txt={};Mr(txt,{diagram:()=>rxt});var rxt,ILt=G(()=>{"use strict";_Lt(),RLt(),$Lt(),LLt(),rxt={parser:Dfe,db:Gbt,renderer:Jbt,styles:ext}}),Nfe,ox,ixt,nxt,axt,sxt,oxt,lxt,J5,cxt=G(()=>{"use strict";Gr(),Za(),Nt(),Ar(),fn(),Nfe=s(()=>({domains:new Map,transitions:[]}),"createDefaultData"),ox=Nfe(),ixt=s(()=>ox.domains,"getDomains"),nxt=s(()=>ox.transitions,"getTransitions"),axt=s(e=>{if(e)for(let t of e){let r=t.domain,i=(t.items??[]).map(n=>({label:n.label}));ox.domains.set(r,{name:r,items:i})}},"setDomains"),sxt=s(e=>{e&&(ox.transitions=e.filter(t=>t.from===t.to?(Ee.warn(`Cynefin: self-loop transition on domain "${t.from}" is not meaningful and will be skipped.`),!1):!0).map(t=>({from:t.from,to:t.to,label:t.label||void 0})))},"setTransitions"),oxt=s(()=>Gn({...vi.cynefin,...Rr().cynefin}),"getConfig"),lxt=s(()=>{pn(),ox=Nfe()},"clear"),J5={getDomains:ixt,getTransitions:nxt,setDomains:axt,setTransitions:sxt,getConfig:oxt,clear:lxt,setAccTitle:bn,getAccTitle:Ln,setDiagramTitle:Dn,getDiagramTitle:xn,getAccDescription:Mn,setAccDescription:In}}),uxt,hxt,MLt=G(()=>{"use strict";Co(),Nt(),vl(),cxt(),uxt=s(e=>{Yo(e,J5),J5.setDomains(e.domains),J5.setTransitions(e.transitions)},"populate"),hxt={parse:s(async e=>{let t=await Pl("cynefin",e);Ee.debug(t),uxt(t)},"parse")}});function eE(e){let t=e+1831565813|0;return t=Math.imul(t^t>>>15,t|1),t^=t+Math.imul(t^t>>>7,t|61),((t^t>>>14)>>>0)/4294967296}function dxt(e){let t=0;for(let r=0;r{"use strict";s(eE,"seededRandom"),s(dxt,"hashString"),s(pxt,"resolveSeed"),s(fxt,"generateFoldPath"),s(gxt,"generateHorizontalBoundary"),s(mxt,"generateCliffPath"),s(yxt,"generateConfusionPath")}),Ofe,vxt,bxt,OM,xxt,Txt,NLt=G(()=>{"use strict";wo(),vn(),Nt(),Gr(),Wh(),Ar(),DLt(),Ofe={complex:{model:"Probe → Sense → Respond",practice:"Emergent Practices"},complicated:{model:"Sense → Analyse → Respond",practice:"Good Practices"},clear:{model:"Sense → Categorise → Respond",practice:"Best Practices"},chaotic:{model:"Act → Sense → Respond",practice:"Novel Practices"},confusion:{model:"",practice:"Disorder"}},vxt=s((e,t)=>{let r=e/2,i=t/2;return{complex:{cx:r/2,cy:i/2,x:0,y:0,w:r,h:i},complicated:{cx:r+r/2,cy:i/2,x:r,y:0,w:r,h:i},chaotic:{cx:r/2,cy:i+i/2,x:0,y:i,w:r,h:i},clear:{cx:r+r/2,cy:i+i/2,x:r,y:i,w:r,h:i},confusion:{cx:r,cy:i,x:r*.7,y:i*.7,w:r*.6,h:i*.6}}},"getDomainLayouts"),bxt=s(()=>{let e=ku(),t=Rr();return Gn(e,t.themeVariables).cynefin},"getCynefinDomainColors"),OM=3,xxt=s((e,t,r,i)=>{let n=i.db,a=n.getDomains(),o=n.getTransitions(),l=n.getDiagramTitle(),u=n.getAccTitle(),h=n.getAccDescription(),d=n.getConfig(),p=bxt();Ee.debug("Rendering Cynefin diagram");let f=d.width,g=d.height,m=d.padding,y=d.showDomainDescriptions,k=d.boundaryAmplitude,C=f+m*2,S=g+m*2,E={complex:p.complexBg,complicated:p.complicatedBg,clear:p.clearBg,chaotic:p.chaoticBg,confusion:p.confusionBg},_=Ks(t);aa(_,S,C,d.useMaxWidth??!0),_.attr("viewBox",`0 0 ${C} ${S}`),u&&_.append("title").text(u),h&&_.append("desc").text(h);let b=_.append("g").attr("transform",`translate(${m}, ${m})`),v=vxt(f,g),x=pxt(d.seed,t),w=b.append("g").attr("class","cynefin-backgrounds"),R=["complex","complicated","chaotic","clear"];for(let B of R){let Y=v[B];w.append("rect").attr("class","cynefinDomain").attr("x",Y.x).attr("y",Y.y).attr("width",Y.w).attr("height",Y.h).attr("fill",E[B]).attr("fill-opacity",.4).attr("stroke","none")}let $=b.append("g").attr("class","cynefin-boundaries");$.append("path").attr("class","cynefinBoundary").attr("d",fxt(f,g,x,k)).attr("fill","none"),$.append("path").attr("class","cynefinBoundary").attr("d",gxt(f,g,x+100,k)).attr("fill","none"),$.append("path").attr("class","cynefinCliff").attr("d",mxt(f,g)).attr("fill","none");let M=f*.15,N=g*.15;b.append("path").attr("class","cynefinConfusion").attr("d",yxt(f/2,g/2,M,N)).attr("fill",E.confusion).attr("fill-opacity",.5);let j=b.append("g").attr("class","cynefin-labels");for(let B of R){let Y=v[B];j.append("text").attr("class","cynefinDomainLabel").attr("x",Y.cx).attr("y",y?Y.cy-30:Y.cy).attr("text-anchor","middle").attr("dominant-baseline","middle").text(B.charAt(0).toUpperCase()+B.slice(1))}if(j.append("text").attr("class","cynefinDomainLabel").attr("x",f/2).attr("y",y?g/2-10:g/2).attr("text-anchor","middle").attr("dominant-baseline","middle").text("Confusion"),y){let B=b.append("g").attr("class","cynefin-subtitles");for(let Y of R){let U=v[Y],H=Ofe[Y];B.append("text").attr("class","cynefinSubtitle").attr("x",U.cx).attr("y",U.cy-10).attr("text-anchor","middle").attr("dominant-baseline","middle").text(H.model),B.append("text").attr("class","cynefinSubtitle").attr("x",U.cx).attr("y",U.cy+5).attr("text-anchor","middle").attr("dominant-baseline","middle").text(H.practice)}B.append("text").attr("class","cynefinSubtitle").attr("x",f/2).attr("y",g/2+8).attr("text-anchor","middle").attr("dominant-baseline","middle").text(Ofe.confusion.practice)}let I=b.append("g").attr("class","cynefin-items"),z=26,F=10,O=["complex","complicated","chaotic","clear","confusion"];for(let B of O){let Y=a.get(B);if(!Y||Y.items.length===0)continue;let U=v[B],H=B==="confusion",J=Y.items,te=0;H&&Y.items.length>OM&&(te=Y.items.length-OM,J=Y.items.slice(0,OM));let ie;if(H){let ue=y?22:14;ie=U.cy+ue}else ie=U.cy+(y?25:15);if([...J].forEach((ue,se)=>{let le=ie+se*(z+4),Te=I.append("g"),oe=Te.append("text").attr("class","cynefinItemText").attr("x",0).attr("y",z/2).attr("text-anchor","middle").attr("dominant-baseline","central").text(ue.label),fe=ue.label.length*7,ge=oe.node();if(ge&&typeof ge.getBBox=="function"){let xe=ge.getBBox();xe.width>0&&(fe=xe.width)}let ke=fe+F*2,Ne=U.cx-ke/2;Te.attr("transform",`translate(${Ne}, ${le})`),Te.insert("rect","text").attr("class","cynefinItem").attr("x",0).attr("y",0).attr("width",ke).attr("height",z).attr("rx",4).attr("ry",4).attr("fill",E[B]).attr("fill-opacity",.95),oe.attr("x",ke/2).attr("y",z/2)}),te>0){let ue=ie+J.length*(z+4),se=`+${te} more`,le=I.append("g"),Te=le.append("text").attr("class","cynefinItemText").attr("x",0).attr("y",z/2).attr("text-anchor","middle").attr("dominant-baseline","central").text(se),oe=se.length*7,fe=Te.node();if(fe&&typeof fe.getBBox=="function"){let Ne=fe.getBBox();Ne.width>0&&(oe=Ne.width)}let ge=oe+F*2,ke=U.cx-ge/2;le.attr("transform",`translate(${ke}, ${ue})`),le.insert("rect","text").attr("class","cynefinItemOverflow").attr("x",0).attr("y",0).attr("width",ge).attr("height",z).attr("rx",4).attr("ry",4).attr("fill",E[B]).attr("fill-opacity",.6),Te.attr("x",ge/2).attr("y",z/2)}}if(o.length>0){let B=_.select("defs").empty()?_.append("defs"):_.select("defs"),Y=`cynefin-arrow-${t}`;B.append("marker").attr("id",Y).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerWidth",6).attr("markerHeight",6).attr("orient","auto-start-reverse").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","cynefinArrowHead");let U=b.append("g").attr("class","cynefin-arrows");o.forEach(H=>{let J=v[H.from],te=v[H.to];if(!J||!te)return;if(H.from===H.to){Ee.warn(`Cynefin renderer: skipping self-loop on domain "${H.from}"`);return}let ie=J.cx,ue=J.cy,se=te.cx,le=te.cy,Te=(ie+se)/2,oe=(ue+le)/2,fe=se-ie,ge=le-ue,ke=Math.sqrt(fe*fe+ge*ge),Ne=ke*.15,xe=-ge/ke,$e=fe/ke,Me=Te+xe*Ne,He=oe+$e*Ne;U.append("path").attr("class","cynefinArrowLine").attr("d",`M${ie},${ue} Q${Me},${He} ${se},${le}`).attr("fill","none").attr("marker-end",`url(#${Y})`),H.label&&U.append("text").attr("class","cynefinArrowLabel").attr("x",Me).attr("y",He-6).attr("text-anchor","middle").attr("dominant-baseline","auto").text(H.label)})}l&&b.append("text").attr("class","cynefinTitle").attr("x",f/2).attr("y",-m/2).attr("text-anchor","middle").attr("dominant-baseline","middle").text(l)},"draw"),Txt={draw:xxt}}),kxt,Cxt,wxt,OLt=G(()=>{"use strict";Ar(),Wh(),Gr(),kxt=s(()=>{let e=ku(),t=Rr();return Gn(e,t.themeVariables).cynefin},"getCynefinTheme"),Cxt=s(()=>{let e=kxt();return` + .cynefinDomain { + stroke: none; + } + .cynefinDomainLabel { + font-size: ${e.domainFontSize}px; + font-weight: bold; + fill: ${e.labelColor}; + } + .cynefinSubtitle { + font-size: ${e.itemFontSize-1}px; + fill: ${e.textColor}; + font-style: italic; + } + .cynefinItem { + fill-opacity: 0.95; + stroke: ${e.boundaryColor}; + stroke-width: 1; + } + .cynefinItemText { + font-size: ${e.itemFontSize}px; + fill: ${e.textColor}; + } + .cynefinItemOverflow { + fill-opacity: 0.6; + stroke: ${e.boundaryColor}; + stroke-width: 1; + stroke-dasharray: 3 2; + } + .cynefinBoundary { + stroke: ${e.boundaryColor}; + stroke-width: ${e.boundaryWidth}; + stroke-dasharray: 6 3; + } + .cynefinCliff { + stroke: ${e.cliffColor}; + stroke-width: ${e.cliffWidth}; + } + .cynefinConfusion { + stroke: ${e.boundaryColor}; + stroke-width: 1.5; + stroke-dasharray: 4 2; + } + .cynefinArrowLine { + stroke: ${e.arrowColor}; + stroke-width: ${e.arrowWidth}; + fill: none; + } + .cynefinArrowHead { + fill: ${e.arrowColor}; + stroke: none; + } + .cynefinArrowLabel { + font-size: ${e.itemFontSize-1}px; + fill: ${e.textColor}; + } + .cynefinTitle { + font-size: ${e.domainFontSize+2}px; + font-weight: bold; + fill: ${e.labelColor}; + } + `},"styles"),wxt=Cxt}),Ext={};Mr(Ext,{diagram:()=>Sxt});var Sxt,BLt=G(()=>{"use strict";MLt(),cxt(),NLt(),OLt(),Sxt={parser:hxt,db:J5,renderer:Txt,styles:wxt}}),BM,PM,FM,zM,tE,hp,V1,_xt,Bfe,Pfe,Axt,Rxt,$xt,Lxt,Ixt,Mxt,Dxt,Nxt,Oxt,zn,dp=G(()=>{"use strict";Er(),Nt(),fn(),mi(),BM="",PM="",FM="",zM=[],tE=new Map,hp=s(e=>Si(e,it()),"sanitizeText"),V1=s(e=>{switch(e.type){case"terminal":return{...e,value:hp(e.value)};case"nonterminal":return{...e,name:hp(e.name)};case"sequence":return{...e,elements:e.elements.map(V1)};case"choice":return{...e,alternatives:e.alternatives.map(V1)};case"optional":return{...e,element:V1(e.element)};case"repetition":return{...e,element:V1(e.element),separator:e.separator?V1(e.separator):void 0};case"special":return{...e,text:hp(e.text)}}},"sanitizeAstNode"),_xt=s(()=>{BM="",PM="",FM="",zM.length=0,tE.clear(),pn(),Ee.debug("[Railroad] Database cleared")},"clear"),Bfe=s(e=>{BM=hp(e),Ee.debug("[Railroad] Title set:",e)},"setTitle"),Pfe=s(()=>BM,"getTitle"),Axt=s(e=>{let t={...e,name:hp(e.name),definition:V1(e.definition),comment:e.comment?hp(e.comment):void 0};Ee.debug("[Railroad] Adding rule:",t.name),tE.has(t.name)&&Ee.warn(`[Railroad] Rule '${t.name}' is already defined. Overwriting.`),zM.push(t),tE.set(t.name,t)},"addRule"),Rxt=s(()=>zM,"getRules"),$xt=s(e=>tE.get(e),"getRule"),Lxt=s(e=>{PM=hp(e).replace(/^\s+/g,""),Ee.debug("[Railroad] Accessibility title set:",e)},"setAccTitle"),Ixt=s(()=>PM,"getAccTitle"),Mxt=s(e=>{FM=hp(e).replace(/\n\s+/g,` +`),Ee.debug("[Railroad] Accessibility description set:",e)},"setAccDescription"),Dxt=s(()=>FM,"getAccDescription"),Nxt=Bfe,Oxt=Pfe,zn={clear:_xt,setTitle:Bfe,getTitle:Pfe,addRule:Axt,getRules:Rxt,getRule:$xt,setAccTitle:Lxt,getAccTitle:Ixt,setAccDescription:Mxt,getAccDescription:Dxt,setDiagramTitle:Nxt,getDiagramTitle:Oxt}}),Bxt,U1,Pxt,Fxt,zxt,PLt=G(()=>{"use strict";Co(),Nt(),vl(),dp(),Bxt=W9().Railroad.parser.LangiumParser,U1=s(e=>{switch(e.$type){case"RailroadTerminalExpr":return{type:"terminal",value:e.value};case"RailroadNonTerminalExpr":return{type:"nonterminal",name:e.name};case"RailroadSpecialExpr":return{type:"special",text:e.text};case"RailroadSequenceExpr":{let t=e.elements.map(U1);return t.length===1?t[0]:{type:"sequence",elements:t}}case"RailroadChoiceExpr":{let t=e.alternatives.map(U1);return t.length===1?t[0]:{type:"choice",alternatives:t}}case"RailroadOptionalExpr":return{type:"optional",element:U1(e.element)};case"RailroadOneOrMoreExpr":return{type:"repetition",element:U1(e.element),min:1,max:1/0};case"RailroadZeroOrMoreExpr":return{type:"repetition",element:U1(e.element),min:0,max:1/0};default:throw new Error(`Unsupported railroad expression: ${e.$type}`)}},"transformExpression"),Pxt=s(e=>({name:e.name,definition:U1(e.definition)}),"transformRule"),Fxt=s(e=>{Yo(e,zn),e.title&&zn.setTitle(e.title),e.rules.map(t=>zn.addRule(Pxt(t)))},"populateDb"),zxt={parse:s(e=>{zn.clear(),Ee.debug("[Railroad Parser] Starting Langium parse");let t=Bxt.parse(e);if(t.lexerErrors.length>0||t.parserErrors.length>0)throw new Fb(t);let r=t.value;Ee.debug("[Railroad Parser] Parsed rules:",r.rules.length),Fxt(r),Ee.debug("[Railroad Parser] Parse complete")},"parse"),parser:{yy:zn}}}),Ns,FLt=G(()=>{"use strict";Ns={compactMode:!1,padding:10,verticalSeparation:8,horizontalSeparation:10,arcRadius:10,fontSize:14,fontFamily:"monospace",terminalFill:"#FFFFC0",terminalStroke:"#000000",terminalTextColor:"#000000",nonTerminalFill:"#FFFFFF",nonTerminalStroke:"#000000",nonTerminalTextColor:"#000000",lineColor:"#000000",strokeWidth:2,markerFill:"#000000",commentFill:"#E8E8E8",commentStroke:"#888888",commentTextColor:"#666666",specialFill:"#F0E0FF",specialStroke:"#8800CC",ruleNameColor:"#000066",showMarkers:!0,markerRadius:5}}),qxt,Wxt,jxt,Ffe,Vxt,Uxt,un,zfe,D0,Yxt,Hxt,qM,rE,iE=G(()=>{"use strict";Gr(),Wh(),FLt(),qxt=/^#(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$|^(?:rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch)\([\d\s%+,./-]+\)$|^[a-z]+$/i,Wxt=/^[\w "',.-]+$/,jxt=new Set(["compactMode","padding","verticalSeparation","horizontalSeparation","arcRadius","fontSize","fontFamily","terminalFill","terminalStroke","terminalTextColor","nonTerminalFill","nonTerminalStroke","nonTerminalTextColor","lineColor","strokeWidth","markerFill","commentFill","commentStroke","commentTextColor","specialFill","specialStroke","ruleNameColor","showMarkers","markerRadius"]),Ffe=s(e=>e?Object.keys(e).every(t=>t==="railroad"||jxt.has(t)):!1,"isRailroadStyleOptions"),Vxt=s(e=>e?"railroad"in e&&e.railroad?e.railroad:Ffe(e)?e:{}:{},"extractRailroadOverrides"),Uxt=s(e=>{if(!e||Ffe(e))return{};let{railroad:t,svgId:r,theme:i,look:n,...a}=e;return a},"extractThemeOverrides"),un=s((e,t)=>{if(typeof e!="string")return t;let r=e.trim();return qxt.test(r)?r:t},"sanitizeColorValue"),zfe=s((e,t)=>{if(typeof e!="string")return t;let r=e.trim();return Wxt.test(r)?r:t},"sanitizeFontFamilyValue"),D0=s((e,t)=>{let r=typeof e=="number"?e:typeof e=="string"?Number.parseFloat(e):Number.NaN;return Number.isFinite(r)&&r>=0?r:t},"sanitizeNumberValue"),Yxt=s(e=>{let t=typeof e=="number"?e:typeof e=="string"?Number.parseFloat(e):Number.NaN;return Number.isFinite(t)&&t>0?t:void 0},"parseThemeFontSize"),Hxt=s(e=>{let t=zfe(e.fontFamily,Ns.fontFamily),r=Yxt(e.fontSize)??Ns.fontSize;return{...Ns,fontFamily:t,fontSize:r,terminalFill:un(e.secondBkg??e.secondaryColor,Ns.terminalFill),terminalStroke:un(e.secondaryBorderColor??e.lineColor,Ns.terminalStroke),terminalTextColor:un(e.secondaryTextColor??e.textColor,Ns.terminalTextColor),nonTerminalFill:un(e.mainBkg??e.background,Ns.nonTerminalFill),nonTerminalStroke:un(e.primaryBorderColor??e.lineColor,Ns.nonTerminalStroke),nonTerminalTextColor:un(e.primaryTextColor??e.textColor,Ns.nonTerminalTextColor),lineColor:un(e.lineColor,Ns.lineColor),markerFill:un(e.lineColor,Ns.markerFill),commentFill:un(e.labelBackground??e.tertiaryColor,Ns.commentFill),commentStroke:un(e.tertiaryBorderColor??e.lineColor,Ns.commentStroke),commentTextColor:un(e.tertiaryTextColor??e.textColor,Ns.commentTextColor),specialFill:un(e.tertiaryColor??e.secondaryColor,Ns.specialFill),specialStroke:un(e.tertiaryBorderColor??e.secondaryBorderColor,Ns.specialStroke),ruleNameColor:un(e.titleColor??e.textColor,Ns.ruleNameColor)}},"buildThemeDefaults"),qM=s(e=>{let t=Rr(),r={...ku(),...t.themeVariables??{},...Uxt(e)},i=Hxt(r),n={...t.railroad??{},...Vxt(e)};return{compactMode:n.compactMode??i.compactMode,padding:D0(n.padding,i.padding),verticalSeparation:D0(n.verticalSeparation,i.verticalSeparation),horizontalSeparation:D0(n.horizontalSeparation,i.horizontalSeparation),arcRadius:D0(n.arcRadius,i.arcRadius),fontSize:D0(n.fontSize,i.fontSize),fontFamily:zfe(n.fontFamily,i.fontFamily),terminalFill:un(n.terminalFill,i.terminalFill),terminalStroke:un(n.terminalStroke,i.terminalStroke),terminalTextColor:un(n.terminalTextColor,i.terminalTextColor),nonTerminalFill:un(n.nonTerminalFill,i.nonTerminalFill),nonTerminalStroke:un(n.nonTerminalStroke,i.nonTerminalStroke),nonTerminalTextColor:un(n.nonTerminalTextColor,i.nonTerminalTextColor),lineColor:un(n.lineColor,i.lineColor),strokeWidth:D0(n.strokeWidth,i.strokeWidth),markerFill:un(n.markerFill,i.markerFill),commentFill:un(n.commentFill,i.commentFill),commentStroke:un(n.commentStroke,i.commentStroke),commentTextColor:un(n.commentTextColor,i.commentTextColor),specialFill:un(n.specialFill,i.specialFill),specialStroke:un(n.specialStroke,i.specialStroke),ruleNameColor:un(n.ruleNameColor,i.ruleNameColor),showMarkers:n.showMarkers??i.showMarkers,markerRadius:D0(n.markerRadius,i.markerRadius)}},"buildRailroadStyleOptions"),rE=s(e=>{let{fontFamily:t,fontSize:r,terminalFill:i,terminalStroke:n,terminalTextColor:a,nonTerminalFill:o,nonTerminalStroke:l,nonTerminalTextColor:u,lineColor:h,strokeWidth:d,markerFill:p,commentFill:f,commentStroke:g,commentTextColor:m,specialFill:y,specialStroke:k,ruleNameColor:C}=qM(e);return` + .railroad-diagram { + font-family: ${t}; + font-size: ${r}px; + } + + .railroad-terminal rect { + fill: ${i}; + stroke: ${n}; + stroke-width: ${d}px; + } + + .railroad-terminal text { + fill: ${a}; + font-family: ${t}; + font-size: ${r}px; + text-anchor: middle; + dominant-baseline: middle; + } + + .railroad-nonterminal rect { + fill: ${o}; + stroke: ${l}; + stroke-width: ${d}px; + } + + .railroad-nonterminal text { + fill: ${u}; + font-family: ${t}; + font-size: ${r}px; + text-anchor: middle; + dominant-baseline: middle; + } + + .railroad-line { + stroke: ${h}; + stroke-width: ${d}px; + fill: none; + } + + .railroad-start circle, + .railroad-end circle { + fill: ${p}; + } + + .railroad-comment ellipse { + fill: ${f}; + stroke: ${g}; + stroke-width: ${d}px; + } + + .railroad-comment text { + fill: ${m}; + font-style: italic; + font-family: ${t}; + font-size: ${r}px; + text-anchor: middle; + dominant-baseline: middle; + } + + .railroad-special rect { + fill: ${y}; + stroke: ${k}; + stroke-width: ${d}px; + stroke-dasharray: 5,3; + } + + .railroad-special text { + fill: ${u}; + font-family: ${t}; + font-size: ${r}px; + text-anchor: middle; + dominant-baseline: middle; + } + + .railroad-rule-name { + font-weight: bold; + fill: ${C}; + font-family: ${t}; + font-size: ${r}px; + } + + .railroad-group { + /* Grouping container, no specific styles */ + } +`},"getStyles")}),jl,Gxt,qfe,Xxt,nE,WM=G(()=>{"use strict";var e,t;Nt(),Gr(),wo(),vn(),dp(),iE(),jl=(e=class{constructor(){this.d=""}moveTo(i,n){return this.d+=`M ${i} ${n} `,this}lineTo(i,n){return this.d+=`L ${i} ${n} `,this}horizontalTo(i){return this.d+=`H ${i} `,this}verticalTo(i){return this.d+=`V ${i} `,this}arcTo(i,n,a,o,l,u,h){return this.d+=`A ${i} ${n} ${a} ${o?1:0} ${l?1:0} ${u} ${h} `,this}build(){return this.d.trim()}},s(e,"PathBuilder"),e),Gxt=(t=class{constructor(i,n=qM()){this.textCache=new Map,this.svg=i,this.config=n}measureText(i){if(this.textCache.has(i))return this.textCache.get(i);let n=this.svg.append("text").attr("font-family",this.config.fontFamily).attr("font-size",this.config.fontSize).text(i),a=n.node().getBBox(),o={width:a.width,height:a.height};return n.remove(),this.textCache.set(i,o),o}renderTerminal(i,n){let a=this.measureText(n),o=a.width+this.config.padding*2,l=a.height+this.config.padding*2,u=i.append("g").attr("class","railroad-terminal");return u.append("rect").attr("x",0).attr("y",0).attr("width",o).attr("height",l).attr("rx",10).attr("ry",10),u.append("text").attr("x",o/2).attr("y",l/2).text(n),{element:u.node(),dimensions:{width:o,height:l,up:l/2,down:l/2}}}renderNonTerminal(i,n){let a=this.measureText(n),o=a.width+this.config.padding*2,l=a.height+this.config.padding*2,u=i.append("g").attr("class","railroad-nonterminal");return u.append("rect").attr("x",0).attr("y",0).attr("width",o).attr("height",l),u.append("text").attr("x",o/2).attr("y",l/2).text(n),{element:u.node(),dimensions:{width:o,height:l,up:l/2,down:l/2}}}renderSequence(i,n){let a=n.map(p=>this.renderExpression(i,p)),o=0,l=0,u=0;for(let p of a)o+=p.dimensions.width,l=Math.max(l,p.dimensions.up),u=Math.max(u,p.dimensions.down);o+=(a.length-1)*this.config.horizontalSeparation;let h=i.append("g").attr("class","railroad-sequence"),d=0;for(let p=0;pthis.renderExpression(i,m)),o=0,l=0;for(let m of a)o=Math.max(o,m.dimensions.width),l+=m.dimensions.height;l+=(a.length-1)*this.config.verticalSeparation;let u=this.config.arcRadius,h=u*4,d=o+h,p=i.append("g").attr("class","railroad-choice"),f=0,g=l/2;for(let m of a){let y=f,k=y+m.dimensions.up,C=u*2+(o-m.dimensions.width)/2;p.node().appendChild(m.element).setAttribute("transform",`translate(${C}, ${y})`);let S=new jl,E=k>g;k===g?S.moveTo(0,g).lineTo(C,k):S.moveTo(0,g).arcTo(u,u,0,!1,E,u,g+(E?u:-u)).lineTo(u,k-(E?u:-u)).arcTo(u,u,0,!1,!E,u*2,k).lineTo(C,k),p.append("path").attr("class","railroad-line").attr("d",S.build());let _=new jl,b=C+m.dimensions.width,v=d-u*2;k===g?_.moveTo(b,k).lineTo(d,g):_.moveTo(b,k).lineTo(v,k).arcTo(u,u,0,!1,!E,d-u,k+(E?-u:u)).lineTo(d-u,g+(E?u:-u)).arcTo(u,u,0,!1,E,d,g),p.append("path").attr("class","railroad-line").attr("d",_.build()),f+=m.dimensions.height+this.config.verticalSeparation}return{element:p.node(),dimensions:{width:d,height:l,up:g,down:l-g}}}renderOptional(i,n){let a=this.renderExpression(i,n),o=this.config.arcRadius,l=o*2,u=a.dimensions.width+o*4,h=a.dimensions.height+l,d=i.append("g").attr("class","railroad-optional"),p=o*2,f=l;d.node().appendChild(a.element).setAttribute("transform",`translate(${p}, ${f})`);let g=f+a.dimensions.up,m=new jl().moveTo(0,g).lineTo(o*2,g);d.append("path").attr("class","railroad-line").attr("d",m.build());let y=new jl().moveTo(p+a.dimensions.width,g).lineTo(u,g);d.append("path").attr("class","railroad-line").attr("d",y.build());let k=new jl().moveTo(0,g).arcTo(o,o,0,!1,!1,o,g-o).lineTo(o,o).arcTo(o,o,0,!1,!0,o*2,0).lineTo(u-o*2,0).arcTo(o,o,0,!1,!0,u-o,o).lineTo(u-o,g-o).arcTo(o,o,0,!1,!1,u,g);return d.append("path").attr("class","railroad-line").attr("d",k.build()),{element:d.node(),dimensions:{width:u,height:h,up:g,down:h-g}}}renderRepetition(i,n,a){let o=this.renderExpression(i,n),l=this.config.arcRadius,u=l*2,h=o.dimensions.width+l*4,d=a===0,p=o.dimensions.height+u+(d?u:0),f=i.append("g").attr("class","railroad-repetition"),g=l*2,m=d?u:0;f.node().appendChild(o.element).setAttribute("transform",`translate(${g}, ${m})`);let y=m+o.dimensions.up;f.append("path").attr("class","railroad-line").attr("d",new jl().moveTo(0,y).lineTo(l*2,y).build()),f.append("path").attr("class","railroad-line").attr("d",new jl().moveTo(g+o.dimensions.width,y).lineTo(h,y).build());let k=m+o.dimensions.height+l,C=new jl().moveTo(g+o.dimensions.width,y).arcTo(l,l,0,!1,!0,g+o.dimensions.width+l,y+l).lineTo(g+o.dimensions.width+l,k).arcTo(l,l,0,!1,!0,g+o.dimensions.width,k+l).lineTo(l*2,k+l).arcTo(l,l,0,!1,!0,l,k).lineTo(l,y+l).arcTo(l,l,0,!1,!0,l*2,y);if(f.append("path").attr("class","railroad-line").attr("d",C.build()),d){let S=new jl().moveTo(0,y).arcTo(l,l,0,!1,!1,l,y-l).lineTo(l,l).arcTo(l,l,0,!1,!0,l*2,0).lineTo(h-l*2,0).arcTo(l,l,0,!1,!0,h-l,l).lineTo(h-l,y-l).arcTo(l,l,0,!1,!1,h,y);f.append("path").attr("class","railroad-line").attr("d",S.build())}return{element:f.node(),dimensions:{width:h,height:p,up:y,down:p-y}}}renderSpecial(i,n){let a=this.measureText("? "+n+" ?"),o=a.width+this.config.padding*2,l=a.height+this.config.padding*2,u=i.append("g").attr("class","railroad-special");return u.append("rect").attr("x",0).attr("y",0).attr("width",o).attr("height",l),u.append("text").attr("x",o/2).attr("y",l/2).text("? "+n+" ?"),{element:u.node(),dimensions:{width:o,height:l,up:l/2,down:l/2}}}renderExpression(i,n){switch(n.type){case"terminal":return this.renderTerminal(i,n.value);case"nonterminal":return this.renderNonTerminal(i,n.name);case"sequence":return this.renderSequence(i,n.elements);case"choice":return this.renderChoice(i,n.alternatives);case"optional":return this.renderOptional(i,n.element);case"repetition":return this.renderRepetition(i,n.element,n.min);case"special":return this.renderSpecial(i,n.text);default:throw new Error(`Unknown node type: ${n.type}`)}}renderRule(i,n){let a=this.svg.append("g").attr("class","railroad-rule").attr("transform",`translate(0, ${n})`),o=i.name+" =",l=this.measureText(o).width+20,u=l+20,h=a.append("g"),d=this.renderExpression(h,i.definition),p=Math.max(20,d.dimensions.up),f=p-d.dimensions.up;return h.attr("transform",`translate(${u}, ${f})`),a.append("g").attr("class","railroad-rule-name-group").append("text").attr("class","railroad-rule-name").attr("x",0).attr("y",p).text(o),a.append("g").attr("class","railroad-start").append("circle").attr("cx",l).attr("cy",p).attr("r",this.config.markerRadius),a.append("g").attr("class","railroad-end").append("circle").attr("cx",u+d.dimensions.width+10).attr("cy",p).attr("r",this.config.markerRadius),a.append("path").attr("class","railroad-line").attr("d",new jl().moveTo(l+this.config.markerRadius,p).lineTo(u,p).build()),a.append("path").attr("class","railroad-line").attr("d",new jl().moveTo(u+d.dimensions.width,p).lineTo(u+d.dimensions.width+10-this.config.markerRadius,p).build()),{height:Math.max(40,f+d.dimensions.height+this.config.padding*2),width:u+d.dimensions.width+10+this.config.markerRadius}}renderDiagram(i){let n=this.config.padding,a=0;for(let o of i){let l=this.renderRule(o,n);n+=l.height+this.config.verticalSeparation,a=Math.max(a,l.width)}return{width:a+this.config.padding*2,height:n+this.config.padding}}},s(t,"RailroadRenderer"),t),qfe=s((r,i,n)=>{aa(r,i.height,i.width,n),r.attr("viewBox",`0 0 ${i.width} ${i.height}`)},"configureRailroadSvgSize"),Xxt=s((r,i,n)=>{Ee.debug(`[Railroad] Rendering diagram +`+r);try{let a=Ks(i);a.attr("class","railroad-diagram");let o=Rr().railroad?.useMaxWidth??!0,l=zn.getRules();if(Ee.debug(`[Railroad] Rendering ${l.length} rules`),l.length===0){Ee.warn("[Railroad] No rules to render"),qfe(a,{height:100,width:200},o);return}let u=new Gxt(a,qM()).renderDiagram(l);qfe(a,u,o),Ee.debug("[Railroad] Render complete")}catch(a){throw Ee.error("[Railroad] Render error:",a),a}},"draw"),nE={draw:Xxt}}),Kxt={};Mr(Kxt,{default:()=>Qxt,diagram:()=>Wfe});var Wfe,Qxt,zLt=G(()=>{"use strict";PLt(),dp(),WM(),iE(),Wfe={parser:zxt,db:zn,renderer:nE,styles:rE},Qxt=Wfe}),Zxt,aE,Jxt,jfe,eTt,tTt,rTt,iTt,nTt,qLt=G(()=>{"use strict";Co(),Nt(),vl(),dp(),Zxt=j9().RailroadEbnf.parser.LangiumParser,aE=s(e=>{let t=e.alternatives.map(Jxt);return t.length===1?t[0]:{type:"choice",alternatives:t}},"transformChoice"),Jxt=s(e=>{let t=e.elements.map(tTt);return t.length===1?t[0]:{type:"sequence",elements:t}},"transformSequence"),jfe=s(e=>{switch(e.$type){case"EbnfTerminal":return{type:"terminal",value:e.value};case"EbnfNonTerminal":return{type:"nonterminal",name:e.name};case"EbnfSpecial":return{type:"special",text:e.text};case"EbnfGroup":return aE(e.element);case"EbnfOptional":return{type:"optional",element:aE(e.element)};case"EbnfRepetition":return{type:"repetition",element:aE(e.element),min:0,max:1/0};default:throw new Error(`Unsupported EBNF primary node: ${e.$type}`)}},"transformPrimary"),eTt=s((e,t)=>{switch(t.$type){case"EbnfOptionalPostfix":return{type:"optional",element:e};case"EbnfZeroOrMorePostfix":return{type:"repetition",element:e,min:0,max:1/0};case"EbnfOneOrMorePostfix":return{type:"repetition",element:e,min:1,max:1/0};case"EbnfExceptionPostfix":return{type:"sequence",elements:[e,{type:"terminal",value:"-"},jfe(t.except)]};default:throw new Error(`Unsupported EBNF postfix node: ${t.$type}`)}},"transformPostfix"),tTt=s(e=>e.postfixes.reduce((t,r)=>eTt(t,r),jfe(e.base)),"transformTerm"),rTt=s(e=>({name:e.name,definition:aE(e.definition)}),"transformRule"),iTt=s(e=>{Yo(e,zn),e.title&&zn.setTitle(e.title),e.rules.map(t=>zn.addRule(rTt(t)))},"populateDb"),nTt={parse:s(e=>{zn.clear(),Ee.debug("[EBNF Parser] Starting Langium parse");let t=Zxt.parse(e);if(t.lexerErrors.length>0||t.parserErrors.length>0)throw new Fb(t);let r=t.value;Ee.debug("[EBNF Parser] Parsed rules:",r.rules.length),iTt(r),Ee.debug("[EBNF Parser] Parse complete")},"parse"),parser:{yy:zn}}}),aTt={};Mr(aTt,{diagram:()=>sTt});var sTt,WLt=G(()=>{"use strict";qLt(),dp(),WM(),iE(),sTt={parser:nTt,db:zn,renderer:nE,styles:rE}}),oTt,jM,lTt,cTt,uTt,hTt,dTt,pTt,fTt,jLt=G(()=>{"use strict";Co(),Nt(),vl(),dp(),oTt=V9().RailroadAbnf.parser.LangiumParser,jM=s(e=>{let t=e.alternatives.map(lTt);return t.length===1?t[0]:{type:"choice",alternatives:t}},"transformAlternation"),lTt=s(e=>{let t=e.elements.map(uTt);return t.length===1?t[0]:{type:"sequence",elements:t}},"transformConcatenation"),cTt=s(e=>{if(e.includes("*")){let[r,i]=e.split("*"),n=r?parseInt(r,10):0,a=i?parseInt(i,10):1/0;return{min:n,max:a}}let t=parseInt(e,10);return{min:t,max:t}},"parseRepeat"),uTt=s(e=>{let t=hTt(e.primary);if(!e.repeat)return t;let{min:r,max:i}=cTt(e.repeat);return r===0&&i===1?{type:"optional",element:t}:{type:"repetition",element:t,min:r,max:i}},"transformElement"),hTt=s(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return jM(e.element);case"AbnfOptionalGroup":return{type:"optional",element:jM(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),dTt=s(e=>({name:e.name,definition:jM(e.definition)}),"transformRule"),pTt=s(e=>{Yo(e,zn),e.title&&zn.setTitle(e.title),e.rules.map(t=>zn.addRule(dTt(t)))},"populateDb"),fTt={parse:s(e=>{zn.clear(),Ee.debug("[ABNF Parser] Starting Langium parse");let t=oTt.parse(e);if(t.lexerErrors.length>0||t.parserErrors.length>0)throw new Fb(t);let r=t.value;Ee.debug("[ABNF Parser] Parsed rules:",r.rules.length),pTt(r),Ee.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:zn}}}),gTt={};Mr(gTt,{diagram:()=>mTt});var mTt,VLt=G(()=>{"use strict";jLt(),dp(),WM(),iE(),mTt={parser:fTt,db:zn,renderer:nE,styles:rE}}),yTt,Vfe,vTt,bTt,Ufe,xTt,TTt,kTt,CTt,wTt,ULt=G(()=>{"use strict";Co(),Nt(),vl(),dp(),yTt=U9().RailroadPeg.parser.LangiumParser,Vfe=s(e=>{let t=e.alternatives.map(vTt);return t.length===1?t[0]:{type:"choice",alternatives:t}},"transformOrderedChoice"),vTt=s(e=>{let t=e.elements.map(bTt);return t.length===1?t[0]:{type:"sequence",elements:t}},"transformSequence"),bTt=s(e=>{let t=xTt(e.suffix);return e.operator?{type:"special",text:e.operator==="&"?`&${Ufe(t)}`:`!${Ufe(t)}`}:t},"transformPrefix"),Ufe=s(e=>{switch(e.type){case"terminal":return`"${e.value}"`;case"nonterminal":return e.name;case"special":return e.text;default:return"(...)"}},"nodeToLabel"),xTt=s(e=>{let t=TTt(e.primary);if(!e.operator)return t;switch(e.operator){case"?":return{type:"optional",element:t};case"*":return{type:"repetition",element:t,min:0,max:1/0};case"+":return{type:"repetition",element:t,min:1,max:1/0};default:throw new Error(`Unsupported PEG suffix operator: ${e.operator}`)}},"transformSuffix"),TTt=s(e=>{switch(e.$type){case"PegLiteral":return{type:"terminal",value:e.value};case"PegIdentifier":return{type:"nonterminal",name:e.name};case"PegGroup":return Vfe(e.element);case"PegAny":return{type:"special",text:e.dot};default:throw new Error(`Unsupported PEG primary node: ${e.$type}`)}},"transformPrimary"),kTt=s(e=>({name:e.name,definition:Vfe(e.definition)}),"transformRule"),CTt=s(e=>{Yo(e,zn),e.title&&zn.setTitle(e.title),e.rules.map(t=>zn.addRule(kTt(t)))},"populateDb"),wTt={parse:s(e=>{zn.clear(),Ee.debug("[PEG Parser] Starting Langium parse");let t=yTt.parse(e);if(t.lexerErrors.length>0||t.parserErrors.length>0)throw new Fb(t);let r=t.value;Ee.debug("[PEG Parser] Parsed rules:",r.rules.length),CTt(r),Ee.debug("[PEG Parser] Parse complete")},"parse"),parser:{yy:zn}}}),ETt={};Mr(ETt,{diagram:()=>STt});var STt,YLt=G(()=>{"use strict";ULt(),dp(),WM(),iE(),STt={parser:wTt,db:zn,renderer:nE,styles:rE}}),_Tt={};Mr(_Tt,{clearLayoutRenderState:()=>_V,createCommonLayoutRenderer:()=>nA,default:()=>yDt,defaultMeasureLayout:()=>AV,paintLayoutData:()=>RV}),Iu(),vye(),yy();var HLt=s(e=>/^\s*C4Context|C4Container|C4Component|C4Dynamic|C4Deployment/.test(e),"detector"),GLt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(zSt(),ySe));return{id:"c4",diagram:e}},"loader"),XLt={id:"c4",detector:HLt,loader:GLt},KLt=XLt,ATt="flowchart",QLt=s((e,t)=>t?.flowchart?.defaultRenderer==="dagre-wrapper"||t?.flowchart?.defaultRenderer==="elk"?!1:/^\s*graph/.test(e),"detector"),ZLt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(N8(),M8));return{id:ATt,diagram:e}},"loader"),JLt={id:ATt,detector:QLt,loader:ZLt},e9t=JLt,RTt="flowchart-v2",t9t=s((e,t)=>t?.flowchart?.defaultRenderer==="dagre-d3"?!1:(t?.flowchart?.defaultRenderer==="elk"&&(t.layout="elk"),/^\s*graph/.test(e)&&t?.flowchart?.defaultRenderer==="dagre-wrapper"?!0:/^\s*flowchart/.test(e)),"detector"),r9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(N8(),M8));return{id:RTt,diagram:e}},"loader"),i9t={id:RTt,detector:t9t,loader:r9t},n9t=i9t,$Tt="swimlane",a9t=s(e=>/^\s*swimlane-beta\b/.test(e),"detector"),s9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(c8t(),oGe));return{id:$Tt,diagram:e}},"loader"),o9t={id:$Tt,detector:a9t,loader:s9t},l9t=o9t,c9t=s(e=>/^\s*erDiagram/.test(e),"detector"),u9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(f8t(),mGe));return{id:"er",diagram:e}},"loader"),h9t={id:"er",detector:c9t,loader:u9t},d9t=h9t,LTt="gitGraph",p9t=s(e=>/^\s*gitGraph/.test(e),"detector"),f9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(O8t(),Cut));return{id:LTt,diagram:e}},"loader"),g9t={id:LTt,detector:p9t,loader:f9t},m9t=g9t,ITt="gantt",y9t=s(e=>/^\s*gantt/.test(e),"detector"),v9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(U8t(),xht));return{id:ITt,diagram:e}},"loader"),b9t={id:ITt,detector:y9t,loader:v9t},x9t=b9t,MTt="info",T9t=s(e=>/^\s*info/.test(e),"detector"),k9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(X8t(),Aht));return{id:MTt,diagram:e}},"loader"),C9t={id:MTt,detector:T9t,loader:k9t},w9t=s(e=>/^\s*pie/.test(e),"detector"),E9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(J8t(),Uht));return{id:"pie",diagram:e}},"loader"),S9t={id:"pie",detector:w9t,loader:E9t},DTt="quadrantChart",_9t=s(e=>/^\s*quadrantChart/.test(e),"detector"),A9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(aRt(),fdt));return{id:DTt,diagram:e}},"loader"),R9t={id:DTt,detector:_9t,loader:A9t},$9t=R9t,NTt="xychart",L9t=s(e=>/^\s*xychart(-beta)?/.test(e),"detector"),I9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(bRt(),Qdt));return{id:NTt,diagram:e}},"loader"),M9t={id:NTt,detector:L9t,loader:I9t},D9t=M9t,OTt="requirement",N9t=s(e=>/^\s*requirement(Diagram)?/.test(e),"detector"),O9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(wRt(),spt));return{id:OTt,diagram:e}},"loader"),B9t={id:OTt,detector:N9t,loader:O9t},P9t=B9t,BTt="sequence",F9t=s(e=>/^\s*sequenceDiagram/.test(e),"detector"),z9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(RRt(),oft));return{id:BTt,diagram:e}},"loader"),q9t={id:BTt,detector:F9t,loader:z9t},W9t=q9t,PTt="class",j9t=s((e,t)=>t?.class?.defaultRenderer==="dagre-wrapper"?!1:/^\s*classDiagram/.test(e),"detector"),V9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(LRt(),yft));return{id:PTt,diagram:e}},"loader"),U9t={id:PTt,detector:j9t,loader:V9t},Y9t=U9t,FTt="classDiagram",H9t=s((e,t)=>/^\s*classDiagram/.test(e)&&t?.class?.defaultRenderer==="dagre-wrapper"?!0:/^\s*classDiagram-v2/.test(e),"detector"),G9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(IRt(),bft));return{id:FTt,diagram:e}},"loader"),X9t={id:FTt,detector:H9t,loader:G9t},K9t=X9t,zTt="state",Q9t=s((e,t)=>t?.state?.defaultRenderer==="dagre-wrapper"?!1:/^\s*stateDiagram/.test(e),"detector"),Z9t=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(ORt(),d0t));return{id:zTt,diagram:e}},"loader"),J9t={id:zTt,detector:Q9t,loader:Z9t},eIt=J9t,qTt="stateDiagram",tIt=s((e,t)=>!!(/^\s*stateDiagram-v2/.test(e)||/^\s*stateDiagram/.test(e)&&t?.state?.defaultRenderer==="dagre-wrapper"),"detector"),rIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(BRt(),f0t));return{id:qTt,diagram:e}},"loader"),iIt={id:qTt,detector:tIt,loader:rIt},nIt=iIt,WTt="journey",aIt=s(e=>/^\s*journey/.test(e),"detector"),sIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(jRt(),B0t));return{id:WTt,diagram:e}},"loader"),oIt={id:WTt,detector:aIt,loader:sIt},lIt=oIt;Nt(),wo(),vn();var cIt=s((e,t,r)=>{Ee.debug(`rendering svg for syntax error +`);let i=Ks(t),n=i.append("g");i.attr("viewBox","0 0 2412 512"),aa(i,100,512,!0),n.append("path").attr("class","error-icon").attr("d","m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z"),n.append("path").attr("class","error-icon").attr("d","m459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z"),n.append("path").attr("class","error-icon").attr("d","m340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z"),n.append("path").attr("class","error-icon").attr("d","m400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z"),n.append("path").attr("class","error-icon").attr("d","m496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z"),n.append("path").attr("class","error-icon").attr("d","m436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z"),n.append("text").attr("class","error-text").attr("x",1440).attr("y",250).attr("font-size","150px").style("text-anchor","middle").text("Syntax error in text"),n.append("text").attr("class","error-text").attr("x",1250).attr("y",400).attr("font-size","100px").style("text-anchor","middle").text(`mermaid version ${r}`)},"draw"),jTt={draw:cIt},uIt=jTt,hIt={db:{},renderer:jTt,parser:{parse:s(()=>{},"parse")}},dIt=hIt,VTt="flowchart-elk",pIt=s((e,t={})=>/^\s*flowchart-elk/.test(e)||/^\s*(flowchart|graph)/.test(e)&&t?.flowchart?.defaultRenderer==="elk"?(t.layout="elk",!0):!1,"detector"),fIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(N8(),M8));return{id:VTt,diagram:e}},"loader"),gIt={id:VTt,detector:pIt,loader:fIt},mIt=gIt,UTt="timeline",yIt=s(e=>/^\s*timeline/.test(e),"detector"),vIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(XRt(),pgt));return{id:UTt,diagram:e}},"loader"),bIt={id:UTt,detector:yIt,loader:vIt},xIt=bIt,YTt="mindmap",TIt=s(e=>/^\s*mindmap/.test(e),"detector"),kIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(a$t(),Lgt));return{id:YTt,diagram:e}},"loader"),CIt={id:YTt,detector:TIt,loader:kIt},wIt=CIt,HTt="kanban",EIt=s(e=>/^\s*kanban/.test(e),"detector"),SIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(u$t(),Qgt));return{id:HTt,diagram:e}},"loader"),_It={id:HTt,detector:EIt,loader:SIt},AIt=_It,GTt="sankey",RIt=s(e=>/^\s*sankey(-beta)?/.test(e),"detector"),$It=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(L$t(),Pmt));return{id:GTt,diagram:e}},"loader"),LIt={id:GTt,detector:RIt,loader:$It},IIt=LIt,XTt="packet",MIt=s(e=>/^\s*packet(-beta)?/.test(e),"detector"),DIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(N$t(),Qmt));return{id:XTt,diagram:e}},"loader"),NIt={id:XTt,detector:MIt,loader:DIt},KTt="radar",OIt=s(e=>/^\s*radar-beta/.test(e),"detector"),BIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(F$t(),C1t));return{id:KTt,diagram:e}},"loader"),PIt={id:KTt,detector:OIt,loader:BIt},QTt="block",FIt=s(e=>/^\s*block(-beta)?/.test(e),"detector"),zIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(Y$t(),uyt));return{id:QTt,diagram:e}},"loader"),qIt={id:QTt,detector:FIt,loader:zIt},WIt=qIt,ZTt="treeView",jIt=s(e=>/^\s*treeView-beta/.test(e),"detector"),VIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(Z$t(),zyt));return{id:ZTt,diagram:e}},"loader"),UIt={id:ZTt,detector:jIt,loader:VIt},YIt=UIt,JTt="architecture",HIt=s(e=>/^\s*architecture/.test(e),"detector"),GIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(aLt(),b2t));return{id:JTt,diagram:e}},"loader"),XIt={id:JTt,detector:HIt,loader:GIt},KIt=XIt,e4t="eventmodeling",QIt=s(e=>/^\s*eventmodeling/.test(e),"detector"),ZIt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(uLt(),ovt));return{id:e4t,diagram:e}},"loader"),JIt={id:e4t,detector:QIt,loader:ZIt},eMt=JIt,t4t="ishikawa",tMt=s(e=>/^\s*ishikawa(-beta)?\b/i.test(e),"detector"),rMt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(gLt(),wvt));return{id:t4t,diagram:e}},"loader"),iMt={id:t4t,detector:tMt,loader:rMt},r4t="venn",nMt=s(e=>/^\s*venn-beta/.test(e),"detector"),aMt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(TLt(),pbt));return{id:r4t,diagram:e}},"loader"),sMt={id:r4t,detector:nMt,loader:aMt},oMt=sMt;yy(),Er();var i4t="treemap",lMt=s(e=>/^\s*treemap/.test(e),"detector"),cMt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(SLt(),Sbt));return{id:i4t,diagram:e}},"loader"),uMt={id:i4t,detector:lMt,loader:cMt},n4t="wardley",hMt=s(e=>/^\s*wardley-beta/i.test(e),"detector"),dMt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(ILt(),txt));return{id:n4t,diagram:e}},"loader"),pMt={id:n4t,detector:hMt,loader:dMt},fMt=pMt,a4t="cynefin",gMt=s(e=>/^\s*cynefin-beta(?:[\s:]|$)/.test(e),"detector"),mMt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(BLt(),Ext));return{id:a4t,diagram:e}},"loader"),yMt={id:a4t,detector:gMt,loader:mMt},s4t="railroad",vMt=s(e=>/^\s*railroad-beta/i.test(e),"detector"),bMt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(zLt(),Kxt));return{id:s4t,diagram:e}},"loader"),xMt={id:s4t,detector:vMt,loader:bMt},o4t="railroadEbnf",TMt=s(e=>/^\s*railroad-ebnf-beta/i.test(e),"detector"),kMt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(WLt(),aTt));return{id:o4t,diagram:e}},"loader"),CMt={id:o4t,detector:TMt,loader:kMt},l4t="railroadAbnf",wMt=s(e=>/^\s*railroad-abnf-beta/i.test(e),"detector"),EMt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(VLt(),gTt));return{id:l4t,diagram:e}},"loader"),SMt={id:l4t,detector:wMt,loader:EMt},c4t="railroadPeg",_Mt=s(e=>/^\s*railroad-peg-beta/i.test(e),"detector"),AMt=s(async()=>{let{diagram:e}=await Promise.resolve().then(()=>(YLt(),ETt));return{id:c4t,diagram:e}},"loader"),RMt={id:c4t,detector:_Mt,loader:AMt},u4t=!1,VM=s(()=>{u4t||(u4t=!0,xT("error",dIt,e=>e.toLowerCase().trim()==="error"),xT("---",{db:{clear:s(()=>{},"clear")},styles:{},renderer:{draw:s(()=>{},"draw")},parser:{parse:s(()=>{throw new Error("Diagrams beginning with --- are not valid. If you were trying to use a YAML front-matter, please ensure that you've correctly opened and closed the YAML front-matter with un-indented `---` blocks")},"parse")},init:s(()=>null,"init")},e=>e.toLowerCase().trimStart().startsWith("---")),AS(mIt,wIt,KIt),AS(KLt,AIt,K9t,Y9t,d9t,x9t,C9t,S9t,P9t,W9t,l9t,n9t,e9t,xIt,m9t,nIt,eIt,lIt,$9t,IIt,NIt,D9t,WIt,eMt,YIt,PIt,iMt,uMt,xMt,CMt,SMt,RMt,oMt,fMt,yMt))},"addDiagrams");Nt(),yy(),Er();var $Mt=s(async()=>{Ee.debug("Loading registered diagrams");let e=(await Promise.allSettled(Object.entries(Cp).map(async([t,{detector:r,loader:i}])=>{if(i)try{OS(t)}catch{try{let{diagram:n,id:a}=await i();xT(a,n,r)}catch(n){throw Ee.error(`Failed to load external diagram with key ${t}. Removing from detectors.`),delete Cp[t],n}}}))).filter(t=>t.status==="rejected");if(e.length>0){Ee.error(`Failed to load ${e.length} external diagrams`);for(let t of e)Ee.error(t);throw new Error(`Failed to load ${e.length} external diagrams`)}},"loadRegisteredDiagrams");Nt(),pi();var Yfe="comm",h4t="rule",d4t="decl",LMt="@media",IMt="@import",MMt="@supports",DMt="@namespace",Hfe="@keyframes",p4t="@layer",NMt="@scope",f4t=Math.abs,Gfe=String.fromCharCode;function Xfe(e){return e.trim()}s(Xfe,"trim");function sE(e,t,r){return e.replace(t,r)}s(sE,"replace");function g4t(e,t,r){return e.indexOf(t,r)}s(g4t,"indexof");function Y1(e,t){return e.charCodeAt(t)|0}s(Y1,"charat");function H1(e,t,r){return e.slice(t,r)}s(H1,"substr");function mu(e){return e.length}s(mu,"strlen");function Kfe(e){return e.length}s(Kfe,"sizeof");function oE(e,t){return t.push(e),e}s(oE,"append");var UM=1,lx=1,m4t=0,Tc=0,Ka=0,cx="";function YM(e,t,r,i,n,a,o,l){return{value:e,root:t,parent:r,type:i,props:n,children:a,line:UM,column:lx,length:o,return:"",siblings:l}}s(YM,"node");function y4t(){return Ka}s(y4t,"char");function v4t(){return Ka=Tc>0?Y1(cx,--Tc):0,lx--,Ka===10&&(lx=1,UM--),Ka}s(v4t,"prev");function kc(){return Ka=Tc2||ux(Ka)>3?"":" "}s(T4t,"whitespace");function k4t(e,t){for(;--t&&kc()&&!(Ka<48||Ka>102||Ka>57&&Ka<65||Ka>70&&Ka<97););return cE(e,lE()+(t<6&&pp()==32&&kc()==32))}s(k4t,"escaping");function GM(e){for(;kc();)switch(Ka){case e:return Tc;case 34:case 39:e!==34&&e!==39&&GM(Ka);break;case 40:e===41&&GM(e);break;case 92:kc();break}return Tc}s(GM,"delimiter");function C4t(e,t){for(;kc()&&e+Ka!==57&&!(e+Ka===84&&pp()===47););return"/*"+cE(t,Tc-1)+"*"+Gfe(e===47?e:kc())}s(C4t,"commenter");function w4t(e){for(;!ux(pp());)kc();return cE(e,Tc)}s(w4t,"identifier");function E4t(e){return x4t(uE("",null,null,null,[""],e=b4t(e),0,[0],e))}s(E4t,"compile");function uE(e,t,r,i,n,a,o,l,u){for(var h=0,d=0,p=o,f=0,g=0,m=0,y=1,k=1,C=1,S=0,E="",_=n,b=a,v=i,x=E;k;)switch(m=S,S=kc()){case 40:if(m!=108&&Y1(x,p-1)==58){g4t(x+=sE(HM(S),"&","&\f"),"&\f",f4t(h?l[h-1]:0))!=-1&&(C=-1);break}case 34:case 39:case 91:x+=HM(S);break;case 9:case 10:case 13:case 32:x+=T4t(m);break;case 92:x+=k4t(lE()-1,7);continue;case 47:switch(pp()){case 42:case 47:oE(S4t(C4t(kc(),lE()),t,r,u),u),(ux(m||1)==5||ux(pp()||1)==5)&&mu(x)&&H1(x,-1,void 0)!==" "&&(x+=" ");break;default:x+="/"}break;case 123*y:l[h++]=mu(x)*C;case 125*y:case 59:case 0:switch(S){case 0:case 125:k=0;case 59+d:C==-1&&(x=sE(x,/\f/g,"")),g>0&&(mu(x)-p||y===0&&m===47)&&oE(g>32?Zfe(x+";",i,r,p-1,u):Zfe(sE(x," ","")+";",i,r,p-2,u),u);break;case 59:x+=";";default:if(oE(v=Qfe(x,t,r,h,d,n,l,E,_=[],b=[],p,a),a),S===123)if(d===0)uE(x,t,v,v,_,a,p,l,b);else{switch(f){case 99:if(Y1(x,3)===110)break;case 108:if(Y1(x,2)===97)break;default:d=0;case 100:case 109:case 115:}d?uE(e,v,v,i&&oE(Qfe(e,v,v,0,0,n,l,E,n,_=[],p,b),b),n,b,p,l,i?_:b):uE(x,v,v,v,[""],b,0,l,b)}}h=d=g=0,y=C=1,E=x="",p=o;break;case 58:p=1+mu(x),g=m;default:if(y<1){if(S==123)--y;else if(S==125&&y++==0&&v4t()==125)continue}switch(x+=Gfe(S),S*y){case 38:C=d>0?1:(x+="\f",-1);break;case 44:l[h++]=(mu(x)-1)*C,C=1;break;case 64:pp()===45&&(x+=HM(kc())),f=pp(),d=p=mu(E=x+=w4t(lE())),S++;break;case 45:m===45&&mu(x)==2&&(y=0)}}return a}s(uE,"parse");function Qfe(e,t,r,i,n,a,o,l,u,h,d,p){for(var f=n-1,g=n===0?a:[""],m=Kfe(g),y=0,k=0,C=0;y0?g[S]+" "+E:sE(E,/&\f/g,g[S])))&&(u[C++]=_);return YM(e,t,r,n===0?h4t:l,u,h,d,p)}s(Qfe,"ruleset");function S4t(e,t,r,i){return YM(e,t,r,Yfe,Gfe(y4t()),H1(e,2,-2),0,i)}s(S4t,"comment");function Zfe(e,t,r,i,n){return YM(e,t,r,d4t,H1(e,0,i),H1(e,i+1,-1),i,n)}s(Zfe,"declaration");function XM(e,t){for(var r="",i=0;i{L4t.forEach(e=>{e()}),L4t=[]},"attachFunctions");Nt(),g4();var PMt=s(e=>e.replace(/^\s*%%(?!{)[^\n]+\n?/gm,"").trimStart(),"cleanupComments");NO(),X_();function I4t(e){let t=e.match(DO);if(!t)return{text:e,metadata:{}};let r=t[1],i=r?t[2].split(` +`).map(o=>o.startsWith(r)?o.slice(r.length):o).join(` +`):t[2],n=B4(i,{schema:O4})??{};n=typeof n=="object"&&!Array.isArray(n)?n:{};let a={};return n.displayMode&&(a.displayMode=n.displayMode.toString()),n.title&&(a.title=n.title.toString()),n.config&&(a.config=n.config),{text:e.slice(t[0].length),metadata:a}}s(I4t,"extractFrontMatter"),Ar();var FMt=s(e=>e.replace(/\r\n?/g,` +`).replace(/<(\w+)([^>]*)>/g,(t,r,i)=>"<"+r+i.replace(/="([^"]*)"/g,"='$1'")+">"),"cleanupText"),zMt=s(e=>{let{text:t,metadata:r}=I4t(e),{displayMode:i,title:n,config:a={}}=r;return i&&(a.gantt||(a.gantt={}),a.gantt.displayMode=i),{title:n,config:a,text:t}},"processFrontmatter"),qMt=s(e=>{let t=ai.detectInit(e)??{},r=ai.detectDirective(e,"wrap");return Array.isArray(r)?t.wrap=r.some(({type:i})=>i==="wrap"):r?.type==="wrap"&&(t.wrap=!0),{text:yCe(e),directive:t}},"processDirectives");function e0e(e){let t=FMt(e),r=zMt(t),i=qMt(r.text),n=Gn(r.config,i.directive);return e=PMt(i.text),{code:e,title:r.title,config:n}}s(e0e,"preprocessDiagram"),Rye(),uD(),Ar();function M4t(e){let t=new TextEncoder().encode(e),r=Array.from(t,i=>String.fromCodePoint(i)).join("");return btoa(r)}s(M4t,"toBase64"),fD();var WMt=5e4,jMt="graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa",VMt="sandbox",UMt="loose",YMt="http://www.w3.org/2000/svg",HMt="http://www.w3.org/1999/xlink",GMt="http://www.w3.org/1999/xhtml",XMt="100%",KMt="100%",QMt="border:0;margin:0;",ZMt="margin:0",JMt="allow-top-navigation-by-user-activation allow-popups",eDt='The "iframe" tag is not supported by your browser.',tDt=["foreignobject"],rDt=["dominant-baseline"];function t0e(e){let t=e0e(e);return Px(),Lge(t.config??{}),t}s(t0e,"processAndSetConfigs");async function D4t(e,t){VM();try{let{code:r,config:i}=t0e(e);return{diagramType:(await P4t(r)).type,config:i}}catch(r){if(t?.suppressErrors)return!1;throw r}}s(D4t,"parse");var N4t=s((e,t,r=[])=>{let i=pD(`{ ${r.join(" !important; ")} !important; }`);return`.${e} ${t} ${i}`},"cssImportantStyles"),iDt=s((e,t=new Map)=>{let r=new CSSStyleSheet;if(e.fontFamily!==void 0&&r.insertRule(`:root { --mermaid-font-family: ${e.fontFamily}}`,r.cssRules.length),e.altFontFamily!==void 0&&r.insertRule(`:root { --mermaid-alt-font-family: ${e.altFontFamily}}`,r.cssRules.length),t instanceof Map){let n=Ps(e)?["> *","span"]:["rect","polygon","ellipse","circle","path"];t.forEach(a=>{DF(a.styles)||n.forEach(o=>{r.insertRule(N4t(a.id,o,a.styles),r.cssRules.length)}),DF(a.textStyles)||r.insertRule(N4t(a.id,"tspan",(a?.textStyles||[]).map(o=>o.replace("color","fill"))),r.cssRules.length)})}let i="";if(e.themeCSS!==void 0)if(typeof r.replaceSync=="function"){let n=new CSSStyleSheet;n.replaceSync(e.themeCSS),i=PO(n)+` +`}else i+=`${e.themeCSS} +`;return i+PO(r)},"createCssStyles"),nDt=s((e,t)=>XM(E4t(`${e}{${t}}`),A4t([s(function(r,i,n,a){if(r.type==="rule"&&Array.isArray(r.props)){if(r.parent&&r.parent.type===Hfe)return;r.props=r.props.map(o=>o===e&&Array.isArray(r.children)&&r.children.every(l=>l.type!=="decl"?!1:new Set(["font-family","font-size","fill"]).has(l.props))||(o.startsWith(`${e} `)||o.startsWith(`${e}>`))&&!o.startsWith(`${e} ||`)?o:`${e} ${o}`)}else r.type.startsWith("@")&&([LMt,MMt,p4t,NMt,"@container","@starting-style",Hfe].includes(r.type)||(Ee.warn(`Removing unsupported at-rule ${r.type} from CSS`),r.type=Yfe))},"addNamespace"),_4t])),"compileCSS"),aDt=s((e,t,r,i)=>{let n=iDt(e,r),a=Aye(t,n,{...e.themeVariables,theme:e.theme,look:e.look},i);return nDt(i,a)},"createUserStyles"),sDt=s((e="",t,r)=>{let i=e;return!r&&!t&&(i=i.replace(/marker-end="url\([\d+./:=?A-Za-z-]*?#/g,'marker-end="url(#')),i=ad(i),i=i.replace(/
    /g,"
    "),i},"cleanUpSvgCode"),oDt=s((e="",t)=>{let r=t?.viewBox?.baseVal?.height?t.viewBox.baseVal.height+"px":KMt,i=M4t(`${e}`);return``},"putIntoIFrame"),O4t=s((e,t,r,i,n)=>{let a=e.append("div");a.attr("id",r),i&&a.attr("style",i);let o=a.append("svg").attr("id",t).attr("width","100%").attr("xmlns",YMt);return n&&o.attr("xmlns:xlink",n),o.append("g"),e},"appendDivSvgG");function r0e(e,t){return e.append("iframe").attr("id",t).attr("style","width: 100%; height: 100%;").attr("sandbox","")}s(r0e,"sandboxedIframe");var lDt=s((e,t,r,i)=>{e.getElementById(t)?.remove(),e.getElementById(r)?.remove(),e.getElementById(i)?.remove()},"removeExistingElements"),cDt=s(async function(e,t,r){VM();let i=t0e(t);t=i.code;let n=Rr();Ee.debug(n),t.length>(n?.maxTextSize??WMt)&&(t=jMt);let a=`#${e}`,o="i"+e,l="#"+o,u="d"+e,h="#"+u,d=s(()=>{let N=zt(f?l:h).node();N&&"remove"in N&&N.remove()},"removeTempElements"),p=zt(document.body),f=n.securityLevel===VMt,g=n.securityLevel===UMt,m=n.fontFamily;if(r!==void 0){if(r&&(r.innerHTML=""),f){let N=r0e(zt(r),o);p=zt(N.nodes()[0].contentDocument.body),p.node().style.margin="0"}else p=zt(r);O4t(p,e,u,`font-family: ${m}`,HMt)}else{if(lDt(document,e,u,o),f){let N=r0e(zt(document.body),o);p=zt(N.nodes()[0].contentDocument.body),p.node().style.margin="0"}else p=zt("body");O4t(p,e,u)}let y,k;try{y=await Jfe.fromText(t,{title:i.title})}catch(N){if(n.suppressErrorRendering)throw d(),N;y=await Jfe.fromText("error"),k=N}let C=p.select(h).node(),S=y.type,E=C.firstChild,_=E.firstChild,b=y.renderer.getClasses?.(t,y),v=aDt(n,S,b,a),x=document.createElement("style");x.innerHTML=v,E.insertBefore(x,_);try{await y.renderer.draw(t,e,"11.17.2",y)}catch(N){throw n.suppressErrorRendering?d():uIt.draw(t,e,"11.17.2"),N}let w=p.select(`${h} svg`),R=y.db.getAccTitle?.(),$=y.db.getAccDescription?.();F4t(S,w,R,$);let M=s(()=>{p.select(`[id="${e}"]`).selectAll("foreignobject > *").attr("xmlns",GMt);let N=p.select(h).node().innerHTML;if(Ee.debug("config.arrowMarkerAbsolute",n.arrowMarkerAbsolute),N=sDt(N,f,_c(n.arrowMarkerAbsolute)),f){let j=p.select(h+" svg").node();N=oDt(N,j)}else g||(N=wu.sanitize(N,{ADD_TAGS:tDt,ADD_ATTR:rDt,HTML_INTEGRATION_POINTS:{foreignobject:!0}}));return BMt(),N},"serializeSvg")();if(k)throw k;return d(),{diagramType:S,svg:M,bindFunctions:y.db.bindFunctions}},"render");function B4t(e={}){let t=Sa({},e);t?.fontFamily&&!t.themeVariables?.fontFamily&&(t.themeVariables||(t.themeVariables={}),t.themeVariables.fontFamily=t.fontFamily),Rge(t),t?.theme&&t.theme in Cu?t.themeVariables=Cu[t.theme].getThemeVariables(t.themeVariables):t&&(t.themeVariables=Cu.default.getThemeVariables(t.themeVariables));let r=typeof t=="object"?Age(t):gD();pE(r.logLevel),VM()}s(B4t,"initialize");var P4t=s((e,t={})=>{let{code:r}=e0e(e);return Jfe.fromText(r,t)},"getDiagramFromText");function F4t(e,t,r,i){R4t(t,e),$4t(t,r,i,t.attr("id"))}s(F4t,"addA11yInfo");var G1=Object.freeze({render:cDt,parse:D4t,getDiagramFromText:P4t,initialize:B4t,getConfig:Rr,setConfig:mD,getSiteConfig:gD,updateSiteConfig:$ge,reset:s(()=>{Px()},"reset"),globalReset:s(()=>{Px(B0)},"globalReset"),defaultConfig:B0});pE(Rr().logLevel),Px(Rr()),xm(),Ar(),LV();var uDt=s((e,t,r)=>{Ee.warn(e),FF(e)?(r&&r(e.str,e.hash),t.push({...e,message:e.str,error:e})):(r&&r(e),e instanceof Error&&t.push({str:e.message,message:e.message,hash:e.name,error:e}))},"handleError"),z4t=s(async function(e={querySelector:".mermaid"}){try{await hDt(e)}catch(t){if(FF(t)&&Ee.error(t.str),fp.parseError&&fp.parseError(t),!e.suppressErrors)throw Ee.error("Use the suppressErrors option to suppress these errors"),t}},"run"),hDt=s(async function({postRenderCallback:e,querySelector:t,nodes:r}={querySelector:".mermaid"}){let i=G1.getConfig();Ee.debug(`${e?"":"No "}Callback function found`);let n;if(r)n=r;else if(t)n=document.querySelectorAll(t);else throw new Error("Nodes and querySelector are both undefined");Ee.debug(`Found ${n.length} diagrams`),i?.startOnLoad!==void 0&&(Ee.debug("Start On Load: "+i?.startOnLoad),G1.updateSiteConfig({startOnLoad:i?.startOnLoad}));let a=new ai.InitIDGenerator(i.deterministicIds,i.deterministicIDSeed),o,l=[];for(let u of Array.from(n)){if(Ee.info("Rendering diagram: "+u.id),u.getAttribute("data-processed"))continue;u.setAttribute("data-processed","true");let h=`mermaid-${a.next()}`;o=u.innerHTML,o=MO(ai.entityDecode(o)).trim().replace(//gi,"
    ");let d=ai.detectInit(o);d&&Ee.debug("Detected early reinit: ",d);try{let{svg:p,bindFunctions:f}=await V4t(h,o,u);u.innerHTML=p,e&&await e(h),f&&f(u)}catch(p){uDt(p,l,fp.parseError)}}if(l.length>0)throw l[0]},"runThrowsErrors"),q4t=s(function(e){G1.initialize(e)},"initialize"),dDt=s(async function(e,t,r){Ee.warn("mermaid.init is deprecated. Please use run instead."),e&&q4t(e);let i={postRenderCallback:r,querySelector:".mermaid"};typeof t=="string"?i.querySelector=t:t&&(t instanceof HTMLElement?i.nodes=[t]:i.nodes=t),await z4t(i)},"init"),pDt=s(async(e,{lazyLoad:t=!0}={})=>{VM(),AS(...e),t===!1&&await $Mt()},"registerExternalDiagrams"),W4t=s(function(){if(fp.startOnLoad){let{startOnLoad:e}=G1.getConfig();e&&fp.run().catch(t=>Ee.error("Mermaid failed to initialize",t))}},"contentLoaded");typeof document<"u"&&window.addEventListener("load",W4t,!1);var fDt=s(function(e){fp.parseError=e},"setParseErrorHandler"),KM=[],i0e=!1,j4t=s(async()=>{if(!i0e){for(i0e=!0;KM.length>0;){let e=KM.shift();if(e)try{await e()}catch(t){Ee.error("Error executing queue",t)}}i0e=!1}},"executeQueue"),gDt=s(async(e,t)=>new Promise((r,i)=>{let n=s(()=>new Promise((a,o)=>{G1.parse(e,t).then(l=>{a(l),r(l)},l=>{Ee.error("Error parsing",l),fp.parseError?.(l),o(l),i(l)})}),"performCall");KM.push(n),j4t().catch(i)}),"parse"),V4t=s((e,t,r)=>new Promise((i,n)=>{let a=s(()=>new Promise((o,l)=>{G1.render(e,t,r).then(u=>{o(u),i(u)},u=>{Ee.error("Error parsing",u),fp.parseError?.(u),l(u),n(u)})}),"performCall");KM.push(a),j4t().catch(n)}),"render"),mDt=s(()=>Object.keys(Cp).map(e=>({id:e})),"getRegisteredDiagramsMetadata"),fp={startOnLoad:!0,mermaidAPI:G1,parse:gDt,render:V4t,init:dDt,run:z4t,registerExternalDiagrams:pDt,registerLayoutLoaders:oK,initialize:q4t,parseError:void 0,contentLoaded:W4t,setParseErrorHandler:fDt,detectType:_S,registerIconPacks:SS,getRegisteredDiagramsMetadata:mDt},yDt=fp;return G4t(_Tt)})();/*! Bundled license information: + +lodash-es/lodash.js: + (** + * @license + * Lodash (Custom Build) + * Build: `lodash modularize exports="es" -o ./` + * Copyright OpenJS Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + *) +*//*! Check if previously processed *//*! + * Wait for document loaded before starting the execution + *//*! Bundled license information: + +dompurify/dist/purify.es.mjs: + (*! @license DOMPurify 3.4.12 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.12/LICENSE *) + +lodash-es/lodash.js: + (** + * @license + * Lodash (Custom Build) + * Build: `lodash modularize exports="es" --repo lodash/lodash#4.18.1 -o ./` + * Copyright OpenJS Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + *) + +cytoscape/dist/cytoscape.esm.mjs: + (*! + Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable + Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com) + Licensed under The MIT License (http://opensource.org/licenses/MIT) + *) + (*! + Event object based on jQuery events, MIT license + + https://jquery.org/license/ + https://tldrlegal.com/license/mit-license + https://github.com/jquery/jquery/blob/master/src/event.js + *) + (*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License *) + (*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License *) +*/globalThis.mermaid=globalThis.__esbuild_esm_mermaid_nm.mermaid.default; diff --git a/mobile/android/app/src/main/assets/reader/reader-compat.js b/mobile/android/app/src/main/assets/reader/reader-compat.js new file mode 100644 index 0000000..e39acd0 --- /dev/null +++ b/mobile/android/app/src/main/assets/reader/reader-compat.js @@ -0,0 +1 @@ +(()=>{var iv=Object.create;var La=Object.defineProperty;var nv=Object.getOwnPropertyDescriptor;var ov=Object.getOwnPropertyNames;var uv=Object.getPrototypeOf,sv=Object.prototype.hasOwnProperty;var i=(r,e)=>()=>{try{return e||r((e={exports:{}}).exports,e),e.exports}catch(t){throw e=0,t}};var cv=(r,e,t,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of ov(e))!sv.call(r,o)&&o!==t&&La(r,o,{get:()=>e[o],enumerable:!(a=nv(e,o))||a.enumerable});return r};var Mr=(r,e,t)=>(t=r!=null?iv(uv(r)):{},cv(e||!r||!r.__esModule?La(t,"default",{value:r,enumerable:!0}):t,r));var O=i((De,Ua)=>{"use strict";var ir=function(r){return r&&r.Math===Math&&r};Ua.exports=ir(typeof globalThis=="object"&&globalThis)||ir(typeof window=="object"&&window)||ir(typeof self=="object"&&self)||ir(typeof global=="object"&&global)||ir(typeof De=="object"&&De)||(function(){return this})()||Function("return this")()});var g=i((rE,Ga)=>{"use strict";Ga.exports=function(r){try{return!!r()}catch{return!0}}});var w=i((eE,$a)=>{"use strict";var vv=g();$a.exports=!vv(function(){return Object.defineProperty({},1,{get:function(){return 7}})[1]!==7})});var Fr=i((tE,Ka)=>{"use strict";var fv=g();Ka.exports=!fv(function(){var r=function(){}.bind();return typeof r!="function"||r.hasOwnProperty("prototype")})});var B=i((aE,Va)=>{"use strict";var lv=Fr(),Br=Function.prototype.call;Va.exports=lv?Br.bind(Br):function(){return Br.apply(Br,arguments)}});var Ya=i(Ha=>{"use strict";var Wa={}.propertyIsEnumerable,za=Object.getOwnPropertyDescriptor,pv=za&&!Wa.call({1:2},1);Ha.f=pv?function(e){var t=za(this,e);return!!t&&t.enumerable}:Wa});var K=i((nE,Xa)=>{"use strict";Xa.exports=function(r,e){return{enumerable:!(r&1),configurable:!(r&2),writable:!(r&4),value:e}}});var h=i((oE,Za)=>{"use strict";var Qa=Fr(),Ja=Function.prototype,Ne=Ja.call,yv=Qa&&Ja.bind.bind(Ne,Ne);Za.exports=Qa?yv:function(r){return function(){return Ne.apply(r,arguments)}}});var V=i((uE,ei)=>{"use strict";var ri=h(),qv=ri({}.toString),dv=ri("".slice);ei.exports=function(r){return dv(qv(r),8,-1)}});var ai=i((sE,ti)=>{"use strict";var gv=h(),hv=g(),Ev=V(),je=Object,Ov=gv("".split);ti.exports=hv(function(){return!je("z").propertyIsEnumerable(0)})?function(r){return Ev(r)==="String"?Ov(r,""):je(r)}:je});var H=i((cE,ii)=>{"use strict";ii.exports=function(r){return r==null}});var nr=i((vE,ni)=>{"use strict";var bv=H(),Sv=TypeError;ni.exports=function(r){if(bv(r))throw new Sv("Can't call method on "+r);return r}});var Y=i((fE,oi)=>{"use strict";var Tv=ai(),Rv=nr();oi.exports=function(r){return Tv(Rv(r))}});var b=i((lE,ui)=>{"use strict";var Me=typeof document=="object"&&document.all;ui.exports=typeof Me>"u"&&Me!==void 0?function(r){return typeof r=="function"||r===Me}:function(r){return typeof r=="function"}});var P=i((pE,si)=>{"use strict";var Iv=b();si.exports=function(r){return typeof r=="object"?r!==null:Iv(r)}});var k=i((yE,ci)=>{"use strict";var Fe=O(),wv=b(),xv=function(r){return wv(r)?r:void 0};ci.exports=function(r,e){return arguments.length<2?xv(Fe[r]):Fe[r]&&Fe[r][e]}});var or=i((qE,vi)=>{"use strict";var mv=h();vi.exports=mv({}.isPrototypeOf)});var Be=i((dE,pi)=>{"use strict";var Pv=O(),fi=Pv.navigator,li=fi&&fi.userAgent;pi.exports=li?String(li):""});var Le=i((gE,Ei)=>{"use strict";var hi=O(),ke=Be(),yi=hi.process,qi=hi.Deno,di=yi&&yi.versions||qi&&qi.version,gi=di&&di.v8,N,kr;gi&&(N=gi.split("."),kr=N[0]>0&&N[0]<4?1:+(N[0]+N[1]));!kr&&ke&&(N=ke.match(/Edge\/(\d+)/),(!N||N[1]>=74)&&(N=ke.match(/Chrome\/(\d+)/),N&&(kr=+N[1])));Ei.exports=kr});var Ue=i((hE,bi)=>{"use strict";var Oi=Le(),_v=g(),Av=O(),Cv=Av.String;bi.exports=!!Object.getOwnPropertySymbols&&!_v(function(){var r=Symbol("symbol detection");return!Cv(r)||!(Object(r)instanceof Symbol)||!Symbol.sham&&Oi&&Oi<41})});var Ge=i((EE,Si)=>{"use strict";var Dv=Ue();Si.exports=Dv&&!Symbol.sham&&typeof Symbol.iterator=="symbol"});var Lr=i((OE,Ti)=>{"use strict";var Nv=k(),jv=b(),Mv=or(),Fv=Ge(),Bv=Object;Ti.exports=Fv?function(r){return typeof r=="symbol"}:function(r){var e=Nv("Symbol");return jv(e)&&Mv(e.prototype,Bv(r))}});var Ur=i((bE,Ri)=>{"use strict";var kv=String;Ri.exports=function(r){try{return kv(r)}catch{return"Object"}}});var Gr=i((SE,Ii)=>{"use strict";var Lv=b(),Uv=Ur(),Gv=TypeError;Ii.exports=function(r){if(Lv(r))return r;throw new Gv(Uv(r)+" is not a function")}});var $r=i((TE,wi)=>{"use strict";var $v=Gr(),Kv=H();wi.exports=function(r,e){var t=r[e];return Kv(t)?void 0:$v(t)}});var mi=i((RE,xi)=>{"use strict";var $e=B(),Ke=b(),Ve=P(),Vv=TypeError;xi.exports=function(r,e){var t,a;if(e==="string"&&Ke(t=r.toString)&&!Ve(a=$e(t,r))||Ke(t=r.valueOf)&&!Ve(a=$e(t,r))||e!=="string"&&Ke(t=r.toString)&&!Ve(a=$e(t,r)))return a;throw new Vv("Can't convert object to primitive value")}});var W=i((IE,Pi)=>{"use strict";Pi.exports=!1});var Kr=i((wE,Ai)=>{"use strict";var _i=O(),Wv=Object.defineProperty;Ai.exports=function(r,e){try{Wv(_i,r,{value:e,configurable:!0,writable:!0})}catch{_i[r]=e}return e}});var Vr=i((xE,Ni)=>{"use strict";var zv=W(),Hv=O(),Yv=Kr(),Ci="__core-js_shared__",Di=Ni.exports=Hv[Ci]||Yv(Ci,{});(Di.versions||(Di.versions=[])).push({version:"3.50.0",mode:zv?"pure":"global",copyright:"© 2013–2025 Denis Pushkarev (zloirock.ru), 2025–2026 CoreJS Company (core-js.io). All rights reserved.",license:"https://github.com/zloirock/core-js/blob/v3.50.0/LICENSE",source:"https://github.com/zloirock/core-js"})});var We=i((mE,Mi)=>{"use strict";var ji=Vr(),Xv=Object.create||Object;Mi.exports=function(r,e){return ji[r]||(ji[r]=e||Xv(null))}});var ur=i((PE,Fi)=>{"use strict";var Qv=nr(),Jv=Object;Fi.exports=function(r){return Jv(Qv(r))}});var x=i((_E,Bi)=>{"use strict";var Zv=h(),rf=ur(),ef=Zv({}.hasOwnProperty);Bi.exports=Object.hasOwn||function(e,t){return ef(rf(e),t)}});var sr=i((AE,ki)=>{"use strict";var tf=h(),af=0,nf=Math.random(),of=tf(1.1.toString);ki.exports=function(r){return"Symbol("+(r===void 0?"":r)+")_"+of(++af+nf,36)}});var C=i((CE,Ui)=>{"use strict";var uf=O(),sf=We(),Li=x(),cf=sr(),vf=Ue(),ff=Ge(),Z=uf.Symbol,ze=sf("wks"),lf=ff?Z.for||Z:Z&&Z.withoutSetter||cf;Ui.exports=function(r){return Li(ze,r)||(ze[r]=vf&&Li(Z,r)?Z[r]:lf("Symbol."+r)),ze[r]}});var Vi=i((DE,Ki)=>{"use strict";var pf=B(),Gi=P(),$i=Lr(),yf=$r(),qf=mi(),df=C(),gf=TypeError,hf=df("toPrimitive");Ki.exports=function(r,e){if(!Gi(r)||$i(r))return r;var t=yf(r,hf),a;if(t){if(e===void 0&&(e="default"),a=pf(t,r,e),!Gi(a)||$i(a))return a;throw new gf("Can't convert object to primitive value")}return e===void 0&&(e="number"),qf(r,e)}});var He=i((NE,Wi)=>{"use strict";var Ef=Vi(),Of=Lr();Wi.exports=function(r){var e=Ef(r,"string");return Of(e)?e:e+""}});var Xe=i((jE,Hi)=>{"use strict";var bf=O(),zi=P(),Ye=bf.document,Sf=zi(Ye)&&zi(Ye.createElement);Hi.exports=function(r){return Sf?Ye.createElement(r):{}}});var Qe=i((ME,Yi)=>{"use strict";var Tf=w(),Rf=g(),If=Xe();Yi.exports=!Tf&&!Rf(function(){return Object.defineProperty(If("div"),"a",{get:function(){return 7}}).a!==7})});var Je=i(Qi=>{"use strict";var wf=w(),xf=B(),mf=Ya(),Pf=K(),_f=Y(),Af=He(),Cf=x(),Df=Qe(),Xi=Object.getOwnPropertyDescriptor;Qi.f=wf?Xi:function(e,t){if(e=_f(e),t=Af(t),Df)try{return Xi(e,t)}catch{}if(Cf(e,t))return Pf(!xf(mf.f,e,t),e[t])}});var Ze=i((BE,Ji)=>{"use strict";var Nf=w(),jf=g();Ji.exports=Nf&&jf(function(){return Object.defineProperty(function(){},"prototype",{value:42,writable:!1}).prototype!==42})});var D=i((kE,Zi)=>{"use strict";var Mf=P(),Ff=String,Bf=TypeError;Zi.exports=function(r){if(Mf(r))return r;throw new Bf(Ff(r)+" is not an object")}});var A=i(en=>{"use strict";var kf=w(),Lf=Qe(),Uf=Ze(),Wr=D(),rn=He(),Gf=TypeError,rt=Object.defineProperty,$f=Object.getOwnPropertyDescriptor,et="enumerable",tt="configurable",at="writable";en.f=kf?Uf?function(e,t,a){if(Wr(e),t=rn(t),Wr(a),typeof e=="function"&&t==="prototype"&&"value"in a&&at in a&&!a[at]){var o=$f(e,t);o&&o[at]&&(e[t]=a.value,a={configurable:tt in a?a[tt]:o[tt],enumerable:et in a?a[et]:o[et],writable:!1})}return rt(e,t,a)}:rt:function(e,t,a){if(Wr(e),t=rn(t),Wr(a),Lf)try{return rt(e,t,a)}catch{}if("get"in a||"set"in a)throw new Gf("Accessors not supported");return"value"in a&&(e[t]=a.value),e}});var cr=i((UE,tn)=>{"use strict";var Kf=w(),Vf=A(),Wf=K();tn.exports=Kf?function(r,e,t){return Vf.f(r,e,Wf(1,t))}:function(r,e,t){return r[e]=t,r}});var ot=i((GE,nn)=>{"use strict";var it=w(),zf=x(),an=Function.prototype,Hf=it&&Object.getOwnPropertyDescriptor,nt=zf(an,"name"),Yf=nt&&function(){}.name==="something",Xf=nt&&(!it||it&&Hf(an,"name").configurable);nn.exports={EXISTS:nt,PROPER:Yf,CONFIGURABLE:Xf}});var st=i(($E,on)=>{"use strict";var Qf=h(),Jf=b(),ut=Vr(),Zf=Qf(Function.toString);Jf(ut.inspectSource)||(ut.inspectSource=function(r){return Zf(r)});on.exports=ut.inspectSource});var cn=i((KE,sn)=>{"use strict";var rl=O(),el=b(),un=rl.WeakMap;sn.exports=el(un)&&/native code/.test(String(un))});var zr=i((VE,fn)=>{"use strict";var tl=We(),al=sr(),vn=tl("keys");fn.exports=function(r){return vn[r]||(vn[r]=al(r))}});var vr=i((WE,ln)=>{"use strict";ln.exports={}});var lr=i((zE,qn)=>{"use strict";var il=cn(),yn=O(),nl=P(),ol=cr(),ct=x(),vt=Vr(),ul=zr(),sl=vr(),pn="Object already initialized",ft=yn.TypeError,cl=yn.WeakMap,Hr,fr,Yr,vl=function(r){return Yr(r)?fr(r):Hr(r,{})},fl=function(r){return function(e){var t;if(!nl(e)||(t=fr(e)).type!==r)throw new ft("Incompatible receiver, "+r+" required");return t}};il||vt.state?(j=vt.state||(vt.state=new cl),j.get=j.get,j.has=j.has,j.set=j.set,Hr=function(r,e){if(j.has(r))throw new ft(pn);return e.facade=r,j.set(r,e),e},fr=function(r){return j.get(r)||{}},Yr=function(r){return j.has(r)}):(X=ul("state"),sl[X]=!0,Hr=function(r,e){if(ct(r,X))throw new ft(pn);return e.facade=r,ol(r,X,e),e},fr=function(r){return ct(r,X)?r[X]:{}},Yr=function(r){return ct(r,X)});var j,X;qn.exports={set:Hr,get:fr,has:Yr,enforce:vl,getterFor:fl}});var yt=i((HE,hn)=>{"use strict";var pt=h(),ll=g(),pl=b(),Xr=x(),lt=w(),yl=ot().CONFIGURABLE,ql=st(),gn=lr(),dl=gn.enforce,gl=gn.get,dn=String,Qr=Object.defineProperty,hl=pt("".slice),El=pt("".replace),Ol=pt([].join),bl=lt&&!ll(function(){return Qr(function(){},"length",{value:8}).length!==8}),Sl=String(String).split("String"),Tl=hn.exports=function(r,e,t){hl(dn(e),0,7)==="Symbol("&&(e="["+El(dn(e),/^Symbol\(([^)]*)\).*$/,"$1")+"]"),t&&t.getter&&(e="get "+e),t&&t.setter&&(e="set "+e),(!Xr(r,"name")||yl&&r.name!==e)&&(lt?Qr(r,"name",{value:e,configurable:!0}):r.name=e),bl&&t&&Xr(t,"arity")&&r.length!==t.arity&&Qr(r,"length",{value:t.arity});try{t&&Xr(t,"constructor")&&t.constructor?lt&&Qr(r,"prototype",{writable:!1}):r.prototype&&(r.prototype=void 0)}catch{}var a=dl(r);return Xr(a,"source")||(a.source=Ol(Sl,typeof e=="string"?e:"")),r};Function.prototype.toString=Tl(function(){return pl(this)&&gl(this).source||ql(this)},"toString")});var L=i((YE,En)=>{"use strict";var Rl=b(),Il=A(),wl=yt(),xl=Kr();En.exports=function(r,e,t,a){a||(a={});var o=a.enumerable,n=a.name!==void 0?a.name:e;if(Rl(t)&&wl(t,n,a),a.global)o?r[e]=t:xl(e,t);else{try{a.unsafe?r[e]&&(o=!0):delete r[e]}catch{}o?r[e]=t:Il.f(r,e,{value:t,enumerable:!1,configurable:!a.nonConfigurable,writable:!a.nonWritable})}return r}});var bn=i((XE,On)=>{"use strict";var ml=Math.ceil,Pl=Math.floor;On.exports=Math.trunc||function(e){var t=+e;return(t>0?Pl:ml)(t)}});var pr=i((QE,Sn)=>{"use strict";var _l=bn();Sn.exports=function(r){var e=+r;return e!==e||e===0?0:_l(e)}});var Rn=i((JE,Tn)=>{"use strict";var Al=pr(),Cl=Math.max,Dl=Math.min;Tn.exports=function(r,e){var t=Al(r);return t<0?Cl(t+e,0):Dl(t,e)}});var wn=i((ZE,In)=>{"use strict";var Nl=pr(),jl=Math.min;In.exports=function(r){var e=Nl(r);return e>0?jl(e,9007199254740991):0}});var yr=i((rO,xn)=>{"use strict";var Ml=wn();xn.exports=function(r){return Ml(r.length)}});var _n=i((eO,Pn)=>{"use strict";var Fl=Y(),Bl=Rn(),kl=yr(),mn=function(r){return function(e,t,a){var o=Fl(e),n=kl(o);if(n===0)return!r&&-1;var u=Bl(a,n),c;if(r&&t!==t){for(;n>u;)if(c=o[u++],c!==c)return!0}else for(;n>u;u++)if((r||u in o)&&o[u]===t)return r||u||0;return!r&&-1}};Pn.exports={includes:mn(!0),indexOf:mn(!1)}});var dt=i((tO,Cn)=>{"use strict";var Ll=h(),qt=x(),Ul=Y(),Gl=_n().indexOf,$l=vr(),An=Ll([].push);Cn.exports=function(r,e){var t=Ul(r),a=0,o=[],n;for(n in t)!qt($l,n)&&qt(t,n)&&An(o,n);for(;e.length>a;)qt(t,n=e[a++])&&(~Gl(o,n)||An(o,n));return o}});var Jr=i((aO,Dn)=>{"use strict";Dn.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]});var Zr=i(Nn=>{"use strict";var Kl=dt(),Vl=Jr(),Wl=Vl.concat("length","prototype");Nn.f=Object.getOwnPropertyNames||function(e){return Kl(e,Wl)}});var Mn=i(jn=>{"use strict";jn.f=Object.getOwnPropertySymbols});var Bn=i((oO,Fn)=>{"use strict";var zl=k(),Hl=h(),Yl=Zr(),Xl=Mn(),Ql=D(),Jl=Hl([].concat);Fn.exports=zl("Reflect","ownKeys")||function(e){var t=Yl.f(Ql(e)),a=Xl.f;return a?Jl(t,a(e)):t}});var Un=i((uO,Ln)=>{"use strict";var kn=x(),Zl=Bn(),rp=Je(),ep=A();Ln.exports=function(r,e,t){for(var a=Zl(e),o=ep.f,n=rp.f,u=0;u{"use strict";var tp=g(),ap=b(),ip=/#|\.prototype\./,qr=function(r,e){var t=op[np(r)];return t===sp?!0:t===up?!1:ap(e)?tp(e):!!e},np=qr.normalize=function(r){return String(r).replace(ip,".").toLowerCase()},op=qr.data={},up=qr.NATIVE="N",sp=qr.POLYFILL="P";Gn.exports=qr});var M=i((cO,$n)=>{"use strict";var re=O(),cp=Je().f,vp=cr(),fp=L(),lp=Kr(),pp=Un(),yp=gt();$n.exports=function(r,e){var t=r.target,a=r.global,o=r.stat,n,u,c,f,v,s;if(a?u=re:o?u=re[t]||lp(t,{}):u=re[t]&&re[t].prototype,u)for(c in e){if(v=e[c],r.dontCallGetSet?(s=cp(u,c),f=s&&s.value):f=u[c],n=yp(a?c:t+(o?".":"#")+c,r.forced),!n&&f!==void 0){if(typeof v==typeof f)continue;pp(v,f)}(r.sham||f&&f.sham)&&vp(v,"sham",!0),fp(u,c,v,r)}}});var Kn=i(()=>{"use strict";var qp=M(),dp=x();qp({target:"Object",stat:!0},{hasOwn:dp})});var ht=i((lO,Vn)=>{"use strict";var gp=O();Vn.exports=gp});var zn=i((pO,Wn)=>{"use strict";Kn();var hp=ht();Wn.exports=hp.Object.hasOwn});var Yn=i((yO,Hn)=>{"use strict";var Ep=zn();Hn.exports=Ep});var Qn=i((qO,Xn)=>{"use strict";var Op=Yn();Xn.exports=Op});var Et=i((dO,Jn)=>{"use strict";var bp=dt(),Sp=Jr();Jn.exports=Object.keys||function(e){return bp(e,Sp)}});var ro=i(Zn=>{"use strict";var Tp=w(),Rp=Ze(),Ip=A(),wp=D(),xp=Y(),mp=Et();Zn.f=Tp&&!Rp?Object.defineProperties:function(e,t){wp(e);for(var a=xp(t),o=mp(t),n=o.length,u=0,c;n>u;)Ip.f(e,c=o[u++],a[c]);return e}});var to=i((hO,eo)=>{"use strict";var Pp=k();eo.exports=Pp("document","documentElement")});var rr=i((EO,co)=>{"use strict";var _p=D(),Ap=ro(),ao=Jr(),Cp=vr(),Dp=to(),Np=Xe(),jp=zr(),io=">",no="<",bt="prototype",St="script",uo=jp("IE_PROTO"),Ot=function(){},so=function(r){return no+St+io+r+no+"/"+St+io},oo=function(r){r.write(so("")),r.close();var e=r.parentWindow.Object;return r=null,e},Mp=function(){var r=Np("iframe"),e="java"+St+":",t;return r.style.display="none",Dp.appendChild(r),r.src=String(e),t=r.contentWindow.document,t.open(),t.write(so("document.F=Object")),t.close(),t.F},ee,te=function(){try{ee=new ActiveXObject("htmlfile")}catch{}te=typeof document<"u"?document.domain&&ee?oo(ee):Mp():oo(ee);for(var r=ao.length;r--;)delete te[bt][ao[r]];return te()};Cp[uo]=!0;co.exports=Object.create||function(e,t){var a;return e!==null?(Ot[bt]=_p(e),a=new Ot,Ot[bt]=null,a[uo]=e):a=te(),t===void 0?a:Ap.f(a,t)}});var It=i((OO,vo)=>{"use strict";var Fp=C(),Bp=rr(),kp=A().f,Tt=Fp("unscopables"),Rt=Array.prototype;Rt[Tt]===void 0&&kp(Rt,Tt,{configurable:!0,value:Bp(null)});vo.exports=function(r){Rt[Tt][r]=!0}});var fo=i(()=>{"use strict";var Lp=M(),Up=ur(),Gp=yr(),$p=pr(),Kp=It();Lp({target:"Array",proto:!0},{at:function(e){var t=Up(this),a=Gp(t),o=$p(e),n=o>=0?o:a+o;return n<0||n>=a?void 0:t[n]}});Kp("at")});var wt=i((TO,lo)=>{"use strict";var Vp=O(),Wp=h();lo.exports=function(r,e){return Wp(Vp[r].prototype[e])}});var yo=i((RO,po)=>{"use strict";fo();var zp=wt();po.exports=zp("Array","at")});var go=i((IO,qo)=>{"use strict";var Hp=yo();qo.exports=Hp});var Eo=i((wO,ho)=>{"use strict";var Yp=go();ho.exports=Yp});var ae=i((xO,bo)=>{"use strict";var Xp=C(),Qp=Xp("toStringTag"),Oo={};Oo[Qp]="z";bo.exports=String(Oo)==="[object z]"});var dr=i((mO,So)=>{"use strict";var Jp=ae(),Zp=b(),ie=V(),ry=C(),ey=ry("toStringTag"),ty=Object,ay=ie((function(){return arguments})())==="Arguments",iy=function(r,e){try{return r[e]}catch{}};So.exports=Jp?ie:function(r){var e,t,a;return r===void 0?"Undefined":r===null?"Null":typeof(t=iy(e=ty(r),ey))=="string"?t:ay?ie(e):(a=ie(e))==="Object"&&Zp(e.callee)?"Arguments":a}});var xt=i((PO,To)=>{"use strict";var ny=dr(),oy=String;To.exports=function(r){if(ny(r)==="Symbol")throw new TypeError("Cannot convert a Symbol value to a string");return oy(r)}});var Ro=i(()=>{"use strict";var uy=M(),sy=h(),cy=nr(),vy=pr(),fy=xt(),ly=g(),py=sy("".charAt),yy=ly(function(){return"𠮷".at(-2)!=="\uD842"});uy({target:"String",proto:!0,forced:yy},{at:function(e){var t=fy(cy(this)),a=t.length,o=vy(e),n=o>=0?o:a+o;return n<0||n>=a?void 0:py(t,n)}})});var wo=i((CO,Io)=>{"use strict";Ro();var qy=wt();Io.exports=qy("String","at")});var mo=i((DO,xo)=>{"use strict";var dy=wo();xo.exports=dy});var _o=i((NO,Po)=>{"use strict";var gy=mo();Po.exports=gy});var ne=i((jO,Ao)=>{"use strict";var hy=xt();Ao.exports=function(r,e){return r===void 0?arguments.length<2?"":e:hy(r)}});var mt=i((MO,Do)=>{"use strict";var Ey=w(),Oy=g(),by=D(),Co=ne(),oe=Error.prototype.toString,Sy=Oy(function(){if(Ey){var r=Object.create(Object.defineProperty({},"name",{get:function(){return this===r}}));if(oe.call(r)!=="true")return!0}return oe.call({message:1,name:2})!=="2: 1"||oe.call({})!=="Error"});Do.exports=Sy?function(){var e=by(this),t=Co(e.name,"Error"),a=Co(e.message);return t?a?t+": "+a:t:a}:oe});var Mo=i(()=>{"use strict";var Ty=L(),No=mt(),jo=Error.prototype;jo.toString!==No&&Ty(jo,"toString",No)});var gr=i((kO,Fo)=>{"use strict";Fo.exports=Object.create?Object.create(null):{}});var ko=i((LO,Bo)=>{"use strict";var Ry=g();Bo.exports=!Ry(function(){function r(){}return r.prototype.constructor=null,Object.getPrototypeOf(new r)!==r.prototype})});var _t=i((UO,Uo)=>{"use strict";var Iy=x(),wy=b(),xy=ur(),my=zr(),Py=ko(),Lo=my("IE_PROTO"),Pt=Object,_y=Pt.prototype;Uo.exports=Py?Pt.getPrototypeOf:function(r){var e=xy(r);if(Iy(e,Lo))return e[Lo];var t=e.constructor;return wy(t)&&e instanceof t?t.prototype:e instanceof Pt?_y:null}});var Nt=i((GO,Ko)=>{"use strict";var Ay=g(),Cy=b(),Dy=P(),Ny=rr(),Go=_t(),jy=L(),My=C(),Fy=W(),Dt=My("iterator"),$o=!1,U,At,Ct;[].keys&&(Ct=[].keys(),"next"in Ct?(At=Go(Go(Ct)),At!==Object.prototype&&(U=At)):$o=!0);var By=!Dy(U)||Ay(function(){var r={};return U[Dt].call(r)!==r});By?U={}:Fy&&(U=Ny(U));Cy(U[Dt])||jy(U,Dt,function(){return this});Ko.exports={IteratorPrototype:U,BUGGY_SAFARI_ITERATORS:$o}});var hr=i(($O,Wo)=>{"use strict";var ky=A().f,Ly=x(),Uy=C(),Vo=Uy("toStringTag");Wo.exports=function(r,e,t){r&&!t&&(r=r.prototype),r&&!Ly(r,Vo)&&ky(r,Vo,{configurable:!0,value:e})}});var Ho=i((KO,zo)=>{"use strict";var Gy=Nt().IteratorPrototype,$y=rr(),Ky=K(),Vy=hr(),Wy=gr(),zy=function(){return this};zo.exports=function(r,e,t,a){var o=e+" Iterator";return r.prototype=$y(Gy,{next:Ky(+!a,t)}),Vy(r,o,!1,!0),Wy[o]=zy,r}});var Xo=i((VO,Yo)=>{"use strict";var Hy=h(),Yy=Gr();Yo.exports=function(r,e,t){try{return Hy(Yy(Object.getOwnPropertyDescriptor(r,e)[t]))}catch{}}});var Jo=i((WO,Qo)=>{"use strict";var Xy=P();Qo.exports=function(r){return Xy(r)||r===null}});var ru=i((zO,Zo)=>{"use strict";var Qy=Jo(),Jy=String,Zy=TypeError;Zo.exports=function(r){if(Qy(r))return r;throw new Zy("Can't set "+Jy(r)+" as a prototype")}});var jt=i((HO,eu)=>{"use strict";var rq=Xo(),eq=P(),tq=nr(),aq=ru();eu.exports=Object.setPrototypeOf||("__proto__"in{}?(function(){var r=!1,e={},t;try{t=rq(Object.prototype,"__proto__","set"),t(e,[]),r=e instanceof Array}catch{}return function(o,n){return tq(o),aq(n),eq(o)&&(r?t(o,n):o.__proto__=n),o}})():void 0)});var Ft=i((YO,fu)=>{"use strict";var iq=M(),nq=B(),ue=W(),cu=ot(),oq=b(),uq=Ho(),tu=_t(),au=jt(),sq=hr(),cq=cr(),Mt=L(),vq=C(),iu=gr(),vu=Nt(),fq=cu.PROPER,lq=cu.CONFIGURABLE,nu=vu.IteratorPrototype,se=vu.BUGGY_SAFARI_ITERATORS,Er=vq("iterator"),ou="keys",Or="values",uu="entries",su=function(){return this};fu.exports=function(r,e,t,a,o,n,u){uq(t,e,a);var c=function(q){if(q===o&&p)return p;if(!se&&q&&q in s)return s[q];switch(q){case ou:return function(){return new t(this,q)};case Or:return function(){return new t(this,q)};case uu:return function(){return new t(this,q)}}return function(){return new t(this)}},f=e+" Iterator",v=!1,s=r.prototype,l=s[Er]||s["@@iterator"]||o&&s[o],p=!se&&l||c(o),y=e==="Array"&&s.entries||l,E,m,F;if(y&&(E=tu(y.call(new r)),E!==Object.prototype&&E.next&&(!ue&&tu(E)!==nu&&(au?au(E,nu):oq(E[Er])||Mt(E,Er,su)),sq(E,f,!0,!0),ue&&(iu[f]=su))),fq&&o===Or&&l&&l.name!==Or&&(!ue&&lq?cq(s,"name",Or):(v=!0,p=function(){return nq(l,this)})),o)if(m={values:c(Or),keys:n?p:c(ou),entries:c(uu)},u)for(F in m)(se||v||!(F in s))&&Mt(s,F,m[F]);else iq({target:e,proto:!0,forced:se||v},m);return(!ue||u)&&s[Er]!==p&&Mt(s,Er,p,{name:o}),iu[e]=p,m}});var Bt=i((XO,lu)=>{"use strict";lu.exports=function(r,e){return{value:r,done:e}}});var hu=i((QO,gu)=>{"use strict";var pq=Y(),kt=It(),pu=gr(),qu=lr(),yq=A().f,qq=Ft(),ce=Bt(),dq=W(),gq=w(),du="Array Iterator",hq=qu.set,Eq=qu.getterFor(du);gu.exports=qq(Array,"Array",function(r,e){hq(this,{type:du,target:pq(r),index:0,kind:e})},function(){var r=Eq(this),e=r.target,t=r.index++;if(!e||t>=e.length)return r.target=null,ce(void 0,!0);switch(r.kind){case"keys":return ce(t,!1);case"values":return ce(e[t],!1)}return ce([t,e[t]],!1)},"values");var yu=pu.Arguments=pu.Array;kt("keys");kt("values");kt("entries");if(!dq&&gq&&yu.name!=="values")try{yq(yu,"name",{value:"values"})}catch{}});var Ou=i(()=>{"use strict";var Oq=M(),bq=ur(),Eu=Et(),Sq=g(),Tq=Sq(function(){Eu(1)});Oq({target:"Object",stat:!0,forced:Tq},{keys:function(e){return Eu(bq(e))}})});var Su=i((rb,bu)=>{"use strict";var Rq=ae(),Iq=dr();bu.exports=Rq?{}.toString:function(){return"[object "+Iq(this)+"]"}});var Tu=i(()=>{"use strict";var wq=ae(),xq=L(),mq=Su();wq||xq(Object.prototype,"toString",mq,{unsafe:!0})});var Iu=i((ab,Ru)=>{"use strict";var Pq=h();Ru.exports=Pq([].slice)});var Pu=i((ib,mu)=>{"use strict";var _q=V(),Aq=Y(),wu=Zr().f,Cq=Iu(),xu=typeof window=="object"&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],Dq=function(r){try{return wu(r)}catch{return Cq(xu)}};mu.exports.f=function(e){return xu&&_q(e)==="Window"?Dq(e):wu(Aq(e))}});var Au=i((nb,_u)=>{"use strict";var Nq=g();_u.exports=Nq(function(){if(typeof ArrayBuffer=="function"){var r=new ArrayBuffer(8);Object.isExtensible(r)&&Object.defineProperty(r,"a",{value:8})}})});var Nu=i((ob,Du)=>{"use strict";var jq=g(),Mq=P(),Fq=V(),Cu=Au(),ve=Object.isExtensible,Bq=jq(function(){ve(1)});Du.exports=Bq||Cu?function(e){return!Mq(e)||Cu&&Fq(e)==="ArrayBuffer"?!1:ve?ve(e):!0}:ve});var Mu=i((ub,ju)=>{"use strict";var kq=g();ju.exports=!kq(function(){return Object.isExtensible(Object.preventExtensions({}))})});var $t=i((sb,ku)=>{"use strict";var Lq=M(),Uq=h(),Gq=vr(),$q=P(),Lt=x(),Kq=A().f,Fu=Zr(),Vq=Pu(),Ut=Nu(),Wq=sr(),zq=Mu(),Bu=!1,G=Wq("meta"),Hq=0,Gt=function(r){Kq(r,G,{value:{objectID:"O"+Hq++,weakData:{}}})},Yq=function(r,e){if(!$q(r))return typeof r=="symbol"?r:(typeof r=="string"?"S":"P")+r;if(!Lt(r,G)){if(!Ut(r))return"F";if(!e)return"E";Gt(r)}return r[G].objectID},Xq=function(r,e){if(!Lt(r,G)){if(!Ut(r))return!0;if(!e)return!1;Gt(r)}return r[G].weakData},Qq=function(r){return zq&&Bu&&Ut(r)&&!Lt(r,G)&&Gt(r),r},Jq=function(){Zq.enable=function(){},Bu=!0;var r=Fu.f,e=Uq([].splice),t={};t[G]=1,r(t).length&&(Fu.f=function(a){for(var o=r(a),n=0,u=o.length;n{"use strict";var rd=V(),ed=h();Lu.exports=function(r){if(rd(r)==="Function")return ed(r)}});var Kt=i((vb,$u)=>{"use strict";var Gu=Uu(),td=Gr(),ad=Fr(),id=Gu(Gu.bind);$u.exports=function(r,e){return td(r),e===void 0?r:ad?id(r,e):function(){return r.apply(e,arguments)}}});var Vu=i((fb,Ku)=>{"use strict";var nd=C(),od=gr(),ud=nd("iterator"),sd=Array.prototype;Ku.exports=function(r){return r!==void 0&&(od.Array===r||sd[ud]===r)}});var Vt=i((lb,Hu)=>{"use strict";var cd=V(),vd=H(),Wu=$r(),fd=C(),zu=fd("iterator"),ld=Array.prototype;Hu.exports=function(r){if(!vd(r))return Wu(r,zu)||Wu(r,"@@iterator")||(cd(r)==="Arguments"?ld[zu]:void 0)}});var Xu=i((pb,Yu)=>{"use strict";var pd=B(),yd=b(),qd=D(),dd=Ur(),gd=Vt(),hd=TypeError;Yu.exports=function(r,e){var t=arguments.length<2?gd(r):e;if(yd(t))return qd(pd(t,r));throw new hd(dd(r)+" is not iterable")}});var Zu=i((yb,Ju)=>{"use strict";var Ed=B(),Qu=D(),Od=$r();Ju.exports=function(r,e,t){var a,o;Qu(r);try{if(a=Od(r,"return"),!a){if(e==="throw")throw t;return t}a=Ed(a,r)}catch(n){o=!0,a=n}if(e==="throw")throw t;if(o)throw a;return Qu(a),t}});var le=i((qb,as)=>{"use strict";var bd=Kt(),Sd=B(),Td=D(),Rd=Ur(),Id=Vu(),wd=yr(),rs=or(),xd=Xu(),md=Vt(),es=Zu(),Pd=TypeError,fe=function(r,e){this.stopped=r,this.result=e},ts=fe.prototype;as.exports=function(r,e,t){var a=t&&t.that,o=!!(t&&t.AS_ENTRIES),n=!!(t&&t.IS_RECORD),u=!!(t&&t.IS_ITERATOR),c=!!(t&&t.INTERRUPTED),f=bd(e,a),v,s,l,p,y,E,m,F=function(d){var I=v;return v=void 0,I&&es(I,"normal"),new fe(!0,d)},q=function(d){return o?(Td(d),c?f(d[0],d[1],F):f(d[0],d[1])):c?f(d,F):f(d)};if(n)v=r.iterator;else if(u)v=r;else{if(s=md(r),!s)throw new Pd(Rd(r)+" is not iterable");if(Id(s)){for(l=0,p=wd(r);p>l;l++)if(y=q(r[l]),y&&rs(ts,y))return y;return new fe(!1)}v=xd(r,s)}for(E=n?r.next:v.next;!(m=Sd(E,v)).done;){var S=m.value;try{y=q(S)}catch(d){if(v)es(v,"throw",d);else throw d}if(typeof y=="object"&&y&&rs(ts,y))return y}return new fe(!1)}});var br=i((db,is)=>{"use strict";var _d=or(),Ad=TypeError;is.exports=function(r,e){if(_d(e,r))return r;throw new Ad("Incorrect invocation")}});var cs=i((gb,ss)=>{"use strict";var Cd=C(),os=Cd("iterator"),us=!1;try{ns=0,Wt={next:function(){return{done:!!ns++}},return:function(){us=!0}},Wt[os]=function(){return this},Array.from(Wt,function(){throw 2})}catch{}var ns,Wt;ss.exports=function(r,e){try{if(!e&&!us)return!1}catch{return!1}var t=!1;try{var a={};a[os]=function(){return{next:function(){return{done:t=!0}}}},r(a)}catch{}return t}});var zt=i((hb,fs)=>{"use strict";var Dd=b(),Nd=P(),vs=jt();fs.exports=function(r,e,t){var a,o;return vs&&Dd(a=e.constructor)&&a!==t&&Nd(o=a.prototype)&&o!==t.prototype&&vs(r,o),r}});var Xt=i((Eb,ps)=>{"use strict";var jd=M(),Md=O(),Fd=h(),ls=gt(),Bd=L(),kd=$t(),Ld=le(),Ud=br(),Gd=b(),$d=H(),Ht=P(),Yt=g(),Kd=cs(),Vd=hr(),Wd=zt();ps.exports=function(r,e,t){var a=r.indexOf("Map")!==-1,o=r.indexOf("Weak")!==-1,n=a?"set":"add",u=Md[r],c=u&&u.prototype,f=u,v={},s=function(q){var S=Fd(c[q]);Bd(c,q,q==="add"?function(I){return S(this,I===0?0:I),this}:q==="delete"?function(d){return o&&!Ht(d)?!1:S(this,d===0?0:d)}:q==="get"?function(I){return o&&!Ht(I)?void 0:S(this,I===0?0:I)}:q==="has"?function(I){return o&&!Ht(I)?!1:S(this,I===0?0:I)}:function(I,av){return S(this,I===0?0:I,av),this})},l=ls(r,!Gd(u)||!(o||c.forEach&&!Yt(function(){new u().entries().next()})));if(l)f=t.getConstructor(e,r,a,n),kd.enable();else if(ls(r,!0)){var p=new f,y=p[n](o?{}:-0,1)!==p,E=Yt(function(){p.has(1)}),m=Kd(function(q){new u(q)}),F=!o&&Yt(function(){for(var q=new u,S=5;S--;)q[n](S,S);return!q.has(-0)});m||(f=e(function(q,S){Ud(q,c);var d=Wd(new u,q,f);return $d(S)||Ld(S,d[n],{that:d,AS_ENTRIES:a}),d}),f.prototype=c,c.constructor=f),(E||F)&&(s("delete"),s("has"),a&&s("get")),(F||y)&&s(n),o&&c.clear&&delete c.clear}return v[r]=f,jd({global:!0,constructor:!0,forced:f!==u},v),Vd(f,r),o||t.setStrong(f,r,a),f}});var pe=i((Ob,qs)=>{"use strict";var ys=yt(),zd=A();qs.exports=function(r,e,t){return t.get&&ys(t.get,e,{getter:!0}),t.set&&ys(t.set,e,{setter:!0}),zd.f(r,e,t)}});var gs=i((bb,ds)=>{"use strict";var Hd=L();ds.exports=function(r,e,t){for(var a in e)Hd(r,a,e[a],t);return r}});var Os=i((Sb,Es)=>{"use strict";var Yd=k(),Xd=pe(),Qd=C(),Jd=w(),hs=Qd("species");Es.exports=function(r){var e=Yd(r);Jd&&e&&!e[hs]&&Xd(e,hs,{configurable:!0,get:function(){return this}})}});var Jt=i((Tb,ws)=>{"use strict";var bs=rr(),Zd=pe(),Ss=gs(),rg=Kt(),eg=br(),tg=H(),ag=le(),ig=Ft(),ye=Bt(),ng=Os(),Sr=w(),Ts=$t().fastKey,Is=lr(),Rs=Is.set,Qt=Is.getterFor;ws.exports={getConstructor:function(r,e,t,a){var o=r(function(v,s){eg(v,n),Rs(v,{type:e,index:bs(null),first:null,last:null,size:0}),Sr||(v.size=0),tg(s)||ag(s,v[a],{that:v,AS_ENTRIES:t})}),n=o.prototype,u=Qt(e),c=function(v,s,l){var p=u(v),y=f(v,s),E,m;return y?y.value=l:(p.last=y={index:m=Ts(s,!0),key:s,value:l,previous:E=p.last,next:null,removed:!1},p.first||(p.first=y),E&&(E.next=y),Sr?p.size++:v.size++,m!=="F"&&(p.index[m]=y)),v},f=function(v,s){var l=u(v),p=Ts(s),y;if(p!=="F")return l.index[p];for(y=l.first;y;y=y.next)if(y.key===s)return y};return Ss(n,{clear:function(){for(var s=this,l=u(s),p=l.first;p;)p.removed=!0,p.previous&&(p.previous=p.previous.next=null),p=p.next;l.first=l.last=null,l.index=bs(null),Sr?l.size=0:s.size=0},delete:function(v){var s=this,l=u(s),p=f(s,v);if(p){var y=p.next,E=p.previous;delete l.index[p.index],p.removed=!0,E&&(E.next=y),y&&(y.previous=E),l.first===p&&(l.first=y),l.last===p&&(l.last=E),Sr?l.size--:s.size--}return!!p},forEach:function(s){for(var l=u(this),p=rg(s,arguments.length>1?arguments[1]:void 0),y;y=y?y.next:l.first;)for(p(y.value,y.key,this);y&&y.removed;)y=y.previous},has:function(s){return!!f(this,s)}}),Ss(n,t?{get:function(s){var l=f(this,s);return l&&l.value},set:function(s,l){return c(this,s===0?0:s,l)}}:{add:function(s){return c(this,s=s===0?0:s,s)}}),Sr&&Zd(n,"size",{configurable:!0,get:function(){return u(this).size}}),o},setStrong:function(r,e,t){var a=e+" Iterator",o=Qt(e),n=Qt(a);ig(r,e,function(u,c){Rs(this,{type:a,target:u,state:o(u),kind:c,last:null})},function(){for(var u=n(this),c=u.kind,f=u.last;f&&f.removed;)f=f.previous;return!u.target||!(u.last=f=f?f.next:u.state.first)?(u.target=null,ye(void 0,!0)):ye(c==="keys"?f.key:c==="values"?f.value:[f.key,f.value],!1)},t?"entries":"values",!t,!0),ng(e)}}});var xs=i(()=>{"use strict";var og=Xt(),ug=Jt();og("Map",function(r){return function(){return r(this,arguments.length?arguments[0]:void 0)}},ug)});var ms=i(()=>{"use strict";xs()});var Ps=i(()=>{"use strict";var sg=Xt(),cg=Jt();sg("Set",function(r){return function(){return r(this,arguments.length?arguments[0]:void 0)}},cg)});var _s=i(()=>{"use strict";Ps()});var Zt=i((Cb,As)=>{"use strict";var Tr=O(),vg=Be(),fg=V(),qe=function(r){return vg.slice(0,r.length)===r};As.exports=(function(){return qe("Bun/")?"BUN":qe("Cloudflare-Workers")?"CLOUDFLARE":qe("Deno/")?"DENO":qe("Node.js/")?"NODE":Tr.Bun&&typeof Bun.version=="string"?"BUN":Tr.Deno&&typeof Deno.version=="object"?"DENO":fg(Tr.process)==="process"?"NODE":Tr.window&&Tr.document?"BROWSER":"REST"})()});var Ds=i((Db,Cs)=>{"use strict";var lg=Zt();Cs.exports=lg==="NODE"});var ra=i((Nb,Ns)=>{"use strict";var pg=O(),yg=Ds();Ns.exports=function(r){if(yg){try{return pg.process.getBuiltinModule(r)}catch{}try{return Function('return require("'+r+'")')()}catch{}}}});var ea=i((jb,js)=>{"use strict";js.exports={IndexSizeError:{s:"INDEX_SIZE_ERR",c:1,m:1},DOMStringSizeError:{s:"DOMSTRING_SIZE_ERR",c:2,m:0},HierarchyRequestError:{s:"HIERARCHY_REQUEST_ERR",c:3,m:1},WrongDocumentError:{s:"WRONG_DOCUMENT_ERR",c:4,m:1},InvalidCharacterError:{s:"INVALID_CHARACTER_ERR",c:5,m:1},NoDataAllowedError:{s:"NO_DATA_ALLOWED_ERR",c:6,m:0},NoModificationAllowedError:{s:"NO_MODIFICATION_ALLOWED_ERR",c:7,m:1},NotFoundError:{s:"NOT_FOUND_ERR",c:8,m:1},NotSupportedError:{s:"NOT_SUPPORTED_ERR",c:9,m:1},InUseAttributeError:{s:"INUSE_ATTRIBUTE_ERR",c:10,m:1},InvalidStateError:{s:"INVALID_STATE_ERR",c:11,m:1},SyntaxError:{s:"SYNTAX_ERR",c:12,m:1},InvalidModificationError:{s:"INVALID_MODIFICATION_ERR",c:13,m:1},NamespaceError:{s:"NAMESPACE_ERR",c:14,m:1},InvalidAccessError:{s:"INVALID_ACCESS_ERR",c:15,m:1},ValidationError:{s:"VALIDATION_ERR",c:16,m:0},TypeMismatchError:{s:"TYPE_MISMATCH_ERR",c:17,m:1},SecurityError:{s:"SECURITY_ERR",c:18,m:1},NetworkError:{s:"NETWORK_ERR",c:19,m:1},AbortError:{s:"ABORT_ERR",c:20,m:1},URLMismatchError:{s:"URL_MISMATCH_ERR",c:21,m:1},QuotaExceededError:{s:"QUOTA_EXCEEDED_ERR",c:22,m:1},TimeoutError:{s:"TIMEOUT_ERR",c:23,m:1},InvalidNodeTypeError:{s:"INVALID_NODE_TYPE_ERR",c:24,m:1},DataCloneError:{s:"DATA_CLONE_ERR",c:25,m:1}}});var ta=i((Mb,Bs)=>{"use strict";var qg=h(),Ms=Error,dg=qg("".replace),gg=(function(r){return String(new Ms(r).stack)})("zxcasd"),Fs=/\n\s*at [^:]*:[^\n]*/,hg=Fs.test(gg);Bs.exports=function(r,e){if(hg&&typeof r=="string"&&!Ms.prepareStackTrace)for(;e--;)r=dg(r,Fs,"");return r}});var Ys=i(()=>{"use strict";var Eg=M(),he=k(),Og=ra(),sa=g(),bg=rr(),ca=K(),Ee=A().f,Sg=L(),de=pe(),ge=x(),Tg=br(),Rg=D(),Us=mt(),ks=ne(),er=ea(),Ig=ta(),Gs=lr(),va=w(),$s=W(),tr="DOMException",ua="DATA_CLONE_ERR",be=he("Error"),$=he(tr)||(function(){try{var r=he("MessageChannel")||Og("worker_threads").MessageChannel;new r().port1.postMessage(new WeakMap)}catch(e){if(e.name===ua&&e.code===25)return e.constructor}})(),wg=$&&$.prototype,Ks=be.prototype,xg=Gs.set,mg=Gs.getterFor(tr),Pg="stack"in new be(tr),Vs=function(r){return ge(er,r)&&er[r].m?er[r].c:0},fa=function(){Tg(this,Ir);var e=arguments.length,t=ks(e<1?void 0:arguments[0]),a=ks(e<2?void 0:arguments[1],"Error"),o=Vs(a);if(xg(this,{type:tr,name:a,message:t,code:o}),va||(this.name=a,this.message=t,this.code=o),Pg){var n=new be(t);n.name=tr,Ee(this,"stack",ca(1,Ig(n.stack,1)))}},Ir=fa.prototype=bg(Ks),Ws=function(r){return{enumerable:!0,configurable:!0,get:r}},aa=function(r){return Ws(function(){return mg(this)[r]})};va&&(de(Ir,"code",aa("code")),de(Ir,"message",aa("message")),de(Ir,"name",aa("name")));Ee(Ir,"constructor",ca(1,fa));var Se=sa(function(){return!(new $ instanceof be)}),zs=Se||sa(function(){return Ks.toString!==Us||String(new $(1,2))!=="2: 1"}),Hs=Se||sa(function(){return new $(1,"DataCloneError").code!==25}),_g=Se||$[ua]!==25||wg[ua]!==25,Ls=$s?zs||Hs||_g:Se;Eg({global:!0,constructor:!0,forced:Ls},{DOMException:Ls?fa:$});var wr=he(tr),Oe=wr.prototype;zs&&($s||$===wr)&&Sg(Oe,"toString",Us);Hs&&va&&$===wr&&de(Oe,"code",Ws(function(){return Vs(Rg(this).name)}));for(ia in er)ge(er,ia)&&(na=er[ia],Rr=na.s,oa=ca(6,na.c),ge(wr,Rr)||Ee(wr,Rr,oa),ge(Oe,Rr)||Ee(Oe,Rr,oa));var na,Rr,oa,ia});var tc=i(()=>{"use strict";var Ag=M(),Cg=O(),Ea=k(),ga=K(),ha=A().f,Xs=x(),Dg=br(),Ng=zt(),Qs=ne(),la=ea(),jg=ta(),Mg=w(),rc=W(),mr="DOMException",ec=Ea("Error"),Pr=Ea(mr),Oa=function(){Dg(this,Fg);var e=arguments.length,t=Qs(e<1?void 0:arguments[0]),a=Qs(e<2?void 0:arguments[1],"Error"),o=new Pr(t,a),n=new ec(t);return n.name=mr,ha(o,"stack",ga(1,jg(n.stack,1))),Ng(o,this,Oa),o},Fg=Oa.prototype=Pr.prototype,Bg="stack"in new ec(mr),kg="stack"in new Pr(1,2),pa=Pr&&Mg&&Object.getOwnPropertyDescriptor(Cg,mr),Lg=!!pa&&!(pa.writable&&pa.configurable),Js=Bg&&!Lg&&!kg;Ag({global:!0,constructor:!0,forced:rc||Js},{DOMException:Js?Oa:Pr});var xr=Ea(mr),Zs=xr.prototype;if(Zs.constructor!==xr){rc||ha(Zs,"constructor",ga(1,xr));for(ya in la)Xs(la,ya)&&(qa=la[ya],da=qa.s,Xs(xr,da)||ha(xr,da,ga(6,qa.c)))}var qa,da,ya});var ic=i(()=>{"use strict";var Ug=k(),Gg=hr(),ac="DOMException";Gg(Ug(ac),ac)});var vc=i(($b,cc)=>{"use strict";var $g=h(),Kg=g(),nc=b(),Vg=dr(),Wg=k(),zg=st(),oc=function(){},uc=Wg("Reflect","construct"),ba=/^\s*(?:class|function)\b/,Hg=$g(ba.exec),Yg=!ba.test(oc),_r=function(e){if(!nc(e))return!1;try{return uc(oc,[],e),!0}catch{return!1}},sc=function(e){if(!nc(e))return!1;switch(Vg(e)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return Yg||!!Hg(ba,zg(e))}catch{return!0}};sc.sham=!0;cc.exports=!uc||Kg(function(){var r;return _r(_r.call)||!_r(Object)||!_r(function(){r=!0})||r})?sc:_r});var lc=i((Kb,fc)=>{"use strict";var Xg=w(),Qg=A(),Jg=K();fc.exports=function(r,e,t){Xg?Qg.f(r,e,Jg(0,t)):r[e]=t}});var yc=i((Vb,pc)=>{"use strict";var Zg=TypeError;pc.exports=function(r,e){if(r{"use strict";var rh=O(),eh=g(),qc=rh.RegExp,th=!eh(function(){var r=!0;try{qc(".","d")}catch{r=!1}var e={},t="",a=r?"dgimsy":"gimsy",o=function(f,v){Object.defineProperty(e,f,{get:function(){return t+=v,!0}})},n={dotAll:"s",global:"g",ignoreCase:"i",multiline:"m",sticky:"y"};r&&(n.hasIndices="d");for(var u in n)o(u,n[u]);var c=Object.getOwnPropertyDescriptor(qc.prototype,"flags").get.call(e);return c!==a||t!==a});dc.exports={correct:th}});var Ec=i((zb,hc)=>{"use strict";var ah=D();hc.exports=function(){var r=ah(this),e="";return r.hasIndices&&(e+="d"),r.global&&(e+="g"),r.ignoreCase&&(e+="i"),r.multiline&&(e+="m"),r.dotAll&&(e+="s"),r.unicode&&(e+="u"),r.unicodeSets&&(e+="v"),r.sticky&&(e+="y"),e}});var Sc=i((Hb,bc)=>{"use strict";var ih=B(),nh=x(),oh=or(),Oc=gc(),uh=Ec(),sh=RegExp.prototype;bc.exports=Oc.correct?function(r){return r.flags}:function(r){return!Oc.correct&&oh(sh,r)&&!nh(r,"flags")?ih(uh,r):r.flags}});var Rc=i((Yb,Tc)=>{"use strict";var Te=h(),Ar=Map.prototype;Tc.exports={Map,set:Te(Ar.set),get:Te(Ar.get),has:Te(Ar.has),remove:Te(Ar.delete),proto:Ar}});var Ta=i((Xb,Ic)=>{"use strict";var Sa=h(),Re=Set.prototype;Ic.exports={Set,add:Sa(Re.add),has:Sa(Re.has),remove:Sa(Re.delete),proto:Re}});var xc=i((Qb,wc)=>{"use strict";var ch=B();wc.exports=function(r,e,t){for(var a=t?r:r.iterator,o=r.next,n,u;!(n=ch(o,a)).done;)if(u=e(n.value),u!==void 0)return u}});var Dc=i((Jb,Cc)=>{"use strict";var mc=h(),vh=xc(),Pc=Ta(),fh=Pc.Set,_c=Pc.proto,lh=mc(_c.forEach),Ac=mc(_c.keys),ph=Ac(new fh).next;Cc.exports=function(r,e,t){return t?vh({iterator:Ac(r),next:ph},e):lh(r,e)}});var wa=i((Zb,jc)=>{"use strict";var yh=O(),qh=g(),Ra=Le(),Ia=Zt(),Nc=yh.structuredClone;jc.exports=!!Nc&&!qh(function(){if(Ia==="DENO"&&Ra>92||Ia==="NODE"&&Ra>94||Ia==="BROWSER"&&Ra>97)return!1;var r=new ArrayBuffer(8),e=Nc(r,{transfer:[r]});return r.byteLength!==0||e.byteLength!==8})});var kc=i((rS,Bc)=>{"use strict";var _a=O(),dh=ra(),gh=wa(),hh=_a.structuredClone,Mc=_a.ArrayBuffer,Ie=_a.MessageChannel,Pa=!1,xa,Fc,we,ma;if(gh)Pa=function(r){hh(r,{transfer:[r]})};else if(Mc)try{Ie||(xa=dh("worker_threads"),xa&&(Ie=xa.MessageChannel)),Ie&&(Fc=new Ie,we=new Mc(2),ma=function(r){Fc.port1.postMessage(null,[r])},we.byteLength===2&&(ma(we),we.byteLength===0&&(Pa=ma)))}catch{}Bc.exports=Pa});var Uc=i((eS,Lc)=>{"use strict";var Eh=g(),Oh=K();Lc.exports=!Eh(function(){var r=new Error("a");return"stack"in r?(Object.defineProperty(r,"stack",Oh(1,7)),r.stack!==7):!0})});var Jc=i(()=>{"use strict";var bh=W(),Sh=M(),R=O(),Dr=k(),jr=h(),Ma=g(),Th=sr(),ar=b(),Rh=vc(),Ih=H(),Ae=P(),wh=Lr(),xh=le(),Kc=D(),Pe=dr(),mh=x(),Ph=lc(),Aa=cr(),xe=yr(),_h=yc(),Ah=Sc(),Ce=Rc(),Fa=Ta(),Ch=Dc(),Gc=kc(),Dh=Uc(),Ba=wa(),Cr=R.Object,Nh=R.Array,Vc=R.Date,Wc=R.Error,jh=R.TypeError,Mh=R.PerformanceMark,J=Dr("DOMException"),Na=Ce.Map,ka=Ce.has,zc=Ce.get,_e=Ce.set,Hc=Fa.Set,Yc=Fa.add,Fh=Fa.has,Bh=Dr("Object","keys"),kh=jr([].push),Lh=jr((!0).valueOf),Uh=jr(1.1.valueOf),Gh=jr("".valueOf),$h=jr(Vc.prototype.getTime),ja=Th("structuredClone"),Nr="DataCloneError",me="Transferring",Xc=function(r){return!Ma(function(){var e=new R.Set([7]),t=r(e),a=r(Cr(7));return t===e||!t.has(7)||!Ae(a)||+a!=7})&&r},$c=function(r,e){return!Ma(function(){var t=new e,a=r({a:t,b:t});return!(a&&a.a===a.b&&a.a instanceof e&&a.a.stack===t.stack)})},Kh=function(r){return!Ma(function(){var e=r(new R.AggregateError([1],ja,{cause:3}));return e.name!=="AggregateError"||e.errors[0]!==1||e.message!==ja||e.cause!==3})},Q=R.structuredClone,Vh=bh||!$c(Q,Wc)||!$c(Q,J)||!Kh(Q),Wh=!Q&&Xc(function(r){return new Mh(ja,{detail:r}).detail}),z=Xc(Q)||Wh,Ca=function(r){throw new J("Uncloneable type: "+r,Nr)},_=function(r,e){throw new J((e||"Cloning")+" of "+r+" cannot be properly polyfilled in this engine",Nr)},Da=function(r,e){return z||_(e),z(r)},zh=function(){var r;try{r=new R.DataTransfer}catch{try{r=new R.ClipboardEvent("").clipboardData}catch{}}return r&&r.items&&r.files?r:null},Qc=function(r,e,t){if(ka(e,r))return zc(e,r);var a=t||Pe(r),o,n,u,c,f,v;if(a==="SharedArrayBuffer")z?o=z(r):o=r;else{var s=R.DataView;!s&&!ar(r.slice)&&_("ArrayBuffer");try{if(ar(r.slice)&&!r.resizable)o=r.slice(0);else for(n=r.byteLength,u=("maxByteLength"in r)?{maxByteLength:r.maxByteLength}:void 0,o=new ArrayBuffer(n,u),c=new s(r),f=new s(o),v=0;v1&&!Ih(arguments[1])?Kc(arguments[1]):void 0,a=t?t.transfer:void 0,o,n;a!==void 0&&(o=new Na,n=Yh(a,o));var u=T(e,o);return n&&Xh(n),u}})});var rv=i((iS,Zc)=>{"use strict";Mo();hu();Ou();Tu();ms();_s();Ys();tc();ic();Jc();var Qh=ht();Zc.exports=Qh.structuredClone});var tv=i((nS,ev)=>{"use strict";var Jh=rv();ev.exports=Jh});var oS=Mr(Qn(),1),uS=Mr(Eo(),1),sS=Mr(_o(),1),cS=Mr(tv(),1);})(); diff --git a/mobile/android/app/src/main/assets/reader/reader.css b/mobile/android/app/src/main/assets/reader/reader.css new file mode 100644 index 0000000..5d6ce79 --- /dev/null +++ b/mobile/android/app/src/main/assets/reader/reader.css @@ -0,0 +1,53 @@ +* { box-sizing: border-box; } +html { color-scheme: light dark; background: var(--background); color: var(--foreground); } +body { margin: 0; font: 16px/1.65 sans-serif; overflow-wrap: anywhere; } +main { max-width: 820px; margin: auto; padding: 12px 18px 40px; } +h1,h2,h3,h4,h5,h6 { line-height: 1.35; margin: 1.5em 0 .65em; scroll-margin-top: 12px; } +h1 { font-size: 1.65em; } h2 { font-size: 1.4em; } h3 { font-size: 1.2em; } +p,ul,ol { margin: .75em 0; } ul,ol { padding-left: 1.5em; } +a { color: var(--accent); text-underline-offset: 3px; } +a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; } +blockquote { margin: 1em 0; padding: 0 1em; border-left: 2px solid var(--border); color: var(--muted); } +hr { border: 0; border-top: 1px solid var(--border); margin: 1.5em 0; } +code,pre { font-family: monospace; font-size: 13px; } +code { background: var(--code-surface); padding: .1em .3em; border-radius: 3px; } +pre { margin: 0; padding: 12px; overflow-x: auto; line-height: 1.6; tab-size: 4; } +pre code { padding: 0; background: none; border-radius: 0; } +.code-block { position: relative; margin: 1em 0; background: var(--code-surface); border-radius: 6px; } +.copy-code { position: absolute; top: 0; right: 0; min-height: 44px; min-width: 60px; padding: 10px 12px; font-size: 12px; text-align: center; text-decoration: none; background: var(--code-surface); border-radius: 0 6px 0 0; } +figcaption { padding: 0 12px 10px; text-align: right; color: var(--muted); font: 11px/1.4 monospace; } +table { display: block; max-width: 100%; width: max-content; border-collapse: collapse; overflow-x: auto; margin: 1em 0; } +th,td { border: 1px solid var(--border); padding: 8px 12px; min-width: 80px; } +th { text-align: left; background: var(--surface); } +input[type=checkbox] { accent-color: var(--accent); margin-right: 6px; } +.image-link { display: block; padding: 12px 0; } +.source-layout { display: flex; overflow: auto; margin: -12px -18px 0; padding: 12px 0; } +.source-layout pre { overflow: visible; padding: 0 12px 0 0; } +.source-layout .line-numbers { position: sticky; left: 0; text-align: right; color: var(--muted); background: var(--background); padding: 0 12px; user-select: none; border-right: 1px solid var(--border); margin-right: 12px; } +.hljs-comment,.hljs-quote { color: var(--muted); font-style: italic; } +.hljs-keyword,.hljs-selector-tag,.hljs-literal,.hljs-meta { color: var(--accent); font-weight: 600; } +.hljs-title,.hljs-function,.hljs-section { color: var(--accent); } +.hljs-string,.hljs-attr,.hljs-number,.hljs-symbol { color: var(--foreground); font-weight: 600; } +@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto; } } +.conversation article { padding: 14px 0 18px; border-bottom: 1px solid var(--border); } +.conversation article:last-child { border-bottom: 0; } +.message-author { color: var(--muted); font-size: 12px; font-weight: 600; margin-bottom: 8px; } +.conversation .user-message { background: var(--surface); padding: 12px 14px; border-radius: 6px; } +.user-message > :first-child { margin-top: 0; } .user-message > :last-child { margin-bottom: 0; } +.tool-message { margin: 6px 0; } +.tool-message summary { cursor: pointer; min-height: 48px; padding: 12px 0; color: var(--muted); font-size: 14px; } +.tool-message summary:focus-visible { outline: 2px solid var(--accent); } +.message-notice { color: var(--muted); font-size: 12px; margin: 8px 0; } +.formula { position: relative; } +.formula-display { display: block; margin: 1em 0; max-width: 100%; } +.math-render { user-select: text; } +.formula-display .math-render { display: block; overflow-x: auto; overflow-y: hidden; padding: 6px 0; } +.formula-actions { position: absolute; z-index: 2; right: 0; bottom: 100%; display: none; background: var(--surface); border: 1px solid var(--border); white-space: nowrap; } +.formula:hover > .formula-actions,.formula:focus-within > .formula-actions { display: flex; } +.formula-actions a { min-height: 44px; padding: 10px 12px; font: 12px/24px sans-serif; text-decoration: none; } +.render-error { font: 13px/1.6 monospace; white-space: pre-wrap; } +.diagram { margin: 1em 0; } +.diagram-render { max-width: 100%; overflow-x: auto; padding: 8px 0; } +.diagram-render svg { display: block; margin: auto; max-width: none !important; } +.diagram > details > summary { min-height: 44px; padding: 10px 0; color: var(--muted); font-size: 12px; cursor: pointer; } +[data-render-error],[data-render-loading] { color: var(--muted); font-size: 13px; } diff --git a/mobile/android/app/src/main/assets/reader/reader.js b/mobile/android/app/src/main/assets/reader/reader.js new file mode 100644 index 0000000..ef5359b --- /dev/null +++ b/mobile/android/app/src/main/assets/reader/reader.js @@ -0,0 +1,36 @@ +// Large source tabs stay selectable without an expensive synchronous grammar pass. +function pebrelHighlight() { + if (!window.hljs) return; + document.querySelectorAll('pre code').forEach(function (block) { + if (block.textContent.length > 262144) return; + var language = (block.className.match(/language-([\w+-]+)/) || [])[1]; + if (language && hljs.getLanguage(language)) hljs.highlightElement(block); + }); +} +pebrelHighlight(); +window.pebrelEnhance(); + +// Keep the reader's place and expanded tools while a running Agent adds messages. +window.pebrelRender = function (html, initial) { + var root = document.scrollingElement || document.documentElement; + var bottom = root.scrollHeight - window.innerHeight - window.scrollY < 80; + var anchor = Array.from(document.querySelectorAll('[data-message]')).find(function (node) { + return node.getBoundingClientRect().bottom > 0; + }); + var anchorId = anchor && anchor.id; + var anchorTop = anchor && anchor.getBoundingClientRect().top; + var opened = new Set(Array.from(document.querySelectorAll('details[open]')).map(function (node) { return node.id; })); + document.querySelector('main').innerHTML = html; + document.querySelectorAll('details').forEach(function (node) { node.open = opened.has(node.id); }); + pebrelHighlight(); + var next = anchorId && document.getElementById(anchorId); + if (initial || bottom) window.scrollTo(0, root.scrollHeight); + else if (next) window.scrollBy(0, next.getBoundingClientRect().top - anchorTop); + var settledY = window.scrollY; + window.pebrelEnhance().then(function () { + // 公式和图表完成后补偿高度;用户已经主动滚动时,不再抢回阅读位置。 + if (Math.abs(window.scrollY - settledY) > 4) return; + if (initial || bottom) window.scrollTo(0, root.scrollHeight); + else if (next && next.isConnected) window.scrollBy(0, next.getBoundingClientRect().top - anchorTop); + }); +}; diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/MainActivity.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/MainActivity.kt new file mode 100644 index 0000000..de312db --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/MainActivity.kt @@ -0,0 +1,444 @@ +package io.github.kuddev.pebrel.mobile + +import android.Manifest +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.BackHandler +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.git.DesktopGitTarget +import io.github.kuddev.pebrel.mobile.session.* +import io.github.kuddev.pebrel.mobile.ui.* +import kotlinx.coroutines.launch +import kotlinx.coroutines.CancellationException + +/** Activity owns navigation only; transports, terminal state and drafts live in the application. */ +class MainActivity : ComponentActivity() { + private val launchTarget = mutableStateOf(null) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + launchTarget.value = intent + setContent { PebrelTheme { Workspace((application as PebrelApplication).sessions, launchTarget.value) } } + } + + override fun onNewIntent(intent: Intent) { super.onNewIntent(intent); launchTarget.value = intent } + override fun onStart() { + super.onStart() + (application as PebrelApplication).sessions.foregroundChanged(true) + } + override fun onStop() { + (application as PebrelApplication).sessions.foregroundChanged(false) + super.onStop() + } + + @OptIn(ExperimentalAnimationApi::class) + @Composable + private fun Workspace(repository: SessionRepository, target: Intent?) { + val sessions by repository.sessions.collectAsStateWithLifecycle() + val sftpTabs by repository.sftpTabs.collectAsStateWithLifecycle() + val desktops by repository.desktops.collectAsStateWithLifecycle() + val hosts by repository.hosts.collectAsStateWithLifecycle() + val relays by repository.relays.collectAsStateWithLifecycle() + val trust by repository.trust.collectAsStateWithLifecycle() + val error by repository.error.collectAsStateWithLifecycle() + val savedCredentials by repository.savedCredentials.collectAsStateWithLifecycle() + val desktopOutput by repository.output.collectAsStateWithLifecycle() + val credentialScope = rememberCoroutineScope() + var credentialBusy by remember { mutableStateOf(false) } + var page by rememberSaveable { + mutableStateOf(if (repository.display.connectionOnboardingCompleted) "home" else "onboarding") + } + var selected by rememberSaveable { mutableStateOf("") } + var desktopId by rememberSaveable { mutableStateOf("") } + var paneId by rememberSaveable { mutableLongStateOf(-1L) } + var windowId by rememberSaveable { mutableLongStateOf(-1L) } + var paneProcess by rememberSaveable { mutableStateOf(null) } + var fileTabId by rememberSaveable { mutableStateOf("") } + var sftpTabId by rememberSaveable { mutableStateOf("") } + var sftpPaths by rememberSaveable { mutableStateOf(mapOf()) } + var chatKind by rememberSaveable { mutableStateOf("") } + var chatSession by rememberSaveable { mutableStateOf("") } + var gitCwd by rememberSaveable { mutableStateOf("") } + var gitGeneration by rememberSaveable { mutableLongStateOf(-1L) } + var pageDirection by rememberSaveable { mutableStateOf("forward") } + var settingsInitial by rememberSaveable { mutableStateOf("") } + var hostForm by rememberSaveable { mutableStateOf(false) } + var editHost by remember { mutableStateOf(null) } + var deleteHost by remember { mutableStateOf(null) } + var addRelay by remember { mutableStateOf(false) } + var deployRelay by remember { mutableStateOf(false) } + var login by remember { mutableStateOf(null) } + var retrySession by remember { mutableStateOf(null) } + var switcher by remember { mutableStateOf(false) } + val desktop = desktops.find { it.id == desktopId } + val pane = desktop?.panes?.find { it.id == paneId && it.window == windowId } + val fileTab = desktop?.tabs?.find { it.id == fileTabId && it.window == windowId } + val motion = rememberPebrelMotion() + fun showPage(destination: String) { + pageDirection = "forward" + page = destination + } + fun finishOnboarding() { + if (page == "onboarding") { + repository.display.completeConnectionOnboarding() + showPage("home") + } + } + fun openSession(id: String) { selected = id; showPage("terminal"); switcher = false } + fun openDesktop(id: String) { desktopId = id; paneId = -1L; showPage("desktop"); switcher = false } + fun openSftpTab(entry: SftpTab) { + selected = entry.session; sftpTabId = entry.id; switcher = false; showPage("sftp_file") + } + fun openSftpFile(entry: SftpEntry) { openSftpTab(repository.openSftpTab(selected, entry)) } + fun closeSftpTab(entry: SftpTab) { + repository.closeSftpTab(entry.id) + if (page == "sftp_file" && sftpTabId == entry.id) { + val next = repository.sftpTabs.value.lastOrNull { it.session == entry.session } + if (next == null) showPage("sftp") else openSftpTab(next) + } + } + fun openPane(id: String, entry: DesktopPane) { + paneProcess = desktops.find { it.id == id }?.runtimeProcess + desktopId = id; windowId = entry.window; paneId = entry.id; showPage("pane"); switcher = false + } + fun openTab(id: String, entry: DesktopTab) { + val owner = desktops.find { it.id == id } ?: return + credentialScope.launch { + try { + if (owner.allowInput && owner.status == "ready" && entry.id != null && !entry.active) repository.desktopTabs(id).focus(entry) + val terminal = entry.primaryPane + if (terminal != null) openPane(id, terminal) + else if (entry.readable) { + desktopId = id; windowId = entry.window; fileTabId = entry.id.orEmpty() + paneProcess = owner.runtimeProcess; switcher = false; showPage("file") + } + } catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { repository.error.value = tabFailureCode(error) } + } + } + LaunchedEffect(desktopId, relays) { + if (page in setOf("pane", "desktop", "git", "file", "conversation")) repository.restoreDesktop(desktopId)?.let { desktopId = it } + } + fun connectHost(host: HostProfile, previous: String? = null) { + if (credentialBusy) return + if (!repository.hasSavedPassword(host)) { + retrySession = previous + login = host + return + } + credentialBusy = true + credentialScope.launch { + var secret: CharArray? = null + try { + secret = repository.loadSavedPassword(host) + if (secret == null) { + retrySession = previous + login = host + } else { + previous?.let(repository::closeTerminal) + openSession(repository.ssh(host, checkNotNull(secret))) + secret = null + } + } finally { + secret?.fill('\u0000') + credentialBusy = false + } + } + } + val openLocal = rememberLocalTerminalLauncher { + val id = repository.local() + finishOnboarding() + openSession(id) + } + var notificationRequested by rememberSaveable { mutableStateOf(false) } + val notificationPermission = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) {} + val hasLiveSessions = sessions.any { it.status == "ready" || it.status == "connecting" } || + desktops.any { it.status == "ready" || it.status == "connecting" } + LaunchedEffect(hasLiveSessions) { + if (hasLiveSessions && !notificationRequested && Build.VERSION.SDK_INT >= 33) { + notificationRequested = true + if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { + notificationPermission.launch(Manifest.permission.POST_NOTIFICATIONS) + } + } + } + fun back() { + val destination = when { + page == "sftp_file" -> "sftp" + page == "sftp" -> "terminal" + page in setOf("git", "conversation") && desktop != null -> "pane" + page in setOf("pane", "file") && desktop != null -> "desktop" + else -> "home" + } + pageDirection = "back" + page = destination + } + BackHandler(page != "home" && page != "onboarding") { back() } + LaunchedEffect(target) { + if (target?.action == "OPEN_TASK") { + desktopId = target.getStringExtra("desktop").orEmpty() + windowId = target.getLongExtra("window", -1) + paneId = target.getLongExtra("pane", -1) + showPage("pane") + } + } + val paneFrame = desktopOutput.frame.takeIf { + page == "pane" && desktopOutput.target == "$desktopId:$windowId:$paneId" + } + DesktopTerminalTheme(paneFrame, systemBars = true) { + Surface(Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { + Column(Modifier.fillMaxSize().systemBarsPadding().imePadding()) { + AnimatedContent( + modifier = Modifier.fillMaxSize(), + targetState = page, + transitionSpec = { motion.pageTransition(if (pageDirection == "back") PebrelNavigationDirection.Backward else PebrelNavigationDirection.Forward) }, + label = "workspace_page", + ) { route -> + Column(Modifier.fillMaxSize()) { + when (route) { + "onboarding" -> ConnectionOnboardingScreen( + onComputer = { addRelay = true }, + onSshHost = { editHost = null; hostForm = true }, + onLocal = openLocal, + onLater = ::finishOnboarding, + ) + "settings" -> key(settingsInitial) { + SettingsScreen(repository, ::back, { showPage("computers") }, { enabled -> + if (enabled) SessionService.start(this@MainActivity) else SessionService.stop(this@MainActivity) + }, settingsInitial) + } + "hosts" -> { + PageHeader(stringResource(R.string.ssh_hosts), ::back) { + GlyphButton(R.drawable.ic_plus, stringResource(R.string.add_ssh), { editHost = null; hostForm = true }) + } + HostsScreen(hosts, { connectHost(it) }, { editHost = it; hostForm = true }, { deleteHost = it }) + } + "computers" -> { + PageHeader(stringResource(R.string.computers), ::back) + Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(22.dp)) { + ComputerRows(desktops, relays, ::openDesktop, { openDesktop(repository.connectRelay(it)) }, + onAddRelay = { addRelay = true }, onForget = repository::forgetRelay) + HelperText(stringResource(R.string.connection_boundary), Modifier.padding(top = 20.dp)) + } + } + "terminal" -> { + val session = sessions.find { it.id == selected } + if (session != null) LocalTerminalScreen(session, repository, ::back, { switcher = true }, + onRetry = { session.host?.let { host -> + connectHost(hosts.find { it.id == host.id } ?: host, session.id) + } }, + onEdit = { session.host?.let { host -> + editHost = hosts.find { it.id == host.id } ?: host; hostForm = true + repository.closeTerminal(session.id); back() + } }, + onClose = { repository.closeTerminal(session.id); back() }, + onFiles = session.files?.let { { showPage("sftp") } }) + else LaunchedEffect(selected) { showPage("home") } + } + "sftp" -> { + val session = sessions.find { it.id == selected } + if (session?.files != null) key(selected) { + SftpBrowserScreen(session, sftpPaths[selected] ?: ".", { sftpPaths = sftpPaths + (selected to it) }, ::back, ::openSftpFile) + } else LaunchedEffect(selected) { showPage("home") } + } + "sftp_file" -> { + val session = sessions.find { it.id == selected } + val entry = sftpTabs.find { it.id == sftpTabId && it.session == selected } + if (session?.files != null && entry != null) key(entry.id) { + SftpFileScreen(session, entry, ::back, { switcher = true }, ::openSftpFile, { closeSftpTab(entry) }) + } else LaunchedEffect(selected, sftpTabId) { showPage(if (session?.files != null) "sftp" else "home") } + } + "pane" -> { + if (desktop != null && pane != null && (paneProcess == null || paneProcess == desktop.runtimeProcess)) DesktopTerminalScreen(desktop, pane, repository, ::back, { switcher = true }, + onGit = { + gitCwd = pane.cwd; gitGeneration = desktop.connectionGeneration; paneProcess = desktop.runtimeProcess + showPage("git") + }, onConversation = { + chatKind = pane.agent?.kind.orEmpty(); chatSession = pane.agent?.session.orEmpty() + showPage("conversation") + }) + else { + PageHeader(stringResource(R.string.computer_tabs), ::back) + HelperText(if (desktop?.status == "connecting") stringResource(R.string.desktop_reconnecting) + else stringResource(R.string.desktop_session_changed), Modifier.padding(22.dp)) + if (desktop != null) TextButton({ openDesktop(desktop.id) }) { Text(stringResource(R.string.computer_tabs)) } + } + } + "git" -> GitScreen(DesktopGitTarget(desktopId, windowId, paneId, gitCwd, gitGeneration, paneProcess), repository, ::back) + "conversation" -> { + if (desktop != null && pane != null && paneProcess == desktop.runtimeProcess && + chatKind in setOf("codex", "claude") && chatSession.isNotEmpty()) { + ConversationScreen(desktop, pane, ConversationIdentity(chatKind, chatSession), repository, + ::back, { switcher = true }, { openTab(desktopId, it) }) + } else { + PageHeader(stringResource(R.string.chat_title), ::back) + HelperText(stringResource(R.string.chat_unavailable), Modifier.padding(22.dp)) + if (chatSession.isEmpty() && pane?.agent?.kind == chatKind && pane.agent.session != null) { + LaunchedEffect(pane.agent.session) { chatSession = pane.agent.session.orEmpty() } + } + } + } + "file" -> { + if (desktop != null && fileTab?.readable == true && (paneProcess == null || paneProcess == desktop.runtimeProcess)) { + key(desktopId, fileTab.key) { + DesktopFileScreen(desktop, fileTab, repository, ::back, { switcher = true }, { openTab(desktopId, it) }) + } + } else { + PageHeader(stringResource(R.string.computer_tabs), ::back) + HelperText(stringResource(R.string.desktop_session_changed), Modifier.padding(22.dp)) + if (desktop != null) TextButton({ openDesktop(desktop.id) }) { Text(stringResource(R.string.computer_tabs)) } + } + } + "desktop" -> { + val profile = desktop?.relayProfile ?: relays.find { it.id == desktop?.host?.id } + DesktopTabsPage(desktop, repository, ::back, { openTab(desktopId, it) }, { openPane(desktopId, it) }, + onRetry = profile?.let { saved -> { openDesktop(repository.connectRelay(saved)) } }, + ) { repository.closeDesktop(desktopId); back() } + } + else -> { + HomeHeader({ settingsInitial = ""; showPage("settings") }, { settingsInitial = "notices"; showPage("settings") }, + showNotices = !isHomeEmpty(sessions, hosts, desktops, relays)) + HomeScreen(sessions, hosts, desktops, relays, ::openSession, { switcher = true }, { showPage("hosts") }, + { connectHost(it) }, { editHost = it; hostForm = true }, { deleteHost = it }, { editHost = null; hostForm = true }, + ::openDesktop, { openDesktop(repository.connectRelay(it)) }, { showPage("computers") }, { addRelay = true }, + openLocal, { deployRelay = true }, ::openPane, ::openTab) + } + } + } + } + } + } + } + if (switcher) AlertDialog(onDismissRequest = { switcher = false }, title = { Text(stringResource(R.string.all_sessions)) }, text = { + Column(Modifier.verticalScroll(rememberScrollState())) { + val cards = sessionCards(sessions, desktops) + if (cards.isEmpty()) HelperText(stringResource(R.string.no_sessions)) + cards.forEach { card -> + val local = card.local + if (local != null) { + NavigationRow(R.drawable.ic_terminal, local.title, statusLabel(local.status)) { openSession(local.id) } + sftpTabs.filter { it.session == local.id }.forEach { file -> + SftpTabRow(file, local.host?.name ?: local.title, page == "sftp_file" && sftpTabId == file.id, + { openSftpTab(file) }, { closeSftpTab(file) }) + } + } + else card.desktop?.let { computer -> + NavigationRow(card.tab?.let(::tabIcon) ?: R.drawable.ic_monitor, + card.tab?.displayTitle ?: card.pane?.displayTitle ?: computer.host.name, + "${computer.host.name} · ${statusLabel(computer.status)}") { + card.tab?.let { openTab(computer.id, it) } + ?: card.pane?.let { openPane(computer.id, it) } ?: openDesktop(computer.id) + } + } + } + desktop?.let { computer -> + NavigationRow(R.drawable.ic_plus, stringResource(R.string.tab_manage), computer.host.name) { openDesktop(computer.id) } + } + } + }, confirmButton = { TextButton({ switcher = false }) { Text(stringResource(R.string.close)) } }) + if (hostForm) HostForm( + initial = editHost, + onCancel = { hostForm = false }, + passwordSaved = savedCredentials.isNotEmpty() && editHost?.let(repository::hasSavedPassword) == true, + busy = credentialBusy, + onClearPassword = { + editHost?.let { host -> + credentialBusy = true + credentialScope.launch { + try { repository.clearHostPassword(host) } finally { credentialBusy = false } + } + } + }, + onSave = { host, password, rememberPassword, connect -> + credentialBusy = true + credentialScope.launch { + var connectionPassword: CharArray? = null + try { + if (connect) connectionPassword = repository.passwordForConnection(host, password) + if (connect && connectionPassword == null) repository.error.value = "credential_missing" + else if (repository.saveHostWithCredentials(host, password, rememberPassword)) { + hostForm = false + finishOnboarding() + connectionPassword?.let { secret -> + val saved = repository.hosts.value.first { it.id == host.id } + openSession(repository.ssh(saved, secret)) + connectionPassword = null + } + } + } finally { + password?.fill('\u0000') + connectionPassword?.fill('\u0000') + credentialBusy = false + } + } + }, + ) + deleteHost?.let { host -> AlertDialog(onDismissRequest = { deleteHost = null }, title = { Text(stringResource(R.string.delete_host)) }, + text = { Text(stringResource(R.string.delete_host_confirm, host.name)) }, + confirmButton = { TextButton({ repository.deleteHost(host); deleteHost = null }) { Text(stringResource(R.string.delete_host)) } }, + dismissButton = { TextButton({ deleteHost = null }) { Text(stringResource(R.string.cancel)) } }) } + if (addRelay) RelayForm(onCancel = { addRelay = false }, onConnect = { invitation -> + repository.importRelay(invitation)?.let { finishOnboarding(); openDesktop(it); addRelay = false } + }, onDeploy = { addRelay = false; deployRelay = true }) + if (deployRelay) RelayDeploymentFlow(repository, onCancel = { deployRelay = false }) + login?.let { host -> LoginForm( + host = host, + onCancel = { login = null; retrySession = null }, + passwordSaved = savedCredentials.isNotEmpty() && repository.hasSavedPassword(host), + busy = credentialBusy, + onClearPassword = { + credentialBusy = true + credentialScope.launch { + try { repository.clearHostPassword(host) } finally { credentialBusy = false } + } + }, + onConnect = { password, computer, input, rememberPassword -> + credentialBusy = true + credentialScope.launch { + var connectionPassword: CharArray? = null + try { + connectionPassword = repository.passwordForConnection(host, password) + if (connectionPassword == null) repository.error.value = "credential_missing" + else if (repository.saveHostWithCredentials(host, password, rememberPassword)) { + val secret = checkNotNull(connectionPassword) + retrySession?.let(repository::closeTerminal) + retrySession = null + if (computer) openDesktop(repository.connectDesktop(host, secret, input)) + else openSession(repository.ssh(host, secret)) + connectionPassword = null + login = null + } + } finally { + password?.fill('\u0000') + connectionPassword?.fill('\u0000') + credentialBusy = false + } + } + }, + ) } + trust?.takeUnless { page == "terminal" && it.ownerId == selected }?.let { request -> + HostTrustForm(request, repository::answerTrust) + } + error?.let { AlertDialog(onDismissRequest = { repository.error.value = null }, title = { Text(stringResource(R.string.operation_failed)) }, + text = { Text(operationErrorText(it)) }, confirmButton = { TextButton({ repository.error.value = null }) { Text(stringResource(R.string.close)) } }) } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/PebrelApplication.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/PebrelApplication.kt new file mode 100644 index 0000000..1c709fe --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/PebrelApplication.kt @@ -0,0 +1,24 @@ +package io.github.kuddev.pebrel.mobile + +import android.app.Application +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import io.github.kuddev.pebrel.mobile.session.TerminalFonts + +data class HostIconSpec(val id: String, val glyph: String, val zh: String, val en: String) + +class PebrelApplication : Application() { + val sessions by lazy { SessionRepository(this) } + val terminalTypeface by lazy { + TerminalFonts.typeface(this, "maple") + } + fun terminalTypeface(family: String): android.graphics.Typeface = TerminalFonts.typeface(this, family) + val themes by lazy { + org.json.JSONObject(assets.open("themes.json").bufferedReader().use { it.readText() }) + } + val hostIcons by lazy { + val rows = org.json.JSONArray(assets.open("host-icons.json").bufferedReader().use { it.readText() }) + (0 until rows.length()).map { rows.getJSONObject(it).let { row -> + HostIconSpec(row.getString("id"), row.getString("glyph"), row.getString("zh"), row.getString("en")) + } } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopConversation.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopConversation.kt new file mode 100644 index 0000000..0c17a08 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopConversation.kt @@ -0,0 +1,137 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import org.json.JSONObject +import java.util.concurrent.atomic.AtomicLong + +data class ConversationIdentity(val kind: String, val session: String, val epoch: Long? = null) { + fun json() = JSONObject().put("kind", kind).put("session_id", session).apply { epoch?.let { put("epoch", it) } } +} + +data class ConversationMessage(val id: String, val role: String, val text: String, val name: String?, + val detail: String?, val complete: Boolean, val truncated: Boolean) +data class ConversationPrompt(val id: String, val text: String, val options: List, val selected: Int, val binary: Boolean = false) +data class ConversationPage(val identity: ConversationIdentity, val messages: List = emptyList(), + val revision: String? = null, val before: Long? = null, val cwd: String = "", + val prompt: ConversationPrompt? = null, val canSend: Boolean = false, + val state: String = "unknown", val loaded: Boolean = false, val truncated: Boolean = false) + +/** Only recent, in-memory views survive navigation. Native transcripts stay on the computer. */ +class ConversationCache { + private val pages = LinkedHashMap() + @Synchronized fun get(key: String): ConversationPage? = pages[key]?.let { + it.copy(identity = it.identity.copy(epoch = null), revision = null, canSend = false, prompt = null) + } + @Synchronized fun put(key: String, page: ConversationPage) { + pages.remove(key) + pages[key] = page + while (pages.size > 4) pages.remove(pages.keys.first()) + } +} + +class DesktopConversation internal constructor( + initial: ConversationIdentity, + private val key: String, + private val cache: ConversationCache, + private val request: suspend (String, JSONObject) -> JSONObject, + private val availability: (Boolean) -> String?, +) { + private val current = MutableStateFlow(cache.get(key) ?: ConversationPage(initial)) + val state = current.asStateFlow() + private val reads = Mutex() + private val mutations = Mutex() + private val mutationVersion = AtomicLong() + private fun available(write: Boolean) { availability(write)?.let { throw DesktopRpcFailure(it) } } + + suspend fun refresh(older: Boolean = false) = reads.withLock { + available(false) + val version = mutationVersion.get() + if (version % 2L != 0L) return@withLock + val old = current.value + if (older && old.before == null) return@withLock + val params = JSONObject().put("identity", old.identity.json()) + if (older) params.put("before", old.before) else old.revision?.let { params.put("revision", it) } + val response = request("conversation.read", params) + currentCoroutineContext().ensureActive() + available(false) + // 发送前开始的慢读取不可重新启用旧选项/发送按钮;提交中的轮询也只等下一轮。 + if (version != mutationVersion.get()) return@withLock + val native = response.getJSONObject("identity") + val identity = ConversationIdentity(native.getString("kind"), native.getString("session_id"), native.getLong("epoch")) + check(identity.kind == old.identity.kind && identity.session == old.identity.session) + if (old.identity.epoch != null && old.identity.epoch != identity.epoch) throw DesktopRpcFailure("conversation_identity_changed") + val unchanged = response.getBoolean("unchanged") + val received = if (unchanged) emptyList() else response.getJSONArray("messages").let { rows -> + (0 until rows.length()).map { i -> rows.getJSONObject(i).let { row -> + ConversationMessage(row.getString("id"), row.getString("role"), row.getString("text"), + if (row.isNull("name")) null else row.getString("name"), + if (row.isNull("detail")) null else row.getString("detail"), row.getBoolean("complete"), row.getBoolean("truncated")) + } } + } + val messages = when { + unchanged -> old.messages + older -> (received + old.messages).distinctBy { it.id } + received.isEmpty() -> old.messages + else -> old.messages.takeWhile { offset(it.id) < offset(received.first().id) } + received + } + // 往前翻页也有明确上限;超限保留已读内容,不无限堆积 WebView 与 JVM 内存。 + if (messages.size > 640 || messages.sumOf { it.text.toByteArray().size + (it.detail?.toByteArray()?.size ?: 0) } > 1024 * 1024) { + throw DesktopRpcFailure("conversation_history_limit") + } + val prompt = response.optJSONObject("prompt")?.let { p -> + val options = p.getJSONArray("options") + ConversationPrompt(p.getString("id"), p.getString("text"), (0 until options.length()).map(options::getString), p.getInt("selected"), p.optBoolean("binary")) + } + val next = old.copy(identity = identity, messages = messages, + revision = if (older) old.revision else response.getString("revision"), + before = if (unchanged || (!older && old.loaded && old.messages.firstOrNull()?.id != received.firstOrNull()?.id)) old.before + else if (response.isNull("before")) null else response.getLong("before"), + cwd = response.optString("cwd", old.cwd), loaded = true, + prompt = prompt, canSend = response.getBoolean("can_send"), state = response.getString("state"), + truncated = old.truncated || response.optBoolean("truncated")) + current.value = next + cache.put(key, next) + } + + suspend fun send(text: String) = mutations.withLock { + available(true) + val page = current.value + if (!page.canSend || page.identity.epoch == null) throw DesktopRpcFailure("agent_busy") + mutate("conversation.send", JSONObject().put("identity", page.identity.json()).put("text", text)) + } + + suspend fun choose(prompt: ConversationPrompt, index: Int) = mutations.withLock { + available(true) + val page = current.value + if (page.prompt?.id != prompt.id || page.identity.epoch == null) throw DesktopRpcFailure("prompt_changed") + mutate("conversation.choose", JSONObject().put("identity", page.identity.json()) + .put("prompt_id", prompt.id).put("option", index)) + } + + suspend fun key(key: String) = mutations.withLock { + available(true) + val page = current.value + if (page.identity.epoch == null) throw DesktopRpcFailure("conversation_unavailable") + mutate("conversation.key", JSONObject().put("identity", page.identity.json()).put("key", key)) + } + + private suspend fun mutate(method: String, params: JSONObject) { + mutationVersion.incrementAndGet() + current.value = current.value.copy(canSend = false, prompt = null) + try { + val result = request(method, params) + available(false) + check(result.getBoolean("accepted")) + } finally { + // 即使结果不确定,也先重新读取;输入不会被重试或自动重放。 + mutationVersion.incrementAndGet() + } + } + + private fun offset(id: String): Long = id.substringBefore(':').toLong() +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopFailure.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopFailure.kt new file mode 100644 index 0000000..4e33a25 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopFailure.kt @@ -0,0 +1,55 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.TimeoutCancellationException +import org.json.JSONException +import java.io.IOException +import java.net.ConnectException +import java.net.NoRouteToHostException +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import java.security.cert.CertificateException +import java.security.cert.CertificateExpiredException +import java.security.cert.CertificateNotYetValidException +import javax.net.ssl.SSLException +import javax.net.ssl.SSLHandshakeException +import javax.net.ssl.SSLPeerUnverifiedException + +/** Stable, bounded diagnostics. Remote text and invitation credentials never reach the UI. */ +enum class DesktopFailureKind(val code: String) { + DNS("desktop_dns"), TIMEOUT("desktop_timeout"), REFUSED("desktop_refused"), + NETWORK("desktop_network"), CERTIFICATE_CHANGED("desktop_certificate_changed"), + CERTIFICATE_DATE("desktop_certificate_date"), TLS("desktop_tls"), + AUTHENTICATION("desktop_authentication"), ALREADY_CONNECTED("desktop_already_connected"), + SERVER("desktop_server"), PEER_OFFLINE("desktop_peer_offline"), + RUNTIME_UNAVAILABLE("desktop_runtime_unavailable"), PROTOCOL("desktop_protocol"), + DISCONNECTED("desktop_disconnected"), UNKNOWN("desktop_connection_failed"), +} + +class DesktopConnectionFailure(val kind: DesktopFailureKind, cause: Throwable? = null) : IOException(kind.code, cause) +internal class PairingCertificateChanged : CertificateException("Pairing certificate changed") + +fun classifyDesktopFailure(error: Throwable?, tlsEstablished: Boolean = false): DesktopFailureKind { + val causes = generateSequence(error) { it.cause }.take(12).toList() + causes.filterIsInstance().firstOrNull()?.let { return it.kind } + return when { + causes.any { it is PairingCertificateChanged } -> DesktopFailureKind.CERTIFICATE_CHANGED + causes.any { it is CertificateExpiredException || it is CertificateNotYetValidException } -> DesktopFailureKind.CERTIFICATE_DATE + causes.any { it is SSLPeerUnverifiedException || it is SSLHandshakeException || it is CertificateException } -> DesktopFailureKind.TLS + // Conscrypt 在已建立的 TLS 连接被网络中断时也会抛 SSLException; + // 只有握手/证书失败才应阻止重连,每次重连仍完整验证证书和主机身份。 + !tlsEstablished && causes.any { it is SSLException } -> DesktopFailureKind.TLS + causes.any { it is UnknownHostException } -> DesktopFailureKind.DNS + causes.any { it is SocketTimeoutException || it is TimeoutCancellationException } -> DesktopFailureKind.TIMEOUT + causes.any { it is ConnectException } -> DesktopFailureKind.REFUSED + causes.any { it is NoRouteToHostException || it is IOException } -> DesktopFailureKind.NETWORK + causes.any { it is JSONException } -> DesktopFailureKind.PROTOCOL + else -> DesktopFailureKind.UNKNOWN + } +} + +internal fun desktopHttpFailure(status: Int?): DesktopFailureKind? = when (status) { + null, 101 -> null + 401, 403 -> DesktopFailureKind.AUTHENTICATION + 409 -> DesktopFailureKind.ALREADY_CONNECTED + else -> DesktopFailureKind.SERVER +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopReadScheduler.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopReadScheduler.kt new file mode 100644 index 0000000..87f9bdb --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopReadScheduler.kt @@ -0,0 +1,21 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel + +/** One read in flight. Input wakes an idle reader and is never lost during a read. + * This remains a negotiated snapshot fallback, not a raw PTY subscription. + */ +internal class DesktopReadScheduler { + private val wake = Channel(Channel.CONFLATED) + fun refresh() { wake.trySend(Unit) } + + suspend fun run(read: suspend () -> Boolean) { + var interval = 0L + while (currentCoroutineContext().isActive) { + if (interval > 0 && withTimeoutOrNull(interval) { wake.receive(); true } == true) interval = 0 + val changed = read() + interval = if (changed) 33 else (interval * 2).coerceIn(33, 500) + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopReconnect.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopReconnect.kt new file mode 100644 index 0000000..b5cf410 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopReconnect.kt @@ -0,0 +1,39 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.* + +/** Main-thread owner. Only previously connected computers are enrolled by the + * repository. Background pauses retries; foreground resumes them immediately. + */ +internal class DesktopReconnect(private val scope: CoroutineScope, private val retry: (String) -> Unit) { + private val jobs = mutableMapOf() + private val attempts = mutableMapOf() + private var foreground = false + + fun foreground(value: Boolean) { + foreground = value + if (!value) { jobs.values.forEach(Job::cancel); jobs.clear() } + } + + fun schedule(id: String, immediate: Boolean = false) { + if (!foreground || jobs[id]?.isActive == true) return + val attempt = attempts[id] ?: 0 + attempts[id] = (attempt + 1).coerceAtMost(6) + jobs[id] = scope.launch(Dispatchers.Main.immediate) { + delay(if (immediate) 1 else (500L shl attempt).coerceAtMost(15_000)) + jobs.remove(id) + if (foreground) retry(id) + } + } + + fun cancelPending(id: String) { jobs.remove(id)?.cancel() } + fun forget(id: String) { cancelPending(id); attempts.remove(id) } + fun clear() { jobs.values.forEach(Job::cancel); jobs.clear(); attempts.clear() } + + companion object { + fun retryable(failure: DesktopFailureKind?) = failure !in setOf( + DesktopFailureKind.AUTHENTICATION, DesktopFailureKind.CERTIFICATE_CHANGED, + DesktopFailureKind.CERTIFICATE_DATE, DesktopFailureKind.TLS, DesktopFailureKind.PROTOCOL, + ) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopRuntimeClient.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopRuntimeClient.kt new file mode 100644 index 0000000..0a75b4c --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopRuntimeClient.kt @@ -0,0 +1,258 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.selects.select +import org.json.JSONObject +import java.io.Closeable +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicLong + +internal class DesktopRpcFailure(val code: String, val detail: String = "") : java.io.IOException(code) +data class DesktopPairingApproval(val code: String, val expiresAt: Long) + +/** One authority for bounded pending RPCs over SSH or a user-owned WSS relay. */ +class DesktopRuntimeClient( + private val transport: DesktopTransport, + private val onSnapshot: (JSONObject) -> Unit, + private val onDisconnected: (DesktopFailureKind) -> Unit, + private val onApproval: (DesktopPairingApproval) -> Unit = {}, +) : Closeable { + constructor(connection: SshConnection, onSnapshot: (JSONObject) -> Unit, onDisconnected: (DesktopFailureKind) -> Unit) : + this(SshDesktopTransport(connection), onSnapshot, onDisconnected) + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + private val writer = Mutex() + private val sequence = AtomicLong() + private val pending = ConcurrentHashMap>() + private val ready = CompletableDeferred() + private val approval = CompletableDeferred() + private val firstSnapshot = CompletableDeferred() + private val snapshotLock = Any() + private var latestSnapshot: JSONObject? = null + private var snapshotsActive = false + private var liveInput: Boolean? = null + private val closed = AtomicBoolean() + @Volatile private var screenUnsupported = false + @Volatile private var screenDelta = false + @Volatile private var screenStream = false + private val streaming = DesktopScreenStream(::request, acknowledge = { params -> + val call = startRequest("pane.screen.ack", params) + scope.async(start = CoroutineStart.UNDISPATCHED) { finishRequest(call); Unit } + }) + suspend fun streamPane(params: JSONObject, consume: suspend (DesktopPaneRead) -> Unit): Boolean { + if (!screenStream) return false + streaming.run(params, consume) + return true + } + private val screenSync = DesktopScreenSync() + private val screenReader = Mutex() + suspend fun resetScreen() = screenReader.withLock { screenSync.reset() } + + suspend fun readPane(params: JSONObject): DesktopPaneRead = screenReader.withLock { + if (screenUnsupported) return@withLock DesktopPaneRead(request("pane.read", params)) + val identity = "${params.getLong("window_id")}:${params.getLong("pane_id")}" + val query = JSONObject(params.toString()).put("screen", true) + if (screenDelta) query.put("screen_since", screenSync.since(identity)) + try { + val response = request("pane.read", query) + if (!screenDelta) return@withLock DesktopPaneRead(response) + try { screenSync.apply(identity, response) } + catch (_: Exception) { + // Only this idempotent read can be repeated; never resend input. + screenSync.reset() + screenSync.apply(identity, request("pane.read", query.put("screen_since", 0))) + } + } catch (failure: DesktopRpcFailure) { + // Old desktop runtimes reject unknown parameters. Do not silently + // downgrade malformed snapshots, transport errors or authorization. + if (failure.code != "invalid_params") throw failure + screenUnsupported = true + DesktopPaneRead(request("pane.read", params)) + } + } + + suspend fun connect(allowInput: Boolean): JSONObject { + try { + transport.open(allowInput, { message -> + if (!closed.get()) { + when { + message.optString("type") == "mobile.ready" -> ready.complete(message) + message.optString("type") == "mobile.approval" -> { + if (approval.complete(message)) onApproval(DesktopPairingApproval( + message.getString("code"), message.getLong("expiresAt"))) + } + message.optString("type") == "mobile.disconnected" -> disconnect( + DesktopConnectionFailure(DesktopFailureKind.RUNTIME_UNAVAILABLE)) + message.optString("type") == "mobile.policy" -> synchronized(snapshotLock) { + liveInput = message.getBoolean("allow_input") + latestSnapshot?.let { previous -> + val snapshot = JSONObject(previous.toString()) + val policy = snapshot.optJSONObject("mobile_policy") ?: JSONObject() + snapshot.put("mobile_policy", policy.put("allow_input", liveInput)) + latestSnapshot = snapshot + if (snapshotsActive && !closed.get()) onSnapshot(snapshot) + } + } + message.optString("event").startsWith("pane.screen") -> streaming.receive(message) + message.optString("event") == "runtime.snapshot" -> acceptSnapshot(message.getJSONObject("data")) + message.has("id") -> pending.remove(message.getString("id"))?.complete(message) + } + } + }, ::disconnect) + val hello = try { + val first = withTimeout(30_000) { + select { ready.onAwait { it }; approval.onAwait { it } } + } + if (first.optString("type") == "mobile.approval") { + val remaining = (first.getLong("expiresAt") * 1000 - System.currentTimeMillis()).coerceIn(1, 120_000) + withTimeout(remaining + 15_000) { ready.await() } + } else first + } + catch (error: TimeoutCancellationException) { + throw DesktopConnectionFailure(transport.readyTimeoutFailure(), error) + } + if (hello.optString("protocol") !in setOf("pebrel.mobile.ssh", "pebrel.mobile.relay") || hello.optInt("version") != 1) { + throw DesktopConnectionFailure(DesktopFailureKind.PROTOCOL) + } + screenDelta = hello.optJSONObject("capabilities")?.optBoolean("screen_delta") == true + screenStream = hello.optJSONObject("capabilities")?.optBoolean("terminal_grid_stream") == true + try { request("events.subscribe") } + catch (error: CancellationException) { throw error } + catch (error: Exception) { + throw if (error is DesktopConnectionFailure) error + else DesktopConnectionFailure(DesktopFailureKind.RUNTIME_UNAVAILABLE, error) + } + withTimeout(30_000) { firstSnapshot.await() } + synchronized(snapshotLock) { + check(!closed.get()) + snapshotsActive = true + onSnapshot(checkNotNull(latestSnapshot)) + } + return hello + } catch (error: TimeoutCancellationException) { + val failure = DesktopConnectionFailure(DesktopFailureKind.RUNTIME_UNAVAILABLE, error) + disconnect(failure) + throw failure + } catch (cancelled: CancellationException) { + close() + throw cancelled + } catch (error: Exception) { + val failure = if (error is DesktopConnectionFailure) error else DesktopConnectionFailure(classifyDesktopFailure(error), error) + disconnect(failure) + throw failure + } + } + + suspend fun request(method: String, params: JSONObject = JSONObject()): JSONObject { + return finishRequest(startRequest(method, params)) + } + + private fun acceptSnapshot(snapshot: JSONObject) = synchronized(snapshotLock) { + val previous = latestSnapshot + if (previous?.optLong("process_id") == snapshot.optLong("process_id") && + previous.optLong("revision") > snapshot.optLong("revision")) return@synchronized + if (!snapshot.has("mobile_policy")) previous?.optJSONObject("mobile_policy")?.let { + snapshot.put("mobile_policy", JSONObject(it.toString())) + } + liveInput?.let { input -> + val policy = snapshot.optJSONObject("mobile_policy") ?: JSONObject() + snapshot.put("mobile_policy", policy.put("allow_input", input)) + } + latestSnapshot = snapshot + firstSnapshot.complete(Unit) + if (snapshotsActive && !closed.get()) onSnapshot(snapshot) + } + + /** Send in wire order, without spending a network RTT before the next key. + * The input owner bounds this pipeline and must cancel abandoned receipts. + */ + internal suspend fun dispatchInput(method: String, params: JSONObject): Deferred { + require(method == "pane.prompt" || method == "pane.send_key") + val call = startRequest(method, params) + return scope.async(start = CoroutineStart.UNDISPATCHED) { finishRequest(call); Unit } + } + + private data class Call(val id: String, val completion: CompletableDeferred) + + private suspend fun startRequest(method: String, params: JSONObject): Call { + val id = sequence.incrementAndGet().toString() + val completion = CompletableDeferred() + val frame = JSONObject().put("id", id).put("method", method).put("params", params) + require(frame.toString().toByteArray().size + 1 <= 40 * 1024) + try { + withContext(Dispatchers.IO) { + writer.withLock { + check(!closed.get() && pending.size < 16) + pending[id] = completion + try { + transport.send(frame) + } catch (error: Exception) { + // A rejected send invalidates this connection as well as + // this request; settle all peers and leave the ready UI. + disconnect(error) + throw error + } + } + } + return Call(id, completion) + } catch (error: Throwable) { + pending.remove(id) + throw error + } + } + + private suspend fun finishRequest(call: Call): JSONObject { + try { + val response = withTimeout(35_000) { call.completion.await() } + if (!response.optBoolean("ok")) { + val error = response.optJSONObject("error") + throw DesktopRpcFailure(error?.optString("code")?.take(80) ?: "runtime_error", + error?.optJSONObject("details")?.optString("stderr")?.take(4096).orEmpty()) + } + val result = response.optJSONObject("result") ?: JSONObject() + result.optJSONObject("snapshot")?.let(::acceptSnapshot) + return result + } catch (timeout: TimeoutCancellationException) { + val failure = DesktopConnectionFailure(DesktopFailureKind.TIMEOUT, timeout) + disconnect(failure) + throw failure + } finally { pending.remove(call.id) } + } + private fun disconnect(error: Throwable?) { + if (!closed.compareAndSet(false, true)) return + val failure = DesktopConnectionFailure(if (error == null) DesktopFailureKind.DISCONNECTED else classifyDesktopFailure(error), error) + settle(failure) + onDisconnected(failure.kind) + scope.launch { try { transport.close() } finally { scope.cancel() } } + } + private fun settle(failure: DesktopConnectionFailure = DesktopConnectionFailure(DesktopFailureKind.DISCONNECTED)) { + streaming.close(failure) + ready.completeExceptionally(failure) + approval.completeExceptionally(failure) + firstSnapshot.completeExceptionally(failure) + pending.values.forEach { it.completeExceptionally(failure) } + pending.clear() + } + override fun close() { + if (!closed.compareAndSet(false, true)) return + settle() + transport.close() + scope.cancel() + } +} + +internal fun readBoundedFrame(input: java.io.InputStream, limit: Int): ByteArray? { + val bytes = java.io.ByteArrayOutputStream(minOf(4096, limit)) + while (true) { + val value = input.read() + if (value < 0) { + if (bytes.size() == 0) return null + throw java.io.EOFException("incomplete_frame") + } + if (bytes.size() >= limit) throw java.io.IOException("frame_too_large") + bytes.write(value) + if (value == 10) return bytes.toByteArray() + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenFrame.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenFrame.kt new file mode 100644 index 0000000..f33802e --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenFrame.kt @@ -0,0 +1,91 @@ +package io.github.kuddev.pebrel.mobile.connection + +import io.github.kuddev.pebrel.terminal.TerminalFrame +import io.github.kuddev.pebrel.terminal.TerminalRow +import org.json.JSONObject + +/** Strict decoder: packet text cannot inject ANSI, clipboard operations or terminal input. */ +internal fun decodeDesktopScreen(screen: JSONObject, theme: IntArray): TerminalFrame { + require(theme.size == 19 && screen.getInt("version") == 1) + val columns = screen.getInt("columns") + val rows = screen.getJSONArray("rows") + require(columns in 1..400 && rows.length() in 1..200 && columns * rows.length() <= 40_000) + val palette = IntArray(269) { index -> + val rgb = when (index) { + in 0..15 -> theme[index + 3] + in 16..231 -> { + fun channel(n: Int) = if (n == 0) 0 else 55 + n * 40 + val n = index - 16 + (channel(n / 36) shl 16) or (channel(n / 6 % 6) shl 8) or channel(n % 6) + } + in 232..255 -> (8 + (index - 232) * 10) * 0x010101 + 257 -> theme[1] + 258 -> theme[2] + in 259..266 -> theme[index - 259 + 3] + else -> theme[0] + } + rgb or 0xff000000.toInt() + } + val overrides = screen.getJSONArray("palette") + require(overrides.length() <= palette.size) + val seen = HashSet() + for (i in 0 until overrides.length()) { + val pair = overrides.getJSONArray(i) + require(pair.length() == 2) + val index = pair.getInt(0) + val rgb = pair.getLong(1) + require(index in palette.indices && seen.add(index) && rgb in 0..0xffffff) + palette[index] = rgb.toInt() or 0xff000000.toInt() + } + fun color(value: Long): Int { + require(value in -269..0xffffff) + return if (value < 0) palette[(-value - 1).toInt()] else value.toInt() or 0xff000000.toInt() + } + var textBytes = 0 + val decoded = Array(rows.length()) { y -> + val source = rows.getJSONArray(y) + require(source.length() in 1..columns) + val text = StringBuilder() + val cells = IntArray(columns * 6) + var x = 0 + for (i in 0 until source.length()) { + val cell = source.getJSONArray(i) + require(cell.length() == 5) + val glyph = cell.getString(0) + val width = cell.getInt(1) + val flags = cell.getInt(4) + val bytes = glyph.toByteArray(Charsets.UTF_8).size + textBytes += bytes + require(glyph.isNotEmpty() && bytes <= 256 && textBytes <= 128 * 1024) + require(glyph.none { it.code in 0..31 || it.code in 127..159 }) + require(width in 1..2 && x + width <= columns && flags in 0..63) + val offset = x * 6 + cells[offset] = text.length + cells[offset + 1] = glyph.length + cells[offset + 2] = width + cells[offset + 3] = color(cell.getLong(2)) + cells[offset + 4] = color(cell.getLong(3)) + cells[offset + 5] = flags + text.append(glyph) + x += width + } + require(x == columns) + TerminalRow(text.toString(), cells) + } + val cursor = screen.getJSONArray("cursor") + val wraps = screen.optJSONArray("wrapped")?.let { values -> + require(values.length() == rows.length()) + BooleanArray(values.length()) { i -> + require(values.get(i) is Boolean) + values.getBoolean(i) + } + } + require(cursor.length() == 3 && cursor.getInt(2) in 0..1) + val cx = cursor.getInt(0) + val cy = cursor.getInt(1) + if (cursor.getInt(2) == 1) require(cx in 0 until columns && cy in decoded.indices) + // The optional tail belongs to the phone's surrounding chrome. Native PTY + // frames keep their original eight entries; the cell painter is unchanged. + return TerminalFrame(decoded, intArrayOf(columns, decoded.size, cx, cy, cursor.getInt(2), + palette[257], palette[258], 2, palette[256], palette[1]), wraps) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenStream.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenStream.kt new file mode 100644 index 0000000..1b35af4 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenStream.kt @@ -0,0 +1,99 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.Semaphore +import kotlinx.coroutines.sync.withLock +import org.json.JSONObject + +/** One visible, identity-bound screen subscription. Apply every delta in order; + * acknowledge only after decode/publication, never when merely receiving bytes. + */ +internal class DesktopScreenStream( + private val request: suspend (String, JSONObject) -> JSONObject, + private val acknowledge: suspend (JSONObject) -> Deferred, + private val decoder: CoroutineDispatcher = Dispatchers.Default, +) { + private class Desynchronized(cause: Throwable? = null) : java.io.IOException("screen_resync_required", cause) + private val owner = Mutex() + @Volatile private var inbox: Channel? = null + + fun receive(frame: JSONObject) { + if (frame.optString("event") == "pane.screen.heartbeat") return + val target = inbox ?: return + if (target.trySend(frame).isFailure) target.close(Desynchronized()) + } + + fun close(cause: Throwable) { inbox?.close(cause) } + + suspend fun run(params: JSONObject, consume: suspend (DesktopPaneRead) -> Unit) = owner.withLock { + var recoveries = 0 + while (true) { + try { subscribe(params, consume); break } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { + val recoverable = error is Desynchronized || error is DesktopRpcFailure && error.code == "screen_stream_lost" + if (!recoverable || recoveries++ >= 2) throw error + // Only rebuild read-only screen state, never reconnect or replay + // uncertain input. Each replacement starts with a full snapshot. + delay(100L * recoveries) + } + } + } + + private suspend fun subscribe(params: JSONObject, consume: suspend (DesktopPaneRead) -> Unit) { + val frames = Channel(8) + inbox = frames + var subscription = 0L + try { + subscription = request("pane.screen.subscribe", params).getLong("subscription_id") + require(subscription > 0) + val identity = "${params.getLong("window_id")}:${params.getLong("pane_id")}" + val sync = DesktopScreenSync() + var sequence = 0L + coroutineScope { + val credit = Semaphore(4) + for (frame in frames) { + if (frame.optLong("subscription_id") != subscription) continue + if (frame.optString("event") == "pane.screen.error") throw DesktopRpcFailure( + frame.optJSONObject("error")?.optString("code") ?: "screen_stream_failed") + val (next, read) = withContext(decoder) { + try { + val next = frame.getLong("sequence") + require(next == sequence + 1) { "screen_sequence_mismatch" } + val data = frame.getJSONObject("data") + require(data.getLong("window_id") == params.getLong("window_id") && + data.getLong("pane_id") == params.getLong("pane_id")) + next to sync.apply(identity, data) + } catch (error: Exception) { throw Desynchronized(error) } + } + consume(read) + sequence = next + credit.acquire() + val receipt = try { acknowledge(control(params, subscription).put("sequence", next)) } + catch (error: Throwable) { credit.release(); throw error } + launch(start = CoroutineStart.UNDISPATCHED) { + try { receipt.await() } + finally { receipt.cancel(); credit.release() } + } + } + } + } finally { + if (inbox === frames) inbox = null + frames.cancel() + // Serializing replacement through owner prevents a late unsubscribe + // from tearing down the next pane's stream. Never cancel/replay input. + if (subscription > 0) withContext(NonCancellable) { + withTimeoutOrNull(2000) { + try { request("pane.screen.unsubscribe", control(params, subscription)) } + catch (_: Exception) { } + } + } + } + } + + private fun control(params: JSONObject, subscription: Long) = JSONObject() + .put("window_id", params.getLong("window_id")).put("pane_id", params.getLong("pane_id")) + .put("subscription_id", subscription) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenSync.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenSync.kt new file mode 100644 index 0000000..bdb70fa --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenSync.kt @@ -0,0 +1,56 @@ +package io.github.kuddev.pebrel.mobile.connection + +import org.json.JSONArray +import org.json.JSONObject + +data class DesktopPaneRead(val response: JSONObject, val screenChanged: Boolean = true) + +/** One bounded baseline per connection. Never apply a delta to another pane or revision. */ +internal class DesktopScreenSync { + private var target = "" + private var sequence = 0L + private var screen: JSONObject? = null + + fun since(identity: String): Long = if (target == identity) sequence else 0 + fun reset() { target = ""; sequence = 0; screen = null } + + fun apply(identity: String, response: JSONObject): DesktopPaneRead { + val next = response.getLong("screen_seq") + require(next > 0) + val full = response.optJSONObject("screen") + val old = screen + val updated = if (full != null) full else { + val delta = response.getJSONObject("screen_delta") + require(old != null && target == identity && delta.getLong("base") == sequence) + require(next == sequence || next == sequence + 1) + val rows = old.getJSONArray("rows") + val patch = delta.getJSONArray("rows") + require(patch.length() <= rows.length()) + val replacement = JSONArray() + for (i in 0 until rows.length()) replacement.put(rows.getJSONArray(i)) + var last = -1 + for (i in 0 until patch.length()) { + val pair = patch.getJSONArray(i) + require(pair.length() == 2) + val index = pair.getInt(0) + require(index > last && index < rows.length()) + replacement.put(index, pair.getJSONArray(1)) + last = index + } + require(next != sequence || patch.length() == 0 && + delta.getJSONArray("cursor").toString() == old.getJSONArray("cursor").toString() && + delta.getJSONArray("palette").toString() == old.getJSONArray("palette").toString() && + delta.optJSONArray("wrapped")?.toString() == old.optJSONArray("wrapped")?.toString()) + JSONObject().put("version", old.getInt("version")).put("columns", old.getInt("columns")) + .put("rows", replacement).put("cursor", delta.getJSONArray("cursor")).put("palette", delta.getJSONArray("palette")) + .apply { delta.optJSONArray("wrapped")?.let { put("wrapped", it) } } + } + val changed = full != null || target != identity || next != sequence + screen = updated + target = identity + sequence = next + response.put("screen", updated) + response.remove("screen_delta") + return DesktopPaneRead(response, changed) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopState.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopState.kt new file mode 100644 index 0000000..7fa9cf3 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopState.kt @@ -0,0 +1,87 @@ +package io.github.kuddev.pebrel.mobile.connection + +import org.json.JSONObject + +data class DesktopPane( + val window: Long, val id: Long, val title: String, val cwd: String, + val task: String, val state: String, val sequence: Long, + val sshDestination: String? = null, + val tabLabel: String = "", + val tabId: String? = null, + val agent: DesktopAgent? = null, +) { + // 显示跟随桌面 Tab;旧端只发进程路径时取末级,原路径仍用于详情和 Git 目标校验。 + val displayTitle: String get() = tabLabel.ifBlank { + val raw = title.ifBlank { cwd } + raw.trimEnd('/', '\\').substringAfterLast('/').substringAfterLast('\\').ifBlank { raw } + } +} + +data class DesktopAgent(val kind: String, val name: String, val session: String?) + +data class DesktopFile(val path: String, val remote: Boolean, val dirty: Boolean, + val saving: Boolean, val ready: Boolean, val revision: Long?) + +data class DesktopTab(val window: Long, val id: String?, val index: Int, val title: String, + val kind: String, val active: Boolean, val focusedPane: Long?, + val panes: List, val file: DesktopFile?) { + val key: String get() = "$window:${id ?: "legacy:$index"}" + val readable: Boolean get() = file != null && kind in setOf("document", "code", "image") + val primaryPane: DesktopPane? get() = panes.find { it.id == focusedPane } ?: panes.firstOrNull() + val displayTitle: String get() = title.trimEnd('/', '\\').substringAfterLast('/').substringAfterLast('\\') +} + +/** A projection of desktop authority; never infer task completion from terminal text. */ +fun parseDesktopTabs(snapshot: JSONObject): List = buildList { + val windows = snapshot.getJSONArray("windows") + for (w in 0 until windows.length()) { + val window = windows.getJSONObject(w) + val tabs = window.getJSONArray("tabs") + for (t in 0 until tabs.length()) { + val tab = tabs.getJSONObject(t) + val id = tab.optString("tab_id").takeIf { !tab.isNull("tab_id") && it.isNotBlank() } + val panes = tab.getJSONArray("panes") + val children = (0 until panes.length()).map { p -> + val pane = panes.getJSONObject(p) + DesktopPane(window.getLong("id"), pane.getLong("id"), + pane.optString("title"), pane.optString("cwd"), + if (pane.isNull("running_program")) "" else pane.optString("running_program"), + pane.optString("task_state", "unknown"), pane.optLong("state_change_seq"), + if (pane.isNull("ssh_destination")) null else pane.optString("ssh_destination"), tab.optString("label"), id, + pane.optJSONObject("agent")?.let { agent -> DesktopAgent(agent.getString("kind"), agent.optString("display_name"), + if (agent.isNull("session_id")) null else agent.getString("session_id")) }) + } + val file = tab.optJSONObject("file")?.let { + DesktopFile(it.getString("path"), it.optBoolean("remote"), it.optBoolean("dirty"), + it.optBoolean("saving"), it.optBoolean("ready"), if (it.isNull("revision")) null else it.getLong("revision")) + } + add(DesktopTab(window.getLong("id"), id, tab.optInt("index", t), tab.optString("label"), + tab.optString("kind", "shell"), tab.optBoolean("active"), + if (tab.isNull("focused_pane_id")) null else tab.getLong("focused_pane_id"), children, file)) + } + } +} + +fun parseDesktopPanes(snapshot: JSONObject): List = parseDesktopTabs(snapshot).flatMap { it.panes } + +/** Live de-dup only. This client does not advertise durable missed-event replay yet. */ +class DesktopTransitions { + private var process: Long? = null + private val sequences = LinkedHashMap, Long>() + fun observe(snapshot: JSONObject): List { + val id = snapshot.getLong("process_id") + val first = process != id + if (first) { sequences.clear(); process = id } + val panes = parseDesktopPanes(snapshot) + val changed = panes.filter { pane -> + val key = pane.window to pane.id + val previous = sequences[key] + if (previous == null || pane.sequence > previous) sequences[key] = pane.sequence + !first && previous != null && pane.sequence > previous && + pane.state in setOf("finished", "failed", "waiting_input", "attention") + } + sequences.keys.retainAll(panes.map { it.window to it.id }.toSet()) + // 停用提醒时仍推进序号;重新开启不会补发停用期间的旧事件。 + return if (snapshot.optJSONObject("mobile_policy")?.optBoolean("notifications", true) != false) changed else emptyList() + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopTabs.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopTabs.kt new file mode 100644 index 0000000..d4c2e48 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopTabs.kt @@ -0,0 +1,88 @@ +package io.github.kuddev.pebrel.mobile.connection + +import android.util.Base64 +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.withContext +import org.json.JSONObject +import java.io.ByteArrayOutputStream + +data class DesktopFileContent(val bytes: ByteArray, val revision: String, val kind: String) + +/** A view-owned handle: reconnecting never silently retargets an in-flight file or close request. */ +class DesktopTabs internal constructor( + private val request: suspend (String, JSONObject) -> JSONObject, + private val availability: (Boolean, DesktopTab?) -> String?, +) { + private fun checkAvailable(write: Boolean, tab: DesktopTab? = null) { + availability(write, tab)?.let { throw DesktopRpcFailure(it) } + if (tab != null && tab.id == null) throw DesktopRpcFailure("tabs_unsupported") + } + + private suspend fun mutate(method: String, params: JSONObject, tab: DesktopTab? = null): JSONObject { + checkAvailable(true, tab) + val result = request(method, params) + // 关闭会主动移除目标,因此返回时只复核连接,不能再要求被关闭的标签仍存在。 + checkAvailable(false) + return result + } + + suspend fun focus(tab: DesktopTab) = mutate("tab.focus", target(tab), tab) + suspend fun close(tab: DesktopTab) = mutate("tab.close", target(tab), tab) + + suspend fun newTerminal(window: Long?, cwd: String?): DesktopTab { + val params = JSONObject() + window?.let { params.put("window_id", it) } + cwd?.takeIf(String::isNotBlank)?.let { params.put("cwd", it) } + val result = mutate("tab.new", params) + val pane = result.getJSONObject("action").getLong("pane_id") + val resultWindow = result.getJSONObject("action").getLong("window_id") + return parseDesktopTabs(result.getJSONObject("snapshot")).first { it.window == resultWindow && it.panes.any { p -> p.id == pane } } + } + + suspend fun openFile(window: Long, path: String): DesktopTab { + val result = mutate("tab.open", JSONObject().put("window_id", window).put("path", path)) + val id = result.getJSONObject("action").getString("tab_id") + return parseDesktopTabs(result.getJSONObject("snapshot")).first { it.window == window && it.id == id } + } + + suspend fun read(tab: DesktopTab, onProgress: (Int, Int) -> Unit = { _, _ -> }): DesktopFileContent = withContext(Dispatchers.IO) { + val output = ByteArrayOutputStream() + var revision: String? = null + var total: Int? = null + var kind: String? = null + while (true) { + currentCoroutineContext().ensureActive() + checkAvailable(false, tab) + val params = target(tab).put("offset", output.size()).put("limit", 65536) + revision?.let { params.put("revision", it) } + val result = request("tab.read", params) + checkAvailable(false, tab) + val nextRevision = result.getString("revision") + val nextTotal = result.getInt("total_bytes") + val nextKind = result.getString("kind") + val bytes = Base64.decode(result.getString("data"), Base64.NO_WRAP) + check(result.getString("tab_id") == tab.id && result.getLong("window_id") == tab.window) + check(nextTotal in 0..MAX_FILE_BYTES && bytes.size <= 65536) + check(revision == null || revision == nextRevision) + check(total == null || total == nextTotal) + check(kind == null || kind == nextKind) + check(result.getInt("offset") == output.size() && result.getInt("next_offset") == output.size() + bytes.size) + check(output.size() + bytes.size <= nextTotal) + revision = nextRevision; total = nextTotal; kind = nextKind + output.write(bytes) + onProgress(output.size(), nextTotal) + if (result.getBoolean("eof")) { + check(output.size() == nextTotal) + return@withContext DesktopFileContent(output.toByteArray(), nextRevision, nextKind) + } + check(bytes.isNotEmpty()) + } + @Suppress("UNREACHABLE_CODE") error("unreachable") + } + + private fun target(tab: DesktopTab) = JSONObject().put("window_id", tab.window).put("tab_id", tab.id) + + companion object { const val MAX_FILE_BYTES = 16 * 1024 * 1024 } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopTerminalInput.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopTerminalInput.kt new file mode 100644 index 0000000..343210b --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopTerminalInput.kt @@ -0,0 +1,164 @@ +package io.github.kuddev.pebrel.mobile.connection + +import android.view.KeyEvent +import io.github.kuddev.pebrel.terminal.TerminalInputTarget +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.sync.Semaphore +import org.json.JSONObject +import java.io.Closeable + +/** One ordered, bounded writer per visible pane. Never replay after an uncertain + * response or reconnect; authorization and the original client are rechecked. + */ +class DesktopTerminalInput( + private val request: suspend (String, JSONObject) -> Unit, + private val active: () -> Boolean, + private val onAccepted: () -> Unit, + private val onRejected: (Boolean) -> Unit, + private val dispatch: (suspend (String, JSONObject) -> Deferred)? = null, +) : TerminalInputTarget, Closeable { + private data class Batch(val commands: List, val result: CompletableDeferred) + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) + private val queue = Channel(64, onUndeliveredElement = { it.result.complete(false) }) + private var closed = false + // Leave half of the RPC budget for screen acknowledgements and navigation. + private val credits = Semaphore(8) + private val results = mutableSetOf>() + + init { + scope.launch { + for (batch in queue) { + try { + val receipts = mutableListOf>() + for (command in batch.commands) { + credits.acquire() + if (!active()) { credits.release(); error("inactive_input_owner") } + if (dispatch == null) { + try { request(command.method, command.params) } + finally { credits.release() } + } else { + val receipt = try { dispatch.invoke(command.method, command.params) } + catch (error: Throwable) { credits.release(); throw error } + receipts += scope.async(start = CoroutineStart.UNDISPATCHED) { + try { receipt.await() } + catch (cancelled: CancellationException) { + if (currentCoroutineContext().isActive) rejectUncertain() + throw cancelled + } + catch (error: Exception) { rejectUncertain(); throw error } + finally { receipt.cancel(); credits.release() } + } + } + } + scope.launch(start = CoroutineStart.UNDISPATCHED) { + try { + receipts.awaitAll() + if (!closed) { batch.result.complete(true); onAccepted() } + } catch (cancelled: CancellationException) { throw cancelled } + catch (_: Exception) { rejectUncertain() } + finally { batch.result.complete(false); results.remove(batch.result) } + } + } catch (cancelled: CancellationException) { + batch.result.complete(false) + results.remove(batch.result) + throw cancelled + } catch (_: Exception) { + batch.result.complete(false) + results.remove(batch.result) + rejectUncertain() + } + } + } + } + + private fun rejectUncertain() { + if (!closed) { onRejected(true); close() } + } + + private fun enqueue(commands: List?): CompletableDeferred? { + if (closed || !active() || commands == null) { onRejected(false); return null } + val result = CompletableDeferred() + results += result + if (queue.trySend(Batch(commands, result)).isFailure) { + results.remove(result); result.complete(false); onRejected(false); return null + } + return result + } + + override fun text(text: String): Boolean = enqueue(encodeText(text)) != null + override fun paste(text: String): Boolean { + // The old Runtime bridge has no bracketed-paste capability. Never turn + // clipboard line breaks into implicit execution. Use the draft instead. + if (text.any { it.isISOControl() }) { onRejected(false); return false } + return text(text) + } + override fun key(code: Int, modifiers: Int, action: Int, text: String, unshifted: Int): Boolean { + if (action == 0) return !closed && active() // Runtime owns key encoding, not key-up reports. + return enqueue(encodeKey(code, modifiers, text)) != null + } + suspend fun submit(text: String): Boolean { + val commands = encodeText(text)?.plus(keyCommand("enter")) + return enqueue(commands)?.await() == true + } + override fun close() { + closed = true + val pendingResults = results.toList() + results.clear() + pendingResults.forEach { it.complete(false) } + queue.cancel() + scope.cancel() + } + + internal data class Command(val method: String, val params: JSONObject) + companion object { + internal fun encodeText(text: String): List? { + if (text.toByteArray().size > 8192 || text.any { it.isISOControl() && it !in "\r\n\t" }) return null + val commands = mutableListOf() + val plain = StringBuilder() + fun flush() { + if (plain.isNotEmpty()) { + commands += Command("pane.prompt", JSONObject().put("text", plain.toString()).put("submit", false)) + plain.clear() + } + } + for (char in text.replace("\r\n", "\n")) { + if (char in "\r\n\t") { + flush() + commands += keyCommand(if (char == '\t') "tab" else "enter") + } else plain.append(char) + if (commands.size > 128) return null + } + flush() + return commands.takeIf { it.size <= 128 } + } + internal fun encodeKey(code: Int, modifiers: Int, text: String): List? { + if (modifiers and 8 != 0) return null // No silent Meta-to-plain-text downgrade. + val name = when (code) { + KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_NUMPAD_ENTER -> "enter" + KeyEvent.KEYCODE_ESCAPE -> "escape" + KeyEvent.KEYCODE_TAB -> "tab" + KeyEvent.KEYCODE_DEL -> "backspace" + KeyEvent.KEYCODE_FORWARD_DEL -> "delete" + KeyEvent.KEYCODE_DPAD_LEFT -> "left" + KeyEvent.KEYCODE_DPAD_RIGHT -> "right" + KeyEvent.KEYCODE_DPAD_UP -> "up" + KeyEvent.KEYCODE_DPAD_DOWN -> "down" + KeyEvent.KEYCODE_MOVE_HOME -> "home" + KeyEvent.KEYCODE_MOVE_END -> "end" + KeyEvent.KEYCODE_INSERT -> "insert" + KeyEvent.KEYCODE_PAGE_UP -> "page_up" + KeyEvent.KEYCODE_PAGE_DOWN -> "page_down" + in KeyEvent.KEYCODE_F1..KeyEvent.KEYCODE_F12 -> "f${code - KeyEvent.KEYCODE_F1 + 1}" + in KeyEvent.KEYCODE_A..KeyEvent.KEYCODE_Z -> + if (modifiers and 2 != 0) ('a' + code - KeyEvent.KEYCODE_A).toString() else null + else -> null + } + if (name != null) return listOf(keyCommand(name, modifiers)) + return if (text.isNotEmpty() && modifiers and 6 == 0) encodeText(text) else null + } + private fun keyCommand(name: String, modifiers: Int = 0) = Command("pane.send_key", JSONObject() + .put("key", name).put("modifiers", JSONObject().put("shift", modifiers and 1 != 0) + .put("control", modifiers and 2 != 0).put("alt", modifiers and 4 != 0))) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopTransport.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopTransport.kt new file mode 100644 index 0000000..789d524 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/DesktopTransport.kt @@ -0,0 +1,56 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.* +import org.json.JSONObject +import java.io.BufferedInputStream +import java.io.Closeable +import java.util.concurrent.atomic.AtomicBoolean + +/** Transport lifetime is separate from RPC identity and the desktop's task state. */ +interface DesktopTransport : Closeable { + suspend fun open(allowInput: Boolean, receive: (JSONObject) -> Unit, disconnected: (Throwable?) -> Unit) + fun send(frame: JSONObject) + fun readyTimeoutFailure(): DesktopFailureKind = DesktopFailureKind.PEER_OFFLINE +} + +class SshDesktopTransport(private val connection: SshConnection) : DesktopTransport { + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + private val closed = AtomicBoolean() + + override suspend fun open(allowInput: Boolean, receive: (JSONObject) -> Unit, disconnected: (Throwable?) -> Unit) { + withContext(Dispatchers.IO) { + connection.connect() + check(!closed.get()) + connection.openExec(if (allowInput) "pebrel mobile-bridge --allow-input" else "pebrel mobile-bridge") + check(!closed.get()) + scope.launch { + var failure: Throwable? = null + try { + BufferedInputStream(connection.input(), 8192).use { input -> + while (!closed.get()) { + val bytes = readBoundedFrame(input, 2 * 1024 * 1024) ?: break + receive(JSONObject(bytes.toString(Charsets.UTF_8))) + } + } + } catch (error: Exception) { failure = error } + finally { if (!closed.get()) disconnected(failure); close() } + } + scope.launch { + runCatching { + val input = connection.input(stderr = true) + val scratch = ByteArray(4096) + while (!closed.get() && input.read(scratch) >= 0) { } + } + } + } + } + override fun send(frame: JSONObject) { + check(!closed.get()) + connection.output().apply { write((frame.toString() + "\n").toByteArray()); flush() } + } + override fun close() { + if (!closed.compareAndSet(false, true)) return + scope.cancel() + connection.close() + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/NativeRelayDeployment.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/NativeRelayDeployment.kt new file mode 100644 index 0000000..8944d81 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/NativeRelayDeployment.kt @@ -0,0 +1,214 @@ +package io.github.kuddev.pebrel.mobile.connection + +import android.content.Context +import kotlinx.coroutines.* +import org.json.JSONObject +import java.io.InputStream +import java.io.OutputStream +import java.security.MessageDigest +import java.util.Base64 + +enum class RelayServiceAction { STATUS, INSTALL, START, STOP, UNINSTALL } +data class RelayServiceState(val installed: Boolean, val running: Boolean, val ready: Boolean, val retained: Boolean) +data class RelayServiceResult(val state: RelayServiceState, val access: String? = null) +class RelayServiceFailure(val code: String) : java.io.IOException(code) + +/** Native Linux service adapter. Only explicit actions mutate the selected SSH host. + * A v2 access export belongs to the desktop, not to the phone's saved-PC list. + */ +object NativeRelayDeployment { + private const val BINARY = "/opt/pebrel-relay/pebrel-relay" + private val stages = setOf("checking", "uploaded", "initializing", "installing", "starting", "verifying", "ready", "stopping", "stopped", "removing", "uninstalled") + private val failures = setOf("systemd_247_required", "systemd_239_required", "linux_systemd_required", "root_required", + "installation_conflict", "managed_file_changed", "explicit_update_required", "service_not_ready", + "port_in_use", "permission_denied", "file_or_service_not_found", "binary_integrity_failed", + "supported_init_required", "openrc_supervisor_required", "service_manager_changed", + "configuration_directory_not_empty", "symlink_installation_path", "invalid_ownership_manifest", + "service_command_failed", "service_command_timeout", "service_command_output_limit", + "unprivileged_account_required", "privilege_drop_failed", "remote_tools_missing", + "operation_timeout", "relay_operation_failed") + + suspend fun execute( + context: Context, host: HostProfile, password: CharArray, + verify: (HostProfile, String) -> Boolean, action: RelayServiceAction, + address: String = host.address, port: Int = 443, purge: Boolean = false, + progress: (RelayServiceProgress) -> Unit, + ): RelayServiceResult = withTimeout(180_000) { + require(port in 1..65535) + val endpoint = validatedAddress(address) + val stage: (String) -> Unit = { progress(RelayServiceProgress(it)) } + DeploymentSsh(host, password, verify).use { ssh -> + stage("connecting") + val preflight = command(ssh, preflightCommand(), progress = stage, connected = { stage("checking") }) + val arch = preflight.last().getString("arch") + val state = command(ssh, """ + set -eu + if [ -L $BINARY ]; then printf '{"error":"managed_file_changed"}\n'; exit 1; fi + if [ -x $BINARY ]; then $BINARY service-status + else printf '{"installed":false,"running":false,"ready":false,"configuration_retained":false}\n'; fi + """.trimIndent(), progress = stage).last() + if (action == RelayServiceAction.STATUS) return@use RelayServiceResult(parseState(state)) + if (action == RelayServiceAction.INSTALL) { + val (sha, encoded) = withContext(Dispatchers.IO) { + val binary = loadBinary(context, arch) + digest(binary) to Base64.getEncoder().encode(binary) + } + // The random directory is created by this command, mode 0700. + // Cleanup names just its one file and then its empty directory. + val install = """ + set -eu + umask 077 + [ ! -L /opt ] || { printf '{"error":"symlink_installation_path"}\n'; exit 1; } + mkdir -p /opt + stage=${'$'}(mktemp -d /opt/.pebrel-relay.XXXXXXXX) + trap 'rm -f "${'$'}stage/pebrel-relay"; rmdir "${'$'}stage"' EXIT + head -c ${encoded.size} | base64 -d > "${'$'}stage/pebrel-relay" + printf '%s %s\n' '$sha' "${'$'}stage/pebrel-relay" | sha256sum -c - >/dev/null || { printf '{"error":"binary_integrity_failed"}\n'; exit 1; } + printf '{"event":"progress","stage":"uploaded"}\n' + chmod 700 "${'$'}stage/pebrel-relay" + "${'$'}stage/pebrel-relay" service-install --source "${'$'}stage/pebrel-relay" --sha256 '$sha' --address ${quote(endpoint)} --port $port + $BINARY export-access --directory /etc/pebrel-relay + printf '\n' + $BINARY service-status + """.trimIndent() + progress(RelayServiceProgress("uploading", 0, encoded.size)) + val result = command(ssh, install, encoded, stage) { sent, total -> + progress(RelayServiceProgress("uploading", sent, total)) + } + val access = result.firstOrNull { it.optInt("version") == 2 && it.has("desktopToken") } + ?: throw RelayServiceFailure("invalid_access") + validateAccess(access) + val final = parseState(result.last()) + if (!final.ready) throw RelayServiceFailure("service_not_ready") + return@use RelayServiceResult(final, access.toString()) + } + if (!state.optBoolean("installed")) throw RelayServiceFailure("file_or_service_not_found") + val operation = when (action) { + RelayServiceAction.START -> "service-start" + RelayServiceAction.STOP -> "service-stop" + RelayServiceAction.UNINSTALL -> "service-uninstall${if (purge) " --purge" else ""}" + else -> error("unreachable") + } + val result = command(ssh, "$BINARY $operation" + if (action != RelayServiceAction.UNINSTALL) "\n$BINARY service-status" else "", progress = stage) + if (action == RelayServiceAction.UNINSTALL) { + check(result.last().optBoolean("ok")) + RelayServiceResult(RelayServiceState(false, false, false, !purge)) + } else RelayServiceResult(parseState(result.last())) + } + } + + internal fun preflightCommand() = """ + set -eu + fail() { printf '{"error":"%s"}\n' "${'$'}1"; exit 1; } + [ "${'$'}(uname -s)" = Linux ] || fail linux_systemd_required + [ "${'$'}(id -u)" = 0 ] || fail root_required + for tool in head base64 sha256sum mktemp chmod; do + command -v "${'$'}tool" >/dev/null 2>&1 || fail remote_tools_missing + done + if [ -d /run/systemd/system ]; then + command -v systemctl >/dev/null 2>&1 || fail supported_init_required + elif [ -d /run/openrc ] && [ -x /sbin/rc-service ] && [ -x /sbin/openrc-run ]; then + [ -x /sbin/supervise-daemon ] && [ -x /sbin/rc-update ] || fail openrc_supervisor_required + else + fail supported_init_required + fi + printf '{"arch":"%s"}\n' "${'$'}(uname -m)" + """.trimIndent() + + internal fun checkedMessages(exit: Int, messages: List, errors: List): List { + val failure = (messages + errors).firstOrNull { it.has("error") }?.optString("error") + if (exit != 0 || failure != null) throw RelayServiceFailure(failure?.takeIf { it in failures } ?: "service_failed") + if (messages.isEmpty()) throw RelayServiceFailure("invalid_response") + return messages + } + + internal fun validatedAddress(value: String): String { + val host = value.trim().removePrefix("[").removeSuffix("]") + if (host.isEmpty() || host.length > 253 || !Regex("[A-Za-z0-9.:-]+").matches(host) || host.startsWith('-')) + throw RelayServiceFailure("invalid_address") + return host + } + private fun quote(value: String) = "'${value.replace("'", "'\"'\"'")}'" + private fun digest(bytes: ByteArray) = MessageDigest.getInstance("SHA-256").digest(bytes).joinToString("") { "%02x".format(it) } + private fun loadBinary(context: Context, arch: String): ByteArray { + if (arch !in setOf("x86_64", "aarch64")) throw RelayServiceFailure("unsupported_arch") + try { + val prefix = "native-relay/$arch" + val manifest = context.assets.open("$prefix/manifest.json").use { JSONObject(String(it.readBytes())) } + val bytes = context.assets.open("$prefix/pebrel-relay").use { it.readBytes() } + if (bytes.size !in 64..64 * 1024 * 1024 || manifest.getString("sha256") != digest(bytes) || + manifest.getString("arch") != arch || manifest.getInt("protocol") != 2) + throw RelayServiceFailure("binary_integrity_failed") + return bytes + } catch (error: RelayServiceFailure) { throw error } + catch (_: Exception) { throw RelayServiceFailure("asset_missing") } + } + internal fun parseState(json: JSONObject) = RelayServiceState(json.getBoolean("installed"), + json.getBoolean("running"), json.getBoolean("ready"), json.getBoolean("configuration_retained")) + internal fun validateAccess(json: JSONObject) { + val url = runCatching { java.net.URI(json.optString("url")) }.getOrNull() + if (json.optInt("version") != 2 || url == null || url.scheme != "wss" || url.host.isNullOrEmpty() || + url.userInfo != null || url.query != null || url.fragment != null || url.path !in setOf("", "/") || + !Regex("[A-Za-z0-9_-]{1,64}").matches(json.optString("room")) || + !Regex("sha256/[A-Za-z0-9+/]{43}=").matches(json.optString("tlsPin")) || + listOf("desktopToken", "mobileToken").any { !Regex("[A-Za-z0-9_-]{43}").matches(json.optString(it)) } || + json.optString("desktopToken") == json.optString("mobileToken")) throw RelayServiceFailure("invalid_access") + } + + private suspend fun command(ssh: DeploymentSsh, text: String, input: ByteArray? = null, + progress: (String) -> Unit, connected: () -> Unit = {}, upload: (Int, Int) -> Unit = { _, _ -> }): List = withTimeout(120_000) { + val connection = ssh.open(30_000) + try { + connected() + ssh.blocking(connection) { connection.openExec(text) } + coroutineScope { + val output = async(Dispatchers.IO) { ssh.blocking(connection) { readMessages(connection.input(false), progress) } } + val errors = async(Dispatchers.IO) { ssh.blocking(connection) { readMessages(connection.input(true), {}) } } + val writer = async(Dispatchers.IO) { if (input != null) ssh.blocking(connection) { + writeUpload(connection.output(), input, upload) + } } + val code = async(Dispatchers.IO) { ssh.blocking(connection) { connection.awaitExit() } } + val exit = code.await() + writer.await() + val messages = output.await() + checkedMessages(exit, messages, errors.await()) + } + } finally { ssh.release(connection) } + } + + internal fun writeUpload(output: OutputStream, bytes: ByteArray, progress: (Int, Int) -> Unit) { + var sent = 0 + while (sent < bytes.size) { + val count = minOf(32 * 1024, bytes.size - sent) + output.write(bytes, sent, count) + sent += count + progress(sent, bytes.size) + } + output.flush() + } + + internal fun readMessages(input: InputStream, progress: (String) -> Unit): List { + // Russh's unbuffered single-byte read allocates and crosses JNI for every byte. + val buffered = input.buffered(8192) + val result = mutableListOf() + var total = 0 + // export-access is pretty JSON; keep it as one bounded object. + val objectText = StringBuilder() + while (true) { + val line = readBoundedFrame(buffered, 8192)?.toString(Charsets.UTF_8)?.trimEnd() ?: break + total += line.length + if (total > 32 * 1024 || line.length > 8192) throw RelayServiceFailure("output_limit") + if (objectText.isNotEmpty() || line.startsWith('{')) { + objectText.append(line) + val json = runCatching { JSONObject(objectText.toString()) }.getOrNull() + if (json != null) { + objectText.clear() + if (json.optString("event") == "progress") { + json.optString("stage").takeIf { it in stages }?.let(progress) + } else result += json + } + } + } + return result + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/PairingCodeLookup.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/PairingCodeLookup.kt new file mode 100644 index 0000000..282a2d4 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/PairingCodeLookup.kt @@ -0,0 +1,81 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withTimeout +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import okhttp3.WebSocket +import okhttp3.WebSocketListener +import okio.ByteString +import org.json.JSONObject +import java.io.IOException +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicReference +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException + +internal class PairingCodeRejected : IOException("pairing_code_invalid") + +/** The short code retrieves an invitation, not a Runtime session or a device grant. */ +internal object PairingCodeLookup { + private val client = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS) + .readTimeout(5, TimeUnit.SECONDS).followRedirects(false).followSslRedirects(false) + .retryOnConnectionFailure(false).build() + + suspend fun redeem(computer: PairingComputer, code: String, base: OkHttpClient = client): RelayProfile { + require(Regex("[0-9]{8}").matches(code)) + val pinned = PinnedDesktopTls.client(base, computer.pin) + val request = Request.Builder().url(computer.address.newBuilder().encodedPath("/v2/pair").build()).build() + try { + return withTimeout(12_000) { + suspendCancellableCoroutine { continuation -> + val finished = AtomicBoolean() + val socket = AtomicReference() + fun fail(error: Throwable) { + if (finished.compareAndSet(false, true)) { + socket.get()?.cancel() + continuation.resumeWithException(error) + } + } + continuation.invokeOnCancellation { finished.set(true); socket.get()?.cancel() } + val connection = pinned.newWebSocket(request, object : WebSocketListener() { + override fun onOpen(webSocket: WebSocket, response: Response) { + if (finished.get()) { webSocket.cancel(); return } + if (!webSocket.send(JSONObject().put("code", code).toString())) fail(IOException("pairing_send_failed")) + } + override fun onMessage(webSocket: WebSocket, text: String) { + if (finished.get()) return + try { + require(text.length <= 16_384 && text.toByteArray(Charsets.UTF_8).size <= 16_384) + val response = JSONObject(text) + if (response.has("error")) throw PairingCodeRejected() + val profile = RelayProfile.parse(response.getString("invitation")) + val expectedUrl = computer.address.toString().replaceFirst("https://", "wss://").trimEnd('/') + require(profile.version == 2 && profile.mode == "lan" && profile.tlsPin == computer.pin && profile.url == expectedUrl) + require(profile.secure?.invitation == true && profile.secure!!.expiresAt > System.currentTimeMillis() / 1000) + // 广播指纹只用于这次引导;最终身份仍由两端本地 Noise 校验码和桌面批准确认。 + if (finished.compareAndSet(false, true)) { + webSocket.close(1000, null) + continuation.resume(profile) + } + } catch (error: Exception) { fail(error) } + } + override fun onMessage(webSocket: WebSocket, bytes: ByteString) = fail(IOException("invalid_pairing_response")) + override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { + webSocket.close(code, null) + fail(IOException("pairing_closed")) + } + override fun onFailure(webSocket: WebSocket, error: Throwable, response: Response?) = fail(error) + }) + socket.set(connection) + if (finished.get()) connection.cancel() + } + } + } catch (error: TimeoutCancellationException) { + throw IOException("pairing_timeout", error) + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/PairingDiscovery.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/PairingDiscovery.kt new file mode 100644 index 0000000..94626ef --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/PairingDiscovery.kt @@ -0,0 +1,160 @@ +package io.github.kuddev.pebrel.mobile.connection + +import android.content.Context +import android.net.nsd.NsdManager +import android.net.nsd.NsdServiceInfo +import android.os.Build +import android.os.Handler +import android.os.Looper +import androidx.annotation.RequiresApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import okhttp3.HttpUrl +import java.io.Closeable +import java.net.Inet4Address +import java.util.concurrent.Executor + +internal data class PairingComputer(val id: String, val name: String, val address: HttpUrl, val pin: String) +internal data class PairingDiscoveryState(val computers: List = emptyList(), val failed: Boolean = false) + +/** The pairing form or foreground reconnect owner closes discovery; it never persists credentials. */ +internal class PairingDiscovery(context: Context) : Closeable { + private val manager = context.applicationContext.getSystemService(NsdManager::class.java) + private val main = Handler(Looper.getMainLooper()) + private val executor = Executor { main.post(it) } + private val mutableState = MutableStateFlow(PairingDiscoveryState()) + val state = mutableState.asStateFlow() + private var closed = false + private var started = false + private val found = linkedMapOf() + private val computers = linkedMapOf() + private val pending = linkedMapOf() + private val registrations = mutableMapOf() + private var resolving: String? = null + + private fun publish() { + mutableState.value = mutableState.value.copy(computers = computers.values.sortedBy { it.name.lowercase() }) + } + + private fun update(id: String, info: NsdServiceInfo) { + if (closed || id !in found) return + val parsed = runCatching { + fun attribute(key: String) = info.attributes[key]?.toString(Charsets.UTF_8).orEmpty() + require(attribute("version") == "2") + val pin = attribute("pin") + require(Regex("sha256/[A-Za-z0-9+/]{43}=").matches(pin)) + require(info.port in 1..65535) + @Suppress("DEPRECATION") + val addresses = if (Build.VERSION.SDK_INT >= 34) info.hostAddresses else listOfNotNull(info.host) + val address = addresses.firstOrNull { it is Inet4Address } + ?: addresses.firstOrNull { !it.isLinkLocalAddress } + require(address != null && !address.isAnyLocalAddress && !address.isMulticastAddress) + val url = HttpUrl.Builder().scheme("https").host(checkNotNull(address.hostAddress)).port(info.port).build() + val name = attribute("name").trim().take(80).ifEmpty { info.serviceName.take(80) } + PairingComputer(id, name, url, pin) + }.getOrNull() + if (parsed != null) computers[id] = parsed else computers.remove(id) + publish() + } + + private val listener = object : NsdManager.DiscoveryListener { + override fun onDiscoveryStarted(type: String) = Unit + override fun onDiscoveryStopped(type: String) = Unit + override fun onStopDiscoveryFailed(type: String, error: Int) = Unit + override fun onStartDiscoveryFailed(type: String, error: Int) { + main.post { + if (!closed) { + mutableState.value = mutableState.value.copy(failed = true) + runCatching { manager.stopServiceDiscovery(this) } + started = false + } + } + } + override fun onServiceFound(info: NsdServiceInfo) { + main.post { + if (closed || info.serviceType.trimEnd('.') != SERVICE_TYPE.trimEnd('.')) return@post + val id = info.serviceName + if (id in found || found.size >= 32) return@post + found[id] = info + if (Build.VERSION.SDK_INT >= 34) track(id, info) + else { pending[id] = info; resolveNext() } + } + } + override fun onServiceLost(info: NsdServiceInfo) { + main.post { + if (closed) return@post + val id = info.serviceName + found.remove(id) + pending.remove(id) + computers.remove(id) + if (Build.VERSION.SDK_INT >= 34) untrack(id) + publish() + } + } + } + + fun start() { + check(Looper.myLooper() == Looper.getMainLooper()) + check(!closed && !started) + runCatching { + manager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, listener) + started = true + }.onFailure { mutableState.value = mutableState.value.copy(failed = true) } + } + + @RequiresApi(34) + private fun track(id: String, info: NsdServiceInfo) { + val callback = object : NsdManager.ServiceInfoCallback { + override fun onServiceUpdated(info: NsdServiceInfo) = update(id, info) + override fun onServiceLost() { if (!closed) { computers.remove(id); publish() } } + override fun onServiceInfoCallbackUnregistered() = Unit + override fun onServiceInfoCallbackRegistrationFailed(error: Int) { + registrations.remove(id) + if (!closed) { computers.remove(id); publish() } + } + } + registrations[id] = callback + runCatching { manager.registerServiceInfoCallback(info, executor, callback) } + .onFailure { registrations.remove(id) } + } + + @RequiresApi(34) + private fun untrack(id: String) { + (registrations.remove(id) as? NsdManager.ServiceInfoCallback)?.let { + runCatching { manager.unregisterServiceInfoCallback(it) } + } + } + + @Suppress("DEPRECATION") + private fun resolveNext() { + if (closed || resolving != null) return + val next = pending.entries.firstOrNull() ?: return + val id = next.key + val info = next.value + pending.remove(id) + resolving = id + val callback = object : NsdManager.ResolveListener { + override fun onResolveFailed(info: NsdServiceInfo, error: Int) { + main.post { resolving = null; resolveNext() } + } + override fun onServiceResolved(info: NsdServiceInfo) { + main.post { update(id, info); resolving = null; resolveNext() } + } + } + // Android 8–13 的旧 API 一次只解析一个服务,避免多台电脑互相触发 ALREADY_ACTIVE。 + runCatching { manager.resolveService(info, callback) }.onFailure { resolving = null; resolveNext() } + } + + override fun close() { + check(Looper.myLooper() == Looper.getMainLooper()) + if (closed) return + closed = true + if (started) runCatching { manager.stopServiceDiscovery(listener) } + if (Build.VERSION.SDK_INT >= 34) registrations.keys.toList().forEach(::untrack) + pending.clear() + found.clear() + computers.clear() + } + + companion object { private const val SERVICE_TYPE = "_pebrel-pair._tcp." } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/PinnedDesktopTls.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/PinnedDesktopTls.kt new file mode 100644 index 0000000..666420a --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/PinnedDesktopTls.kt @@ -0,0 +1,32 @@ +package io.github.kuddev.pebrel.mobile.connection + +import android.util.Base64 +import okhttp3.OkHttpClient +import java.security.MessageDigest +import java.security.cert.CertificateException +import java.security.cert.X509Certificate +import javax.net.ssl.SSLContext +import javax.net.ssl.X509TrustManager + +/** A scanned invitation can pin a private certificate without trusting other self-signed servers. */ +internal object PinnedDesktopTls { + fun client(base: OkHttpClient, pin: String): OkHttpClient { + val expected = Base64.decode(pin.removePrefix("sha256/"), Base64.NO_WRAP) + require(expected.size == 32) + val trust = object : X509TrustManager { + override fun getAcceptedIssuers(): Array = emptyArray() + override fun checkClientTrusted(chain: Array, authType: String) { + throw CertificateException("Client certificates are not accepted") + } + override fun checkServerTrusted(chain: Array, authType: String) { + val leaf = chain.firstOrNull() ?: throw CertificateException("Missing server certificate") + leaf.checkValidity() + val actual = MessageDigest.getInstance("SHA-256").digest(leaf.publicKey.encoded) + if (!MessageDigest.isEqual(expected, actual)) throw PairingCertificateChanged() + } + } + val context = SSLContext.getInstance("TLS").apply { init(null, arrayOf(trust), null) } + // OkHttp's normal SAN/hostname verification remains enabled. + return base.newBuilder().sslSocketFactory(context.socketFactory, trust).build() + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayDeployment.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayDeployment.kt new file mode 100644 index 0000000..1b86b51 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayDeployment.kt @@ -0,0 +1,880 @@ +package io.github.kuddev.pebrel.mobile.connection + +import android.content.Context +import android.util.Base64 +import io.github.kuddev.pebrel.ssh.NativeSshException +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.job +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import org.json.JSONObject +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream +import java.net.IDN +import java.nio.charset.StandardCharsets +import java.util.Locale +import java.util.UUID +import java.io.Closeable + +/** User-controlled values used to install one relay project on one SSH host. */ +data class RelayDeploymentRequest( + val domain: String, + val httpsPort: Int = 443, + val httpChallengePort: Int = 80, + val installDirectory: String = "~/.pebrel-relay", + val computerName: String = "Pebrel computer", + val allowInput: Boolean = false, +) { + /** Alias used by callers that call the public HTTPS endpoint a service port. */ + val servicePort: Int get() = httpsPort +} + +enum class RelayDeploymentStage { + VALIDATING, + CONNECTING, + CHECKING_PREREQUISITES, + PREPARING, + UPLOADING, + EXTRACTING, + INITIALIZING, + STARTING, + HEALTH_CHECK, + COMPLETE, +} + +/** Progress deliberately contains no remote output, paths or credentials. */ +data class RelayDeploymentProgress( + val stage: RelayDeploymentStage, + val step: Int, + val totalSteps: Int = 10, + val bytesSent: Long = 0, + val bytesTotal: Long = 0, +) + +enum class RelayDeploymentErrorCode { + ASSET_MISSING, + ASSET_INVALID, + INVALID_INPUT, + MISSING_CREDENTIALS, + SSH_TIMEOUT, + SSH_TRUST_REJECTED, + SSH_HOST_KEY_CHANGED, + SSH_AUTH, + SSH_FAILED, + DOCKER_MISSING, + DOCKER_UNAVAILABLE, + COMPOSE_MISSING, + TAR_MISSING, + BASE64_MISSING, + FIND_MISSING, + REMOTE_PERMISSION, + INSTALL_DIRECTORY_NOT_EMPTY, + UNSUPPORTED_PROJECT_LAYOUT, + UPLOAD_FAILED, + EXTRACT_FAILED, + INITIALIZE_FAILED, + START_FAILED, + HEALTH_CLIENT_MISSING, + HEALTH_FAILED, + RESULT_INVALID, + CANCELLED, + UNKNOWN, +} + +/** Typed deployment failures. The message is a stable code, never server output. */ +class RelayDeploymentException( + val code: RelayDeploymentErrorCode, + val safeOutput: String = "", + cause: Throwable? = null, +) : IOException(code.name.lowercase(Locale.ROOT), cause) + +/** + * Result of a completed and externally health-checked deployment. + * + * The two configuration strings are intentionally returned to the caller so the UI can + * provide an explicit copy/share action. They are never sent to progress callbacks. + */ +data class RelayDeploymentResult( + val mobileProfile: RelayProfile, + val mobileInvitation: String, + val desktopConfig: String, + val mobileInvitationFileName: String, + val desktopConfigFileName: String, + val desktopCommand: String, + val healthUrl: String, + val installDirectory: String, +) + +/** + * The native russh adapter owns one exec channel per session. Deployment therefore + * opens a fresh authenticated session for every remote command while retaining the + * fingerprint accepted during the first session. + */ +internal class DeploymentSsh( + private val host: HostProfile, + private val password: CharArray, + private val verifyHost: (HostProfile, String) -> Boolean, +) : Closeable { + private val guard = Any() + @Volatile private var active: SshConnection? = null + @Volatile private var closed = false + @Volatile private var trustedFingerprint = host.fingerprint + + suspend fun open(timeoutMs: Long): SshConnection { + val connection = synchronized(guard) { + check(!closed) { "closed" } + SshConnection(host.copy(fingerprint = trustedFingerprint), password.copyOf(), ::verify) + .also { active = it } + } + return try { + withTimeout(timeoutMs) { + cancellableBlocking(connection) { connection.connect() } + } + connection + } catch (error: Throwable) { + release(connection) + throw error + } + } + + /** Run one blocking native call without making coroutine cancellation wait for JNI. */ + private suspend fun cancellableBlocking( + connection: SshConnection, + action: () -> T, + ): T = withContext(Dispatchers.IO) { + suspendCancellableCoroutine { continuation -> + continuation.invokeOnCancellation { connection.close() } + try { + continuation.resume(action()) + } catch (error: Throwable) { + continuation.resumeWithException(error) + } + } + } + + suspend fun blocking(connection: SshConnection, action: () -> T): T = + cancellableBlocking(connection, action) + + fun release(connection: SshConnection) { + synchronized(guard) { + if (active === connection) active = null + } + connection.close() + } + + override fun close() { + val connection = synchronized(guard) { + closed = true + active.also { active = null } + } + connection?.close() + } + + private fun verify(profile: HostProfile, fingerprint: String): Boolean { + val known = trustedFingerprint + if (known.isNotEmpty()) return known == fingerprint + val accepted = verifyHost(host, fingerprint) + if (!accepted) return false + synchronized(guard) { + if (trustedFingerprint.isEmpty()) trustedFingerprint = fingerprint + return trustedFingerprint == fingerprint + } + } +} + +/** Android adapter for deploying the existing user-hosted relay over password SSH. */ +object RelayDeployment { + const val ASSET_NAME = "relay-kit.bin" + + private const val MAX_ARCHIVE_BYTES = 16 * 1024 * 1024 + private const val MAX_COMMAND_OUTPUT_BYTES = 48 * 1024 + private const val MAX_SMALL_OUTPUT_BYTES = 8 * 1024 + private const val CONNECT_TIMEOUT_MS = 90_000L + private const val COMMAND_TIMEOUT_MS = 120_000L + private const val UPLOAD_TIMEOUT_MS = 10 * 60_000L + private const val INIT_TIMEOUT_MS = 240_000L + private const val BUILD_TIMEOUT_MS = 15 * 60_000L + private const val HEALTH_TIMEOUT_MS = 45_000L + private const val CLEANUP_TIMEOUT_MS = 8_000L + private const val TOTAL_STEPS = 10 + + /** Loads the build-generated relay kit from the APK and deploys it on [host]. */ + suspend fun deploy( + context: Context, + host: HostProfile, + password: CharArray, + verify: (HostProfile, String) -> Boolean, + request: RelayDeploymentRequest, + onProgress: (RelayDeploymentProgress) -> Unit = {}, + ): RelayDeploymentResult { + val archive = try { + withContext(Dispatchers.IO) { + context.assets.open(ASSET_NAME).use { readBounded(it, MAX_ARCHIVE_BYTES) } + } + } catch (cancelled: CancellationException) { + throw cancelled + } catch (error: IOException) { + throw RelayDeploymentException(RelayDeploymentErrorCode.ASSET_MISSING, cause = error) + } catch (error: Exception) { + throw RelayDeploymentException(RelayDeploymentErrorCode.ASSET_MISSING, cause = error) + } + return deploy(host, password, verify, request, archive, onProgress) + } + + /** Entry point useful to tests and to build tooling that already has the asset bytes. */ + suspend fun deploy( + host: HostProfile, + password: CharArray, + verify: (HostProfile, String) -> Boolean, + request: RelayDeploymentRequest, + archive: ByteArray, + onProgress: (RelayDeploymentProgress) -> Unit = {}, + ): RelayDeploymentResult { + if (archive.isEmpty() || archive.size > MAX_ARCHIVE_BYTES) { + throw RelayDeploymentException(RelayDeploymentErrorCode.ASSET_INVALID) + } + val config = validate(request) + emit(onProgress, RelayDeploymentStage.VALIDATING, 1) + + val ssh = DeploymentSsh(host, password, verify) + val parentJob = currentCoroutineContext().job + val closeOnCancel = parentJob.invokeOnCompletion { cause -> + if (cause is CancellationException) ssh.close() + } + val deploymentId = UUID.randomUUID().toString().replace("-", "").take(16) + val project = config.installDirectory + val projectExpression = remotePathExpression(project) + val stage = ".pebrel-relay-stage-$deploymentId" + val archivePath = ".pebrel-relay-kit-$deploymentId.tar.gz" + var outputTail = "" + return try { + emit(onProgress, RelayDeploymentStage.CONNECTING, 2) + + emit(onProgress, RelayDeploymentStage.CHECKING_PREREQUISITES, 3) + val prerequisites = checkPrerequisites(ssh) + + val identity = runChecked( + ssh, + "identity", + "printf 'uid=%s\\ngid=%s\\n' \"\$(id -u)\" \"\$(id -g)\"", + MAX_SMALL_OUTPUT_BYTES, + COMMAND_TIMEOUT_MS, + ) + outputTail = safeRemoteOutput(identity) + val uid = marker(identity.stdout, "uid") ?: throw deploymentFailure(RelayDeploymentErrorCode.REMOTE_PERMISSION, identity) + val gid = marker(identity.stdout, "gid") ?: throw deploymentFailure(RelayDeploymentErrorCode.REMOTE_PERMISSION, identity) + + runChecked( + ssh, + "project", + "mkdir -p $projectExpression", + MAX_SMALL_OUTPUT_BYTES, + COMMAND_TIMEOUT_MS, + ) + emit(onProgress, RelayDeploymentStage.PREPARING, 4) + try { + runChecked( + ssh, + "prepare", + inProject(projectExpression, prepareCommand()), + MAX_SMALL_OUTPUT_BYTES, + COMMAND_TIMEOUT_MS, + ) + } catch (error: RelayDeploymentException) { + if (error.safeOutput.contains("install_directory_not_empty")) { + throw error.copyWith(RelayDeploymentErrorCode.INSTALL_DIRECTORY_NOT_EMPTY) + } + if (error.safeOutput.contains("unsupported_project_layout")) { + throw error.copyWith(RelayDeploymentErrorCode.UNSUPPORTED_PROJECT_LAYOUT) + } + throw error + } + + emit(onProgress, RelayDeploymentStage.UPLOADING, 5, 0, archive.size.toLong()) + uploadArchive(ssh, projectExpression, archivePath, archive, onProgress) + + emit(onProgress, RelayDeploymentStage.EXTRACTING, 6) + runChecked( + ssh, + "extract", + inProject(projectExpression, extractCommand(stage, archivePath)), + MAX_SMALL_OUTPUT_BYTES, + COMMAND_TIMEOUT_MS, + ) + runChecked( + ssh, + "install_source", + inProject(projectExpression, installSourceCommand(stage)), + MAX_SMALL_OUTPUT_BYTES, + COMMAND_TIMEOUT_MS, + ) + runChecked( + ssh, + "compose_config", + inProject(projectExpression, composeConfigCommand(config, uid, gid)), + MAX_SMALL_OUTPUT_BYTES, + COMMAND_TIMEOUT_MS, + ) + + emit(onProgress, RelayDeploymentStage.INITIALIZING, 7) + val init = try { + runChecked( + ssh, + "initialize", + inProject(projectExpression, initializeCommand(config, uid, gid)), + MAX_COMMAND_OUTPUT_BYTES, + INIT_TIMEOUT_MS, + ) + } catch (error: RelayDeploymentException) { + throw error.copyWith(RelayDeploymentErrorCode.INITIALIZE_FAILED) + } + outputTail = safeRemoteOutput(init) + val deviceId = marker(init.stdout, "PEBREL_DEVICE") + ?: throw RelayDeploymentException(RelayDeploymentErrorCode.RESULT_INVALID, safeRemoteOutput(init)) + val invitation = markerBlock(init.stdout, "PEBREL_INVITATION_BEGIN", "PEBREL_INVITATION_END") + ?: throw RelayDeploymentException(RelayDeploymentErrorCode.RESULT_INVALID, safeRemoteOutput(init)) + val desktop = markerBlock(init.stdout, "PEBREL_DESKTOP_BEGIN", "PEBREL_DESKTOP_END") + ?: throw RelayDeploymentException(RelayDeploymentErrorCode.RESULT_INVALID, safeRemoteOutput(init)) + val resultData = parseResult(config, project, deviceId, invitation, desktop) + + emit(onProgress, RelayDeploymentStage.STARTING, 8) + try { + val compose = composeCommand(prerequisites.compose) + val start = runChecked( + ssh, + "start", + inProject(projectExpression, "$compose up -d --build relay tls"), + MAX_COMMAND_OUTPUT_BYTES, + BUILD_TIMEOUT_MS, + ) + outputTail = safeRemoteOutput(start) + } catch (error: RelayDeploymentException) { + throw error.copyWith(RelayDeploymentErrorCode.START_FAILED) + } + + emit(onProgress, RelayDeploymentStage.HEALTH_CHECK, 9) + val health = try { + runCommand( + ssh, + healthCommand(resultData.healthUrl), + MAX_SMALL_OUTPUT_BYTES, + HEALTH_TIMEOUT_MS, + ) + } catch (error: RelayDeploymentException) { + throw error.copyWith(RelayDeploymentErrorCode.HEALTH_FAILED) + } + outputTail = safeRemoteOutput(health) + if (health.stdout.contains("health_client_missing") || health.stderr.contains("health_client_missing")) { + throw RelayDeploymentException(RelayDeploymentErrorCode.HEALTH_CLIENT_MISSING, outputTail) + } + if (health.exitCode != 0 || health.stdout.trim() != "ok") { + throw RelayDeploymentException(RelayDeploymentErrorCode.HEALTH_FAILED, outputTail) + } + + runChecked( + ssh, + "marker", + inProject(projectExpression, markerCommand()), + MAX_SMALL_OUTPUT_BYTES, + COMMAND_TIMEOUT_MS, + ) + emit(onProgress, RelayDeploymentStage.COMPLETE, 10) + resultData + } catch (cancelled: CancellationException) { + throw cancelled + } catch (error: RelayDeploymentException) { + throw error + } catch (error: Exception) { + throw RelayDeploymentException(RelayDeploymentErrorCode.UNKNOWN, outputTail, error) + } finally { + runCatching { + withContext(kotlinx.coroutines.NonCancellable) { + runCommand( + ssh, + inProject(projectExpression, "rm -rf -- ${shellQuote(stage)} ${shellQuote(archivePath)}"), + MAX_SMALL_OUTPUT_BYTES, + CLEANUP_TIMEOUT_MS, + ) + } + } + closeOnCancel.dispose() + ssh.close() + } + } + + private data class ValidatedRequest( + val domain: String, + val httpsPort: Int, + val httpChallengePort: Int, + val installDirectory: String, + val computerName: String, + val allowInput: Boolean, + ) + + private data class Prerequisites(val compose: ComposeKind) + + private enum class ComposeKind { PLUGIN, LEGACY } + + private data class CommandResult( + val exitCode: Int, + val stdout: String, + val stderr: String, + ) + + private fun validate(request: RelayDeploymentRequest): ValidatedRequest { + val domain = request.domain.trim().trimEnd('.') + val asciiDomain = try { IDN.toASCII(domain).lowercase(Locale.ROOT) } catch (_: Exception) { "" } + val labels = asciiDomain.split('.') + val validDomain = domain.isNotEmpty() && asciiDomain.length <= 253 && labels.all { label -> + label.isNotEmpty() && label.length <= 63 && label.first().isLetterOrDigit() && + label.last().isLetterOrDigit() && label.all { it.isLetterOrDigit() || it == '-' } + } + if (!validDomain || domain.any { it.isWhitespace() || it == '/' || it == ':' || it == '\u0000' }) { + throw RelayDeploymentException(RelayDeploymentErrorCode.INVALID_INPUT) + } + if (request.httpsPort !in 1..65535 || request.httpChallengePort !in 1..65535 || + request.httpsPort == request.httpChallengePort) { + throw RelayDeploymentException(RelayDeploymentErrorCode.INVALID_INPUT) + } + val directory = request.installDirectory.trim() + if (directory.isEmpty() || directory.length > 240 || directory == "/" || directory == "~" || + directory.split('/').any { it == "." || it == ".." } || directory.contains(':') || + !(directory.startsWith("/") || directory == "~" || directory.startsWith("~/")) || + directory.any { it == '\u0000' || it == '\r' || it == '\n' }) { + throw RelayDeploymentException(RelayDeploymentErrorCode.INVALID_INPUT) + } + val name = request.computerName.trim() + if (name.isEmpty() || name.length > 80 || name.any { it == '\u0000' || it == '\r' || it == '\n' }) { + throw RelayDeploymentException(RelayDeploymentErrorCode.INVALID_INPUT) + } + return ValidatedRequest(asciiDomain, request.httpsPort, request.httpChallengePort, directory, name, request.allowInput) + } + + private suspend fun checkPrerequisites(ssh: DeploymentSsh): Prerequisites { + val result = runCommand( + ssh, + """ + set -eu + for tool in docker tar base64 head find id; do + if ! command -v "${'$'}tool" >/dev/null 2>&1; then + printf 'tool_missing=%s\n' "${'$'}tool" + exit 20 + fi + done + if ! docker info >/dev/null 2>&1; then + printf 'docker_unavailable\n' + exit 21 + fi + if docker compose version >/dev/null 2>&1; then + printf 'compose=plugin\n' + exit 0 + fi + if command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then + printf 'compose=legacy\n' + exit 0 + fi + printf 'compose_missing\n' + exit 22 + """.trimIndent(), + MAX_SMALL_OUTPUT_BYTES, + COMMAND_TIMEOUT_MS, + ) + val text = result.stdout.trim() + if (result.exitCode != 0) { + when { + text.contains("tool_missing=docker") -> throw RelayDeploymentException(RelayDeploymentErrorCode.DOCKER_MISSING, safeRemoteOutput(result)) + text.contains("tool_missing=tar") -> throw RelayDeploymentException(RelayDeploymentErrorCode.TAR_MISSING, safeRemoteOutput(result)) + text.contains("tool_missing=base64") -> throw RelayDeploymentException(RelayDeploymentErrorCode.BASE64_MISSING, safeRemoteOutput(result)) + text.contains("tool_missing=head") -> throw RelayDeploymentException(RelayDeploymentErrorCode.BASE64_MISSING, safeRemoteOutput(result)) + text.contains("tool_missing=find") -> throw RelayDeploymentException(RelayDeploymentErrorCode.FIND_MISSING, safeRemoteOutput(result)) + text.contains("docker_unavailable") -> throw RelayDeploymentException(RelayDeploymentErrorCode.DOCKER_UNAVAILABLE, safeRemoteOutput(result)) + else -> throw RelayDeploymentException(RelayDeploymentErrorCode.COMPOSE_MISSING, safeRemoteOutput(result)) + } + } + return when { + text.contains("compose=plugin") -> Prerequisites(ComposeKind.PLUGIN) + text.contains("compose=legacy") -> Prerequisites(ComposeKind.LEGACY) + else -> throw RelayDeploymentException(RelayDeploymentErrorCode.COMPOSE_MISSING, safeRemoteOutput(result)) + } + } + + private suspend fun uploadArchive( + ssh: DeploymentSsh, + projectExpression: String, + archivePath: String, + archive: ByteArray, + onProgress: (RelayDeploymentProgress) -> Unit, + ) { + val encoded = Base64.encodeToString(archive, Base64.NO_WRAP).toByteArray(StandardCharsets.US_ASCII) + try { + runChecked( + ssh, + "upload", + inProject(projectExpression, "head -c ${encoded.size} | base64 -d > ${shellQuote(archivePath)}"), + MAX_SMALL_OUTPUT_BYTES, + UPLOAD_TIMEOUT_MS, + encoded, + ) + emit(onProgress, RelayDeploymentStage.UPLOADING, 5, archive.size.toLong(), archive.size.toLong()) + } finally { + encoded.fill(0) + } + } + + private fun prepareCommand(): String = """ + set -eu + umask 077 + if [ -L .pebrel-relay-deployment ] || [ -L relay ] || [ -L protocol ]; then + printf 'unsupported_project_layout\n'; exit 41 + fi + if [ -f .pebrel-relay-deployment ]; then + [ "${'$'}(cat .pebrel-relay-deployment)" = 'pebrel-relay-deployment-v1' ] || { + printf 'unsupported_project_layout\n'; exit 41; + } + elif [ -n "${'$'}(find . -mindepth 1 -maxdepth 1 -print -quit)" ]; then + printf 'install_directory_not_empty\n'; exit 41 + fi + mkdir -p relay protocol + [ ! -L relay/private ] || { printf 'unsupported_project_layout\n'; exit 41; } + printf '%s\n' 'pebrel-relay-deployment-v1' > .pebrel-relay-deployment + """.trimIndent() + + private fun extractCommand(stage: String, archivePath: String): String = """ + set -eu + rm -rf -- ${shellQuote(stage)} + mkdir -- ${shellQuote(stage)} + tar -tzf ${shellQuote(archivePath)} | while IFS= read -r entry; do + case "${'$'}entry" in + /*|../*|*/../*|*"/.."*) exit 42 ;; + esac + done + tar -xzf ${shellQuote(archivePath)} -C ${shellQuote(stage)} + test -f ${shellQuote("$stage/relay/compose.yaml")} + test -f ${shellQuote("$stage/relay/Caddyfile")} + test -f ${shellQuote("$stage/relay/Dockerfile")} + test -f ${shellQuote("$stage/relay/init.mjs")} + test -f ${shellQuote("$stage/relay/package.json")} + test -f ${shellQuote("$stage/relay/package-lock.json")} + test -f ${shellQuote("$stage/relay/connector.mjs")} + test -f ${shellQuote("$stage/relay/protocol.mjs")} + test -f ${shellQuote("$stage/relay/runtime-link.mjs")} + test -f ${shellQuote("$stage/relay/server.mjs")} + test -f ${shellQuote("$stage/protocol/bridge-policy.json")} + """.trimIndent() + + private fun installSourceCommand(stage: String): String = """ + set -eu + for file in Caddyfile Dockerfile compose.yaml connector.mjs init.mjs package-lock.json package.json protocol.mjs runtime-link.mjs server.mjs invite.mjs tls.mjs lan.mjs loopback-proxy.mjs pairing.mjs qr-page.mjs README.md THIRD-PARTY-NOTICES.md; do + [ ! -L "relay/${'$'}file" ] || { printf 'unsupported_project_layout\n'; exit 41; } + cp -- ${shellQuote("$stage/relay/")}${'$'}file relay/${'$'}file + done + [ ! -L protocol/bridge-policy.json ] || exit 41 + cp -- ${shellQuote("$stage/protocol/bridge-policy.json")} protocol/bridge-policy.json + """.trimIndent() + + private fun composeConfigCommand(config: ValidatedRequest, uid: String, gid: String): String { + val projectId = java.security.MessageDigest.getInstance("SHA-256") + .digest(config.installDirectory.toByteArray()).take(8).joinToString("") { "%02x".format(it) } + val environment = listOf( + "COMPOSE_PROJECT_NAME=pebrel-$uid-$projectId", + "PEBREL_RELAY_DOMAIN=${config.domain}", + "PEBREL_RELAY_HTTP_PORT=${config.httpChallengePort}", + "PEBREL_RELAY_HTTPS_PORT=${config.httpsPort}", + "PEBREL_RELAY_UID=$uid", + "PEBREL_RELAY_GID=$gid", + ).joinToString(" ") { shellQuote(it) } + return """ + set -eu + umask 077 + [ ! -L relay/.pebrel-deploy.env ] || exit 41 + printf '%s\n' $environment > relay/.pebrel-deploy.env + chmod 600 relay/.pebrel-deploy.env + """.trimIndent() + } + + private fun initializeCommand(config: ValidatedRequest, uid: String, gid: String): String { + val initialize = """ + import { readFileSync, writeFileSync, existsSync, readdirSync } from 'node:fs'; + import { execFileSync } from 'node:child_process'; + const domain = process.env.PEBREL_RELAY_DOMAIN; + const name = process.env.PEBREL_COMPUTER_NAME; + const port = Number(process.env.PEBREL_RELAY_HTTPS_PORT); + const origin = 'wss://' + domain + (port === 443 ? '' : ':' + port); + const marker = 'private/.pebrel-last-pairing.json'; + const previous = existsSync(marker) ? JSON.parse(readFileSync(marker, 'utf8')) : null; + let id; + if (previous && previous.name === name && previous.url === origin) { + id = previous.device; + if (!/^[a-zA-Z0-9_-]{1,64}${'$'}/.test(id)) throw new Error('invalid_previous_pairing'); + } else { + const before = new Set(existsSync('private') ? readdirSync('private') : []); + execFileSync(process.execPath, ['init.mjs', '--url', 'wss://' + domain, '--name', name, '--output', 'private'], { stdio: 'ignore' }); + const file = readdirSync('private').find(value => /^phone-[a-f0-9]+\.txt${'$'}/.test(value) && !before.has(value)); + if (!file) throw new Error('missing_phone_configuration'); + id = file.slice(6, -4); + } + for (const file of ['private/phone-' + id + '.txt', 'private/computer-' + id + '.json']) { + const value = JSON.parse(readFileSync(file, 'utf8')); + value.url = origin; + writeFileSync(file, JSON.stringify(value, null, 2) + '\n', { mode: 0o600 }); + } + writeFileSync(marker, JSON.stringify({ name, url: origin, device: id }), { mode: 0o600 }); + console.log('PEBREL_DEVICE=' + id); + console.log('PEBREL_INVITATION_BEGIN'); + console.log(readFileSync('private/phone-' + id + '.txt', 'utf8').trim()); + console.log('PEBREL_INVITATION_END'); + console.log('PEBREL_DESKTOP_BEGIN'); + console.log(readFileSync('private/computer-' + id + '.json', 'utf8').trim()); + console.log('PEBREL_DESKTOP_END'); + """.trimIndent() + return """ + set -eu + docker run --rm --user ${shellQuote("$uid:$gid")} \ + -e ${shellQuote("PEBREL_RELAY_DOMAIN=${config.domain}")} \ + -e ${shellQuote("PEBREL_RELAY_HTTPS_PORT=${config.httpsPort}")} \ + -e ${shellQuote("PEBREL_COMPUTER_NAME=${config.computerName}")} \ + -v "${'$'}PWD:/work" -w /work/relay node:22-alpine node --input-type=module -e ${shellQuote(initialize)} + """.trimIndent() + } + + private fun composeCommand(kind: ComposeKind): String { + val binary = when (kind) { + ComposeKind.PLUGIN -> "docker compose" + ComposeKind.LEGACY -> "docker-compose" + } + return "$binary --env-file relay/.pebrel-deploy.env -f relay/compose.yaml" + } + + private fun healthCommand(url: String): String = """ + set -eu + if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then + printf 'health_client_missing\n'; exit 31 + fi + for attempt in 1 2 3 4 5; do + if command -v curl >/dev/null 2>&1; then + curl --fail --silent --show-error --max-time 5 ${shellQuote(url)} && exit 0 + else + wget -qO- --timeout=5 --tries=1 ${shellQuote(url)} && exit 0 + fi + sleep 2 + done + exit 32 + """.trimIndent() + + private fun markerCommand(): String = """ + set -eu + printf '%s\n' 'pebrel-relay-deployment-v1' > .pebrel-relay-deployment + """.trimIndent() + + private fun parseResult( + config: ValidatedRequest, + project: String, + deviceId: String, + invitation: String, + desktop: String, + ): RelayDeploymentResult { + val profile = try { RelayProfile.parse(invitation.trim()) } + catch (error: Exception) { throw RelayDeploymentException(RelayDeploymentErrorCode.RESULT_INVALID, cause = error) } + val desktopObject = try { JSONObject(desktop.trim()) } + catch (error: Exception) { throw RelayDeploymentException(RelayDeploymentErrorCode.RESULT_INVALID, cause = error) } + val desktopDevice = desktopObject.optString("device") + val desktopToken = desktopObject.optString("token") + val desktopUrl = desktopObject.optString("url") + val desktopName = desktopObject.optString("name") + if (desktopDevice != deviceId || desktopDevice != profile.device || + desktopToken.isEmpty() || desktopToken == profile.token || desktopToken.length != 43 || + !Regex("[a-zA-Z0-9_-]{43}").matches(desktopToken) || + desktopUrl != profile.url || desktopName != config.computerName) { + throw RelayDeploymentException(RelayDeploymentErrorCode.RESULT_INVALID) + } + val authority = if (config.httpsPort == 443) config.domain else "${config.domain}:${config.httpsPort}" + val healthUrl = "https://$authority/healthz" + val desktopFile = "computer-$deviceId.json" + val command = buildString { + append("node pairing.mjs --config ./") + append(desktopFile) + append(" --invitation ./phone-$deviceId.txt") + if (config.allowInput) append(" --allow-input") + } + return RelayDeploymentResult( + mobileProfile = profile, + mobileInvitation = invitation.trim(), + desktopConfig = desktop.trim(), + mobileInvitationFileName = "phone-$deviceId.txt", + desktopConfigFileName = desktopFile, + desktopCommand = command, + healthUrl = healthUrl, + installDirectory = project, + ) + } + + private suspend fun runChecked( + ssh: DeploymentSsh, + operation: String, + command: String, + maxOutput: Int, + timeoutMs: Long, + input: ByteArray? = null, + ): CommandResult { + val result = runCommand(ssh, command, maxOutput, timeoutMs, input) + if (result.exitCode != 0) { + val code = when (operation) { + "project", "identity" -> RelayDeploymentErrorCode.REMOTE_PERMISSION + "upload_prepare", "upload_chunk", "upload" -> RelayDeploymentErrorCode.UPLOAD_FAILED + "extract", "install_source" -> RelayDeploymentErrorCode.EXTRACT_FAILED + "compose_config" -> RelayDeploymentErrorCode.REMOTE_PERMISSION + else -> RelayDeploymentErrorCode.UNKNOWN + } + throw RelayDeploymentException(code, safeRemoteOutput(result)) + } + return result + } + + private suspend fun runCommand( + ssh: DeploymentSsh, + command: String, + maxOutput: Int, + timeoutMs: Long, + input: ByteArray? = null, + ): CommandResult = withTimeout(timeoutMs) { + val connection = try { + ssh.open(minOf(timeoutMs, CONNECT_TIMEOUT_MS)) + } catch (cancelled: CancellationException) { + throw cancelled + } catch (error: Exception) { + throw mapSshFailure(error) + } + try { + ssh.blocking(connection) { connection.openExec(command) } + withContext(Dispatchers.IO) { + val stdout = connection.input(false) + val stderr = connection.input(true) + kotlinx.coroutines.coroutineScope { + val out: Deferred = async(Dispatchers.IO) { + ssh.blocking(connection) { readBounded(stdout, maxOutput) } + } + val err: Deferred = async(Dispatchers.IO) { + ssh.blocking(connection) { readBounded(stderr, maxOutput) } + } + val code: Deferred = async(Dispatchers.IO) { + ssh.blocking(connection) { connection.awaitExit() } + } + val writer: Deferred? = input?.let { payload -> + async(Dispatchers.IO) { + ssh.blocking(connection) { connection.output().write(payload) } + } + } + val exitCode = code.await() + writer?.await() + val bytes = awaitAll(out, err) + CommandResult( + exitCode, + String(bytes[0] as ByteArray, StandardCharsets.UTF_8), + String(bytes[1] as ByteArray, StandardCharsets.UTF_8), + ) + } + } + } catch (cancelled: CancellationException) { + throw cancelled + } catch (error: RelayDeploymentException) { + throw error + } catch (error: Exception) { + throw mapSshFailure(error) + } finally { + ssh.release(connection) + } + } + + private fun mapSshFailure(error: Throwable): RelayDeploymentException { + val kind = when (error) { + is SshFailure -> error.kind + is NativeSshException -> SshFailureKind.entries.firstOrNull { it.name == error.code } + else -> null + } + val code = when (kind) { + SshFailureKind.TIMEOUT -> RelayDeploymentErrorCode.SSH_TIMEOUT + SshFailureKind.TRUST_REJECTED -> RelayDeploymentErrorCode.SSH_TRUST_REJECTED + SshFailureKind.HOST_KEY_CHANGED -> RelayDeploymentErrorCode.SSH_HOST_KEY_CHANGED + SshFailureKind.AUTH -> RelayDeploymentErrorCode.SSH_AUTH + else -> RelayDeploymentErrorCode.SSH_FAILED + } + return RelayDeploymentException(code, cause = error) + } + + private fun deploymentFailure(code: RelayDeploymentErrorCode, result: CommandResult) = + RelayDeploymentException(code, safeRemoteOutput(result)) + + private fun RelayDeploymentException.copyWith(code: RelayDeploymentErrorCode) = + RelayDeploymentException(code, safeOutput, cause) + + private fun emit( + callback: (RelayDeploymentProgress) -> Unit, + stage: RelayDeploymentStage, + step: Int, + bytesSent: Long = 0, + bytesTotal: Long = 0, + ) { + callback(RelayDeploymentProgress(stage, step, TOTAL_STEPS, bytesSent, bytesTotal)) + } + + private fun marker(text: String, name: String): String? { + val prefix = "$name=" + return text.lineSequence().firstOrNull { it.startsWith(prefix) }?.removePrefix(prefix)?.trim()?.takeIf { it.isNotEmpty() } + } + + private fun markerBlock(text: String, begin: String, end: String): String? { + val start = text.indexOf("$begin\n") + if (start < 0) return null + val contentStart = start + begin.length + 1 + val contentEnd = text.indexOf("\n$end", contentStart) + if (contentEnd < 0) return null + return text.substring(contentStart, contentEnd).trim().takeIf { it.isNotEmpty() } + } + + private fun safeRemoteOutput(result: CommandResult): String = redactOutput( + (result.stdout + "\n" + result.stderr).trim(), + ) + + private fun redactOutput(text: String): String { + if (text.isEmpty()) return "" + return text.replace(Regex("[A-Za-z0-9_-]{43}"), "") + .filter { it == '\n' || it == '\r' || it == '\t' || it.code in 0x20..0x7e || it.code >= 0xa0 } + .takeLast(2048) + } + + private fun inProject(projectExpression: String, command: String) = + "set -eu\ncd -- $projectExpression\n$command" + + private fun remotePathExpression(path: String): String = when { + path == "~" -> "\"\$HOME\"" + path.startsWith("~/") -> "\"\$HOME\"/${shellQuote(path.removePrefix("~/"))}" + else -> shellQuote(path) + } + + private fun shellQuote(value: String): String = "'${value.replace("'", "'\"'\"'")}'" + + private fun readBounded(input: InputStream, maxBytes: Int): ByteArray { + val output = ByteArrayOutputStream(minOf(maxBytes, 16 * 1024)) + val buffer = ByteArray(16 * 1024) + var remaining = maxBytes + while (true) { + val count = input.read(buffer) + if (count < 0) break + if (remaining > 0) { + val accepted = minOf(remaining, count) + output.write(buffer, 0, accepted) + remaining -= accepted + } + } + return output.toByteArray() + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayInstallProgress.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayInstallProgress.kt new file mode 100644 index 0000000..69ad09e --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayInstallProgress.kt @@ -0,0 +1,38 @@ +package io.github.kuddev.pebrel.mobile.connection + +/** Upload counts are bytes handed to the SSH channel, not proof of remote installation. */ +data class RelayServiceProgress(val stage: String, val sent: Int = 0, val total: Int = 0) + +enum class InstallStepState { WAITING, ACTIVE, DONE, FAILED, CANCELLED } + +data class RelayInstallProgress( + val step: Int = 1, + val update: RelayServiceProgress = RelayServiceProgress("connecting"), + val finished: Boolean = false, + val failed: Boolean = false, + val cancelled: Boolean = false, +) { + fun advance(next: RelayServiceProgress): RelayInstallProgress { + if (finished || failed || cancelled) return this + val nextStep = when (next.stage) { + "connecting" -> 1 + "checking" -> 2 + "uploading", "uploaded" -> 3 + "initializing", "installing", "starting", "verifying", "ready" -> 4 + else -> return this + } + // The service emits its own preflight after upload. Never move the UI backwards. + if (nextStep < step) return this + if (next.stage == "uploading" && update.stage == "uploaded") return this + if (next.stage == "uploading" && next.sent < update.sent) return this + return copy(step = nextStep, update = next) + } + + fun state(number: Int): InstallStepState = when { + finished || number < step -> InstallStepState.DONE + number > step -> InstallStepState.WAITING + failed -> InstallStepState.FAILED + cancelled -> InstallStepState.CANCELLED + else -> InstallStepState.ACTIVE + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayProfile.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayProfile.kt new file mode 100644 index 0000000..997c9f3 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayProfile.kt @@ -0,0 +1,85 @@ +package io.github.kuddev.pebrel.mobile.connection + +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.json.JSONObject +import java.security.MessageDigest + +/** Persisted invitations share the same wire contract for LAN and relayed connections. */ +class RelayProfile(val url: String, val device: String, val token: String, val name: String, + val tlsPin: String? = null, val mode: String = "relay", secure: SecureRelayProfile? = null) { + @Volatile var secure: SecureRelayProfile? = secure + internal set + val version: Int = if (secure == null) 1 else 2 + internal val legacyId: String = digest("$url/$device" + (tlsPin?.let { "/$it" } ?: "") + (secure?.let { "/${it.host}" } ?: "")) + // 地址、房间和授权可以轮换;电脑身份只跟随 Noise 公钥,认证成功前不写入保存记录。 + val id: String = secure?.let { digest("pebrel.mobile.host.v2\n${it.host}") } ?: legacyId + + internal fun sameConnection(other: RelayProfile): Boolean { + val first = secure + val second = other.secure + return url == other.url && device == other.device && token == other.token && tlsPin == other.tlsPin && mode == other.mode && + first?.host == second?.host && first?.grant == second?.grant && first?.secret == second?.secret && + first?.invitation == second?.invitation + } + internal val canRediscover: Boolean get() = mode == "lan" && tlsPin != null && secure?.invitation == false + + internal fun discoveredAt(computer: PairingComputer): RelayProfile? { + // mDNS 不是授权来源:只移动已配对且 SPKI 匹配的端点,绝不采纳广播中的新凭据。 + if (!canRediscover || computer.pin != tlsPin) return null + return runCatching { atLanAddress(computer.address.toString()) }.getOrNull()?.takeIf { it.url != url } + } + + internal fun atLanAddress(value: String): RelayProfile { + require(canRediscover) + val raw = value.trim() + require(raw.length in 1..512 && raw.none(Char::isWhitespace)) + val address = when { + raw.startsWith("wss://") -> raw.replaceFirst("wss://", "https://") + "://" !in raw -> "https://$raw" + else -> raw + }.toHttpUrl() + require(address.scheme == "https" && address.username.isEmpty() && address.password.isEmpty() && + address.encodedPath == "/" && address.query == null && address.fragment == null) + val nextUrl = address.toString().replaceFirst("https://", "wss://").trimEnd('/') + return RelayProfile(nextUrl, device, token, name, tlsPin, mode, secure) + } + fun toJson(): JSONObject = JSONObject().put("version", version).put("url", url).put("device", device).put("token", token).put("name", name).put("mode", mode).apply { + tlsPin?.let { put("tlsPin", it) } + secure?.let { put("secure", it.toJson()) } + } + override fun toString() = "RelayProfile($name)" + companion object { + private fun digest(value: String): String = MessageDigest.getInstance("SHA-256") + .digest(value.toByteArray(Charsets.UTF_8)).joinToString("") { "%02x".format(it) } + + // 保存列表按成功连接时间追加;旧版留下的同机记录应保留最后一次有效连接。 + internal fun latestByComputer(profiles: List): List = + profiles.asReversed().distinctBy { it.id }.asReversed() + + fun parse(text: String): RelayProfile { + require(text.length <= 8192) + val data = JSONObject(text) + val version = data.getInt("version") + require(version == 1 || version == 2) + require(version != 1 || !data.has("secure")) // Never silently discard E2EE metadata. + val raw = data.getString("url") + require(raw.startsWith("wss://")) + val url = raw.replaceFirst("wss://", "https://").toHttpUrl() + require(url.username.isEmpty() && url.password.isEmpty() && url.query == null && url.fragment == null && url.encodedPath == "/") + val device = data.getString("device") + val token = data.getString("token") + require(Regex("[a-zA-Z0-9_-]{1,64}").matches(device)) + require(Regex("[a-zA-Z0-9_-]{43}").matches(token)) + val name = data.optString("name", device).trim().take(80) + require(name.isNotEmpty()) + val pin = data.optString("tlsPin").takeIf { it.isNotEmpty() } + require(pin == null || Regex("sha256/[A-Za-z0-9+/]{43}=").matches(pin)) + val mode = data.optString("mode", "relay") + require(mode in setOf("lan", "relay")) + require(mode != "lan" || pin != null) + val secure = if (version == 2) SecureRelayProfile.parse(data.getJSONObject("secure")) else null + require(version != 2 || pin != null) + return RelayProfile(url.toString().replaceFirst("https://", "wss://").trimEnd('/'), device, token, name, pin, mode, secure) + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayProfileStore.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayProfileStore.kt new file mode 100644 index 0000000..f67eab6 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayProfileStore.kt @@ -0,0 +1,44 @@ +package io.github.kuddev.pebrel.mobile.connection + +import android.content.Context +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import android.util.Base64 +import org.json.JSONArray +import java.security.KeyStore +import javax.crypto.Cipher +import javax.crypto.KeyGenerator +import javax.crypto.SecretKey +import javax.crypto.spec.GCMParameterSpec + +/** Cold-path credential storage. The app disables backup; keys stay in Android Keystore. */ +class RelayProfileStore(context: Context) { + private val preferences = context.getSharedPreferences("pebrel_relay", Context.MODE_PRIVATE) + private fun key(): SecretKey { + val store = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } + val existing = store.getKey("pebrel-relay", null) + if (existing is SecretKey) return existing + return KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore").apply { + init(KeyGenParameterSpec.Builder("pebrel-relay", KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE).build()) + }.generateKey() + } + @Synchronized fun load(): List { + val encoded = preferences.getString("profiles", null) ?: return emptyList() + val bytes = Base64.decode(encoded, Base64.NO_WRAP) + require(bytes.size in 29..262144) + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + cipher.init(Cipher.DECRYPT_MODE, key(), GCMParameterSpec(128, bytes.copyOfRange(0, 12))) + val array = JSONArray(String(cipher.doFinal(bytes, 12, bytes.size - 12), Charsets.UTF_8)) + require(array.length() <= 64) + return (0 until array.length()).map { RelayProfile.parse(array.getJSONObject(it).toString()) } + } + @Synchronized fun save(profiles: List) { + require(profiles.size <= 64) + val array = JSONArray().apply { profiles.forEach { put(it.toJson()) } } + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + cipher.init(Cipher.ENCRYPT_MODE, key()) + val bytes = cipher.iv + cipher.doFinal(array.toString().toByteArray(Charsets.UTF_8)) + check(preferences.edit().putString("profiles", Base64.encodeToString(bytes, Base64.NO_WRAP)).commit()) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayTransport.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayTransport.kt new file mode 100644 index 0000000..f435f33 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/RelayTransport.kt @@ -0,0 +1,117 @@ +package io.github.kuddev.pebrel.mobile.connection + +import okhttp3.* +import okhttp3.HttpUrl.Companion.toHttpUrl +import okio.ByteString +import okio.ByteString.Companion.toByteString +import org.json.JSONObject +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean + +/** TLS validates the user's server. Relay credentials never appear in a URL or log. */ +class RelayTransport( + private val profile: RelayProfile, + private val client: OkHttpClient = profile.tlsPin?.let { PinnedDesktopTls.client(sharedClient, it) } ?: sharedClient, +) : DesktopTransport { + private val closed = AtomicBoolean() + @Volatile private var link: String? = null + @Volatile private var socket: WebSocket? = null + @Volatile private var relayReached = false + @Volatile private var secure: SecureRelaySession? = null + override suspend fun open(allowInput: Boolean, receive: (JSONObject) -> Unit, disconnected: (Throwable?) -> Unit) { + check(!closed.get()) + val url = profile.url.replaceFirst("wss://", "https://").toHttpUrl().newBuilder() + .encodedPath("/v${profile.version}/link").addQueryParameter("device", profile.device).addQueryParameter("role", "mobile").build() + val request = Request.Builder().url(url).header("Authorization", "Bearer ${profile.token}").build() + fun fail(kind: DesktopFailureKind, cause: Throwable? = null) { + if (!closed.compareAndSet(false, true)) return + // 故障诊断只记录异常类型和代码位置,不泄露配对凭据、URL 或远端正文。 + if (cause != null) { + android.util.Log.w("PebrelConnection", "$kind: ${cause.javaClass.name}\n" + + cause.stackTrace.take(8).joinToString("\n")) + } + link = null + secure?.close() + disconnected(DesktopConnectionFailure(kind, cause)) + socket?.cancel() + } + socket = client.newWebSocket(request, object : WebSocketListener() { + override fun onOpen(webSocket: WebSocket, response: Response) { + if (closed.get()) { webSocket.cancel(); return } + if (profile.version == 2) secure = SecureRelaySession(profile, { bytes -> + check(!closed.get() && webSocket.queueSize() <= MAX_FRAME) + check(webSocket.send(bytes.toByteString())) + }, receive) + relayReached = true + if (closed.get()) secure?.close() + } + override fun onMessage(webSocket: WebSocket, text: String) { + if (closed.get()) return + try { + if (text.length > MAX_FRAME || text.toByteArray().size > MAX_FRAME) error("frame_too_large") + val frame = JSONObject(text) + if (profile.version == 2) require(frame.getInt("version") == 2) + when (frame.getString("type")) { + "relay.waiting" -> Unit + "relay.paired" -> { + check(link == null) + val newLink = frame.getString("link") + require(newLink.length in 1..80) + link = newLink + secure?.paired(newLink) + } + "relay.peer_left" -> fail(DesktopFailureKind.PEER_OFFLINE) + "relay.data" -> { + check(profile.version == 1) + check(link != null && frame.getString("link") == link) + receive(frame.getJSONObject("body")) + } + else -> error("invalid_frame") + } + } catch (error: Exception) { fail(DesktopFailureKind.PROTOCOL, error) } + catch (error: LinkageError) { fail(DesktopFailureKind.PROTOCOL, error) } + } + override fun onMessage(webSocket: WebSocket, bytes: ByteString) { + if (closed.get()) return + try { + check(profile.version == 2 && bytes.size in 1..65535) + checkNotNull(secure).binary(bytes.toByteArray()) + } catch (error: Exception) { fail(DesktopFailureKind.PROTOCOL, error) } + catch (error: LinkageError) { fail(DesktopFailureKind.PROTOCOL, error) } + } + override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { + webSocket.close(code, null) + fail(if (code == 1008 || code == 1009) DesktopFailureKind.PROTOCOL else DesktopFailureKind.DISCONNECTED) + } + override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { fail(DesktopFailureKind.DISCONNECTED) } + override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { + fail(desktopHttpFailure(response?.code) ?: classifyDesktopFailure(t, tlsEstablished = relayReached), t) + } + }) + if (closed.get()) socket?.cancel() + } + override fun readyTimeoutFailure(): DesktopFailureKind = + if (relayReached) DesktopFailureKind.PEER_OFFLINE else DesktopFailureKind.TIMEOUT + + override fun send(frame: JSONObject) { + check(!closed.get()) + if (profile.version == 2) { checkNotNull(secure).runtime(frame); return } + val socket = checkNotNull(socket) + val epoch = checkNotNull(link) + check(socket.queueSize() <= MAX_FRAME) + val envelope = JSONObject().put("type", "relay.data").put("link", epoch).put("body", frame).toString() + check(envelope.toByteArray().size <= MAX_FRAME && socket.send(envelope)) + } + override fun close() { + if (!closed.compareAndSet(false, true)) return + link = null + secure?.close() + socket?.cancel() + } + companion object { + private const val MAX_FRAME = 2 * 1024 * 1024 + 1024 + private val sharedClient = OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.SECONDS).pingInterval(30, TimeUnit.SECONDS) + .followRedirects(false).followSslRedirects(false).retryOnConnectionFailure(false).build() + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SecureRelaySession.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SecureRelaySession.kt new file mode 100644 index 0000000..1bd92e3 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SecureRelaySession.kt @@ -0,0 +1,150 @@ +package io.github.kuddev.pebrel.mobile.connection + +import io.github.kuddev.pebrel.ssh.SecureMobileLink +import org.json.JSONObject +import java.io.Closeable +import java.util.Base64 + +/** Never a data class: generated toString must not expose the QR/device secret. */ +class SecureRelayProfile(val host: String, val grant: String, val secret: String, + val invitation: Boolean, val expiresAt: Long = 0) { + fun toJson(): JSONObject = JSONObject().put("host", host).put("grant", grant) + .put("secret", secret).put("invitation", invitation).put("expiresAt", expiresAt) + override fun toString() = "SecureRelayProfile(redacted)" + companion object { + internal fun key(value: String): String { + require(value.length == 43) + val bytes = Base64.getUrlDecoder().decode(value) + try { require(bytes.size == 32 && Base64.getUrlEncoder().withoutPadding().encodeToString(bytes) == value) } + finally { bytes.fill(0) } + return value + } + internal fun id(value: String): String { + require(Regex("[A-Za-z0-9_-]{1,64}").matches(value)) + return value + } + fun parse(data: JSONObject): SecureRelayProfile { + val invitation = data.getBoolean("invitation") + val expires = data.optLong("expiresAt", 0) + require(!invitation || expires > 0) + return SecureRelayProfile(key(data.getString("host")), id(data.getString("grant")), + key(data.getString("secret")), invitation, expires) + } + } +} + +/** Injectable only to test lifecycle/wire contracts; production has one native + * crypto authority and never falls back to a Kotlin cipher or plaintext. */ +internal interface RelayCipher : Closeable { + fun hello(): ByteArray + fun finish(bytes: ByteArray) + fun verificationCode(): String + fun seal(bytes: ByteArray): Array + fun open(bytes: ByteArray): ByteArray? +} + +private class NativeRelayCipher(host: String, secret: String, context: String) : RelayCipher { + private val link = SecureMobileLink(host, secret, context) + override fun hello() = link.hello() + override fun finish(bytes: ByteArray) = link.finish(bytes) + override fun verificationCode() = link.verificationCode() + override fun seal(bytes: ByteArray) = link.seal(bytes) + override fun open(bytes: ByteArray) = link.open(bytes) + override fun close() = link.close() +} + +internal class SecureRelaySession( + private val profile: RelayProfile, + private val send: (ByteArray) -> Unit, + private val receive: (JSONObject) -> Unit, + private val cipherFactory: (String, String, String) -> RelayCipher = ::NativeRelayCipher, + private val now: () -> Long = { System.currentTimeMillis() / 1000 }, + private val deviceName: String = android.os.Build.MODEL.take(80).ifBlank { "Pebrel Android" }, +) : Closeable { + private enum class Phase { NEW, HANDSHAKE, ENROLLMENT, READY, CLOSED } + private var phase = Phase.NEW + private var cipher: RelayCipher? = null + private var credential: SecureRelayProfile? = null + private var approvalReceived = false + + @Synchronized fun paired(epoch: String) { + check(phase == Phase.NEW && profile.version == 2) + SecureRelayProfile.id(epoch) + val secure = checkNotNull(profile.secure) + require(!secure.invitation || now() < secure.expiresAt) + val context = "pebrel.mobile.v2\n${secure.host}\n${secure.grant}\n${if (secure.invitation) "invite" else "device"}\n$epoch" + val link = cipherFactory(secure.host, secure.secret, context) + cipher = link + credential = secure + phase = Phase.HANDSHAKE + val header = JSONObject().put("host", secure.host).put("grant", secure.grant).put("invitation", secure.invitation) + send(byteArrayOf(1) + header.toString().toByteArray(Charsets.UTF_8)) + send(link.hello()) + } + + @Synchronized fun binary(bytes: ByteArray) { + require(bytes.size in 1..65535) + val link = checkNotNull(cipher) + when (phase) { + Phase.HANDSHAKE -> { + link.finish(bytes) + phase = Phase.ENROLLMENT + encrypted(JSONObject().put("type", "secure.connect").put("name", deviceName).put("approval", true)) + } + Phase.ENROLLMENT, Phase.READY -> { + val plain = link.open(bytes) ?: return + try { + require(plain.size in 1..(2 * 1024 * 1024)) + if (phase == Phase.ENROLLMENT) require(plain.size <= 1024) + val frame = JSONObject(String(plain, Charsets.UTF_8)) + if (phase == Phase.READY) receive(frame) else enrolled(frame) + } finally { plain.fill(0) } + } + else -> error("invalid_secure_link_state") + } + } + + private fun enrolled(frame: JSONObject) { + val previous = checkNotNull(credential) + if (frame.optString("type") == "secure.approval") { + check(previous.invitation && !approvalReceived) + val code = checkNotNull(cipher).verificationCode() + require(Regex("[0-9]{6}").matches(code) && frame.getString("code") == code) + val expires = frame.getLong("expiresAt") + require(expires > 0) + approvalReceived = true + // 校验码来自本机 Noise 握手,不把服务器随意发来的文本当作身份证明。 + // 过期由电脑执行;手机仅限制本地等待时间,避免两端时钟偏差误杀合法握手。 + receive(JSONObject().put("type", "mobile.approval").put("code", code).put("expiresAt", now() + 120)) + return + } + val grant = SecureRelayProfile.id(frame.getString("grant")) + if (previous.invitation) { + check(frame.getString("type") == "secure.enrolled" && grant != previous.grant) + val secret = SecureRelayProfile.key(frame.getString("secret")) + require(secret != previous.secret) + // Keep the rotated credential for a retry if Runtime startup fails. + // The repository still saves a computer only after its first snapshot. + profile.secure = SecureRelayProfile(previous.host, grant, secret, false) + } else check(frame.getString("type") == "secure.accepted" && grant == previous.grant) + encrypted(JSONObject().put("type", "secure.ack").put("grant", grant)) + phase = Phase.READY + } + + @Synchronized fun runtime(frame: JSONObject) { + check(phase == Phase.READY) + require(frame.toString().toByteArray().size <= 40 * 1024) + encrypted(frame) + } + private fun encrypted(frame: JSONObject) { + val plain = frame.toString().toByteArray(Charsets.UTF_8) + try { checkNotNull(cipher).seal(plain).forEach(send) } + finally { plain.fill(0) } + } + @Synchronized override fun close() { + phase = Phase.CLOSED + cipher?.close() + cipher = null + credential = null + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SftpClient.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SftpClient.kt new file mode 100644 index 0000000..3ffa931 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SftpClient.kt @@ -0,0 +1,138 @@ +package io.github.kuddev.pebrel.mobile.connection + +import android.util.Base64 +import io.github.kuddev.pebrel.ssh.NativeSshException +import kotlinx.coroutines.* +import org.json.JSONObject +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.OutputStream + +data class SftpEntry(val path: String, val name: String, val kind: String, val size: Long?, + val modified: Long?, val permissions: Int?, val revision: String) { + val directory: Boolean get() = kind == "directory" + companion object { + fun parse(value: JSONObject) = SftpEntry(value.getString("path"), value.getString("name"), value.getString("kind"), + if (value.isNull("size")) null else value.getLong("size"), + if (value.isNull("modified")) null else value.getLong("modified"), + if (value.isNull("permissions")) null else value.getInt("permissions"), value.getString("revision")) + } +} +data class SftpListing(val path: String, val entries: List, val cursor: Long?, val skipped: Int) +data class SftpContent(val file: SftpEntry, val bytes: ByteArray) +data class SftpTab(val id: String, val session: String, val file: SftpEntry) + +/** View operations borrow the already authenticated SSH connection; no extra password or socket. */ +class SftpClient(private val execute: (JSONObject) -> JSONObject, private val active: () -> Boolean = { true }) { + private suspend fun request(op: String, fields: JSONObject = JSONObject()): JSONObject = withContext(Dispatchers.IO) { + ensureActive() + if (!active()) throw NativeSshException("SFTP_CLOSED") + val response = execute(fields.put("op", op)) + ensureActive() + if (!active()) throw NativeSshException("SFTP_CLOSED") + response + } + + suspend fun list(path: String, cursor: Long? = null): SftpListing { + currentCoroutineContext().ensureActive() + // JNI 返回前的取消也必须取回新游标并归还,避免连续导航耗尽原生句柄。 + val result = withContext(NonCancellable) { + request("list", JSONObject().put("path", path).apply { cursor?.let { put("cursor", it) } }) + } + val next = if (result.isNull("cursor")) null else result.getLong("cursor") + if (!currentCoroutineContext().isActive) { + if (next != null) withContext(NonCancellable) { runCatching { closeList(next) } } + currentCoroutineContext().ensureActive() + } + val rows = result.getJSONArray("entries") + check(rows.length() <= 256) + return SftpListing(result.getString("path"), (0 until rows.length()).map { SftpEntry.parse(rows.getJSONObject(it)) }, + next, result.optInt("skipped")) + } + + suspend fun closeList(cursor: Long) { request("close_list", JSONObject().put("cursor", cursor)) } + suspend fun stat(path: String): SftpEntry = SftpEntry.parse(request("stat", JSONObject().put("path", path))) + suspend fun mkdir(path: String) { request("mkdir", JSONObject().put("path", path)) } + suspend fun rename(entry: SftpEntry, destination: String) { + request("rename", JSONObject().put("path", entry.path).put("destination", destination).put("revision", entry.revision)) + } + suspend fun remove(entry: SftpEntry) { + request("remove", JSONObject().put("path", entry.path).put("revision", entry.revision)) + } + + suspend fun preview(path: String, progress: (Long, Long) -> Unit = { _, _ -> }): SftpContent { + val bytes = ByteArrayOutputStream() + val file = download(path, bytes, MAX_PREVIEW_BYTES, progress) + return SftpContent(file, bytes.toByteArray()) + } + + suspend fun download(path: String, sink: OutputStream, maximum: Long = Long.MAX_VALUE, + progress: (Long, Long) -> Unit = { _, _ -> }): SftpEntry = withContext(Dispatchers.IO) { + ensureActive() + val opened = withContext(NonCancellable) { request("open_read", JSONObject().put("path", path)) } + val transfer = opened.getLong("transfer") + try { + ensureActive() + val file = SftpEntry.parse(opened.getJSONObject("file")) + val size = file.size ?: throw NativeSshException("SFTP_FILE_TYPE") + if (size > maximum) throw NativeSshException("SFTP_TOO_LARGE") + var offset = 0L + while (true) { + ensureActive() + val chunk = request("read", JSONObject().put("transfer", transfer).put("offset", offset)) + val bytes = Base64.decode(chunk.getString("data"), Base64.NO_WRAP) + check(bytes.size <= CHUNK && chunk.getLong("offset") == offset) + check(offset + bytes.size <= size && chunk.getLong("next_offset") == offset + bytes.size) + sink.write(bytes) + offset += bytes.size + progress(offset, size) + if (chunk.getBoolean("eof")) { check(offset == size); break } + check(bytes.isNotEmpty()) + } + sink.flush() + file + } finally { release(transfer) } + } + + suspend fun upload(path: String, source: InputStream, size: Long?, + progress: (Long, Long?) -> Unit = { _, _ -> }): String = withContext(Dispatchers.IO) { + ensureActive() + val transfer = withContext(NonCancellable) { request("begin_upload", JSONObject().put("path", path)) }.getLong("transfer") + try { + ensureActive() + var offset = 0L + val buffer = ByteArray(CHUNK) + while (true) { + ensureActive() + val count = source.read(buffer) + if (count < 0) break + if (count == 0) continue + val data = Base64.encodeToString(buffer, 0, count, Base64.NO_WRAP) + val written = request("write", JSONObject().put("transfer", transfer).put("offset", offset).put("data", data)) + check(written.getLong("next_offset") == offset + count) + offset += count + progress(offset, size) + } + if (size != null && size != offset) throw NativeSshException("SFTP_CHANGED") + request("commit", JSONObject().put("transfer", transfer)).getString("path") + } finally { release(transfer) } + } + + private suspend fun release(transfer: Long) = withContext(NonCancellable + Dispatchers.IO) { + // 取消只关闭自己的远程 handle / 临时上传,不关闭用户仍在使用的 SSH shell。 + if (active()) runCatching { request("close", JSONObject().put("transfer", transfer)) } + Unit + } + + companion object { + const val CHUNK = 32 * 1024 + const val MAX_PREVIEW_BYTES = 16L * 1024 * 1024 + } +} + +fun sftpChild(parent: String, name: String): String { + require(name.isNotBlank() && name !in setOf(".", "..") && '/' !in name && name.none(Char::isISOControl)) + return "${parent.trimEnd('/')}/$name" +} + +fun sftpParent(path: String): String = path.trimEnd('/').substringBeforeLast('/', "").ifBlank { "/" } diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshConnection.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshConnection.kt new file mode 100644 index 0000000..4068c83 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshConnection.kt @@ -0,0 +1,82 @@ +package io.github.kuddev.pebrel.mobile.connection + +import io.github.kuddev.pebrel.ssh.NativeSshException +import io.github.kuddev.pebrel.ssh.RusshSession +import io.github.kuddev.pebrel.terminal.SessionTransport +import java.io.Closeable +import java.io.InputStream +import java.io.OutputStream +import org.json.JSONObject + +data class HostProfile( + val id: String, val name: String, val address: String, val port: Int = 22, + val user: String, val fingerprint: String = "", + val icon: String = "term", val group: String = "development", + val sessionMode: SshSessionMode = SshSessionMode.SHELL, val sessionName: String = "", +) + +/** Kotlin owns UI trust and lifetime; russh owns SSH negotiation and encrypted IO. */ +class SshConnection( + private val host: HostProfile, + private val password: CharArray, + private val verify: (HostProfile, String) -> Boolean, + private val progress: (SshStage) -> Unit = {}, +) : Closeable { + private val guard = Any() + @Volatile private var opened: RusshSession? = null + private var closed = false + private fun session(): RusshSession = opened ?: throw NativeSshException("CLOSED") + + fun connect() { + try { + val transport = synchronized(guard) { + if (closed) throw NativeSshException("CLOSED") + check(opened == null) + val endpoint = parseSshEndpoint(host.address, host.user) + RusshSession.create(endpoint.address, host.port, endpoint.user, password, host.fingerprint).also { opened = it } + } + transport.connect({ progress(SshStage.valueOf(it)) }, { fingerprint -> verify(host, fingerprint) }) + } catch (error: Exception) { + close() + throw SshFailure(classifySshFailure(error), error) + } finally { password.fill('\u0000') } + } + + fun openShell(columns: Int, rows: Int, cellWidth: Int, cellHeight: Int) { + progress(SshStage.OPENING_SHELL) + val command = host.attachCommand() + if (command == null) session().openShell(columns, rows, cellWidth, cellHeight) + else session().openPtyExec(command, columns, rows, cellWidth, cellHeight) + } + fun openExec(command: String) { + progress(SshStage.OPENING_SHELL) + session().openExec(command) + } + fun input(stderr: Boolean = false): InputStream = session().input(stderr) + fun output(): OutputStream = session().output + fun resize(columns: Int, rows: Int, cellWidth: Int, cellHeight: Int) = session().resize(columns, rows, cellWidth, cellHeight) + fun awaitExit(): Int = session().awaitExit() + fun sftp(request: JSONObject): JSONObject = JSONObject(session().sftp(request.toString())) + + override fun close() { + val transport = synchronized(guard) { + closed = true + password.fill('\u0000') + opened.also { opened = null } + } + transport?.close() + } +} + +/** The terminal engine owns VT, IME and selection; the independent russh module supplies bytes. */ +class SshTerminalTransport(private val connection: SshConnection) : SessionTransport { + override fun open(columns: Int, rows: Int, cellWidth: Int, cellHeight: Int) { + connection.connect() + connection.openShell(columns, rows, cellWidth, cellHeight) + } + override fun input(): InputStream = connection.input() + override fun output(): OutputStream = connection.output() + override fun resize(columns: Int, rows: Int, cellWidth: Int, cellHeight: Int) = connection.resize(columns, rows, cellWidth, cellHeight) + override fun awaitExit(): Int = connection.awaitExit() + override fun close() = connection.close() +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshEndpoint.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshEndpoint.kt new file mode 100644 index 0000000..10eb47c --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshEndpoint.kt @@ -0,0 +1,25 @@ +package io.github.kuddev.pebrel.mobile.connection + +/** One authority for separate fields and the familiar user@host address form. */ +data class SshEndpoint(val address: String, val user: String) + +fun parseSshEndpoint(address: String, user: String): SshEndpoint { + val input = address.trim() + require(input.isNotEmpty() && !input.contains("://") && !input.any { it.isWhitespace() || it.isISOControl() }) + val separator = input.indexOf('@') + require(separator == input.lastIndexOf('@')) + val login = (if (separator >= 0) input.substring(0, separator) else user).trim() + val host = if (separator >= 0) input.substring(separator + 1) else input + require(login.isNotEmpty() && login.none { it.isWhitespace() || it.isISOControl() || it == '@' }) + require(host.isNotEmpty() && host.none { it in "/?#" }) + val unwrapped = if (host.startsWith('[') && host.endsWith(']')) host.substring(1, host.length - 1) else host + require(unwrapped.isNotEmpty() && unwrapped.none { it == '[' || it == ']' }) + return SshEndpoint(unwrapped, login) +} + +val HostProfile.endpointLabel: String + get() { + val endpoint = runCatching { parseSshEndpoint(address, user) }.getOrNull() ?: return "$address:$port" + val host = if (endpoint.address.contains(':')) "[${endpoint.address}]" else endpoint.address + return "${endpoint.user}@$host:$port" + } diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshSessionMode.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshSessionMode.kt new file mode 100644 index 0000000..b3e3499 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshSessionMode.kt @@ -0,0 +1,17 @@ +package io.github.kuddev.pebrel.mobile.connection + +enum class SshSessionMode(val id: String) { SHELL("shell"), TMUX("tmux"), HERDR("herdr") } + +fun validRemoteSessionName(mode: SshSessionMode, name: String): Boolean = + name.isEmpty() || (name.length <= 64 && name !in setOf(".", "..") && name.first() != '-' && + name.all { it in 'a'..'z' || it in 'A'..'Z' || it in '0'..'9' || it in "_-" || (mode == SshSessionMode.HERDR && it == '.') }) + +internal fun HostProfile.attachCommand(): String? { + require(sessionMode == SshSessionMode.SHELL || validRemoteSessionName(sessionMode, sessionName)) + // 通过 SSH 的 PTY exec 请求启动客户端,不向已经运行的用户 shell 偷塞命令。 + return when (sessionMode) { + SshSessionMode.SHELL -> null + SshSessionMode.TMUX -> "exec tmux new-session -A -s '${sessionName.ifEmpty { "pebrel" }}'" + SshSessionMode.HERDR -> if (sessionName.isEmpty()) "exec herdr" else "exec herdr session attach '$sessionName'" + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshStatus.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshStatus.kt new file mode 100644 index 0000000..80a26e1 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/SshStatus.kt @@ -0,0 +1,33 @@ +package io.github.kuddev.pebrel.mobile.connection + +import io.github.kuddev.pebrel.ssh.NativeSshException +import java.io.IOException +import java.net.ConnectException +import java.net.NoRouteToHostException +import java.net.SocketTimeoutException +import java.net.UnknownHostException + +enum class SshStage { NETWORK, VERIFYING, AUTHENTICATING, OPENING_SHELL } +enum class SshFailureKind { + UNKNOWN_HOST, TIMEOUT, REFUSED, AUTH, HOST_KEY_CHANGED, TRUST_REJECTED, CHANNEL, NETWORK, CRYPTO, NEGOTIATION, UNKNOWN, +} + +class SshFailure(val kind: SshFailureKind, cause: Exception) : IOException(kind.name, cause) + +/** Typed errors, never raw server messages, credentials or command content in UI. */ +fun classifySshFailure(error: Throwable?): SshFailureKind { + val causes = generateSequence(error) { it.cause }.take(12).toList() + causes.filterIsInstance().firstOrNull()?.let { return it.kind } + return when { + causes.any { it is UnknownHostException } -> SshFailureKind.UNKNOWN_HOST + causes.any { it is SocketTimeoutException || it is java.util.concurrent.TimeoutException } -> SshFailureKind.TIMEOUT + causes.any { it is ConnectException } -> SshFailureKind.REFUSED + causes.any { it is java.security.GeneralSecurityException } -> SshFailureKind.CRYPTO + causes.any { it is NativeSshException } -> causes.filterIsInstance().first().let { + SshFailureKind.entries.firstOrNull { kind -> kind.name == it.code } ?: SshFailureKind.UNKNOWN + } + causes.any { it is NoRouteToHostException } -> SshFailureKind.NETWORK + causes.any { it is IOException } -> SshFailureKind.NETWORK + else -> SshFailureKind.UNKNOWN + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/TerminalAttachments.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/TerminalAttachments.kt new file mode 100644 index 0000000..f0a84ad --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/connection/TerminalAttachments.kt @@ -0,0 +1,682 @@ +package io.github.kuddev.pebrel.mobile.connection + +import android.content.ContentResolver +import android.content.Context +import android.database.Cursor +import android.net.Uri +import android.os.CancellationSignal +import android.provider.OpenableColumns +import android.util.Base64 +import io.github.kuddev.pebrel.mobile.session.LocalTerminalStorage +import io.github.kuddev.pebrel.ssh.NativeSshException +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.NonCancellable +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout +import java.io.ByteArrayOutputStream +import java.io.Closeable +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.nio.charset.StandardCharsets +import java.nio.file.AtomicMoveNotSupportedException +import java.nio.file.Files +import java.nio.file.StandardCopyOption +import java.util.Locale +import java.util.UUID +import java.util.concurrent.atomic.AtomicReference +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException + +/** Stable failure categories for the attachment boundary. No remote output is exposed. */ +enum class AttachmentError { + TOO_LARGE, + UNREADABLE, + STORAGE, + REMOTE_TOOLS, + TRANSFER, + INVALID_RESULT, +} + +class AttachmentException( + val code: AttachmentError, + cause: Throwable? = null, + val sshFailure: SshFailureKind? = null, +) : IOException("attachment_${code.name.lowercase(Locale.ROOT)}", cause) + +/** + * Imports a user-selected document without touching the interactive terminal. + * + * The local operation commits through a temporary file and the SSH operation stages the + * document in the app cache. Both paths keep the source bounded and remove an incomplete + * staging file when a coroutine is cancelled or the transfer fails. + */ +object TerminalAttachments { + const val MAX_BYTES: Long = 16L * 1024L * 1024L + + private const val MAX_OUTPUT_BYTES = 16 * 1024 + private const val MAX_FILE_NAME_BYTES = 240 + private const val MAX_REMOTE_PATH_CHARS = 4096 + private const val STAGE_TIMEOUT_MS = 5 * 60_000L + private const val CONNECT_TIMEOUT_MS = 30_000L + private const val TRANSFER_TIMEOUT_MS = 5 * 60_000L + private const val COPY_BUFFER_BYTES = 16 * 1024 + private const val BASE64_BUFFER_BYTES = 24 * 1024 + + /** Copies a SAF document into the shared terminal HOME and returns its canonical path. */ + suspend fun importLocal(context: Context, uri: Uri): String { + val attachments = withContext(Dispatchers.IO) { + try { + val directory = File(LocalTerminalStorage.homePath(context), "attachments") + ensureDirectory(directory) + directory + } catch (error: AttachmentException) { + throw error + } catch (error: Exception) { + throw AttachmentException(AttachmentError.STORAGE, error) + } + } + val staged = withTimeout(STAGE_TIMEOUT_MS) { + stageSource(context.contentResolver, uri, attachments.resolve(".staging")) + } + var moved = false + return try { + withContext(Dispatchers.IO) { + val target = uniqueTarget(attachments, staged.safeName) + moveWithoutOverwrite(staged.file, target) + moved = true + target.canonicalPath + } + } catch (cancelled: CancellationException) { + throw cancelled + } catch (error: AttachmentException) { + throw error + } catch (error: Exception) { + throw AttachmentException(AttachmentError.STORAGE, error) + } finally { + if (!moved) deleteStaging(staged.file) + } + } + + /** + * Uploads a SAF document through one independent SSH exec session and returns the + * absolute path printed by the remote shell after the file is committed. + */ + suspend fun uploadSsh( + context: Context, + uri: Uri, + host: HostProfile, + password: CharArray, + verify: (HostProfile, String) -> Boolean, + ): String { + val stagingRoot = withContext(Dispatchers.IO) { + try { + File(context.cacheDir, "pebrel-attachments") + } catch (error: Exception) { + throw AttachmentException(AttachmentError.STORAGE, error) + } + } + val staged = withTimeout(STAGE_TIMEOUT_MS) { + stageSource(context.contentResolver, uri, stagingRoot) + } + return try { + uploadStaged(host, password, verify, staged) + } finally { + deleteStaging(staged.file) + } + } + + private suspend fun stageSource( + resolver: ContentResolver, + uri: Uri, + stagingDirectory: File, + ): StagedAttachment = withContext(Dispatchers.IO) { + val metadata = sourceMetadata(resolver, uri) + currentCoroutineContext().ensureActive() + if (metadata.size != null && metadata.size > MAX_BYTES) { + throw AttachmentException(AttachmentError.TOO_LARGE) + } + ensureDirectory(stagingDirectory) + val file = stagingDirectory.resolve(".attachment-${UUID.randomUUID()}.part") + try { + val total = copySourceCancellable(resolver, uri, file) + StagedAttachment(file, safeFileName(metadata.name), total) + } catch (cancelled: CancellationException) { + deleteQuietly(file) + throw cancelled + } catch (error: AttachmentException) { + deleteQuietly(file) + throw error + } catch (error: Exception) { + deleteQuietly(file) + throw AttachmentException(AttachmentError.STORAGE, error) + } + } + + private suspend fun copySourceCancellable( + resolver: ContentResolver, + uri: Uri, + file: File, + ): Long = withContext(Dispatchers.IO) { + suspendCancellableCoroutine { continuation -> + val source = AtomicReference(null) + val destination = AtomicReference(null) + continuation.invokeOnCancellation { + runCatching { source.getAndSet(null)?.close() } + runCatching { destination.getAndSet(null)?.close() } + } + try { + source.set(try { + resolver.openInputStream(uri) + } catch (error: Exception) { + throw AttachmentException(AttachmentError.UNREADABLE, error) + } ?: throw AttachmentException(AttachmentError.UNREADABLE)) + if (!continuation.isActive) { + runCatching { source.getAndSet(null)?.close() } + return@suspendCancellableCoroutine + } + destination.set(FileOutputStream(file)) + if (!continuation.isActive) { + runCatching { source.getAndSet(null)?.close() } + runCatching { destination.getAndSet(null)?.close() } + return@suspendCancellableCoroutine + } + var total = 0L + source.get()!!.use { input -> + destination.get()!!.use { output -> + val buffer = ByteArray(COPY_BUFFER_BYTES) + while (true) { + continuation.context.ensureActive() + val count = input.read(buffer) + if (count < 0) break + if (count == 0) continue + total += count.toLong() + if (total > MAX_BYTES) throw AttachmentException(AttachmentError.TOO_LARGE) + output.write(buffer, 0, count) + } + output.flush() + buffer.fill(0) + } + } + source.set(null) + destination.set(null) + if (continuation.isActive) runCatching { continuation.resume(total) } + } catch (error: Throwable) { + runCatching { source.getAndSet(null)?.close() } + runCatching { destination.getAndSet(null)?.close() } + if (continuation.isActive) runCatching { continuation.resumeWithException(error) } + } + } + } + + private suspend fun uploadStaged( + host: HostProfile, + password: CharArray, + verify: (HostProfile, String) -> Boolean, + staged: StagedAttachment, + ): String = withTimeout(TRANSFER_TIMEOUT_MS) { + val ssh = UploadSsh(host, password, verify) + try { + val connection = ssh.open() + try { + val encodedLength = encodedLength(staged.bytes) + val marker = "PEBREL_ATTACHMENT_END_${UUID.randomUUID().toString().replace("-", "")}" + val plan = uploadCommand(staged.safeName, staged.bytes, encodedLength, marker) + ssh.blocking(connection) { connection.openExec(plan.command) } + + val stdout = connection.input(false) + val stderr = connection.input(true) + val result = coroutineScope { + val output: Deferred = async(Dispatchers.IO) { + readBoundedOrClose(connection, stdout) + } + val diagnostics: Deferred = async(Dispatchers.IO) { + readBoundedOrClose(connection, stderr) + } + val writer: Deferred = async(Dispatchers.IO) { + try { + ssh.blocking(connection) { + FileInputStream(staged.file).use { source -> + val sent = writeBase64(source, connection.output()) + check(sent == encodedLength) + connection.output().write(marker.toByteArray(StandardCharsets.UTF_8)) + connection.output().flush() + } + } + } catch (error: Throwable) { + connection.close() + throw error + } + } + val exit: Deferred = async(Dispatchers.IO) { + try { + ssh.blocking(connection) { connection.awaitExit() } + } catch (error: Throwable) { + connection.close() + throw error + } + } + + val exitCode = exit.await() + if (exitCode != 0 && writer.isActive) { + connection.close() + writer.cancel() + } + val writerFailure = try { + writer.await() + null + } catch (cancelled: CancellationException) { + currentCoroutineContext().ensureActive() + cancelled + } catch (error: Exception) { + error + } + val outputText = awaitOutputOrEmpty(output) + val diagnosticsText = awaitOutputOrEmpty(diagnostics) + RemoteCommandResult(exitCode, outputText, diagnosticsText, writerFailure) + } + if (result.exitCode != 0) throw remoteFailure(result) + result.writerFailure?.let { throw transferFailure(it) } + parseRemotePath(result.stdout, staged.safeName, plan.randomDirectory) + } finally { + ssh.release(connection) + } + } catch (cancelled: CancellationException) { + throw cancelled + } catch (error: AttachmentException) { + throw error + } catch (error: SshFailure) { + throw AttachmentException(AttachmentError.TRANSFER, error, error.kind) + } catch (error: NativeSshException) { + throw AttachmentException(AttachmentError.TRANSFER, error, nativeSshFailureKind(error)) + } catch (error: Exception) { + throw transferFailure(error) + } finally { + ssh.close() + } + } + + private fun uploadCommand( + fileName: String, + sourceLength: Long, + encodedLength: Long, + marker: String, + ): RemoteUploadPlan { + val random = UUID.randomUUID().toString().replace("-", "") + val markerBytes = marker.toByteArray(StandardCharsets.UTF_8).size + val quotedName = shellQuote(fileName) + val quotedRandom = shellQuote(random) + val quotedMarker = shellQuote(marker) + val dollar = "${'$'}" + val command = """ + set -eu + umask 077 + if ! command -v head >/dev/null 2>&1; then + printf '%s\n' 'tool_missing=head' >&2 + exit 71 + fi + if ! command -v base64 >/dev/null 2>&1; then + printf '%s\n' 'tool_missing=base64' >&2 + exit 72 + fi + if ! command -v wc >/dev/null 2>&1; then + printf '%s\n' 'tool_missing=wc' >&2 + exit 73 + fi + home=${dollar}{HOME:-} + if [ -z "${dollar}home" ]; then + printf '%s\n' 'home_missing' >&2 + exit 74 + fi + home=${dollar}(cd "${dollar}home" 2>/dev/null && pwd -P) || exit 75 + root="${dollar}home/.cache/pebrel/attachments" + directory="${dollar}root"/$quotedRandom + target="${dollar}directory"/$quotedName + temporary="${dollar}directory/.upload-$random" + mkdir -p "${dollar}root" + if ! mkdir "${dollar}directory"; then + printf '%s\n' 'directory_create_failed' >&2 + exit 76 + fi + cleanup() { rm -rf "${dollar}directory"; } + trap cleanup 0 1 2 3 15 + if ! head -c $encodedLength | base64 -d > "${dollar}temporary"; then + printf '%s\n' 'upload_decode_failed' >&2 + exit 77 + fi + decoded=${dollar}(wc -c < "${dollar}temporary") + if [ "${dollar}decoded" -ne $sourceLength ]; then + printf '%s\n' 'upload_size_mismatch' >&2 + exit 78 + fi + confirmation=${dollar}(head -c $markerBytes) + if [ "${dollar}confirmation" != $quotedMarker ]; then + printf '%s\n' 'upload_incomplete' >&2 + exit 79 + fi + mv "${dollar}temporary" "${dollar}target" + printf 'PEBREL_ATTACHMENT=%s\n' "${dollar}target" + trap - 0 1 2 3 15 + """.trimIndent() + return RemoteUploadPlan(command, random) + } + + private fun writeBase64(input: InputStream, output: OutputStream): Long { + val buffer = ByteArray(BASE64_BUFFER_BYTES + 2) + var carry = 0 + var encodedBytes = 0L + while (true) { + val count = input.read(buffer, carry, BASE64_BUFFER_BYTES) + if (count < 0) break + if (count == 0) continue + val total = carry + count + val complete = total / 3 * 3 + if (complete > 0) { + val encoded = Base64.encode(buffer, 0, complete, Base64.NO_WRAP) + output.write(encoded) + encodedBytes += encoded.size.toLong() + encoded.fill(0) + } + carry = total - complete + if (carry > 0) System.arraycopy(buffer, complete, buffer, 0, carry) + } + if (carry > 0) { + val encoded = Base64.encode(buffer, 0, carry, Base64.NO_WRAP) + output.write(encoded) + encodedBytes += encoded.size.toLong() + encoded.fill(0) + } + buffer.fill(0) + return encodedBytes + } + + private fun readBoundedOrClose(connection: SshConnection, input: InputStream): String { + return try { + readBounded(input) + } catch (error: Throwable) { + connection.close() + throw error + } + } + + private suspend fun awaitOutputOrEmpty(output: Deferred): String = try { + output.await() + } catch (cancelled: CancellationException) { + currentCoroutineContext().ensureActive() + "" + } catch (_: Exception) { + "" + } + + private fun readBounded(input: InputStream): String { + val output = ByteArrayOutputStream(minOf(MAX_OUTPUT_BYTES, 4096)) + val buffer = ByteArray(COPY_BUFFER_BYTES) + var remaining = MAX_OUTPUT_BYTES + while (true) { + val count = input.read(buffer) + if (count < 0) break + if (remaining > 0) { + val accepted = minOf(remaining, count) + output.write(buffer, 0, accepted) + remaining -= accepted + } + } + return String(output.toByteArray(), StandardCharsets.UTF_8) + } + + private fun remoteFailure(result: RemoteCommandResult): AttachmentException { + val toolMissing = result.stderr.lineSequence().firstOrNull { it.startsWith("tool_missing=") } + return if (toolMissing != null) { + AttachmentException(AttachmentError.REMOTE_TOOLS) + } else { + AttachmentException(AttachmentError.TRANSFER) + } + } + + private fun parseRemotePath(stdout: String, fileName: String, randomDirectory: String): String { + val prefix = "PEBREL_ATTACHMENT=" + val path = stdout.lineSequence().firstOrNull { it.startsWith(prefix) } + ?.removePrefix(prefix) + ?.trim() + ?: throw AttachmentException(AttachmentError.INVALID_RESULT) + val directoryMarker = "/.cache/pebrel/attachments/$randomDirectory/" + if (!path.startsWith("/") || path.length > MAX_REMOTE_PATH_CHARS || + path.any { it.code < 0x20 || it.code == 0x7f } || + !path.contains(directoryMarker) || !path.endsWith("/$fileName")) { + throw AttachmentException(AttachmentError.INVALID_RESULT) + } + return path + } + + private suspend fun sourceMetadata(resolver: ContentResolver, uri: Uri): SourceMetadata = + withContext(Dispatchers.IO) { + try { + suspendCancellableCoroutine { continuation -> + val cancellation = CancellationSignal() + var cursor: Cursor? = null + continuation.invokeOnCancellation { cancellation.cancel(); runCatching { cursor?.close() } } + try { + cursor = resolver.query( + uri, + arrayOf(OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE), + null, + null, + null, + cancellation, + ) + if (!continuation.isActive) { + cursor?.close() + return@suspendCancellableCoroutine + } + val result = cursor?.use { row -> + if (!row.moveToFirst()) return@use SourceMetadata(null, null) + val name = row.getColumnIndex(OpenableColumns.DISPLAY_NAME).let { index -> + if (index >= 0) row.getString(index) else null + } + val size = row.getColumnIndex(OpenableColumns.SIZE).let { index -> + if (index >= 0 && !row.isNull(index)) { + row.getLong(index).takeIf { it >= 0L } + } else { + null + } + } + SourceMetadata(name, size) + } ?: SourceMetadata(null, null) + cursor = null + if (continuation.isActive) runCatching { continuation.resume(result) } + } catch (error: Throwable) { + cursor?.close() + if (continuation.isActive) runCatching { continuation.resumeWithException(error) } + } + } + } catch (cancelled: CancellationException) { + throw cancelled + } catch (_: Exception) { + SourceMetadata(null, null) + } + } + + private fun safeFileName(candidate: String?): String { + val raw = candidate.orEmpty().substringAfterLast('/').substringAfterLast('\\').trim() + if (raw.isEmpty() || raw == "." || raw == "..") return "attachment" + val dot = raw.lastIndexOf('.').takeIf { it > 0 && it < raw.lastIndex } + val stem = sanitizeNamePart(if (dot == null) raw else raw.substring(0, dot)) + val extension = if (dot == null) "" else sanitizeNamePart(raw.substring(dot)) + val safeStem = stem.ifEmpty { "attachment" } + val safeExtension = takeUtf8Bytes(extension, 80) + val available = (MAX_FILE_NAME_BYTES - safeExtension.toByteArray(StandardCharsets.UTF_8).size).coerceAtLeast(1) + val boundedStem = takeUtf8Bytes(safeStem, available) + val result = boundedStem + safeExtension + return result.takeIf { it != "." && it != ".." && it.isNotEmpty() } ?: "attachment" + } + + private fun sanitizeNamePart(value: String): String = buildString(value.length) { + value.forEach { character -> + when { + character.code < 0x20 || character.code == 0x7f -> append('_') + character == '/' || character == '\\' || character == ':' -> append('_') + else -> append(character) + } + } + }.trim() + + private fun uniqueTarget(directory: File, safeName: String): File { + val dot = safeName.lastIndexOf('.').takeIf { it > 0 } + val stem = if (dot == null) safeName else safeName.substring(0, dot) + val extension = if (dot == null) "" else safeName.substring(dot) + repeat(8) { + val suffix = "-${UUID.randomUUID().toString().replace("-", "")}" + val extensionBytes = extension.toByteArray(StandardCharsets.UTF_8).size + val maxStem = (MAX_FILE_NAME_BYTES - suffix.length - extensionBytes).coerceAtLeast(1) + val name = takeUtf8Bytes(stem, maxStem) + suffix + extension + val target = directory.resolve(name) + if (!target.exists()) return target + } + throw AttachmentException(AttachmentError.STORAGE) + } + + private fun moveWithoutOverwrite(source: File, target: File) { + try { + Files.move(source.toPath(), target.toPath(), StandardCopyOption.ATOMIC_MOVE) + } catch (_: AtomicMoveNotSupportedException) { + Files.move(source.toPath(), target.toPath()) + } + } + + private fun ensureDirectory(directory: File) { + if (directory.exists()) { + if (!directory.isDirectory || !directory.canWrite()) throw AttachmentException(AttachmentError.STORAGE) + return + } + if (!directory.mkdirs() && !directory.isDirectory) throw AttachmentException(AttachmentError.STORAGE) + if (!directory.canWrite()) throw AttachmentException(AttachmentError.STORAGE) + } + + private fun deleteQuietly(file: File) { + runCatching { + if (file.exists()) Files.deleteIfExists(file.toPath()) + } + } + + private suspend fun deleteStaging(file: File) { + withContext(NonCancellable + Dispatchers.IO) { deleteQuietly(file) } + } + + private fun encodedLength(bytes: Long): Long = ((bytes + 2L) / 3L) * 4L + + private fun takeUtf8Bytes(value: String, maxBytes: Int): String { + if (maxBytes <= 0 || value.isEmpty()) return "" + val result = StringBuilder() + var offset = 0 + var used = 0 + while (offset < value.length) { + val codePoint = value.codePointAt(offset) + val width = Character.charCount(codePoint) + val piece = value.substring(offset, offset + width) + val bytes = piece.toByteArray(StandardCharsets.UTF_8).size + if (used + bytes > maxBytes) break + result.append(piece) + used += bytes + offset += width + } + return result.toString() + } + + private fun shellQuote(value: String): String = "'${value.replace("'", "'\"'\"'")}'" + + private fun transferFailure(error: Throwable): AttachmentException = + AttachmentException(AttachmentError.TRANSFER, error, sshFailureKind(error)) + + private fun sshFailureKind(error: Throwable?): SshFailureKind? = + generateSequence(error) { it.cause }.firstNotNullOfOrNull { + when (it) { + is SshFailure -> it.kind + is NativeSshException -> nativeSshFailureKind(it) + else -> null + } + } + + private fun nativeSshFailureKind(error: NativeSshException): SshFailureKind? = + SshFailureKind.entries.firstOrNull { it.name == error.code } + + private data class SourceMetadata(val name: String?, val size: Long?) + + private data class StagedAttachment(val file: File, val safeName: String, val bytes: Long) + + private data class RemoteCommandResult( + val exitCode: Int, + val stdout: String, + val stderr: String, + val writerFailure: Throwable?, + ) + + private data class RemoteUploadPlan(val command: String, val randomDirectory: String) + + /** One cancellation-aware native connection for this upload only. */ + private class UploadSsh( + private val host: HostProfile, + password: CharArray, + private val verify: (HostProfile, String) -> Boolean, + ) : Closeable { + private val initialPassword = password.copyOf() + private val guard = Any() + @Volatile private var active: SshConnection? = null + @Volatile private var closed = false + + suspend fun open(): SshConnection { + val connection = synchronized(guard) { + check(!closed) { "closed" } + SshConnection(host, initialPassword.copyOf(), verify).also { active = it } + } + return try { + withTimeout(CONNECT_TIMEOUT_MS) { + blocking(connection) { connection.connect() } + } + initialPassword.fill('\u0000') + connection + } catch (error: Throwable) { + release(connection) + initialPassword.fill('\u0000') + throw error + } + } + + suspend fun blocking(connection: SshConnection, action: () -> T): T = + withContext(Dispatchers.IO) { + suspendCancellableCoroutine { continuation -> + continuation.invokeOnCancellation { connection.close() } + try { + continuation.resume(action()) + } catch (error: Throwable) { + continuation.resumeWithException(error) + } + } + } + + fun release(connection: SshConnection) { + synchronized(guard) { + if (active === connection) active = null + } + connection.close() + } + + override fun close() { + val connection = synchronized(guard) { + closed = true + active.also { active = null } + } + initialPassword.fill('\u0000') + connection?.close() + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/git/DesktopGit.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/git/DesktopGit.kt new file mode 100644 index 0000000..5ca6096 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/git/DesktopGit.kt @@ -0,0 +1,119 @@ +package io.github.kuddev.pebrel.mobile.git + +import io.github.kuddev.pebrel.mobile.connection.DesktopConnectionFailure +import io.github.kuddev.pebrel.mobile.connection.DesktopRpcFailure +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import org.json.JSONObject +import java.util.concurrent.atomic.AtomicLong + +/** 会话拥有操作回执;退出页面不重发写操作,重连也不把旧请求送给新电脑。 */ +class DesktopGit(val target: DesktopGitTarget, private val scope: CoroutineScope, + private val request: suspend (String, JSONObject) -> JSONObject, + val availability: (Boolean) -> String?, private val committed: (String) -> Unit = {}) { + private val mutable = MutableStateFlow(GitViewState()) + val state = mutable.asStateFlow() + private val operation = Mutex() + private val diffGeneration = AtomicLong() + + private suspend fun rpc(method: String, params: JSONObject = JSONObject(), write: Boolean = false): JSONObject { + availability(write)?.let { throw GitProblem(it) } + val reply = try { + request("git.$method", params.put("window_id", target.window).put("pane_id", target.pane).put("expected_cwd", target.cwd)) + } catch (error: DesktopRpcFailure) { + throw GitProblem(error.code, error.detail) + } catch (error: DesktopConnectionFailure) { + throw GitProblem(if (write) "git_outcome_unknown" else "git_disconnected") + } catch (cancelled: CancellationException) { + throw cancelled + } catch (_: Exception) { + throw GitProblem(if (write) "git_outcome_unknown" else "git_disconnected") + } + // 已确认的写入回执属于旧目标,不能因随后掉线而伪装成可重试的失败。 + if (!write) availability(false)?.let { throw GitProblem(it) } + return reply + } + + private fun run(block: suspend () -> Unit) { + scope.launch { + if (!operation.tryLock()) return@launch + mutable.update { it.copy(busy = true, error = null) } + try { block() } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { + mutable.update { it.copy(error = error as? GitProblem ?: GitProblem("git_failed", error.message.orEmpty().take(4096))) } + } finally { + mutable.update { it.copy(busy = false) } + operation.unlock() + } + } + } + + private suspend fun readStatus(): GitSnapshot = GitSnapshot.parse(rpc("status")) + + fun refresh() = run { + mutable.update { it.copy(needsRefresh = true) } + val snapshot = readStatus() + mutable.update { it.copy(snapshot = snapshot, needsRefresh = false) } + } + + fun mutate(action: GitAction, path: String = "", all: Boolean = false, message: String = "") = run { + availability(true)?.let { throw GitProblem(it) } + var snapshot = mutable.value.snapshot + if (snapshot == null || mutable.value.needsRefresh) throw GitProblem("git_stale") + mutable.update { it.copy(completed = emptyList(), needsRefresh = true) } + val steps = if (action == GitAction.Sync) listOf(GitAction.Fetch, GitAction.Pull, GitAction.Push) else listOf(action) + for (step in steps) { + mutable.update { it.copy(needsRefresh = true) } + val params = JSONObject().put("revision", snapshot!!.revision) + when (step) { + GitAction.Stage, GitAction.Unstage -> params.put("path", path).put("all", all) + GitAction.Commit -> params.put("message", message) + else -> Unit + } + val result = rpc(step.method, params, write = true) + if (!result.optBoolean("success")) throw GitProblem("git_outcome_unknown") + if (step == GitAction.Commit) committed(message) + diffGeneration.incrementAndGet() + mutable.update { it.copy(completed = it.completed + step, history = emptyList(), hasMore = false, diff = null, diffText = null) } + try { + snapshot = readStatus() + mutable.update { it.copy(snapshot = snapshot, needsRefresh = false) } + } catch (error: Exception) { + if (error is CancellationException) throw error + throw GitProblem("git_refresh_failed", (error as? GitProblem)?.detail.orEmpty()) + } + } + } + + fun history(more: Boolean = false) = run { + val previous = if (more) mutable.value.history else emptyList() + val result = rpc("history", JSONObject().put("offset", previous.size)) + val commits = result.getJSONArray("commits") + val next = List(commits.length()) { index -> commits.getJSONObject(index).let { item -> + GitCommit(item.getString("hash"), item.getString("short_hash"), item.getString("subject"), + item.getString("author"), item.getLong("timestamp"), item.optString("refs")) + } } + mutable.update { it.copy(history = (previous + next).distinctBy(GitCommit::hash), hasMore = result.optBoolean("has_more")) } + } + + fun openDiff(selection: GitDiffSelection) { + val generation = diffGeneration.get() + run { + if (generation != diffGeneration.get()) return@run + mutable.update { it.copy(diff = selection, diffText = null) } + val result = rpc("diff", JSONObject().put("path", selection.path).put("staged", selection.staged).put("commit", selection.commit)) + if (generation == diffGeneration.get()) mutable.update { it.copy(diffText = result.getString("text")) } + } + } + + fun closeDiff() { + diffGeneration.incrementAndGet() + mutable.update { it.copy(diff = null, diffText = null, error = null) } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/git/GitModels.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/git/GitModels.kt new file mode 100644 index 0000000..f91c652 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/git/GitModels.kt @@ -0,0 +1,65 @@ +package io.github.kuddev.pebrel.mobile.git + +import io.github.kuddev.pebrel.mobile.session.DesktopWorkspace +import org.json.JSONObject + +data class DesktopGitTarget(val desktop: String, val window: Long, val pane: Long, val cwd: String, + val generation: Long, val process: Long?) { + val draftKey: String get() = "$desktop:git:$process:$window:$pane:$cwd" + + fun availability(current: DesktopWorkspace?, write: Boolean): String? = when { + current?.status != "ready" -> "git_disconnected" + current.connectionGeneration != generation || current.runtimeProcess != process -> "git_target_changed" + current.panes.none { it.window == window && it.id == pane && it.cwd == cwd && it.sshDestination == null } -> "git_target_changed" + write && !current.allowInput -> "input_not_authorized" + else -> null + } +} + +data class GitEntry(val path: String, val original: String?, val index: String, val worktree: String, val conflict: Boolean) { + val staged: Boolean get() = index != "." && index != "?" + val unstaged: Boolean get() = worktree != "." +} + +data class GitSnapshot(val root: String, val branch: String, val upstream: String?, val ahead: Int, val behind: Int, + val entries: List, val revision: String) { + companion object { + fun parse(value: JSONObject): GitSnapshot { + val status = value.getJSONObject("status") + val entries = status.getJSONArray("entries") + return GitSnapshot(value.getString("root"), status.getString("branch"), + if (status.isNull("upstream")) null else status.getString("upstream"), status.getInt("ahead"), status.getInt("behind"), + List(entries.length()) { i -> entries.getJSONObject(i).let { entry -> + GitEntry(entry.getString("path"), if (entry.isNull("original")) null else entry.getString("original"), + entry.getString("index"), entry.getString("worktree"), entry.getBoolean("conflict")) + } }, value.getString("revision")) + } + } +} + +enum class GitAction(val method: String) { Stage("stage"), Unstage("unstage"), Commit("commit"), Fetch("fetch"), Pull("pull"), Push("push"), Sync("sync") } +data class GitCommit(val hash: String, val shortHash: String, val subject: String, val author: String, val timestamp: Long, val refs: String) +data class GitDiffSelection(val path: String = "", val staged: Boolean = false, val commit: String = "") +data class GitProblem(val code: String, val detail: String = "") : java.io.IOException(code) +data class GitViewState(val snapshot: GitSnapshot? = null, val busy: Boolean = false, val needsRefresh: Boolean = true, + val error: GitProblem? = null, val completed: List = emptyList(), + val history: List = emptyList(), val hasMore: Boolean = false, + val diff: GitDiffSelection? = null, val diffText: String? = null) + +data class GitTreeRow(val path: String, val depth: Int, val entry: GitEntry? = null) + +/** 仅生成可见的展示行;原始路径仍用于请求,缩进或换行不改变文件身份。 */ +fun gitTree(entries: List, collapsed: Set): List = buildList { + val folders = HashSet() + entries.sortedBy { it.path }.forEach { entry -> + val parts = entry.path.split('/') + var parent = "" + var hidden = false + for (depth in 0 until parts.lastIndex) { + parent = if (parent.isEmpty()) parts[depth] else "$parent/${parts[depth]}" + if (folders.add(parent)) add(GitTreeRow(parent, depth)) + if (parent in collapsed) { hidden = true; break } + } + if (!hidden) add(GitTreeRow(entry.path, parts.lastIndex, entry)) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/DisplayPreferences.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/DisplayPreferences.kt new file mode 100644 index 0000000..bd23ddb --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/DisplayPreferences.kt @@ -0,0 +1,97 @@ +package io.github.kuddev.pebrel.mobile.session + +import android.content.Context +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** Stable values shared by the settings page and the native terminal surface. */ +object TerminalPreferenceValues { + const val MAPLE_FONT = "maple" + const val JETBRAINS_FONT = "jetbrains" + const val SYSTEM_FONT = "system" + const val BAR_CURSOR = "bar" + const val UNDERLINE_CURSOR = "underline" + const val BLOCK_CURSOR = "block" + const val MIN_FONT_SIZE = 8 + const val MAX_FONT_SIZE = 32 +} + +data class TerminalPreferences( + val fontFamily: String = TerminalPreferenceValues.MAPLE_FONT, + val fontSize: Int = 16, + val cursorStyle: String = TerminalPreferenceValues.BLOCK_CURSOR, + val cursorBlink: Boolean = true, + val pinchZoom: Boolean = true, + val suggestions: Boolean = true, + val directInput: Boolean = true, +) { + /** Preserve the persisted contract when callers pass a stale or unknown value. */ + fun normalized(): TerminalPreferences = copy( + fontFamily = when (fontFamily) { + TerminalPreferenceValues.JETBRAINS_FONT -> TerminalPreferenceValues.JETBRAINS_FONT + TerminalPreferenceValues.SYSTEM_FONT -> TerminalPreferenceValues.SYSTEM_FONT + else -> TerminalPreferenceValues.MAPLE_FONT + }, + fontSize = fontSize.coerceIn(TerminalPreferenceValues.MIN_FONT_SIZE, TerminalPreferenceValues.MAX_FONT_SIZE), + cursorStyle = when (cursorStyle) { + TerminalPreferenceValues.BAR_CURSOR -> TerminalPreferenceValues.BAR_CURSOR + TerminalPreferenceValues.UNDERLINE_CURSOR -> TerminalPreferenceValues.UNDERLINE_CURSOR + else -> TerminalPreferenceValues.BLOCK_CURSOR + }, + ) + + /** Compatibility constructor for the original three scalar preferences. */ + constructor(fontSize: Int, suggestions: Boolean, directInput: Boolean) : this( + fontFamily = TerminalPreferenceValues.MAPLE_FONT, + fontSize = fontSize, + suggestions = suggestions, + directInput = directInput, + ) +} + +/** Small UI preferences only. Session identity, credentials and terminal state remain elsewhere. */ +class DisplayPreferences(context: Context) { + private val stored = context.getSharedPreferences("terminal_display", Context.MODE_PRIVATE) + init { + // Old previews persisted composer-first even when the user had never + // selected it. Migrate once; subsequent explicit preferences still win. + if (!stored.getBoolean("compact_input_default_v1", false)) { + stored.edit().putBoolean("direct_input", true).putBoolean("compact_input_default_v1", true).apply() + } + } + private val current = MutableStateFlow( + TerminalPreferences( + fontFamily = stored.getString("font_family", TerminalPreferenceValues.MAPLE_FONT).orEmpty(), + fontSize = stored.getInt("font_size", 16), + cursorStyle = stored.getString("cursor_style", TerminalPreferenceValues.BLOCK_CURSOR).orEmpty(), + cursorBlink = stored.getBoolean("cursor_blink", true), + pinchZoom = stored.getBoolean("pinch_zoom", true), + suggestions = stored.getBoolean("suggestions", true), + directInput = stored.getBoolean("direct_input", true), + ).normalized(), + ) + val state = current.asStateFlow() + + val connectionOnboardingCompleted: Boolean + get() = stored.getBoolean("connection_onboarding_completed", false) + + fun completeConnectionOnboarding() { + // 不用会话是否为空推断首启,否则点“以后再说”的用户每次都会重新看到引导。 + stored.edit().putBoolean("connection_onboarding_completed", true).apply() + } + + fun update(value: TerminalPreferences) { + current.value = value.normalized() + stored.edit().putString("font_family", current.value.fontFamily) + .putInt("font_size", current.value.fontSize) + .putString("cursor_style", current.value.cursorStyle) + .putBoolean("cursor_blink", current.value.cursorBlink) + .putBoolean("pinch_zoom", current.value.pinchZoom) + .putBoolean("suggestions", current.value.suggestions) + .putBoolean("direct_input", current.value.directInput).apply() + } + + fun update(transform: (TerminalPreferences) -> TerminalPreferences) { + update(transform(current.value)) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/HostCredentialStore.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/HostCredentialStore.kt new file mode 100644 index 0000000..e1fa099 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/HostCredentialStore.kt @@ -0,0 +1,88 @@ +package io.github.kuddev.pebrel.mobile.session + +import android.content.Context +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import android.util.Base64 +import java.nio.ByteBuffer +import java.nio.CharBuffer +import java.nio.charset.StandardCharsets +import java.security.KeyStore +import javax.crypto.Cipher +import javax.crypto.KeyGenerator +import javax.crypto.SecretKey +import javax.crypto.spec.GCMParameterSpec + +/** Encrypted records are independently replaceable; keys stay in Android Keystore. */ +class HostCredentialStore(context: Context) { + private val preferences = context.getSharedPreferences("pebrel_host_credentials", Context.MODE_PRIVATE) + + @Synchronized + fun ids(): Set = preferences.all.keys.filter { it.startsWith(PREFIX) }.map { it.removePrefix(PREFIX) }.toSet() + + @Synchronized + fun load(hostKey: String): CharArray? { + val encoded = preferences.getString(record(hostKey), null) ?: return null + require(encoded.length <= MAX_RECORD_CHARS) + val bytes = Base64.decode(encoded, Base64.NO_WRAP) + require(bytes.size >= IV_BYTES + 16) + val cipher = Cipher.getInstance(CIPHER) + cipher.init(Cipher.DECRYPT_MODE, key(create = false), GCMParameterSpec(128, bytes.copyOfRange(0, IV_BYTES))) + cipher.updateAAD(hostKey.toByteArray(StandardCharsets.UTF_8)) + val plain = cipher.doFinal(bytes, IV_BYTES, bytes.size - IV_BYTES) + return try { + val decoded = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(plain)) + try { CharArray(decoded.remaining()).also { decoded.get(it) } } + finally { if (decoded.hasArray()) decoded.array().fill('\u0000') } + } finally { plain.fill(0) } + } + + @Synchronized + fun save(hostKey: String, password: CharArray) { + require(password.isNotEmpty() && password.size <= 1024) + require(preferences.contains(record(hostKey)) || ids().size < 256) + val buffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(password)) + val plain = ByteArray(buffer.remaining()).also { buffer.get(it) } + if (buffer.hasArray()) buffer.array().fill(0) + val encrypted = try { + val cipher = Cipher.getInstance(CIPHER) + cipher.init(Cipher.ENCRYPT_MODE, key(create = true)) + cipher.updateAAD(hostKey.toByteArray(StandardCharsets.UTF_8)) + cipher.iv + cipher.doFinal(plain) + } finally { plain.fill(0) } + val encoded = Base64.encodeToString(encrypted, Base64.NO_WRAP) + encrypted.fill(0) + check(preferences.edit().putString(record(hostKey), encoded).commit()) + } + + @Synchronized + fun clear(hostKey: String) { + check(preferences.edit().remove(record(hostKey)).commit()) + } + + private fun record(hostKey: String): String { + require(hostKey.length == 64 && hostKey.all { it in '0'..'9' || it in 'a'..'f' }) + return PREFIX + hostKey + } + + private fun key(create: Boolean): SecretKey { + val store = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } + val existing = store.getKey(KEY_ALIAS, null) + if (existing is SecretKey) return existing + check(create) { "credential_key_missing" } + return KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore").apply { + init(KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .setUserAuthenticationRequired(false).build()) + }.generateKey() + } + + private companion object { + const val PREFIX = "password_" + const val KEY_ALIAS = "pebrel-host-passwords" + const val CIPHER = "AES/GCM/NoPadding" + const val IV_BYTES = 12 + const val MAX_RECORD_CHARS = 8192 + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/LocalTerminalStorage.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/LocalTerminalStorage.kt new file mode 100644 index 0000000..1f4c0dd --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/LocalTerminalStorage.kt @@ -0,0 +1,16 @@ +package io.github.kuddev.pebrel.mobile.session + +import android.content.Context +import java.io.File + +/** Small application-facing adapter for the local PTY's cwd and HOME. */ +object LocalTerminalStorage { + fun access(context: Context): SharedStorageAccess.State = SharedStorageAccess.state(context) + + fun defaultHome(): File = SharedStorageAccess.defaultDirectory() + + /** Throws an explicit permission/storage error instead of selecting private app data. */ + fun home(context: Context): File = SharedStorageAccess.requireDirectory(context) + + fun homePath(context: Context): String = home(context).absolutePath +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/SessionRepository.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/SessionRepository.kt new file mode 100644 index 0000000..8c1fc22 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/SessionRepository.kt @@ -0,0 +1,709 @@ +package io.github.kuddev.pebrel.mobile.session + +import android.content.Context +import android.os.Handler +import android.os.Looper +import io.github.kuddev.pebrel.terminal.LocalPtyTransport +import io.github.kuddev.pebrel.terminal.SessionTransport +import io.github.kuddev.pebrel.terminal.TerminalSession +import io.github.kuddev.pebrel.terminal.TerminalCallbacks +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.git.DesktopGit +import io.github.kuddev.pebrel.mobile.git.DesktopGitTarget +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import org.json.JSONArray +import org.json.JSONObject +import java.util.UUID +import java.util.concurrent.CompletableFuture +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean + +data class LocalSession( + val id: String, val title: String, val source: String, val terminal: TerminalSession, + val status: String = "connecting", val host: HostProfile? = null, + val stage: SshStage = SshStage.NETWORK, val failure: SshFailureKind? = null, val hasConnected: Boolean = false, + val files: SftpClient? = null, +) +data class DesktopWorkspace(val id: String, val host: HostProfile, val panes: List = emptyList(), val status: String = "connecting", val allowInput: Boolean = false, val transport: String = "SSH", val failure: DesktopFailureKind? = null, + val hasConnected: Boolean = false, val relayProfile: RelayProfile? = null, + val connectionGeneration: Long = 0, val runtimeProcess: Long? = null, + val pairingApproval: DesktopPairingApproval? = null, + val tabs: List = emptyList(), val windows: List = emptyList()) +data class TrustRequest(val ownerId: String, val host: HostProfile, val fingerprint: String, val answer: CompletableFuture) +data class DesktopOutput(val target: String = "", val text: String = "", val loading: Boolean = false, + val frame: io.github.kuddev.pebrel.terminal.TerminalFrame? = null) + +/** Application owns sessions; activities only attach views. Metadata never updates per cell. */ +class SessionRepository(private val context: Context, + private val relayTransport: (RelayProfile) -> DesktopTransport = { RelayTransport(it) }) { + val display = DisplayPreferences(context) + private val main = Handler(Looper.getMainLooper()) + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + private val preferences = context.getSharedPreferences("pebrel_mobile", Context.MODE_PRIVATE) + private val credentialStore = HostCredentialStore(context) + private val live = MutableStateFlow>(emptyList()) + val sessions = live.asStateFlow() + private val remoteFiles = MutableStateFlow>(emptyList()) + val sftpTabs = remoteFiles.asStateFlow() + private val computers = MutableStateFlow>(emptyList()) + val desktops = computers.asStateFlow() + private val relayStore = RelayProfileStore(context) + private val relayWrites = Mutex() + private val savedRelays = MutableStateFlow>(emptyList()) + private val restoredRelayIds = mutableMapOf() + val relays = savedRelays.asStateFlow() + private val savedHosts = MutableStateFlow(loadHosts()) + val hosts = savedHosts.asStateFlow() + val trust = MutableStateFlow(null) + val error = MutableStateFlow(null) + private val savedCredentialIds = MutableStateFlow>(emptySet()) + val savedCredentials = savedCredentialIds.asStateFlow() + val backgroundActive = MutableStateFlow(false) + val theme = MutableStateFlow(preferences.getString("theme", "system") ?: "system") + fun selectTheme(value: String) { + theme.value = value + scope.launch { preferences.edit().putString("theme", theme.value).apply() } + } + val output = MutableStateFlow(DesktopOutput()) + private val hostWrites = Mutex() + private val credentialWrites = Mutex() + private val sshOperations = java.util.concurrent.ConcurrentHashMap.newKeySet() + private val pendingTrust = java.util.concurrent.ConcurrentHashMap>() + private var readJob: Job? = null + private var readGeneration = 0L + private var desktopReader: DesktopReadScheduler? = null + private val desktopClients = java.util.concurrent.ConcurrentHashMap() + private val conversationCache = ConversationCache() + private var activeGit: DesktopGit? = null + private var foreground = false + private var lanDiscovery: PairingDiscovery? = null + private var lanDiscoveryJob: Job? = null + private val resumeChecks = mutableMapOf() + private val reconnect = DesktopReconnect(scope) { id -> + val current = computers.value.find { it.id == id } + if (foreground && current?.hasConnected == true && current.status != "ready" && + current.status !in setOf("connecting", "approval") && DesktopReconnect.retryable(current.failure)) { + current.relayProfile?.let { connectRelay(it) } + } + } + + fun foregroundChanged(value: Boolean) { + foreground = value + reconnect.foreground(value) + resumeChecks.values.forEach(Job::cancel) + resumeChecks.clear() + refreshLanDiscovery() + if (!value) return + for (desktop in computers.value.filter { it.hasConnected && it.relayProfile != null }) { + val client = desktopClients[desktop.id] + if (client == null && DesktopReconnect.retryable(desktop.failure)) reconnect.schedule(desktop.id, immediate = true) + else if (client != null && desktop.status == "ready") resumeChecks[desktop.id] = scope.launch { + try { withTimeout(5000) { client.request("runtime.describe") } } + catch (error: Exception) { + if (error is CancellationException && error !is TimeoutCancellationException) throw error + withContext(Dispatchers.Main) { + if (foreground) { + desktopFailed(desktop.id, client, DesktopFailureKind.NETWORK, immediate = true) + client.close() + } + } + } + } + } + } + + private fun refreshLanDiscovery() { + val needed = foreground && computers.value.any { + it.hasConnected && it.relayProfile?.canRediscover == true && it.status !in setOf("ready", "approval") && + DesktopReconnect.retryable(it.failure) + } + if (!needed) { + val previous = lanDiscovery + lanDiscovery = null + lanDiscoveryJob?.cancel() + lanDiscoveryJob = null + previous?.close() + } else if (lanDiscovery == null) { + val discovery = PairingDiscovery(context) + lanDiscovery = discovery + lanDiscoveryJob = scope.launch(Dispatchers.Main.immediate) { + discovery.state.collect { if (lanDiscovery === discovery) recoverLanComputers(it.computers) } + } + discovery.start() + } + } + + internal fun recoverLanComputers(discovered: List) { + if (!foreground) return + for (desktop in computers.value) { + if (!desktop.hasConnected || desktop.status in setOf("ready", "approval") || + !DesktopReconnect.retryable(desktop.failure)) continue + val profile = desktop.relayProfile ?: continue + val computer = discovered.firstOrNull { it.pin == profile.tlsPin } ?: continue + profile.discoveredAt(computer)?.let(::connectRelay) + } + } + + fun restoreDesktop(id: String): String? { + val canonical = restoredRelayIds[id] ?: id + if (computers.value.any { it.id == canonical }) return canonical + return savedRelays.value.find { "relay:${it.id}" == canonical || "relay:${it.legacyId}" == id }?.let(::connectRelay) + } + private var renderOwner: String? = null + private var renderToken: Any? = null + private var redraw: (() -> Unit)? = null + val drafts = MutableStateFlow>(emptyMap()) + private val recentCommands = MutableStateFlow>>(emptyMap()) + val commandHistory = recentCommands.asStateFlow() + fun setDraft(id: String, text: String) { drafts.value = drafts.value + (id to text) } + fun acknowledgeDraft(id: String, sent: String) { + if (sent.isNotBlank()) { + // Local composition history follows the live session, without writing shell input to disk. + var remaining = 32_768 + val entries = (listOf(sent) + recentCommands.value[id].orEmpty()) + .distinct().take(40).takeWhile { remaining -= it.length; remaining >= 0 } + recentCommands.value = recentCommands.value + (id to entries) + } + if (drafts.value[id] == sent) drafts.value = drafts.value - id + } + + init { + scope.launch { + credentialWrites.withLock { + runCatching { credentialStore.ids() } + .onSuccess { savedCredentialIds.value = it } + .onFailure { error.value = "credential_load_failed" } + } + } + scope.launch { + relayWrites.withLock { + runCatching { relayStore.load() }.onSuccess { restored -> + val unique = RelayProfile.latestByComputer(restored) + withContext(Dispatchers.Main) { + restored.forEach { restoredRelayIds["relay:${it.legacyId}"] = "relay:${it.id}" } + savedRelays.value = RelayProfile.latestByComputer(unique + savedRelays.value) + } + // 迁移写盘失败也保留已经解密的可用记录,避免把存储错误变成重新扫码。 + if (unique.size != restored.size) runCatching { relayStore.save(savedRelays.value) } + .onFailure { error.value = "relay_storage_failed" } + }.onFailure { error.value = "relay_storage_failed" } + } + } + } + + private fun loadHosts(): List = runCatching { + val rows = JSONArray(preferences.getString("hosts", "[]")) + (0 until rows.length()).map { i -> rows.getJSONObject(i).let { + HostProfile(it.getString("id"), it.getString("name"), it.getString("address"), it.getInt("port"), it.getString("user"), + it.optString("fingerprint"), it.optString("icon", "term"), it.optString("group", "development"), + SshSessionMode.entries.firstOrNull { mode -> mode.id == it.optString("session_mode") } ?: SshSessionMode.SHELL, it.optString("session_name")) + } } + }.getOrDefault(emptyList()) + + fun saveHost(host: HostProfile) { + val previous = savedHosts.value.find { it.id == host.id } + val fingerprint = if (previous != null && sameSshServer(previous, host)) previous.fingerprint else "" + savedHosts.value = savedHosts.value.filterNot { it.id == host.id } + host.copy(fingerprint = fingerprint) + persistHosts() + } + + /** Credentials are scoped to both the saved identity and the login endpoint. */ + private fun credentialKey(host: HostProfile): String { + val identity = "${host.id}\u0000${host.address}\u0000${host.port}\u0000${host.user}" + return java.security.MessageDigest.getInstance("SHA-256").digest(identity.toByteArray()) + .joinToString("") { "%02x".format(it.toInt() and 255) } + } + + private fun sameSshServer(first: HostProfile, second: HostProfile): Boolean = + first.port == second.port && runCatching { + parseSshEndpoint(first.address, first.user).address == parseSshEndpoint(second.address, second.user).address + }.getOrDefault(false) + + private fun sameSshLogin(first: HostProfile, second: HostProfile): Boolean = + first.id == second.id && first.port == second.port && runCatching { + parseSshEndpoint(first.address, first.user) == parseSshEndpoint(second.address, second.user) + }.getOrDefault(false) + + private fun previousCredential(host: HostProfile): HostProfile? = + savedHosts.value.firstOrNull { sameSshLogin(it, host) } + + fun hasSavedPassword(host: HostProfile): Boolean = savedCredentialIds.value.contains(credentialKey(host)) || + previousCredential(host)?.let { savedCredentialIds.value.contains(credentialKey(it)) } == true + + /** Completes only after encrypted credentials and host metadata reach storage. */ + suspend fun saveHostWithCredentials(host: HostProfile, password: CharArray?, rememberPassword: Boolean): Boolean = + withContext(Dispatchers.IO) { + hostWrites.withLock { + credentialWrites.withLock { + try { + val previous = savedHosts.value.find { it.id == host.id } + val key = credentialKey(host) + if (rememberPassword && password?.isNotEmpty() == true) credentialStore.save(key, password) + else if (!rememberPassword) credentialStore.clear(key) + else if (previous != null && credentialKey(previous) != key && sameSshLogin(previous, host)) { + // Canonicalizing user@host must retain the explicitly saved credential. + credentialStore.load(credentialKey(previous))?.let { saved -> + try { credentialStore.save(key, saved) } finally { saved.fill('\u0000') } + } + } + if (previous != null && credentialKey(previous) != key) credentialStore.clear(credentialKey(previous)) + val fingerprint = if (previous != null && sameSshServer(previous, host)) previous.fingerprint else "" + val next = savedHosts.value.filterNot { it.id == host.id } + host.copy(fingerprint = fingerprint) + check(writeHostMetadata(next)) + val ids = credentialStore.ids() + withContext(Dispatchers.Main) { + savedHosts.value = next + savedCredentialIds.value = ids + } + true + } catch (cancelled: CancellationException) { throw cancelled } + catch (_: Exception) { error.value = "credential_save_failed"; false } + } + } + } + + suspend fun loadSavedPassword(host: HostProfile): CharArray? = withContext(Dispatchers.IO) { + credentialWrites.withLock { + try { + credentialStore.load(credentialKey(host)) ?: previousCredential(host)?.let { + credentialStore.load(credentialKey(it)) + } + } + catch (cancelled: CancellationException) { throw cancelled } + catch (_: Exception) { error.value = "credential_load_failed"; null } + } + } + + suspend fun passwordForConnection(host: HostProfile, entered: CharArray?): CharArray? { + if (entered != null) return entered.copyOf() + // 无已存凭据才尝试免密;凭据读取失败仍阻止连接,不静默降级。 + return if (hasSavedPassword(host)) loadSavedPassword(host) else charArrayOf() + } + + suspend fun clearHostPassword(host: HostProfile): Boolean = withContext(Dispatchers.IO) { + credentialWrites.withLock { + try { + credentialStore.clear(credentialKey(host)) + savedCredentialIds.value = credentialStore.ids() + true + } catch (cancelled: CancellationException) { throw cancelled } + catch (_: Exception) { error.value = "credential_clear_failed"; false } + } + } + + fun deleteHost(host: HostProfile) { + savedHosts.value = savedHosts.value.filterNot { it.id == host.id } + persistHosts() + scope.launch { clearHostPassword(host) } + } + + private fun writeHostMetadata(hosts: List): Boolean { + val rows = JSONArray() + hosts.forEach { h -> rows.put(JSONObject().put("id", h.id).put("name", h.name).put("address", h.address) + .put("port", h.port).put("user", h.user).put("fingerprint", h.fingerprint).put("icon", h.icon).put("group", h.group) + .put("session_mode", h.sessionMode.id).put("session_name", h.sessionName)) } + return preferences.edit().putString("hosts", rows.toString()).commit() + } + + private fun persistHosts() { + scope.launch { + hostWrites.withLock { + if (!writeHostMetadata(savedHosts.value)) error.value = "host_save_failed" + } + } + } + fun beginSshOperation(): String = UUID.randomUUID().toString().also { sshOperations.add(it) } + fun endSshOperation(ownerId: String) { sshOperations.remove(ownerId); cancelTrust(ownerId) } + fun verifySshOperation(ownerId: String, host: HostProfile, fingerprint: String): Boolean = verify(ownerId, host, fingerprint) + + private fun verify(ownerId: String, host: HostProfile, fingerprint: String): Boolean { + val answer = CompletableFuture() + pendingTrust[ownerId] = answer + main.post { + val active = live.value.any { it.id == ownerId && it.status == "connecting" } || + computers.value.any { it.id == ownerId && it.status == "connecting" } || sshOperations.contains(ownerId) + if (!active || answer.isDone || trust.value != null) answer.complete(false) + else trust.value = TrustRequest(ownerId, host, fingerprint, answer) + } + val accepted = runCatching { answer.get(60, TimeUnit.SECONDS) }.getOrDefault(false) + pendingTrust.remove(ownerId, answer) + main.post { + if (trust.value?.answer === answer) trust.value = null + if (accepted) { + savedHosts.value = savedHosts.value.map { if (it.id == host.id && it.address == host.address && it.port == host.port) it.copy(fingerprint = fingerprint) else it } + persistHosts() + } + } + return accepted + } + private fun cancelTrust(ownerId: String) { + pendingTrust.remove(ownerId)?.complete(false) + if (trust.value?.ownerId == ownerId) { trust.value?.answer?.complete(false); trust.value = null } + } + fun answerTrust(accept: Boolean) { trust.value?.answer?.complete(accept); trust.value = null } + fun attachRenderer(id: String, token: Any, callback: () -> Unit) { + renderOwner = id; renderToken = token; redraw = callback + } + fun detachRenderer(id: String, token: Any) { + if (renderOwner == id && renderToken === token) { renderOwner = null; renderToken = null; redraw = null } + } + @Volatile private var terminalColors: IntArray? = null + fun setTerminalColors(value: IntArray) { + terminalColors = value.copyOf() + live.value.forEach { it.terminal.colors(value) } + } + fun local(): String = addTerminal("Term", "Local", LocalPtyTransport(LocalTerminalStorage.homePath(context))) + fun ssh(host: HostProfile, password: CharArray): String { + val id = UUID.randomUUID().toString() + val connection = SshConnection(host, password, { h, fingerprint -> verify(id, h, fingerprint) }) { stage -> + main.post { update(id) { if (it.status == "connecting") it.copy(stage = stage) else it } } + } + val files = SftpClient(connection::sftp) { live.value.any { it.id == id && it.status == "ready" } } + return addTerminal(host.name, "SSH", SshTerminalTransport(connection), id, host, files) + } + + private fun addTerminal(title: String, source: String, transport: SessionTransport, + id: String = UUID.randomUUID().toString(), host: HostProfile? = null, files: SftpClient? = null): String { + val callbacks = object : TerminalCallbacks() { + override fun onTextChanged(session: TerminalSession) { if (renderOwner == id) redraw?.invoke() } + override fun onTitleChanged(session: TerminalSession) { update(id) { it.copy(title = session.title?.take(80) ?: it.title) } } + override fun onTransportReady(session: TerminalSession) { update(id) { it.copy(status = "ready", hasConnected = true) } } + override fun onSessionFinished(session: TerminalSession) { + cancelTrust(id) + update(id) { it.copy(status = if (session.failure == null) "ended" else "failed", + failure = session.failureCause?.let(::classifySshFailure)) } + stopIdleService() + } + override fun onInputRejected(session: TerminalSession) { error.value = "input_rejected" } + + } + val terminal = TerminalSession(transport, callbacks) + live.value = live.value + LocalSession(id, title, source, terminal, host = host, files = files) + SessionService.ensureStarted(context) + terminal.start() + terminalColors?.let { terminal.colors(it) } + return id + } + private fun update(id: String, change: (LocalSession) -> LocalSession) { live.value = live.value.map { if (it.id == id) change(it) else it } } + fun closeTerminal(id: String) { + remoteFiles.value = remoteFiles.value.filterNot { it.session == id } + cancelTrust(id) + live.value.find { it.id == id }?.terminal?.finishIfRunning() + live.value = live.value.filterNot { it.id == id } + drafts.value = drafts.value - id + recentCommands.value = recentCommands.value - id + stopIdleService() + } + + fun openSftpTab(session: String, file: SftpEntry): SftpTab { + require(live.value.any { it.id == session && it.files != null }) + remoteFiles.value.find { it.session == session && it.file.path == file.path }?.let { return it } + return SftpTab(UUID.randomUUID().toString(), session, file).also { remoteFiles.value = remoteFiles.value + it } + } + + fun closeSftpTab(id: String) { remoteFiles.value = remoteFiles.value.filterNot { it.id == id } } + fun importRelay(text: String): String? { + return try { + val profile = RelayProfile.parse(text) + check(savedRelays.value.size < 64 || savedRelays.value.any { it.id == profile.id }) + connectRelay(profile) + } catch (_: Exception) { error.value = "invalid_relay_invite"; null } + } + private fun persistRelays() { + scope.launch { + relayWrites.withLock { + runCatching { relayStore.save(savedRelays.value) }.onFailure { error.value = "relay_storage_failed" } + } + } + } + fun forgetRelay(profile: RelayProfile) { + computers.value.filter { it.host.id == profile.id }.forEach { closeDesktop(it.id) } + savedRelays.value = savedRelays.value.filterNot { it.id == profile.id } + persistRelays() + } + fun connectRelay(profile: RelayProfile): String { + val previous = computers.value.find { it.host.id == profile.id } + if (previous != null && previous.status in setOf("ready", "connecting", "approval") && + previous.relayProfile?.sameConnection(profile) == true) return previous.id + val host = HostProfile(profile.id, profile.name, profile.url, 443, "") + return addDesktop(host, true, relayTransport(profile), if (profile.mode == "lan") "LAN" else "Relay", + id = previous?.id ?: "relay:${profile.id}", relayProfile = profile) + } + fun connectDesktop(host: HostProfile, password: CharArray, allowInput: Boolean): String { + val id = UUID.randomUUID().toString() + return addDesktop(host, allowInput, SshDesktopTransport(SshConnection(host, password, + { h, fingerprint -> verify(id, h, fingerprint) })), "SSH", id) + } + + private fun addDesktop(host: HostProfile, allowInput: Boolean, transport: DesktopTransport, source: String, + id: String = UUID.randomUUID().toString(), relayProfile: RelayProfile? = null): String { + reconnect.cancelPending(id) + resumeChecks.remove(id)?.cancel() + val previous = computers.value.find { it.id == id } + val entry = if (previous == null) DesktopWorkspace(id, host, allowInput = false, transport = source, relayProfile = relayProfile, + hasConnected = relayProfile != null && savedRelays.value.any { it.id == relayProfile.id }) + else previous.copy(host = host, status = "connecting", allowInput = false, failure = null, pairingApproval = null, + connectionGeneration = previous.connectionGeneration + 1, transport = source, relayProfile = relayProfile) + computers.value = if (previous == null) computers.value + entry else computers.value.map { if (it.id == id) entry else it } + refreshLanDiscovery() + SessionService.ensureStarted(context) + val transitions = DesktopTransitions() + val negotiatedInput = AtomicBoolean(false) + lateinit var client: DesktopRuntimeClient + client = DesktopRuntimeClient(transport, { snapshot -> + val tabs = parseDesktopTabs(snapshot) + val panes = tabs.flatMap { it.panes } + val windows = snapshot.getJSONArray("windows").let { rows -> (0 until rows.length()).map { rows.getJSONObject(it).getLong("id") } } + val events = transitions.observe(snapshot) + main.post { + if (desktopClients[id] !== client) return@post + if (computers.value.none { it.id == id }) return@post + if (relayProfile != null && savedRelays.value.find { it.id == relayProfile.id } != relayProfile) { + if (savedRelays.value.size < 64 || savedRelays.value.any { it.id == relayProfile.id }) { + savedRelays.value = savedRelays.value.filterNot { it.id == relayProfile.id } + relayProfile + persistRelays() + } else error.value = "relay_storage_failed" + } + reconnect.forget(id) + val input = allowInput && (snapshot.optJSONObject("mobile_policy")?.optBoolean("allow_input") ?: negotiatedInput.get()) + computers.value = computers.value.map { if (it.id == id) it.copy(panes = panes, status = "ready", hasConnected = true, failure = null, + allowInput = input, pairingApproval = null, + runtimeProcess = snapshot.getLong("process_id"), tabs = tabs, windows = windows) else it } + refreshLanDiscovery() + if (computers.value.any { it.id == id }) events.forEach { SessionNotices.task(context, id, host.name, it) } + } + }, { failure -> main.post { + desktopFailed(id, client, failure) + } }, { approval -> main.post { + if (desktopClients[id] === client) computers.value = computers.value.map { + if (it.id == id) it.copy(status = "approval", pairingApproval = approval) else it + } + } }) + val replaced = desktopClients.put(id, client) + scope.launch { + try { + // 先交接所有权再异步释放旧连接;旧回调不会覆盖新状态,也不在 UI 线程等待网络。 + replaced?.close() + if (desktopClients[id] !== client) { client.close(); return@launch } + val hello = client.connect(allowInput) + negotiatedInput.set(allowInput && hello.optJSONObject("capabilities")?.optBoolean("input") == true) + // v2 权限以实时快照为准,旧版 SSH 通道才使用首次协商结果。 + if (relayProfile?.version != 2) main.post { + if (desktopClients[id] === client) computers.value = computers.value.map { + if (it.id == id) it.copy(allowInput = negotiatedInput.get()) else it + } + } + } catch (cancelled: CancellationException) { + client.close() + throw cancelled + } catch (failure: Exception) { + client.close() + main.post { + desktopFailed(id, client, classifyDesktopFailure(failure)) + } + } + } + return id + } + private fun desktopFailed(id: String, client: DesktopRuntimeClient, failure: DesktopFailureKind, immediate: Boolean = false) { + if (!desktopClients.remove(id, client)) return // A late old callback cannot remove a recovered client. + val current = computers.value.find { it.id == id } ?: return + computers.value = computers.value.map { if (it.id == id) it.copy(status = if (it.hasConnected) "disconnected" else "failed", allowInput = false, failure = failure, pairingApproval = null) else it } + if (current.hasConnected && current.relayProfile != null && DesktopReconnect.retryable(failure)) reconnect.schedule(id, immediate) + else error.value = failure.code + refreshLanDiscovery() + stopIdleService() + } + fun readDesktop(id: String, pane: DesktopPane) { + if (output.value.target == "$id:${pane.window}:${pane.id}") desktopReader?.refresh() + } + suspend fun watchDesktop(id: String, pane: DesktopPane) { + val client = desktopClients[id] ?: return + val identity = "$id:${pane.window}:${pane.id}" + readJob?.cancel() + val generation = ++readGeneration + val reader = DesktopReadScheduler() + desktopReader = reader + readJob = currentCoroutineContext().job + output.value = if (output.value.target == identity) output.value else DesktopOutput(identity, loading = true) + try { + suspend fun publish(read: DesktopPaneRead) { + val previous = output.value + val next = withContext(Dispatchers.IO) { + val response = read.response + val frame = if (!read.screenChanged && previous.frame != null) previous.frame else response.optJSONObject("screen")?.let { + decodeDesktopScreen(it, checkNotNull(terminalColors)) + } + DesktopOutput(identity, response.optString("text"), frame = frame) + } + currentCoroutineContext().ensureActive() + if (generation != readGeneration || desktopClients[id] !== client) throw CancellationException() + output.value = next + } + // Negotiated change stream: no pane.read timer or input-triggered + // round trip. Older desktops retain the explicit snapshot fallback. + if (client.streamPane(target(pane).put("lines", 100), ::publish)) return + reader.run { + val previous = output.value + val result = runCatching { + withContext(Dispatchers.IO) { + val read = client.readPane(target(pane).put("lines", 100)) + val response = read.response + val frame = if (!read.screenChanged && previous.frame != null) previous.frame else response.optJSONObject("screen")?.let { + decodeDesktopScreen(it, checkNotNull(terminalColors)) + } + DesktopOutput(identity, response.optString("text"), frame = frame) + } + } + currentCoroutineContext().ensureActive() + if (generation != readGeneration || desktopClients[id] !== client) throw CancellationException() + val next = result.getOrNull() ?: previous.copy(loading = false) + output.value = next + if (result.isFailure) { + client.resetScreen() + error.value = "desktop_read_failed" + } + next.frame !== previous.frame || next.text != previous.text + } + } catch (cancelled: CancellationException) { + throw cancelled + } catch (_: Exception) { + if (generation == readGeneration) { + output.value = output.value.copy(loading = false) + error.value = "desktop_read_failed" + } + } finally { + if (generation == readGeneration) { + desktopReader = null + readJob = null + } + } + } + fun leaveDesktopPane() { + readGeneration++ + readJob?.cancel() + desktopReader = null + output.value = DesktopOutput() + } + fun desktopInput(id: String, pane: DesktopPane): DesktopTerminalInput { + val client = desktopClients[id] + return DesktopTerminalInput( + request = { method, params -> + checkNotNull(client).request(method, params.put("window_id", pane.window).put("pane_id", pane.id)) + }, + active = { client != null && desktopClients[id] === client && + computers.value.any { it.id == id && it.allowInput && it.status == "ready" } }, + onAccepted = { if (output.value.target == "$id:${pane.window}:${pane.id}") readDesktop(id, pane) }, + onRejected = { uncertain -> error.value = if (uncertain) "delivery_unknown" else "input_rejected" }, + dispatch = { method, params -> + checkNotNull(client).dispatchInput(method, params.put("window_id", pane.window).put("pane_id", pane.id)) + }, + ) + } + + fun desktopTabs(id: String): DesktopTabs { + val client = desktopClients[id] + val owner = computers.value.find { it.id == id } + return DesktopTabs(request = { method, params -> checkNotNull(client).request(method, params) }, + availability = { write, tab -> + val current = computers.value.find { it.id == id } + when { + client == null || desktopClients[id] !== client || current?.status != "ready" -> "desktop_disconnected" + owner == null || owner.runtimeProcess != current.runtimeProcess || owner.connectionGeneration != current.connectionGeneration -> "desktop_session_changed" + write && !current.allowInput -> "input_not_authorized" + tab != null && current.tabs.none { it.window == tab.window && it.id == tab.id } -> "target_not_found" + else -> null + } + }) + } + + fun desktopConversation(id: String, pane: DesktopPane, identity: ConversationIdentity): DesktopConversation { + val client = desktopClients[id] + val owner = computers.value.find { it.id == id } + val key = "$id:${owner?.runtimeProcess}:${pane.window}:${pane.id}:${identity.kind}:${identity.session}" + return DesktopConversation(identity, key, conversationCache, + request = { method, params -> checkNotNull(client).request(method, params.put("window_id", pane.window).put("pane_id", pane.id)) }, + availability = { write -> + val current = computers.value.find { it.id == id } + val active = current?.panes?.find { it.window == pane.window && it.id == pane.id } + val agent = active?.agent + when { + client == null || desktopClients[id] !== client || current?.status != "ready" -> "desktop_disconnected" + owner == null || owner.runtimeProcess != current.runtimeProcess || owner.connectionGeneration != current.connectionGeneration -> "desktop_session_changed" + agent?.kind != identity.kind || agent.session != identity.session -> "conversation_identity_changed" + write && !current.allowInput -> "input_not_authorized" + else -> null + } + }) + } + + fun desktopGit(target: DesktopGitTarget): DesktopGit { + activeGit?.takeIf { it.target == target }?.let { return it } + val client = desktopClients[target.desktop] + return DesktopGit(target, scope, + request = { method, params -> checkNotNull(client).request(method, params) }, + availability = { write -> + if (client == null || desktopClients[target.desktop] !== client) "git_disconnected" + else target.availability(computers.value.find { it.id == target.desktop }, write) + }, + committed = { sent -> + main.post { if (drafts.value[target.draftKey] == sent) drafts.value = drafts.value - target.draftKey } + }, + ).also { activeGit = it } + } + suspend fun sendDesktop(id: String, pane: DesktopPane, text: String): Boolean { + if (computers.value.none { it.id == id && it.allowInput && it.status == "ready" }) return false + return try { + checkNotNull(desktopClients[id]).request("pane.prompt", target(pane).put("text", text).put("submit", true)) + if (output.value.target == "$id:${pane.window}:${pane.id}") readDesktop(id, pane) + true + } catch (cancelled: CancellationException) { + throw cancelled + } catch (_: Exception) { + error.value = "delivery_unknown" + false + } + } + private fun target(pane: DesktopPane) = JSONObject().put("window_id", pane.window).put("pane_id", pane.id) + fun closeAll() { + activeGit = null + reconnect.clear() + resumeChecks.values.forEach(Job::cancel) + resumeChecks.clear() + leaveDesktopPane() + pendingTrust.values.forEach { it.complete(false) }; pendingTrust.clear() + trust.value?.answer?.complete(false); trust.value = null + live.value.forEach { it.terminal.finishIfRunning() }; live.value = emptyList() + val clients = desktopClients.values.toList(); desktopClients.clear(); computers.value = emptyList() + refreshLanDiscovery() + drafts.value = emptyMap() + recentCommands.value = emptyMap() + scope.launch { clients.forEach { it.close() } } + stopIdleService() + } + fun closeDesktop(id: String) { + if (activeGit?.target?.desktop == id) activeGit = null + reconnect.forget(id) + resumeChecks.remove(id)?.cancel() + cancelTrust(id) + leaveDesktopPane() + val client = desktopClients.remove(id) + computers.value = computers.value.filterNot { it.id == id } + refreshLanDiscovery() + drafts.value = drafts.value.filterKeys { !it.startsWith("$id:") } + recentCommands.value = recentCommands.value.filterKeys { !it.startsWith("$id:") } + scope.launch { client?.close() } + stopIdleService() + } + private fun stopIdleService() { + if (live.value.none { it.status in setOf("ready", "connecting") } && + computers.value.none { it.status in setOf("ready", "connecting", "approval") }) { + context.stopService(android.content.Intent(context, SessionService::class.java)) + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/SessionService.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/SessionService.kt new file mode 100644 index 0000000..f2ca8a7 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/SessionService.kt @@ -0,0 +1,271 @@ +package io.github.kuddev.pebrel.mobile.session + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.os.IBinder +import android.os.PowerManager +import io.github.kuddev.pebrel.mobile.MainActivity +import io.github.kuddev.pebrel.mobile.PebrelApplication +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.DesktopPane +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.launch + +/** Keeps live terminal transports alive after the activity leaves the foreground. */ +class SessionService : Service() { + private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) + private lateinit var repository: SessionRepository + private var foregroundStarted = false + private var liveSessionCount = 0 + private var wakeLock: PowerManager.WakeLock? = null + private var startCommandReceived = false + private var idleStopPending = false + + override fun onCreate() { + super.onCreate() + repository = (application as PebrelApplication).sessions + createSessionChannel() + wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager) + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "${packageName}:sessions") + .apply { setReferenceCounted(false) } + // A startForegroundService call has a short deadline. Register the service + // notification before the StateFlow collector can observe its initial empty state. + ensureForeground() + + serviceScope.launch { + combine(repository.sessions, repository.desktops) { sessions, desktops -> + sessions.count { it.status in LIVE_STATES } + desktops.count { it.status in LIVE_STATES } + }.distinctUntilChanged().collect { count -> + onLiveSessionCountChanged(count) + } + } + } + + override fun onDestroy() { + releaseWakeLock() + repository.backgroundActive.value = false + serviceScope.cancel() + foregroundStarted = false + super.onDestroy() + } + + override fun onBind(intent: Intent?): IBinder? = null + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + startCommandReceived = true + when (intent?.action) { + ACTION_EXIT, LEGACY_STOP_ACTION -> { + repository.closeAll() + releaseWakeLock() + repository.backgroundActive.value = false + removeForeground() + stopSelfResult(startId) + } + ACTION_TOGGLE_WAKE_LOCK -> { + liveSessionCount = currentLiveSessionCount() + ensureForeground() + if (liveSessionCount == 0) stopForNoSessions() else toggleWakeLock() + } + else -> { + liveSessionCount = currentLiveSessionCount() + ensureForeground() + if (liveSessionCount > 0) { + repository.backgroundActive.value = true + publishNotification() + idleStopPending = false + } else { + stopForNoSessions() + } + } + } + return START_NOT_STICKY + } + + private fun onLiveSessionCountChanged(count: Int) { + liveSessionCount = count + if (count == 0) { + releaseWakeLock() + repository.backgroundActive.value = false + if (startCommandReceived) stopForNoSessions() else idleStopPending = true + return + } + idleStopPending = false + repository.backgroundActive.value = true + ensureForeground() + publishNotification() + } + + private fun stopForNoSessions() { + if (!startCommandReceived && !idleStopPending) return + idleStopPending = false + releaseWakeLock() + repository.backgroundActive.value = false + removeForeground() + stopSelf() + } + + private fun currentLiveSessionCount(): Int = + repository.sessions.value.count { it.status in LIVE_STATES } + + repository.desktops.value.count { it.status in LIVE_STATES } + + private fun ensureForeground() { + if (foregroundStarted) return + startForeground(NOTIFICATION_ID, buildNotification()) + foregroundStarted = true + } + + private fun removeForeground() { + if (!foregroundStarted) return + stopForeground(STOP_FOREGROUND_REMOVE) + foregroundStarted = false + } + + private fun toggleWakeLock() { + if (liveSessionCount == 0) { + releaseWakeLock() + return + } + val lock = wakeLock ?: return + if (lock.isHeld) { + releaseWakeLock() + } else { + runCatching { lock.acquire() } + } + publishNotification() + } + + private fun releaseWakeLock() { + wakeLock?.let { lock -> + if (lock.isHeld) runCatching { lock.release() } + } + } + + private fun publishNotification() { + if (!foregroundStarted || liveSessionCount == 0) return + getSystemService(NotificationManager::class.java).notify(NOTIFICATION_ID, buildNotification()) + } + + private fun buildNotification(): Notification { + val open = PendingIntent.getActivity( + this, + REQUEST_OPEN, + Intent(this, MainActivity::class.java).addFlags( + Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP, + ), + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, + ) + val exit = PendingIntent.getService( + this, + REQUEST_EXIT, + Intent(this, SessionService::class.java).setAction(ACTION_EXIT), + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, + ) + val toggleWakeLock = PendingIntent.getService( + this, + REQUEST_WAKE_LOCK, + Intent(this, SessionService::class.java).setAction(ACTION_TOGGLE_WAKE_LOCK), + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, + ) + val wakeLockHeld = wakeLock?.isHeld == true + return Notification.Builder(this, SESSION_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(getString(R.string.app_name)) + .setContentText(resources.getQuantityString(R.plurals.background_live_sessions, liveSessionCount, liveSessionCount)) + .setSubText(if (wakeLockHeld) getString(R.string.notification_wakelock_active) else null) + .setContentIntent(open) + .setCategory(Notification.CATEGORY_SERVICE) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setOngoing(true) + .setOnlyAlertOnce(true) + .setShowWhen(false) + .addAction(Notification.Action.Builder(null, getString(R.string.notification_exit), exit).build()) + .addAction( + Notification.Action.Builder( + null, + getString(if (wakeLockHeld) R.string.notification_release_wakelock else R.string.notification_acquire_wakelock), + toggleWakeLock, + ).build(), + ) + .build() + } + + private fun createSessionChannel() { + val manager = getSystemService(NotificationManager::class.java) + manager.createNotificationChannel( + NotificationChannel( + SESSION_CHANNEL_ID, + getString(R.string.background_title), + NotificationManager.IMPORTANCE_LOW, + ), + ) + } + + companion object { + const val ACTION_EXIT = "io.github.kuddev.pebrel.mobile.session.EXIT" + const val ACTION_TOGGLE_WAKE_LOCK = "io.github.kuddev.pebrel.mobile.session.TOGGLE_WAKE_LOCK" + const val ACTION_START = "io.github.kuddev.pebrel.mobile.session.START" + + private const val LEGACY_STOP_ACTION = "STOP" + private const val SESSION_CHANNEL_ID = "sessions" + private const val NOTIFICATION_ID = 1 + private const val REQUEST_OPEN = 10 + private const val REQUEST_EXIT = 11 + private const val REQUEST_WAKE_LOCK = 12 + private val LIVE_STATES = setOf("connecting", "approval", "ready") + + /** Starts the service from an activity or another user initiated app action. */ + fun start(context: Context) { + val appContext = context.applicationContext + val intent = Intent(appContext, SessionService::class.java).setAction(ACTION_START) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) appContext.startForegroundService(intent) + else appContext.startService(intent) + } + + /** Idempotent at the Android service boundary; safe to call for each new live session. */ + fun ensureStarted(context: Context) = start(context) + + fun stop(context: Context) { + context.applicationContext.stopService(Intent(context, SessionService::class.java)) + } + + fun toggleWakeLock(context: Context) { + context.applicationContext.startService( + Intent(context, SessionService::class.java).setAction(ACTION_TOGGLE_WAKE_LOCK), + ) + } + + fun exit(context: Context) { + context.applicationContext.startService( + Intent(context, SessionService::class.java).setAction(ACTION_EXIT), + ) + } + } +} + +object SessionNotices { + fun task(context: Context, desktop: String, host: String, pane: DesktopPane) { + if (Build.VERSION.SDK_INT >= 33 && context.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) return + val manager = context.getSystemService(NotificationManager::class.java) + manager.createNotificationChannel(NotificationChannel("tasks", context.getString(R.string.task_notifications), NotificationManager.IMPORTANCE_DEFAULT)) + val key = "$desktop:${pane.window}:${pane.id}" + val intent = Intent(context, MainActivity::class.java).setAction("OPEN_TASK").setData(android.net.Uri.parse("pebrel://task/$key")) + .putExtra("desktop", desktop).putExtra("window", pane.window).putExtra("pane", pane.id) + val pending = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) + manager.notify(key, 2, Notification.Builder(context, "tasks").setSmallIcon(R.drawable.ic_notification) + .setContentTitle(host).setContentText(context.getString(R.string.task_updated)).setContentIntent(pending) + .setVisibility(Notification.VISIBILITY_PRIVATE).setAutoCancel(true).build()) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/SharedStorageAccess.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/SharedStorageAccess.kt new file mode 100644 index 0000000..9cf736c --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/SharedStorageAccess.kt @@ -0,0 +1,94 @@ +package io.github.kuddev.pebrel.mobile.session + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.Settings +import java.io.File + +/** + * Owns the Android permission boundary for the user's shared Pebrel directory. + * A denied or unavailable directory is an explicit state; it never becomes app-private storage. + */ +object SharedStorageAccess { + const val DIRECTORY_NAME = "Pebrel" + + sealed class State { + data class Ready(val directory: File) : State() + data class RuntimePermissionRequired(val directory: File, val permissions: List) : State() + data class AllFilesAccessRequired(val directory: File) : State() + data class Unavailable(val directory: File, val reason: Reason) : State() + } + + enum class Reason { + MEDIA_UNAVAILABLE, + DIRECTORY_NOT_WRITABLE, + DIRECTORY_CREATION_FAILED, + } + + /** The default HOME requested for every local terminal. */ + @Suppress("DEPRECATION") + fun defaultDirectory(): File = Environment.getExternalStorageDirectory().resolve(DIRECTORY_NAME) + + fun state(context: Context): State { + val directory = defaultDirectory() + return try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (!Environment.isExternalStorageManager()) return State.AllFilesAccessRequired(directory) + } else { + val missing = runtimePermissions(context).filter { + context.checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED + } + if (missing.isNotEmpty()) return State.RuntimePermissionRequired(directory, missing) + } + + if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) { + return State.Unavailable(directory, Reason.MEDIA_UNAVAILABLE) + } + if (directory.exists()) { + return if (directory.isDirectory && directory.canWrite()) State.Ready(directory) + else State.Unavailable(directory, Reason.DIRECTORY_NOT_WRITABLE) + } + return if ((directory.mkdirs() || directory.isDirectory) && directory.canWrite()) State.Ready(directory) + else State.Unavailable(directory, Reason.DIRECTORY_CREATION_FAILED) + } catch (_: SecurityException) { + State.Unavailable(directory, Reason.DIRECTORY_NOT_WRITABLE) + } + } + + fun requireDirectory(context: Context): File { + val state = state(context) + return (state as? State.Ready)?.directory ?: throw SharedStorageUnavailableException(state) + } + + fun runtimePermissions(context: Context): List = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { + listOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE) + } else { + emptyList() + } + + /** Opens the one-time special-access screen on Android 11 and later. */ + fun allFilesAccessIntent(context: Context): Intent? { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return null + val appIntent = Intent( + Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, + Uri.parse("package:${context.packageName}"), + ) + if (appIntent.resolveActivity(context.packageManager) != null) return appIntent + val globalIntent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) + return globalIntent.takeIf { it.resolveActivity(context.packageManager) != null } + } + + /** Useful after a user has denied a runtime permission or wants to inspect app access. */ + fun appDetailsIntent(context: Context): Intent = Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.parse("package:${context.packageName}"), + ) +} + +class SharedStorageUnavailableException(val state: SharedStorageAccess.State) : + IllegalStateException("shared_storage_unavailable:${state.javaClass.simpleName}") diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/TerminalFonts.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/TerminalFonts.kt new file mode 100644 index 0000000..3106e9e --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/session/TerminalFonts.kt @@ -0,0 +1,40 @@ +package io.github.kuddev.pebrel.mobile.session + +import android.content.Context +import android.graphics.Typeface + +/** + * The application font registry. Asset names are generated by the Android + * packaging task and are kept here so every terminal surface resolves the same + * family from the same canonical preference value. + */ +object TerminalFonts { + const val MAPLE_ASSET = "terminal.ttf" + const val JETBRAINS_ASSET = "fonts/JetBrainsMono-Regular.ttf" + const val SYSTEM_FONT = TerminalPreferenceValues.SYSTEM_FONT + + data class Option(val id: String, val assetPath: String?) + + val options: List

    ") + messages.forEach { message -> + val id = readerEscape(message.id) + if (message.role == "tool") { + append("
    ") + append(readerEscape(message.name?.ifBlank { toolLabel } ?: toolLabel)) + if (!message.complete) append(" · ${readerEscape(pendingLabel)}") + append("") + if (message.text.isNotEmpty()) append(markup.codeBlock(message.text)) + message.detail?.let { append(markup.codeBlock(it)) } + if (message.truncated) append("

    ${readerEscape(clippedLabel)}

    ") + append("
    ") + } else { + append("
    ") + append(readerEscape(if (message.role == "user") userLabel else agent)) + append("
    ") + if (message.role == "user") append("
    ") + append(markup.markdown(message.text)) + if (message.role == "user") append("
    ") + if (message.truncated) append("

    ${readerEscape(clippedLabel)}

    ") + append("
    ") + } + } + append("
    ") + } + return markup.document(body, colors) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ConversationScreen.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ConversationScreen.kt new file mode 100644 index 0000000..9ec5538 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ConversationScreen.kt @@ -0,0 +1,183 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.content.Intent +import android.net.Uri +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.repeatOnLifecycle +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.session.DesktopWorkspace +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import kotlinx.coroutines.* + +@Composable +fun ConversationScreen(desktop: DesktopWorkspace, pane: DesktopPane, identity: ConversationIdentity, + repository: SessionRepository, onTerminal: () -> Unit, onTabs: () -> Unit, onFile: (DesktopTab) -> Unit) { + val client = remember(desktop.id, desktop.connectionGeneration, desktop.runtimeProcess, pane.window, pane.id, identity) { + repository.desktopConversation(desktop.id, pane, identity) + } + val page by client.state.collectAsStateWithLifecycle() + val lifecycle = LocalLifecycleOwner.current + val scope = rememberCoroutineScope() + val context = LocalContext.current + var failure by remember(client) { mutableStateOf(null) } + var loading by remember(client) { mutableStateOf(!page.loaded) } + var choosing by remember(client) { mutableStateOf(false) } + var refresh by remember(client) { mutableIntStateOf(0) } + val clipboard = rememberReaderClipboard(pane.displayTitle) { failure = "clipboard_failed" } + val allowed = desktop.status == "ready" && desktop.allowInput + val copyLabel = stringResource(R.string.reader_copy) + val copiedLabel = stringResource(R.string.reader_copied) + val imageLabel = stringResource(R.string.reader_open_image) + val userLabel = stringResource(R.string.chat_you) + val toolLabel = stringResource(R.string.chat_tool) + val pendingLabel = stringResource(R.string.chat_tool_pending) + val clippedLabel = stringResource(R.string.chat_content_clipped) + val agentName = pane.agent?.name?.ifBlank { identity.kind } ?: identity.kind + val colors = MaterialTheme.colorScheme.readerColors() + val document by produceState(null, page.messages, colors, agentName, userLabel, toolLabel, copyLabel) { + value = withContext(Dispatchers.Default) { + prepareConversation(page.messages, colors, agentName, userLabel, toolLabel, pendingLabel, clippedLabel, copyLabel, imageLabel) + } + } + LaunchedEffect(client, desktop.status, refresh) { + if (desktop.status != "ready") return@LaunchedEffect + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + while (isActive) { + try { client.refresh(); failure = null } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { + failure = conversationFailureCode(error) + if (failure in setOf("conversation_identity_changed", "conversation_ended", "desktop_session_changed", "method_not_found")) break + } finally { loading = false } + delay(if (client.state.value.state == "running") 1500 else 3000) + } + } + } + fun openLink(link: String) { + val uri = Uri.parse(link) + if (uri.scheme in setOf("https", "http", "mailto")) { + try { context.startActivity(Intent(Intent.ACTION_VIEW, uri)) } + catch (_: Exception) { failure = "link_open_failed" } + return + } + val base = DesktopFile("${page.cwd.replace('\\', '/').trimEnd('/')}/conversation.md", false, false, false, true, null) + val path = resolveReaderLink(base, link) + if (path == null) { failure = "link_open_failed"; return } + scope.launch { + try { onFile(repository.desktopTabs(desktop.id).openFile(pane.window, path)) } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = conversationFailureCode(error) } + } + } + Column(Modifier.fillMaxSize()) { + TerminalHeader(pane.displayTitle, desktop.host.name, desktop.status, onTerminal, onTabs, + onConversation = onTerminal, conversationActive = true) + Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically) { + HelperText("$agentName · ${if (desktop.status == "ready") statusLabel(page.state) else statusLabel(desktop.status)}", Modifier.weight(1f)) + if (page.before != null) TextButton({ + if (loading) return@TextButton + loading = true + scope.launch { + try { client.refresh(older = true); failure = null } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = conversationFailureCode(error) } + finally { loading = false } + } + }, enabled = !loading && desktop.status == "ready") { Text(stringResource(R.string.chat_older)) } + } + if (loading) LinearProgressIndicator(Modifier.fillMaxWidth()) + if (desktop.status != "ready") HelperText(stringResource(R.string.reader_offline), Modifier.padding(horizontal = 16.dp, vertical = 6.dp)) + else if (!desktop.allowInput) HelperText(stringResource(R.string.composer_pc_read_only_short), Modifier.padding(horizontal = 16.dp, vertical = 6.dp)) + failure?.let { code -> Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically) { + Text(conversationFailureText(code), Modifier.weight(1f), color = MaterialTheme.colorScheme.error, fontSize = 13.sp) + val returnToTerminal = code in setOf("conversation_identity_changed", "conversation_ended", "desktop_session_changed", "method_not_found") + TextButton({ if (returnToTerminal) onTerminal() else refresh++ }) { + Text(stringResource(if (returnToTerminal) R.string.chat_terminal else R.string.chat_refresh)) + } + } } + Box(Modifier.weight(1f).fillMaxWidth()) { + document?.let { prepared -> + ReaderWebView(prepared, colors, clipboard.copied, copyLabel, copiedLabel, null, + { index -> clipboard.copy(prepared.code[index], index) }, ::openLink, Modifier.fillMaxSize(), followUpdates = true) + } + if (page.loaded && page.messages.isEmpty()) HelperText(stringResource(R.string.chat_empty), Modifier.align(Alignment.Center).padding(24.dp)) + } + page.prompt?.let { prompt -> ConversationChoices(prompt, allowed && !choosing) { index -> + choosing = true + scope.launch { + try { client.choose(prompt, index); failure = null; refresh++ } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = conversationFailureCode(error); refresh++ } + finally { choosing = false } + } + } } + if (page.prompt == null && page.state in setOf("waiting_input", "attention")) { + TextButton(onTerminal, Modifier.fillMaxWidth()) { Text(stringResource(R.string.chat_terminal_question)) } + } + CommandComposer("${desktop.id}:${pane.window}:${pane.id}:chat:${identity.session}", repository, + allowed && page.canSend && !choosing, false, null, { label -> + scope.launch { + try { client.key(label); failure = null; refresh++ } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = conversationFailureCode(error); refresh++ } + } + }, shortcutsEnabled = allowed && page.identity.epoch != null && !choosing, send = { text -> + try { client.send(text); failure = null; refresh++; true } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { + failure = if (error is DesktopRpcFailure) error.code else "delivery_unknown" + false + } + }) + } +} + +@Composable +internal fun ConversationChoices(prompt: ConversationPrompt, enabled: Boolean, onChoose: (Int) -> Unit) { + Column(Modifier.fillMaxWidth().heightIn(max = 320.dp).verticalScroll(rememberScrollState()).padding(horizontal = 16.dp, vertical = 8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp)) { + HorizontalDivider() + Text(prompt.text, Modifier.padding(vertical = 8.dp), fontSize = 14.sp) + val labels = if (prompt.binary) listOf(stringResource(R.string.chat_allow), stringResource(R.string.chat_deny)) else prompt.options + labels.forEachIndexed { index, label -> + OutlinedButton({ onChoose(index) }, Modifier.fillMaxWidth().heightIn(min = 48.dp), enabled = enabled, shape = RoundedCornerShape(6.dp)) { + Text(label, Modifier.fillMaxWidth()) + } + } + } +} + +private fun conversationFailureCode(error: Exception): String = when (error) { + is DesktopRpcFailure -> error.code + is DesktopConnectionFailure -> "desktop_disconnected" + else -> "conversation_unavailable" +} + +@Composable +private fun conversationFailureText(code: String): String = when (code) { + "method_not_found" -> stringResource(R.string.chat_update_desktop) + "conversation_identity_changed", "conversation_ended", "desktop_session_changed" -> stringResource(R.string.chat_replaced) + "conversation_unavailable" -> stringResource(R.string.chat_unavailable) + "conversation_changed" -> stringResource(R.string.chat_refreshing) + "conversation_history_limit" -> stringResource(R.string.chat_history_limit) + "prompt_changed" -> stringResource(R.string.chat_prompt_changed) + "agent_busy", "input_in_progress" -> stringResource(R.string.chat_busy) + "delivery_unknown" -> stringResource(R.string.chat_delivery_unknown) + else -> tabFailureText(code) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopFileScreen.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopFileScreen.kt new file mode 100644 index 0000000..f9bd7a4 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopFileScreen.kt @@ -0,0 +1,228 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Intent +import android.graphics.BitmapFactory +import android.net.Uri +import androidx.compose.foundation.* +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.gestures.detectTransformGestures +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.session.DesktopWorkspace +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import kotlinx.coroutines.* + +@Composable +fun DesktopFileScreen(desktop: DesktopWorkspace, tab: DesktopTab, repository: SessionRepository, + onBack: () -> Unit, onTabs: () -> Unit, onOpen: (DesktopTab) -> Unit) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + val client = remember(desktop.id, desktop.connectionGeneration, desktop.runtimeProcess) { repository.desktopTabs(desktop.id) } + var content by remember(tab.key, desktop.connectionGeneration) { mutableStateOf(null) } + var loadRevision by remember(tab.key) { mutableStateOf(null) } + var failure by remember(tab.key) { mutableStateOf(null) } + var busy by remember(tab.key) { mutableStateOf(false) } + var progress by remember(tab.key) { mutableFloatStateOf(0f) } + var refresh by remember(tab.key) { mutableIntStateOf(0) } + var preview by rememberSaveable(tab.key) { mutableStateOf(true) } + var menu by remember { mutableStateOf(false) } + var details by remember { mutableStateOf(false) } + var outline by remember { mutableStateOf(false) } + var closing by remember { mutableStateOf(false) } + var heading by remember { mutableStateOf(null) } + val clipboard = rememberReaderClipboard(tab.displayTitle) { failure = "clipboard_failed" } + val copied = clipboard.copied + var imageReset by remember { mutableIntStateOf(0) } + val file = checkNotNull(tab.file) + val markdown = file.path.substringAfterLast('.').lowercase() in setOf("md", "markdown") + LaunchedEffect(tab.key, desktop.connectionGeneration, desktop.status, file.ready, refresh) { + if (desktop.status != "ready" || !file.ready) return@LaunchedEffect + busy = true; failure = null; progress = 0f + try { + val loaded = client.read(tab) { count, total -> progress = if (total == 0) 1f else count.toFloat() / total } + ensureActive() + content = loaded + loadRevision = file.revision + } catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = tabFailureCode(error) } + finally { busy = false } + } + LaunchedEffect(heading) { if (heading != null) { delay(200); heading = null } } + val colors = MaterialTheme.colorScheme.readerColors() + val copyLabel = stringResource(R.string.reader_copy) + val copiedLabel = stringResource(R.string.reader_copied) + val imageLabel = stringResource(R.string.reader_open_image) + val document by produceState(null, content, preview, colors) { + val loaded = content + value = if (loaded?.kind == "text") withContext(Dispatchers.Default) { + prepareReader(loaded.bytes.toString(Charsets.UTF_8), file.path, markdown && preview, colors, copyLabel, imageLabel) + } else null + } + val bitmap by produceState(null, content) { + value = null + val loaded = content + if (loaded?.kind == "image") { + try { value = withContext(Dispatchers.IO) { decodeReaderImage(loaded.bytes) } } + catch (cancelled: CancellationException) { throw cancelled } + catch (_: Exception) { failure = "image_decode_failed" } + } + } + fun copy(text: String, index: Int) { + clipboard.copy(text, index) + } + fun openLink(link: String) { + if (link.startsWith('#')) { heading = link; return } + val uri = Uri.parse(link) + if (uri.scheme in setOf("https", "http", "mailto")) { + try { context.startActivity(Intent(Intent.ACTION_VIEW, uri)) } + catch (_: Exception) { failure = "link_open_failed" } + return + } + val path = resolveReaderLink(file, link) + if (path == null) { failure = "link_open_failed"; return } + if (busy) return + scope.launch { + busy = true + try { onOpen(client.openFile(tab.window, path)) } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = tabFailureCode(error) } + finally { busy = false } + } + } + Column(Modifier.fillMaxSize()) { + Row(Modifier.fillMaxWidth().heightIn(min = 48.dp), verticalAlignment = Alignment.CenterVertically) { + GlyphButton(R.drawable.ic_back, stringResource(R.string.back), onBack) + Row(Modifier.weight(1f).heightIn(min = 48.dp).clickable(onClick = onTabs), verticalAlignment = Alignment.CenterVertically) { + Glyph(tabIcon(tab), Modifier.size(18.dp)) + Text(tab.displayTitle, Modifier.weight(1f).padding(horizontal = 8.dp), maxLines = 1, overflow = TextOverflow.Ellipsis, fontSize = 14.sp) + Glyph(R.drawable.ic_down, Modifier.size(12.dp)) + } + GlyphButton(R.drawable.ic_git_refresh, stringResource(R.string.reader_refresh), { refresh++ }, !busy && desktop.status == "ready") + Box { + GlyphButton(R.drawable.ic_more, stringResource(R.string.more_actions), { menu = true }) + DropdownMenu(menu, { menu = false }) { + if (markdown && preview) DropdownMenuItem({ Text(stringResource(R.string.reader_outline)) }, { menu = false; outline = true }, enabled = !document?.headings.isNullOrEmpty()) + if (content?.kind == "text") DropdownMenuItem({ Text(if (copied == -1) copiedLabel else stringResource(R.string.reader_copy_source)) }, { + menu = false; content?.let { copy(it.bytes.toString(Charsets.UTF_8), -1) } + }) + if (content?.kind == "image") DropdownMenuItem({ Text(stringResource(R.string.reader_reset_zoom)) }, { menu = false; imageReset++ }) + DropdownMenuItem({ Text(stringResource(R.string.reader_details)) }, { menu = false; details = true }) + DropdownMenuItem({ Text(stringResource(R.string.tab_close)) }, { menu = false; closing = true }, enabled = desktop.allowInput && !busy) + } + } + } + if (markdown) TabRow(if (preview) 0 else 1, containerColor = MaterialTheme.colorScheme.background) { + Tab(preview, { preview = true }, text = { Text(stringResource(R.string.reader_preview)) }) + Tab(!preview, { preview = false }, text = { Text(stringResource(R.string.reader_source)) }) + } + val changed = content != null && file.revision != null && file.revision != loadRevision + if (desktop.status != "ready" || file.dirty || changed || copied == -1) { + HelperText(when { + desktop.status != "ready" -> stringResource(R.string.reader_offline) + copied == -1 -> copiedLabel + changed -> stringResource(R.string.reader_changed) + else -> stringResource(R.string.reader_unsaved) + }, Modifier.padding(horizontal = 16.dp, vertical = 5.dp)) + } + if (busy) LinearProgressIndicator(progress = { progress }, modifier = Modifier.fillMaxWidth()) + failure?.let { code -> Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically) { + Text(tabFailureText(code), Modifier.weight(1f), color = MaterialTheme.colorScheme.error, fontSize = 13.sp) + TextButton({ refresh++ }) { Text(stringResource(R.string.retry)) } + } } + Box(Modifier.fillMaxWidth().weight(1f)) { + val prepared = document + val loadedImage = bitmap + when { + prepared != null -> ReaderWebView(prepared, colors, copied, copyLabel, copiedLabel, heading, + { index -> copy(prepared.code[index], index) }, ::openLink, Modifier.fillMaxSize()) + loadedImage != null -> ReaderImage(loadedImage, tab.displayTitle, imageReset) + failure == null -> CircularProgressIndicator(Modifier.align(Alignment.Center)) + } + } + } + if (details) AlertDialog(onDismissRequest = { details = false }, title = { Text(tab.displayTitle) }, + text = { androidx.compose.foundation.text.selection.SelectionContainer { Text("${desktop.host.name}\n${file.path}") } }, + confirmButton = { TextButton({ details = false }) { Text(stringResource(R.string.close)) } }) + if (outline) AlertDialog(onDismissRequest = { outline = false }, title = { Text(stringResource(R.string.reader_outline)) }, + text = { Column(Modifier.verticalScroll(rememberScrollState())) { + document?.headings?.forEach { item -> + TextButton({ heading = item.id; outline = false }, Modifier.fillMaxWidth().padding(start = ((item.level - 1) * 10).dp)) { + Text(item.text, Modifier.fillMaxWidth()) + } + } + } }, confirmButton = { TextButton({ outline = false }) { Text(stringResource(R.string.close)) } }) + if (closing) DesktopTabCloseDialog(tab, client, { closing = false }, onBack) +} + +@Composable +internal fun ReaderImage(bitmap: ImageBitmap, title: String, reset: Int) { + var scale by remember(bitmap, reset) { mutableFloatStateOf(1f) } + var offset by remember(bitmap, reset) { mutableStateOf(Offset.Zero) } + BoxWithConstraints(Modifier.fillMaxSize().clipToBounds()) { + val width = constraints.maxWidth.toFloat() + val height = constraints.maxHeight.toFloat() + val fit = minOf(width / bitmap.width, height / bitmap.height) + Image(bitmap, title, Modifier.fillMaxSize() + .pointerInput(bitmap, reset) { detectTapGestures(onDoubleTap = { scale = 1f; offset = Offset.Zero }) } + .pointerInput(bitmap, reset, width, height) { detectTransformGestures { centroid, pan, zoom, _ -> + val next = (scale * zoom).coerceIn(1f, 8f) + val anchor = centroid - Offset(width / 2, height / 2) + val moved = (offset - anchor) * (next / scale) + anchor + pan + val maxX = ((bitmap.width * fit * next - width) / 2).coerceAtLeast(0f) + val maxY = ((bitmap.height * fit * next - height) / 2).coerceAtLeast(0f) + offset = Offset(moved.x.coerceIn(-maxX, maxX), moved.y.coerceIn(-maxY, maxY)); scale = next + } } + .graphicsLayer { scaleX = scale; scaleY = scale; translationX = offset.x; translationY = offset.y }) + } +} + +internal fun decodeReaderImage(bytes: ByteArray): ImageBitmap { + val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options) + check(options.outWidth > 0 && options.outHeight > 0) + options.inSampleSize = 1 + while (options.outWidth / options.inSampleSize > 4096 || options.outHeight / options.inSampleSize > 4096) options.inSampleSize *= 2 + options.inJustDecodeBounds = false + return checkNotNull(BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options)).asImageBitmap() +} + + +internal fun resolveReaderLink(file: DesktopFile, link: String): String? { + if (file.remote) return null + val path = Uri.decode(link.substringBefore('#')).replace('\\', '/') + if (path.isBlank() || path.any(Char::isISOControl)) return null + val drive = Regex("^[A-Za-z]:/") + if (':' in path && !drive.containsMatchIn(path)) return null + val base = file.path.replace('\\', '/') + val absolute = if (path.startsWith('/') || drive.containsMatchIn(path)) path else "${base.substringBeforeLast('/')}/$path" + val prefix = if (absolute.startsWith("//")) "//" else if (absolute.startsWith('/')) "/" else "" + val segments = mutableListOf() + val minimum = if (prefix == "//") 2 else if (drive.containsMatchIn(absolute)) 1 else 0 + for (part in absolute.removePrefix(prefix).split('/')) when (part) { + "", "." -> Unit + ".." -> if (segments.size > minimum) segments.removeAt(segments.lastIndex) + else -> segments += part + } + return prefix + segments.joinToString("/") +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopOutputSurface.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopOutputSurface.kt new file mode 100644 index 0000000..6bbb5e8 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopOutputSurface.kt @@ -0,0 +1,197 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.view.View +import androidx.compose.foundation.gestures.awaitEachGesture +import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.foundation.gestures.calculateZoom +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.Alignment +import kotlinx.coroutines.launch +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.TextButton +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.PointerEventPass +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.unit.em +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.onClick +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.viewinterop.AndroidView +import io.github.kuddev.pebrel.terminal.TerminalFrame +import io.github.kuddev.pebrel.terminal.TerminalSnapshotView +import io.github.kuddev.pebrel.terminal.TerminalInputTarget +import io.github.kuddev.pebrel.mobile.PebrelApplication +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.session.TerminalPreferenceValues +import kotlin.math.roundToInt + +/** Only two-finger gestures are claimed; one-finger scroll/selection is unchanged. */ +@Composable +internal fun DesktopOutputSurface( + identity: String, + text: String, + fontSize: Int, + pinchZoom: Boolean, + onFontSize: (Int) -> Unit, + modifier: Modifier = Modifier, + frame: TerminalFrame? = null, + inputTarget: TerminalInputTarget? = null, + keyboardRequest: Int = 0, + loading: Boolean = false, + connected: Boolean = true, + wrapLines: Boolean = true, +) { + val legacyText = frame == null && text.isNotEmpty() + var copiedText by remember(identity) { mutableStateOf(null) } + var lastKeyboardRequest by remember(identity) { mutableIntStateOf(keyboardRequest) } + var inputView by remember(identity) { mutableStateOf(null) } + val zoomFeedback = rememberTerminalZoomFeedback(identity) + Box(modifier) { + // 旧桌面只返回文本,也必须保留同一原生 IME 所有者;不把输出伪装成可编辑文本或虚构光标。 + key(identity) { + AndroidView(modifier = Modifier.fillMaxSize(), factory = { context -> TerminalSnapshotView(context) }, + onRelease = { view -> + view.inputTarget = null + view.onZoomChanged = null + if (inputView === view) inputView = null + }, update = { view -> + inputView = view + val app = view.context.applicationContext as PebrelApplication + view.setFont(app.terminalTypeface(app.sessions.display.state.value.fontFamily), fontSize) + view.pinchZoom = pinchZoom + view.wrapLines = wrapLines + view.onZoomChanged = zoomFeedback::update + view.frame = frame + view.contentDescription = frame?.text().orEmpty() + view.importantForAccessibility = if (legacyText) View.IMPORTANT_FOR_ACCESSIBILITY_NO + else View.IMPORTANT_FOR_ACCESSIBILITY_AUTO + view.onCopyRequested = { copiedText = it } + view.inputTarget = inputTarget + if (lastKeyboardRequest != keyboardRequest) { + lastKeyboardRequest = keyboardRequest + view.post { if (view.isAttachedToWindow) view.showKeyboard() } + } + }) + } + if (legacyText) { + LegacyDesktopText(identity, text, fontSize, pinchZoom, onFontSize, + onKeyboard = if (inputTarget == null) null else ({ inputView?.showKeyboard() }), + onZoomChanged = zoomFeedback::update, wrapLines = wrapLines) + } else if (frame == null && connected) { + HelperText(stringResource(if (loading) R.string.terminal_loading else R.string.terminal_waiting), + Modifier.align(Alignment.Center).padding(16.dp)) + } + TerminalZoomIndicator(zoomFeedback) + } + copiedText?.let { content -> + AlertDialog(onDismissRequest = { copiedText = null }, + title = { Text(stringResource(R.string.desktop_snapshot_text)) }, + text = { SelectionContainer { Text(content, maxLines = 12) } }, + confirmButton = { TextButton({ copiedText = null }) { Text(stringResource(R.string.close)) } }) + } +} + +@Composable +private fun LegacyDesktopText( + identity: String, + text: String, + fontSize: Int, + pinchZoom: Boolean, + onFontSize: (Int) -> Unit, + onKeyboard: (() -> Unit)?, + onZoomChanged: (Int, Boolean) -> Unit, + wrapLines: Boolean, +) { + // Keep the gesture handler's state object stable after a persisted zoom. + // Replacing it while pointerInput retains the same identity makes the next + // gesture write to a detached state object. + var displayedSize by remember(identity) { mutableFloatStateOf(fontSize.toFloat()) } + LaunchedEffect(identity, fontSize) { displayedSize = fontSize.toFloat() } + val saveSize by rememberUpdatedState(onFontSize) + val showKeyboard by rememberUpdatedState(onKeyboard) + val reportZoom by rememberUpdatedState(onZoomChanged) + val scroll = rememberScrollState() + val scope = rememberCoroutineScope() + var previousExtent by remember(identity) { mutableIntStateOf(0) } + LaunchedEffect(identity, text, scroll.maxValue) { + // 首帧显示最新输出;用户上翻后保留阅读位置,不裁掉原始空行或伪造光标。 + val follow = scroll.value >= minOf(previousExtent, scroll.maxValue) - 1 + previousExtent = scroll.maxValue + if (follow && !scroll.isScrollInProgress) scroll.scrollTo(scroll.maxValue) + } + val keyboardLabel = stringResource(R.string.composer_show_keyboard) + val inputGestures = if (onKeyboard == null) Modifier else Modifier + .semantics { onClick(keyboardLabel) { showKeyboard?.invoke(); true } } + .pointerInput(identity) { + awaitEachGesture { + val down = awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Final) + var tapped = !down.isConsumed + do { + val event = awaitPointerEvent(PointerEventPass.Final) + // 只观察未被选区、滚动或缩放认领的短点按,不消费事件,保留原有阅读手势。 + if (event.changes.any { it.isConsumed || it.id != down.id || + (it.position - down.position).getDistance() > viewConfiguration.touchSlop || + it.uptimeMillis - down.uptimeMillis >= viewConfiguration.longPressTimeoutMillis }) { + tapped = false + } + } while (event.changes.any { it.pressed }) + if (tapped) showKeyboard?.invoke() + } + } + val gestures = if (!pinchZoom) Modifier else Modifier.pointerInput(identity) { + awaitEachGesture { + awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Initial) + var zooming = false + do { + val event = awaitPointerEvent(PointerEventPass.Initial) + if (event.changes.count { it.pressed } >= 2) { + if (!zooming) reportZoom(displayedSize.roundToInt(), true) + zooming = true + val zoom = event.calculateZoom() + if (zoom.isFinite() && zoom > 0f) { + displayedSize = (displayedSize * zoom).coerceIn( + TerminalPreferenceValues.MIN_FONT_SIZE.toFloat(), + TerminalPreferenceValues.MAX_FONT_SIZE.toFloat(), + ) + reportZoom(displayedSize.roundToInt(), true) + } + } + // Finish the whole two-finger gesture before handing control + // back, so the remaining finger does not cause a scroll jump. + if (zooming) event.changes.forEach { it.consume() } + } while (event.changes.any { it.pressed }) + if (zooming) { + val size = displayedSize.roundToInt() + saveSize(size) + reportZoom(size, false) + } + } + } + Box(Modifier.fillMaxSize()) { + Box(Modifier.fillMaxSize().then(gestures).then(inputGestures) + .verticalScroll(scroll).then(if (wrapLines) Modifier else Modifier.horizontalScroll(rememberScrollState()))) { + SelectionContainer { + Text(text, fontFamily = LocalTerminalFont.current, fontSize = displayedSize.sp, + // 固定正文行高会只放大横向字宽;终端行距必须随字号等比变化。 + lineHeight = 1.25.em, letterSpacing = 0.sp, + softWrap = wrapLines, modifier = Modifier.padding(4.dp).then(if (wrapLines) Modifier.fillMaxWidth() else Modifier)) + } + } + if (scroll.canScrollForward) TextButton( + onClick = { scope.launch { scroll.scrollTo(scroll.maxValue) } }, + modifier = Modifier.align(Alignment.BottomEnd).padding(8.dp), + ) { Text(stringResource(R.string.terminal_latest_output)) } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopStatus.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopStatus.kt new file mode 100644 index 0000000..756d441 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopStatus.kt @@ -0,0 +1,25 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.DesktopFailureKind + +@Composable +fun desktopFailureText(kind: DesktopFailureKind): String = stringResource(when (kind) { + DesktopFailureKind.DNS -> R.string.desktop_dns + DesktopFailureKind.TIMEOUT -> R.string.desktop_timeout + DesktopFailureKind.REFUSED -> R.string.desktop_refused + DesktopFailureKind.NETWORK -> R.string.desktop_network + DesktopFailureKind.CERTIFICATE_CHANGED -> R.string.desktop_certificate_changed + DesktopFailureKind.CERTIFICATE_DATE -> R.string.desktop_certificate_date + DesktopFailureKind.TLS -> R.string.desktop_tls + DesktopFailureKind.AUTHENTICATION -> R.string.desktop_authentication + DesktopFailureKind.ALREADY_CONNECTED -> R.string.desktop_already_connected + DesktopFailureKind.SERVER -> R.string.desktop_server + DesktopFailureKind.PEER_OFFLINE -> R.string.desktop_peer_offline + DesktopFailureKind.RUNTIME_UNAVAILABLE -> R.string.desktop_runtime_unavailable + DesktopFailureKind.PROTOCOL -> R.string.desktop_protocol + DesktopFailureKind.DISCONNECTED -> R.string.desktop_disconnected + DesktopFailureKind.UNKNOWN -> R.string.desktop_connection_failed +}) diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopTabsPage.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopTabsPage.kt new file mode 100644 index 0000000..6a53c2e --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopTabsPage.kt @@ -0,0 +1,193 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.session.DesktopWorkspace +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.launch + +@Composable +fun DesktopTabsPage(desktop: DesktopWorkspace?, repository: SessionRepository, onBack: () -> Unit, + onTab: (DesktopTab) -> Unit, onPane: (DesktopPane) -> Unit, + onRetry: (() -> Unit)?, onDisconnect: () -> Unit) { + val scope = rememberCoroutineScope() + // 首次快照才带进程身份;连接中创建的句柄不能沿用到 ready 状态。 + val client = remember(desktop?.id, desktop?.connectionGeneration, desktop?.runtimeProcess) { desktop?.let { repository.desktopTabs(it.id) } } + var addMenu by remember { mutableStateOf(false) } + var adding by remember { mutableStateOf(null) } + var closing by remember { mutableStateOf(null) } + var value by remember { mutableStateOf("") } + var busy by remember { mutableStateOf(false) } + var failure by remember { mutableStateOf(null) } + var computerMenu by remember { mutableStateOf(false) } + var changingAddress by remember { mutableStateOf(null) } + val savedComputer = desktop?.relayProfile?.takeIf { desktop.hasConnected && it.canRediscover } + val active = desktop?.tabs?.find { it.active } ?: desktop?.tabs?.firstOrNull() + val window = active?.window ?: desktop?.windows?.firstOrNull() + val directory = active?.primaryPane?.takeIf { it.sshDestination == null }?.cwd + ?: active?.file?.takeUnless { it.remote }?.path?.replace('\\', '/')?.substringBeforeLast('/') + val enabled = desktop?.status == "ready" && desktop.allowInput && !busy + fun begin(kind: String) { + addMenu = false; failure = null + value = directory.orEmpty().let { if (kind == "file" && it.isNotBlank()) it.trimEnd('/', '\\') + "/" else it } + adding = kind + } + Column(Modifier.fillMaxSize()) { + PageHeader(stringResource(R.string.computer_tabs), onBack) { + Box { + GlyphButton(R.drawable.ic_plus, stringResource(R.string.tab_new), { addMenu = true }, enabled) + DropdownMenu(addMenu, { addMenu = false }) { + DropdownMenuItem({ Text(stringResource(R.string.tab_new_terminal)) }, { begin("terminal") }) + DropdownMenuItem({ Text(stringResource(R.string.tab_open_file)) }, { begin("file") }, enabled = window != null) + } + } + if (savedComputer != null) Box { + GlyphButton(R.drawable.ic_more, stringResource(R.string.more_actions), { computerMenu = true }) + DropdownMenu(computerMenu, { computerMenu = false }) { + DropdownMenuItem({ Text(stringResource(R.string.computer_edit_address)) }, { + computerMenu = false + changingAddress = savedComputer + }) + } + } + } + DesktopScreen(desktop, onPane, onRetry, onTab = onTab, onCloseTab = { closing = it }, onDisconnect = onDisconnect) + } + changingAddress?.let { profile -> + ComputerAddressDialog(profile, { changingAddress = null }) { replacement -> + changingAddress = null + repository.connectRelay(replacement) + } + } + adding?.let { kind -> + AlertDialog(onDismissRequest = { if (!busy) adding = null }, + title = { Text(stringResource(if (kind == "terminal") R.string.tab_new_terminal else R.string.tab_open_file)) }, + text = { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { + OutlinedTextField(value, { value = it; failure = null }, Modifier.fillMaxWidth(), enabled = !busy, + label = { Text(stringResource(if (kind == "terminal") R.string.tab_directory else R.string.tab_file_path)) }, singleLine = true) + if (kind == "file") HelperText(stringResource(R.string.tab_file_types)) + failure?.let { Text(tabFailureText(it), color = MaterialTheme.colorScheme.error) } + if (busy) LinearProgressIndicator(Modifier.fillMaxWidth()) + } }, + confirmButton = { TextButton({ + if (busy || client == null) return@TextButton + busy = true + scope.launch { + try { + val tab = if (kind == "terminal") client.newTerminal(window, value.takeIf(String::isNotBlank)) + else client.openFile(checkNotNull(window), value.trim()) + adding = null; onTab(tab) + } catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = tabFailureCode(error) } + finally { busy = false } + } + }, enabled = enabled && (kind == "terminal" || value.isNotBlank())) { Text(stringResource(R.string.tab_open)) } }, + dismissButton = { TextButton({ adding = null }, enabled = !busy) { Text(stringResource(R.string.cancel)) } }) + } + closing?.let { tab -> if (client != null) DesktopTabCloseDialog(tab, client, { closing = null }, { closing = null }) } +} + +@Composable +internal fun DesktopTabRows(desktop: DesktopWorkspace, onTab: (DesktopTab) -> Unit, + onPane: (DesktopPane) -> Unit, onClose: ((DesktopTab) -> Unit)?) { + desktop.tabs.forEach { tab -> + key(tab.key) { + Row(Modifier.fillMaxWidth().heightIn(min = 64.dp), verticalAlignment = Alignment.CenterVertically) { + Row(Modifier.weight(1f).heightIn(min = 64.dp) + .clickable(enabled = desktop.status == "ready" && (tab.readable || tab.panes.isNotEmpty())) { onTab(tab) } + .padding(vertical = 8.dp), verticalAlignment = Alignment.CenterVertically) { + Box(Modifier.width(36.dp), contentAlignment = Alignment.CenterStart) { Glyph(tabIcon(tab)) } + Column(Modifier.weight(1f)) { + Text(tab.displayTitle, fontSize = 14.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) + Text(tabKindLabel(tab), fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) + } + if (tab.active) Glyph(R.drawable.ic_check, Modifier.padding(horizontal = 8.dp).size(16.dp), MaterialTheme.colorScheme.primary) + } + if (onClose != null && tab.id != null) GlyphButton(R.drawable.ic_close, + stringResource(R.string.tab_close_named, tab.displayTitle), { onClose(tab) }, desktop.status == "ready" && desktop.allowInput) + } + if (tab.panes.size > 1) tab.panes.forEach { pane -> + TextButton({ onPane(pane) }, Modifier.fillMaxWidth().padding(start = 36.dp), enabled = desktop.status == "ready") { + Text("${pane.id} · ${pane.title.ifBlank { pane.displayTitle }}", Modifier.fillMaxWidth(), maxLines = 1, overflow = TextOverflow.Ellipsis) + } + } + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) + } + } +} + +@Composable +internal fun DesktopTabCloseDialog(tab: DesktopTab, client: DesktopTabs, onDismiss: () -> Unit, onClosed: () -> Unit) { + val scope = rememberCoroutineScope() + var busy by remember { mutableStateOf(false) } + var error by remember { mutableStateOf(null) } + AlertDialog(onDismissRequest = { if (!busy) onDismiss() }, title = { Text(stringResource(R.string.tab_close)) }, + text = { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { + Text(stringResource(R.string.tab_close_confirm, tab.displayTitle)) + error?.let { Text(tabFailureText(it), color = MaterialTheme.colorScheme.error) } + if (busy) LinearProgressIndicator(Modifier.fillMaxWidth()) + } }, + confirmButton = { TextButton({ + if (busy) return@TextButton + busy = true + scope.launch { + try { + check(client.close(tab).getJSONObject("action").getBoolean("closed")) + onClosed() + } catch (cancelled: CancellationException) { throw cancelled } + catch (failure: Exception) { error = tabFailureCode(failure) } + finally { busy = false } + } + }, enabled = !busy) { Text(stringResource(R.string.tab_close)) } }, + dismissButton = { TextButton(onDismiss, enabled = !busy) { Text(stringResource(R.string.cancel)) } }) +} + +internal fun tabIcon(tab: DesktopTab): Int = when (tab.kind) { + "image" -> R.drawable.ic_image + "document", "code" -> R.drawable.ic_git_file + else -> R.drawable.ic_terminal +} + +@Composable +internal fun tabKindLabel(tab: DesktopTab): String = stringResource(when (tab.kind) { + "image" -> R.string.tab_kind_image + "document" -> R.string.tab_kind_document + "code" -> R.string.tab_kind_code + "ssh" -> R.string.tab_kind_ssh + else -> R.string.tab_kind_terminal +}) + +internal fun tabFailureCode(error: Exception): String = when (error) { + is DesktopRpcFailure -> error.code + is DesktopConnectionFailure -> "desktop_disconnected" + else -> "file_read_failed" +} + +@Composable +internal fun tabFailureText(code: String): String = stringResource(when (code) { + "tabs_unsupported", "method_not_found" -> R.string.tabs_unsupported + "input_not_authorized" -> R.string.composer_pc_read_only + "desktop_session_changed", "target_not_found" -> R.string.desktop_session_changed + "desktop_disconnected" -> R.string.reader_disconnected + "confirmation_required" -> R.string.tab_busy + "unsaved_changes" -> R.string.tab_unsaved + "file_saving", "close_pending" -> R.string.tab_saving + "file_changed" -> R.string.reader_changed + "file_too_large" -> R.string.reader_too_large + "unsupported_file" -> R.string.reader_unsupported + "image_decode_failed" -> R.string.reader_image_failed + "link_open_failed" -> R.string.reader_link_failed + "clipboard_failed" -> R.string.reader_copy_failed + else -> R.string.reader_load_failed +}) diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopTerminalTheme.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopTerminalTheme.kt new file mode 100644 index 0000000..4ba39fe --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/DesktopTerminalTheme.kt @@ -0,0 +1,48 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.lerp +import androidx.compose.ui.graphics.luminance +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat +import io.github.kuddev.pebrel.terminal.TerminalFrame + +/** Only the active desktop pane adopts remote colors. Settings and SSH keep + * the user's phone theme. No persistent theme preference is changed. */ +internal fun desktopTerminalColors(frame: TerminalFrame?, fallback: ColorScheme): ColorScheme { + if (frame == null || frame.meta.size < 10) return fallback + val background = Color(frame.background) + val foreground = Color(frame.meta[8]) + val accent = Color(frame.meta[6]) + val base = if (background.luminance() < .5f) darkColorScheme() else lightColorScheme() + val surface = lerp(background, foreground, .06f) + return base.copy( + background = background, surface = background, + onBackground = foreground, onSurface = foreground, + primary = accent, onPrimary = background, + onSurfaceVariant = lerp(background, foreground, .72f), + surfaceVariant = surface, surfaceContainer = surface, + surfaceContainerLow = background, surfaceContainerHigh = surface, + secondaryContainer = surface, onSecondaryContainer = foreground, + outline = lerp(background, foreground, .4f), + outlineVariant = lerp(background, foreground, .2f), + error = Color(frame.meta[9]), surfaceTint = Color.Transparent, + ) +} + +@Composable +fun DesktopTerminalTheme(frame: TerminalFrame?, systemBars: Boolean = false, content: @Composable () -> Unit) { + val colors = desktopTerminalColors(frame, MaterialTheme.colorScheme) + val view = LocalView.current + if (systemBars) SideEffect { + (view.context as? android.app.Activity)?.window?.let { window -> + WindowCompat.getInsetsController(window, view).apply { + isAppearanceLightStatusBars = colors.background.luminance() >= .5f + isAppearanceLightNavigationBars = colors.background.luminance() >= .5f + } + } + } + MaterialTheme(colorScheme = colors, content = content) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/EmptyHomeScreen.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/EmptyHomeScreen.kt new file mode 100644 index 0000000..12e4301 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/EmptyHomeScreen.kt @@ -0,0 +1,59 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.heading +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R + +@Composable +internal fun EmptyHomeScreen(onComputer: () -> Unit, onSshHost: () -> Unit, onLocal: () -> Unit) { + BoxWithConstraints(Modifier.fillMaxSize()) { + val stackActions = maxWidth < 360.dp || LocalDensity.current.fontScale > 1.1f + // 短屏减少留白,大字体仍可滚动;入口不依赖固定屏高才能触达。 + val topSpace = if (maxHeight < 480.dp) 40.dp else 112.dp + Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()) + .padding(start = 28.dp, end = 28.dp, top = topSpace, bottom = 28.dp)) { + Text(stringResource(R.string.home_empty_title), fontSize = 17.sp, lineHeight = 26.sp, + fontWeight = FontWeight.Medium, modifier = Modifier.semantics { heading() }) + Text(stringResource(R.string.home_empty_hint), fontSize = 12.sp, lineHeight = 20.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(top = 8.dp)) + Button(onClick = onComputer, shape = RoundedCornerShape(14.dp), + modifier = Modifier.padding(top = 22.dp).fillMaxWidth().heightIn(min = 48.dp)) { + Text(stringResource(R.string.computer_connect), fontSize = 14.sp, lineHeight = 20.sp, + textAlign = TextAlign.Center) + } + Spacer(Modifier.height(8.dp)) + if (stackActions) { + EmptyHomeAction(R.string.add_ssh, onSshHost, Modifier.fillMaxWidth()) + EmptyHomeAction(R.string.home_open_local, onLocal, Modifier.fillMaxWidth()) + } else { + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) { + EmptyHomeAction(R.string.add_ssh, onSshHost, Modifier.weight(1f)) + EmptyHomeAction(R.string.home_open_local, onLocal, Modifier.weight(1f)) + } + } + } + } +} + +@Composable +private fun EmptyHomeAction(label: Int, onClick: () -> Unit, modifier: Modifier) { + TextButton(onClick, modifier.heightIn(min = 48.dp), contentPadding = PaddingValues(8.dp)) { + Text(stringResource(label), fontSize = 14.sp, lineHeight = 20.sp, textAlign = TextAlign.Center) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/GitScreen.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/GitScreen.kt new file mode 100644 index 0000000..76e3814 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/GitScreen.kt @@ -0,0 +1,421 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material3.* +import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardCapitalization +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.git.* +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import java.text.DateFormat +import java.util.Date + +@Composable +fun GitScreen(target: DesktopGitTarget, repository: SessionRepository, onBack: () -> Unit) { + val controller = remember(target) { repository.desktopGit(target) } + val state by controller.state.collectAsStateWithLifecycle() + val desktops by repository.desktops.collectAsStateWithLifecycle() + val drafts by repository.drafts.collectAsStateWithLifecycle() + val desktop = desktops.find { it.id == target.desktop } + val unavailable = target.availability(desktop, false) ?: controller.availability(false) + LaunchedEffect(target, unavailable) { + if (unavailable != null) { + val pane = desktop?.panes?.find { it.window == target.window && it.id == target.pane } + android.util.Log.w("PebrelGit", "availability=$unavailable generation=${target.generation}/${desktop?.connectionGeneration} process=${target.process}/${desktop?.runtimeProcess} pane=${pane != null} cwdEqual=${pane?.cwd == target.cwd} ssh=${pane?.sshDestination != null}") + } + } + LaunchedEffect(controller) { controller.refresh() } + GitScreenContent(target, desktop?.host?.name.orEmpty(), state, unavailable, desktop?.allowInput == true, + drafts[target.draftKey].orEmpty(), { repository.setDraft(target.draftKey, it) }, controller, onBack) +} + +@Composable +internal fun GitScreenContent(target: DesktopGitTarget, desktopName: String, state: GitViewState, + unavailable: String?, allowInput: Boolean, draft: String, + onDraft: (String) -> Unit, controller: DesktopGit, onBack: () -> Unit) { + val canRead = unavailable == null && !state.busy + val canWrite = canRead && allowInput && !state.needsRefresh + var history by rememberSaveable(target) { mutableStateOf(false) } + var details by remember(target) { mutableStateOf(false) } + var collapsed by rememberSaveable(target, state.snapshot?.root) { mutableStateOf(emptyList()) } + // 列表位置归页面所有,打开 diff 或切换历史不应把用户送回文件树顶部。 + val changesScroll = rememberLazyListState() + val historyScroll = rememberLazyListState() + BackHandler(state.diff != null) { controller.closeDiff() } + val back = { if (state.diff != null) controller.closeDiff() else onBack() } + val root = state.snapshot?.root ?: target.cwd + val diff = state.diff + Column(Modifier.fillMaxSize()) { + GitHeader(stringResource(if (diff == null) R.string.git_title else R.string.git_diff), + if (diff == null) gitPathName(root) else diff.path.ifEmpty { diff.commit.take(12) }, back, + onRefresh = { + state.diff?.let(controller::openDiff) ?: if (history) controller.history() else controller.refresh() + }, canRefresh = canRead, onDetails = { details = true }) + if (diff == null) { + TabRow(selectedTabIndex = if (history) 1 else 0, + containerColor = MaterialTheme.colorScheme.surface, + contentColor = MaterialTheme.colorScheme.onSurface, + indicator = { positions -> + TabRowDefaults.SecondaryIndicator(Modifier.tabIndicatorOffset(positions[if (history) 1 else 0]), + height = 2.dp, color = MaterialTheme.colorScheme.onSurface) + }) { + Tab(!history, { history = false }, modifier = Modifier.heightIn(min = 48.dp), + text = { Text(stringResource(R.string.git_changes), fontSize = 14.sp) }) + Tab(history, { history = true; controller.history() }, enabled = canRead, + modifier = Modifier.heightIn(min = 48.dp), + text = { Text(stringResource(R.string.git_history), fontSize = 14.sp) }) + } + } + if (state.busy) LinearProgressIndicator(Modifier.fillMaxWidth().height(2.dp)) else Spacer(Modifier.height(2.dp)) + if (diff == null) state.snapshot?.let { GitBranchSummary(it) } + GitFeedback(state, unavailable, !allowInput) + if (diff != null) { + GitDiffContent(diff, state, canWrite, controller) + } else if (history) { + GitHistoryContent(state, controller, unavailable == null, historyScroll) + } else { + GitChangesContent(state.snapshot, state.busy, canRead, canWrite, draft, onDraft, controller, + changesScroll, collapsed, { key -> collapsed = if (key in collapsed) collapsed - key else collapsed + key }) + } + } + if (details) AlertDialog(onDismissRequest = { details = false }, + title = { Text(stringResource(R.string.git_repository_details)) }, + text = { SelectionContainer { + Column(Modifier.verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(12.dp)) { + Text(desktopName) + Column { + HelperText(stringResource(R.string.terminal_directory)) + Text(root, fontFamily = LocalTerminalFont.current) + } + state.snapshot?.let { snapshot -> + Column { + Text(snapshot.branch) + Text(snapshot.upstream ?: stringResource(R.string.git_no_upstream)) + HelperText(stringResource(R.string.git_ahead_behind, snapshot.ahead, snapshot.behind)) + } + } + } + } }, confirmButton = { TextButton({ details = false }) { Text(stringResource(R.string.close)) } }) +} + +private fun gitPathName(path: String): String = path.replace('\\', '/').trimEnd('/').substringAfterLast('/').ifBlank { path } + +@Composable +private fun GitHeader(title: String, subtitle: String, onBack: () -> Unit, onRefresh: () -> Unit, + canRefresh: Boolean, onDetails: () -> Unit) { + Row(Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.surface).heightIn(min = 58.dp) + .padding(horizontal = 4.dp), verticalAlignment = Alignment.CenterVertically) { + GlyphButton(R.drawable.ic_back, stringResource(R.string.back), onBack) + Column(Modifier.weight(1f).padding(horizontal = 4.dp, vertical = 8.dp)) { + Text(title, fontSize = 16.sp, lineHeight = 21.sp, fontWeight = FontWeight.SemiBold) + Text(subtitle, fontSize = 12.sp, lineHeight = 17.sp, maxLines = 1, overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.onSurfaceVariant) + } + GlyphButton(R.drawable.ic_git_refresh, stringResource(R.string.git_refresh), onRefresh, canRefresh) + GlyphButton(R.drawable.ic_info, stringResource(R.string.git_repository_details), onDetails) + } +} + +@Composable +private fun GitBranchSummary(snapshot: GitSnapshot) { + val colors = MaterialTheme.colorScheme + val shape = RoundedCornerShape(10.dp) + val syncDescription = stringResource(R.string.git_ahead_behind, snapshot.ahead, snapshot.behind) + Column(Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 12.dp) + .background(colors.surface, shape).border(0.5.dp, colors.outlineVariant, shape).padding(12.dp)) { + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Glyph(R.drawable.ic_git_branch, Modifier.size(16.dp)) + Text(snapshot.branch, Modifier.weight(1f), fontSize = 14.sp, lineHeight = 20.sp, + fontWeight = FontWeight.SemiBold, maxLines = 1, overflow = TextOverflow.Ellipsis) + Text(if (snapshot.upstream == null) stringResource(R.string.git_no_upstream) + else if (snapshot.ahead == 0 && snapshot.behind == 0) stringResource(R.string.git_up_to_date) + else stringResource(R.string.git_sync_counts, snapshot.ahead, snapshot.behind), + modifier = Modifier.semantics { if (snapshot.upstream != null) contentDescription = syncDescription }, + color = colors.onSurfaceVariant, fontSize = 12.sp, lineHeight = 18.sp) + } + Text(stringResource(R.string.git_change_counts, snapshot.entries.count(GitEntry::unstaged), snapshot.entries.count(GitEntry::staged)), + Modifier.padding(top = 6.dp), fontSize = 12.sp, lineHeight = 18.sp, color = colors.onSurfaceVariant) + } +} + +@Composable +private fun GitFeedback(state: GitViewState, unavailable: String?, readonly: Boolean) { + var details by remember { mutableStateOf(false) } + val problem = unavailable?.let { GitProblem(it) } ?: state.error + Column(Modifier.fillMaxWidth().padding(horizontal = 16.dp).semantics { liveRegion = LiveRegionMode.Polite }) { + if (unavailable == null && readonly) Text(stringResource(R.string.git_readonly), fontSize = 13.sp, + modifier = Modifier.padding(bottom = 8.dp), color = MaterialTheme.colorScheme.onSurfaceVariant) + if (state.completed.isNotEmpty()) { + val labels = state.completed.map { gitActionLabel(it) }.joinToString(" · ") + Text(stringResource(R.string.git_completed, labels), fontSize = 13.sp, modifier = Modifier.padding(bottom = 8.dp)) + } + if (problem != null) { + Text(gitProblemText(problem), color = MaterialTheme.colorScheme.error, fontSize = 13.sp, + modifier = Modifier.padding(vertical = 8.dp)) + if (problem.detail.isNotBlank()) TextButton({ details = true }) { Text(stringResource(R.string.git_error_details)) } + } + } + if (details && problem != null) AlertDialog(onDismissRequest = { details = false }, + title = { Text(stringResource(R.string.git_error_details)) }, + text = { SelectionContainer { Text(problem.detail, Modifier.verticalScroll(rememberScrollState()), fontFamily = LocalTerminalFont.current) } }, + confirmButton = { TextButton({ details = false }) { Text(stringResource(R.string.close)) } }) +} + +@Composable +private fun GitChangesContent(snapshot: GitSnapshot?, busy: Boolean, canRead: Boolean, canWrite: Boolean, draft: String, + onDraft: (String) -> Unit, controller: DesktopGit, scroll: LazyListState, + collapsed: List, onFolder: (String) -> Unit) { + val staged = snapshot?.entries.orEmpty().filter(GitEntry::staged) + val unstaged = snapshot?.entries.orEmpty().filter(GitEntry::unstaged) + val tooLong = draft.toByteArray(Charsets.UTF_8).size > 16 * 1024 + var menu by remember { mutableStateOf(false) } + val groups = listOf( + Triple("work:", R.string.git_section_changes, unstaged.filter { it.worktree != "?" }), + Triple("new:", R.string.git_section_untracked, unstaged.filter { it.worktree == "?" }), + Triple("index:", R.string.git_section_staged, staged), + ) + Column(Modifier.fillMaxSize()) { + if (snapshot != null) { + Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp)) { + GitBulkButton(R.drawable.ic_plus, stringResource(R.string.git_stage_all), canWrite && unstaged.isNotEmpty(), Modifier.weight(1f)) { + controller.mutate(GitAction.Stage, all = true) + } + GitBulkButton(R.drawable.ic_git_unstage, stringResource(R.string.git_unstage_all), canWrite && staged.isNotEmpty(), Modifier.weight(1f)) { + controller.mutate(GitAction.Unstage, all = true) + } + Box { + GlyphButton(R.drawable.ic_more, stringResource(R.string.more_actions), { menu = true }, !busy) + DropdownMenu(menu, { menu = false }) { + listOf(GitAction.Fetch, GitAction.Pull, GitAction.Push, GitAction.Sync).forEach { action -> + DropdownMenuItem(text = { Text(gitActionLabel(action)) }, enabled = canWrite, + onClick = { menu = false; controller.mutate(action) }) + } + } + } + } + } + LazyColumn(Modifier.weight(1f).fillMaxWidth(), state = scroll, contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp)) { + if (snapshot?.entries?.isEmpty() == true) item("clean") { + Column(Modifier.fillMaxWidth().padding(vertical = 32.dp), horizontalAlignment = Alignment.CenterHorizontally) { + Glyph(R.drawable.ic_check, Modifier.size(24.dp)) + Text(stringResource(R.string.git_clean), Modifier.padding(top = 12.dp), fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant) + } + } + groups.forEach { (prefix, title, entries) -> + if (entries.isNotEmpty()) { + val index = prefix == "index:" + val rows = gitTree(entries, collapsed.filter { it.startsWith(prefix) }.map { it.removePrefix(prefix) }.toSet()) + item("${prefix}header") { + Row(Modifier.fillMaxWidth().padding(top = 12.dp, bottom = 4.dp), verticalAlignment = Alignment.CenterVertically) { + Text(stringResource(title), Modifier.weight(1f), fontSize = 12.sp, lineHeight = 18.sp, + fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.onSurfaceVariant) + Text(entries.size.toString(), fontSize = 12.sp, lineHeight = 18.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) + } + } + items(rows, key = { prefix + it.path }) { row -> + val key = prefix + row.path + GitTreeItem(row, index, key in collapsed, canRead, canWrite, + onFolder = { onFolder(key) }, + onDiff = { controller.openDiff(GitDiffSelection(row.path, index)) }, + onStage = { controller.mutate(if (index) GitAction.Unstage else GitAction.Stage, path = row.path) }) + } + } + } + } + if (snapshot != null) GitCommitBar(draft, onDraft, !busy, tooLong, staged.size, + canWrite && staged.isNotEmpty() && snapshot.entries.none { it.conflict } && draft.isNotBlank() && !tooLong) { + controller.mutate(GitAction.Commit, message = draft) + } + } +} + +@Composable +private fun GitBulkButton(icon: Int, label: String, enabled: Boolean, modifier: Modifier, onClick: () -> Unit) { + FilledTonalButton(onClick, modifier.heightIn(min = 48.dp), enabled = enabled, shape = RoundedCornerShape(8.dp), + colors = ButtonDefaults.filledTonalButtonColors(containerColor = MaterialTheme.colorScheme.surfaceVariant, + contentColor = MaterialTheme.colorScheme.onSurface), contentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp)) { + Glyph(icon, Modifier.size(14.dp), LocalContentColor.current) + Spacer(Modifier.width(5.dp)) + Text(label, fontSize = 12.sp, lineHeight = 18.sp) + } +} + +@Composable +private fun GitCommitBar(draft: String, onDraft: (String) -> Unit, editable: Boolean, tooLong: Boolean, + stagedCount: Int, canCommit: Boolean, onCommit: () -> Unit) { + val colors = MaterialTheme.colorScheme + var focused by remember { mutableStateOf(false) } + val label = stringResource(R.string.git_commit_message) + val commitLabel = stringResource(R.string.git_commit_staged, stagedCount) + val shape = RoundedCornerShape(8.dp) + Column(Modifier.fillMaxWidth().background(colors.surface)) { + HorizontalDivider(color = colors.outlineVariant) + // 底栏占真实布局空间;系统 IME inset 由 MainActivity 统一处理,避免盖住文件或重复抬升。 + Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.Bottom, horizontalArrangement = Arrangement.spacedBy(8.dp)) { + BasicTextField(draft, onDraft, Modifier.weight(1f).heightIn(min = 48.dp) + .onFocusChanged { focused = it.isFocused }.semantics { contentDescription = label } + .background(colors.background, shape) + .border(if (focused) 1.dp else 0.5.dp, if (tooLong) colors.error else if (focused) colors.primary else colors.outline, shape) + .padding(horizontal = 12.dp, vertical = 13.dp), + enabled = editable, maxLines = 3, textStyle = TextStyle(color = colors.onSurface, fontSize = 14.sp, lineHeight = 20.sp), + cursorBrush = SolidColor(colors.primary), + keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences), + decorationBox = { inner -> + if (draft.isEmpty()) Text(label, color = colors.onSurfaceVariant, fontSize = 14.sp, lineHeight = 20.sp) + inner() + }) + Button(onCommit, Modifier.heightIn(min = 48.dp).semantics { contentDescription = commitLabel }, + enabled = canCommit, shape = shape, + colors = ButtonDefaults.buttonColors(containerColor = colors.onSurface, contentColor = colors.background, + disabledContainerColor = colors.surfaceVariant, disabledContentColor = colors.onSurfaceVariant), + contentPadding = PaddingValues(horizontal = 18.dp, vertical = 12.dp)) { + Text(stringResource(R.string.git_commit), fontSize = 14.sp, lineHeight = 20.sp, fontWeight = FontWeight.SemiBold) + } + } + if (tooLong) Text(stringResource(R.string.git_message_too_long), Modifier.padding(start = 16.dp, end = 16.dp, bottom = 8.dp), + color = colors.error, fontSize = 12.sp) + } +} + +@Composable +private fun GitTreeItem(row: GitTreeRow, staged: Boolean, collapsed: Boolean, enabled: Boolean, canWrite: Boolean, + onFolder: () -> Unit, onDiff: () -> Unit, onStage: () -> Unit) { + val entry = row.entry + val colors = MaterialTheme.colorScheme + val folderState = stringResource(if (collapsed) R.string.git_folder_collapsed else R.string.git_folder_expanded) + val code = if (staged) entry?.index else entry?.worktree + Column { + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + Row(Modifier.weight(1f).heightIn(min = 48.dp).clickable(enabled = enabled, role = Role.Button, + onClick = if (entry == null) onFolder else onDiff) + .padding(start = (row.depth.coerceAtMost(6) * 12).dp, top = 8.dp, bottom = 8.dp) + .semantics { contentDescription = row.path; if (entry == null) stateDescription = folderState }, + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Box(Modifier.width(20.dp), contentAlignment = Alignment.Center) { + if (entry == null) Glyph(if (collapsed) R.drawable.ic_chevron else R.drawable.ic_down, Modifier.size(14.dp)) + else Text(if (code == "?") "U" else code.orEmpty(), fontFamily = LocalTerminalFont.current, + fontSize = 12.sp, fontWeight = FontWeight.SemiBold, + color = if (entry.conflict || code == "D") colors.error else if (code == "A" || code == "?") colors.tertiary else colors.primary) + } + Glyph(if (entry == null) R.drawable.ic_git_folder else R.drawable.ic_git_file, Modifier.size(16.dp)) + Column(Modifier.weight(1f)) { + Text(row.path.substringAfterLast('/'), fontSize = 14.sp, lineHeight = 20.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) + if (entry?.original != null) Text(entry.original, fontSize = 12.sp, color = colors.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis) + if (entry?.conflict == true) Text(stringResource(R.string.git_conflict), color = colors.error, fontSize = 12.sp) + } + if (entry != null) Glyph(R.drawable.ic_chevron, Modifier.size(14.dp)) + } + if (entry != null) GlyphButton(if (staged) R.drawable.ic_git_unstage else R.drawable.ic_plus, + stringResource(if (staged) R.string.git_unstage_file else R.string.git_stage_file, row.path), onStage, canWrite) + } + HorizontalDivider(color = colors.outlineVariant.copy(alpha = .6f), thickness = 0.5.dp) + } +} + +@Composable +private fun GitHistoryContent(state: GitViewState, controller: DesktopGit, connected: Boolean, scroll: LazyListState) { + val dates = remember { DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT) } + LazyColumn(Modifier.fillMaxSize(), state = scroll, contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)) { + if (!state.busy && state.history.isEmpty()) item { Text(stringResource(R.string.git_no_commits), Modifier.padding(vertical = 24.dp)) } + items(state.history, key = GitCommit::hash) { commit -> + Column(Modifier.fillMaxWidth().clickable(enabled = connected && !state.busy) { controller.openDiff(GitDiffSelection(commit = commit.hash)) } + .padding(vertical = 12.dp)) { + Text(commit.subject, fontSize = 15.sp, fontWeight = FontWeight.Medium, maxLines = 3, overflow = TextOverflow.Ellipsis) + Text("${commit.shortHash} · ${commit.author} · ${dates.format(Date(commit.timestamp * 1000))}", + fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(top = 6.dp)) + if (commit.refs.isNotBlank()) Text(commit.refs, fontSize = 12.sp, color = MaterialTheme.colorScheme.primary) + } + HorizontalDivider() + } + if (state.hasMore) item { TextButton({ controller.history(more = true) }, enabled = connected && !state.busy) { Text(stringResource(R.string.git_load_more)) } } + } +} + +@Composable +private fun GitDiffContent(selection: GitDiffSelection, state: GitViewState, canWrite: Boolean, controller: DesktopGit) { + val entry = state.snapshot?.entries?.find { it.path == selection.path } + Column(Modifier.fillMaxSize()) { + Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically) { + Text(selection.path.ifEmpty { selection.commit.take(12) }, Modifier.weight(1f), fontFamily = LocalTerminalFont.current, fontSize = 13.sp) + if (selection.commit.isEmpty() && entry != null) TextButton({ + controller.mutate(if (selection.staged) GitAction.Unstage else GitAction.Stage, path = selection.path) + }, enabled = canWrite) { Text(gitActionLabel(if (selection.staged) GitAction.Unstage else GitAction.Stage)) } + } + if (selection.commit.isNotEmpty()) HelperText(stringResource(R.string.git_history_readonly), Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) + val lines = remember(state.diffText) { state.diffText?.split('\n').orEmpty() } + if (!state.busy && state.diffText?.isBlank() == true) HelperText(stringResource(R.string.git_empty_diff), Modifier.padding(16.dp)) + SelectionContainer { + LazyColumn(Modifier.fillMaxSize(), contentPadding = PaddingValues(bottom = 20.dp)) { + items(lines.size, key = { it }) { index -> + val line = lines[index] + val color = when { + line.startsWith("+") -> MaterialTheme.colorScheme.primary + line.startsWith("-") -> MaterialTheme.colorScheme.error + else -> MaterialTheme.colorScheme.onSurface + } + Text(line.ifEmpty { " " }, Modifier.fillMaxWidth().background(color.copy(alpha = if (line.startsWith('+') || line.startsWith('-')) .08f else 0f)) + .padding(horizontal = 12.dp, vertical = 1.dp), fontFamily = LocalTerminalFont.current, fontSize = 13.sp, lineHeight = 19.sp, color = color) + } + } + } + } +} + +@Composable +private fun gitActionLabel(action: GitAction): String = stringResource(when (action) { + GitAction.Stage -> R.string.git_stage + GitAction.Unstage -> R.string.git_unstage + GitAction.Commit -> R.string.git_commit + GitAction.Fetch -> R.string.git_fetch + GitAction.Pull -> R.string.git_pull + GitAction.Push -> R.string.git_push + GitAction.Sync -> R.string.git_sync +}) + +@Composable +private fun gitProblemText(problem: GitProblem): String = stringResource(when (problem.code) { + "method_not_found" -> R.string.git_update_desktop + "git_disconnected", "runtime_connection_lost" -> R.string.git_disconnected + "git_target_changed", "target_not_found" -> R.string.git_target_changed + "input_not_authorized" -> R.string.git_readonly + "git_stale" -> R.string.git_stale + "git_busy" -> R.string.git_busy + "git_not_committable" -> R.string.git_not_committable + "git_too_large", "git_encoding" -> R.string.git_large_output + "git_refresh_failed" -> R.string.git_refresh_failed + "git_outcome_unknown", "runtime_timeout" -> R.string.git_unknown_result + "remote_exec_unsupported", "exec_context_unavailable" -> R.string.git_host_only + else -> R.string.git_failed +}) diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/HomeScreen.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/HomeScreen.kt new file mode 100644 index 0000000..5f5d3cb --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/HomeScreen.kt @@ -0,0 +1,357 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.stateDescription +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.HostProfile +import io.github.kuddev.pebrel.mobile.connection.endpointLabel +import io.github.kuddev.pebrel.mobile.connection.RelayProfile +import io.github.kuddev.pebrel.mobile.connection.DesktopPane +import io.github.kuddev.pebrel.mobile.connection.DesktopTab +import io.github.kuddev.pebrel.mobile.session.DesktopWorkspace +import io.github.kuddev.pebrel.mobile.session.LocalSession + +@Composable +fun HomeHeader(onSettings: () -> Unit, onNotices: () -> Unit, showNotices: Boolean = true) { + Row(Modifier.fillMaxWidth().height(67.dp).background(MaterialTheme.colorScheme.surface).padding(horizontal = 17.dp), + verticalAlignment = Alignment.CenterVertically) { + Image(painterResource(R.drawable.ic_pebrel), null, Modifier.size(24.dp)) + Text("Pebrel", fontSize = 19.sp, fontWeight = FontWeight.SemiBold, modifier = Modifier.weight(1f).padding(start = 9.dp)) + if (showNotices) GlyphButton(R.drawable.ic_bell, stringResource(R.string.notifications), onNotices) + GlyphButton(R.drawable.ic_settings, stringResource(R.string.settings), onSettings) + } +} + +@OptIn(ExperimentalAnimationApi::class) +@Composable +fun HomeScreen( + sessions: List, hosts: List, desktops: List, relays: List, + onSession: (String) -> Unit, onSessions: () -> Unit, onHosts: () -> Unit, onLogin: (HostProfile) -> Unit, + onEditHost: (HostProfile) -> Unit, onDeleteHost: (HostProfile) -> Unit, onAddHost: () -> Unit, + onDesktop: (String) -> Unit, onRelay: (RelayProfile) -> Unit, onComputers: () -> Unit, + onAddRelay: () -> Unit, onLocal: () -> Unit, onDeployRelay: () -> Unit, + onPane: ((String, DesktopPane) -> Unit)? = null, + onTab: ((String, DesktopTab) -> Unit)? = null, +) { + if (isHomeEmpty(sessions, hosts, desktops, relays)) { + EmptyHomeScreen(onAddRelay, onAddHost, onLocal) + return + } + val motion = rememberPebrelMotion() + val cards = sessionCards(sessions, desktops) + var newConnection by rememberSaveable { mutableStateOf(false) } + BoxWithConstraints(Modifier.fillMaxSize()) { + val thumbnailWidth = ((maxWidth - 44.dp) * .45f).coerceAtLeast(133.dp) + // 按 fontScale 留高;大字号 sp 的非线性换算会让卡片高度不随小标签一起放大。 + val thumbnailHeight = 180.dp * LocalDensity.current.fontScale.coerceAtLeast(1f) + LazyColumn(Modifier.fillMaxSize(), contentPadding = PaddingValues(start = 22.dp, end = 22.dp, top = 8.dp, bottom = 105.dp)) { + item { + GroupHeading(stringResource(R.string.sessions), cards.size, stringResource(R.string.all_sessions), onSessions) + Spacer(Modifier.height(8.dp)) + AnimatedContent( + targetState = cards, + contentKey = { visibleSessions -> visibleSessions.map { it.key } }, + transitionSpec = { motion.collectionTransition() }, + label = "home_sessions", + ) { visibleSessions -> + if (visibleSessions.isEmpty()) { + Row(Modifier.fillMaxWidth().heightIn(min = 96.dp).animateContentSize(motion.contentSizeSpec()) + .workspaceFrame().padding(16.dp), verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(14.dp)) { + WorkspaceSymbol { Glyph(R.drawable.ic_terminal, Modifier.size(22.dp)) } + HelperText(stringResource(R.string.no_sessions)) + } + } else LazyRow(horizontalArrangement = Arrangement.spacedBy(14.dp)) { + items(visibleSessions, key = { it.key }) { card -> + val local = card.local + val thumbnailModifier = Modifier.width(thumbnailWidth).height(thumbnailHeight) + if (local != null) SessionThumbnail(local, thumbnailModifier) { onSession(local.id) } + else DesktopSessionThumbnail(checkNotNull(card.desktop), card.pane, thumbnailModifier, card.tab) { + if (card.tab != null && onTab != null) onTab.invoke(card.desktop.id, card.tab) + else if (card.pane != null && onPane != null) onPane.invoke(card.desktop.id, card.pane) + else onDesktop(card.desktop.id) + } + } + } + } + } + item { + Spacer(Modifier.height(22.dp)) + GroupHeading(stringResource(R.string.ssh_hosts), action = stringResource(R.string.all_count, hosts.size), onAction = onHosts) + Spacer(Modifier.height(8.dp)) + } + item { + AnimatedContent( + targetState = hosts.take(2), + contentKey = { visibleHosts -> visibleHosts.map { it.id } }, + transitionSpec = { motion.collectionTransition() }, + label = "home_hosts", + ) { visibleHosts -> + Column(Modifier.animateContentSize(motion.contentSizeSpec())) { + if (visibleHosts.isEmpty()) { + HelperText(stringResource(R.string.no_hosts), Modifier.fillMaxWidth().workspaceFrame().padding(18.dp)) + } else { + visibleHosts.forEach { host -> + HostRow(host, { onLogin(host) }, { onEditHost(host) }, { onDeleteHost(host) }) + } + } + } + } + } + item { + Spacer(Modifier.height(26.dp)) + GroupHeading(stringResource(R.string.computers), action = stringResource(R.string.all), onAction = onComputers) + ComputerRows(desktops, relays, onDesktop, onRelay, onAddRelay) + Spacer(Modifier.height(22.dp)) + NavigationRow(R.drawable.ic_terminal, stringResource(R.string.local_terminal), onClick = onLocal) + } + } + FloatingActionButton({ newConnection = true }, shape = CircleShape, containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary, + modifier = Modifier.align(Alignment.BottomEnd).padding(end = 22.dp, bottom = 23.dp).size(58.dp)) { + Icon(painterResource(R.drawable.ic_plus), stringResource(R.string.new_connection), Modifier.size(27.dp)) + } + } + if (newConnection) NewConnectionSheet( + onDismiss = { newConnection = false }, + onComputer = onAddRelay, + onSshHost = onAddHost, + onLocal = onLocal, + onDeployRelay = onDeployRelay, + ) +} + +internal fun isHomeEmpty(sessions: List, hosts: List, + desktops: List, relays: List): Boolean = + // 暂时没有会话不代表首次使用;已保存的主机和电脑必须仍可打开。 + sessions.isEmpty() && hosts.isEmpty() && relays.isEmpty() && desktops.none { it.hasConnected } + +internal data class SessionCard(val key: String, val local: LocalSession? = null, + val desktop: DesktopWorkspace? = null, val pane: DesktopPane? = null, val tab: DesktopTab? = null) + +internal fun sessionCards(sessions: List, desktops: List): List = buildList { + sessions.forEach { add(SessionCard("local:${it.id}", local = it)) } + desktops.filter { it.hasConnected }.forEach { computer -> + if (computer.tabs.isNotEmpty()) computer.tabs.forEach { tab -> + add(SessionCard("pc:${computer.id}:${tab.key}", desktop = computer, pane = tab.primaryPane, tab = tab)) + } + else if (computer.panes.isEmpty()) add(SessionCard("pc:${computer.id}", desktop = computer)) + else computer.panes.forEach { pane -> + add(SessionCard("pc:${computer.id}:${pane.window}:${pane.id}", desktop = computer, pane = pane)) + } + } +} + +@Composable +private fun DesktopSessionThumbnail(desktop: DesktopWorkspace, pane: DesktopPane?, modifier: Modifier, tab: DesktopTab? = null, onClick: () -> Unit) { + val colors = MaterialTheme.colorScheme + val status = statusLabel(desktop.status) + Column(modifier.workspaceFrame().clickable(onClick = onClick)) { + Column(Modifier.weight(1f).fillMaxWidth().background(colors.surfaceVariant.copy(alpha = .3f)).padding(10.dp)) { + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + Glyph(tab?.let(::tabIcon) ?: R.drawable.ic_monitor, Modifier.size(14.dp)) + Text("${desktop.transport} · $status", fontSize = 12.sp, lineHeight = 18.sp, color = colors.onSurfaceVariant, + modifier = Modifier.weight(1f).padding(start = 6.dp), maxLines = 1, overflow = TextOverflow.Ellipsis) + } + Spacer(Modifier.height(10.dp)) + Text(desktop.host.name, fontSize = 14.sp, lineHeight = 20.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) + pane?.task?.takeIf(String::isNotBlank)?.let { + Text(it, fontSize = 12.sp, lineHeight = 18.sp, fontFamily = LocalTerminalFont.current, maxLines = 1, overflow = TextOverflow.Ellipsis) + } + Text(tab?.file?.path ?: pane?.cwd.orEmpty(), fontSize = 12.sp, lineHeight = 18.sp, fontFamily = LocalTerminalFont.current, maxLines = 3, + color = colors.onSurfaceVariant, overflow = TextOverflow.Ellipsis) + } + Text(tab?.displayTitle ?: pane?.displayTitle ?: desktop.host.name, fontSize = 14.sp, lineHeight = 20.sp, fontWeight = FontWeight.Medium, maxLines = 1, + overflow = TextOverflow.Ellipsis, modifier = Modifier.fillMaxWidth().padding(horizontal = 11.dp, vertical = 9.dp)) + } +} + +@Composable +private fun SessionThumbnail(session: LocalSession, modifier: Modifier, onClick: () -> Unit) { + // One bounded capture when the gallery appears. No hidden terminal renderer or polling per card. + val preview = remember(session.id, session.status) { + session.terminal.previewText() + } + val colors = MaterialTheme.colorScheme + val source = if (session.source == "Local") stringResource(R.string.local_device) else session.source + val status = statusLabel(session.status) + Column(modifier.workspaceFrame().clickable(onClick = onClick)) { + Column(Modifier.weight(1f).fillMaxWidth().background(colors.surfaceVariant.copy(alpha = .3f)).padding(10.dp)) { + Row(Modifier.fillMaxWidth().heightIn(min = 20.dp), verticalAlignment = Alignment.CenterVertically) { + Box(Modifier.size(8.dp).semantics { stateDescription = status }, contentAlignment = Alignment.Center) { + if (session.status == "connecting") CircularProgressIndicator(Modifier.size(8.dp), strokeWidth = 1.2.dp) + else Box(Modifier.size(6.dp).background(when (session.status) { + "ready", "finished" -> colors.tertiary + "failed" -> colors.error + else -> colors.onSurfaceVariant.copy(alpha = .6f) + }, CircleShape)) + } + Spacer(Modifier.width(8.dp)) + Text(source, fontSize = 12.sp, lineHeight = 18.sp, color = colors.onSurfaceVariant, + maxLines = 1, overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f).wrapContentWidth(Alignment.End) + .background(colors.background.copy(alpha = .65f), RoundedCornerShape(50)) + .padding(horizontal = 7.dp, vertical = 3.dp)) + } + Spacer(Modifier.height(5.dp)) + Box(Modifier.weight(1f).fillMaxWidth()) { + if (preview.isBlank()) Glyph(R.drawable.ic_terminal, Modifier.align(Alignment.Center).size(22.dp)) + else Text(preview, fontSize = 9.sp, lineHeight = 13.sp, fontFamily = LocalTerminalFont.current, + color = colors.onSurfaceVariant, softWrap = false, maxLines = 6, overflow = TextOverflow.Clip) + } + } + Text(session.title, fontSize = 14.sp, lineHeight = 20.sp, fontWeight = FontWeight.Medium, maxLines = 1, + overflow = TextOverflow.Ellipsis, modifier = Modifier.fillMaxWidth().padding(horizontal = 11.dp, vertical = 9.dp)) + } +} + +@Composable +fun HostRow(host: HostProfile, onLogin: () -> Unit, onEdit: () -> Unit, onDelete: () -> Unit) { + var menu by remember { mutableStateOf(false) } + Row(Modifier.fillMaxWidth().padding(bottom = 10.dp).workspaceFrame().heightIn(min = 83.dp).padding(start = 13.dp, end = 4.dp), + verticalAlignment = Alignment.CenterVertically) { + Row(Modifier.weight(1f).clickable(onClick = onLogin).padding(vertical = 15.dp), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(14.dp)) { + WorkspaceSymbol { HostSymbol(host.icon, Modifier.size(23.dp)) } + Column(Modifier.weight(1f)) { + Text(host.name, fontSize = 16.sp, fontWeight = FontWeight.Medium, maxLines = 1, overflow = TextOverflow.Ellipsis) + Text(host.endpointLabel, fontFamily = LocalTerminalFont.current, fontSize = 12.sp, lineHeight = 18.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.padding(top = 5.dp)) + } + } + Box { + GlyphButton(R.drawable.ic_more, "${stringResource(R.string.host_actions)} · ${host.name}", { menu = true }) + DropdownMenu(menu, { menu = false }) { + DropdownMenuItem(text = { Text(stringResource(R.string.edit_host)) }, onClick = { menu = false; onEdit() }) + DropdownMenuItem(text = { Text(stringResource(R.string.delete_host)) }, onClick = { menu = false; onDelete() }) + } + } + } +} + +@Composable +fun ComputerRows(desktops: List, relays: List, onDesktop: (String) -> Unit, + onRelay: (RelayProfile) -> Unit, onAddRelay: () -> Unit, onForget: ((RelayProfile) -> Unit)? = null) { + var removing by remember { mutableStateOf(null) } + val computers = computerSummaries(desktops, relays) + val motion = rememberPebrelMotion() + Column(Modifier.fillMaxWidth().animateContentSize(motion.contentSizeSpec())) { + if (computers.isEmpty()) HelperText(stringResource(R.string.no_computers), Modifier.padding(bottom = 8.dp)) + Column(Modifier.fillMaxWidth().workspaceFrame()) { + computers.forEach { computer -> + key(computer.id) { + val relay = computer.relay + val profileId = desktops.find { it.id == computer.id }?.host?.id ?: computer.id + val saved = relays.find { it.id == profileId } + ComputerRow(computer.title, computer.status, computer.transport, computer.address, + onForget = if (saved != null && onForget != null) ({ removing = saved }) else null) { + if (relay == null) onDesktop(computer.id) else onRelay(relay) + } + HorizontalDivider(Modifier.padding(start = 56.dp), color = MaterialTheme.colorScheme.outlineVariant) + } + } + ComputerRow(stringResource(R.string.relay_connect), onClick = onAddRelay) + } + } + removing?.let { profile -> + AlertDialog(onDismissRequest = { removing = null }, + title = { Text(stringResource(R.string.computer_remove_title)) }, + text = { Text(stringResource(R.string.computer_remove_hint, profile.name)) }, + confirmButton = { TextButton({ removing = null; onForget?.invoke(profile) }) { + Text(stringResource(R.string.computer_remove_title)) + } }, + dismissButton = { TextButton({ removing = null }) { Text(stringResource(R.string.cancel)) } }) + } +} + +private data class ComputerSummary( + val id: String, + val title: String, + val status: String, + val transport: String, + val address: String, + val relay: RelayProfile? = null, +) + +private fun computerSummaries(desktops: List, relays: List): List = buildList { + // A failed first attempt lives only on its connection page, not in the + // user's computer library. Existing, previously connected PCs remain. + desktops.filter { it.hasConnected || relays.any { profile -> profile.id == it.host.id } }.forEach { desktop -> + val retryProfile = if (desktop.status in setOf("ready", "connecting", "approval")) null + else relays.find { it.id == desktop.host.id } + add(ComputerSummary(desktop.id, desktop.host.name, desktop.status, desktop.transport, desktop.host.address, retryProfile)) + } + relays.filter { profile -> desktops.none { it.host.id == profile.id } }.forEach { profile -> + add(ComputerSummary(profile.id, profile.name, "disconnected", if (profile.mode == "lan") "LAN" else "Relay", profile.url, profile)) + } +} + +@Composable +private fun ComputerRow(title: String, status: String? = null, transport: String = "", address: String = "", + onForget: (() -> Unit)? = null, onClick: () -> Unit) { + // 电脑和添加入口属于同一级;共用图标列与行高,点击留白也有完整的按压反馈。 + Row(Modifier.fillMaxWidth().clickable(role = Role.Button, onClick = onClick) + .heightIn(min = 72.dp).padding(start = 16.dp, end = 8.dp, top = 8.dp, bottom = 8.dp), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp)) { + Glyph(if (status == null) R.drawable.ic_plus else R.drawable.ic_monitor, Modifier.size(24.dp)) + Column(Modifier.weight(1f)) { + Text(title, fontSize = 14.sp, lineHeight = 20.sp, fontWeight = FontWeight.Medium, maxLines = 2, overflow = TextOverflow.Ellipsis) + if (status != null) Box(Modifier.padding(top = 4.dp)) { + val endpoint = address.removePrefix("wss://").trimEnd('/') + StatusCaption(status, listOf(transport, endpoint).filter(String::isNotBlank).joinToString(" · ", postfix = " · ")) + } + } + Box(Modifier.size(48.dp), contentAlignment = Alignment.Center) { + if (onForget != null) { + var menu by remember { mutableStateOf(false) } + GlyphButton(R.drawable.ic_more, stringResource(R.string.more_actions), { menu = true }) + DropdownMenu(menu, { menu = false }) { + DropdownMenuItem(text = { Text(stringResource(R.string.computer_remove_title)) }, + onClick = { menu = false; onForget() }) + } + } else Glyph(R.drawable.ic_chevron, Modifier.size(14.dp)) + } + } +} + +@Composable +fun HostsScreen(hosts: List, onLogin: (HostProfile) -> Unit, onEdit: (HostProfile) -> Unit, onDelete: (HostProfile) -> Unit) { + var query by remember { mutableStateOf("") } + var group by remember { mutableStateOf("all") } + val found = hosts.filter { (group == "all" || it.group == group) && "${it.name} ${it.address} ${it.user}".contains(query, ignoreCase = true) } + LazyColumn(contentPadding = PaddingValues(horizontal = 22.dp, vertical = 12.dp)) { + item { + ConnectionSearchField(query, { query = it }, Modifier.fillMaxWidth().padding(bottom = 10.dp)) + } + item { + ConnectionSegments(listOf("all" to stringResource(R.string.all), "production" to stringResource(R.string.group_production), + "development" to stringResource(R.string.group_development)), group, { group = it }, Modifier.fillMaxWidth().padding(bottom = 14.dp)) + } + items(found, key = { it.id }) { host -> HostRow(host, { onLogin(host) }, { onEdit(host) }, { onDelete(host) }) } + item { HelperText(stringResource(if (found.isEmpty() && query.isNotBlank()) R.string.no_search_results else R.string.host_hint), Modifier.padding(top = 20.dp)) } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/HostForms.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/HostForms.kt new file mode 100644 index 0000000..fffc925 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/HostForms.kt @@ -0,0 +1,204 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.HostProfile +import io.github.kuddev.pebrel.mobile.connection.SshSessionMode +import io.github.kuddev.pebrel.mobile.connection.validRemoteSessionName +import io.github.kuddev.pebrel.mobile.connection.parseSshEndpoint +import io.github.kuddev.pebrel.mobile.connection.endpointLabel +import io.github.kuddev.pebrel.mobile.session.TrustRequest +import java.util.UUID + +@Composable +fun HostForm( + initial: HostProfile?, + onCancel: () -> Unit, + passwordSaved: Boolean, + busy: Boolean, + onClearPassword: () -> Unit, + onSave: (HostProfile, CharArray?, Boolean, Boolean) -> Unit, +) { + var name by rememberSaveable { mutableStateOf(initial?.name.orEmpty()) } + var address by rememberSaveable { mutableStateOf(initial?.address.orEmpty()) } + var user by rememberSaveable { mutableStateOf(initial?.user ?: "root") } + var port by rememberSaveable { mutableStateOf((initial?.port ?: 22).toString()) } + var icon by rememberSaveable { mutableStateOf(initial?.icon ?: "term") } + var group by rememberSaveable { mutableStateOf(initial?.group ?: "development") } + var sessionMode by rememberSaveable { mutableStateOf(initial?.sessionMode ?: SshSessionMode.SHELL) } + var sessionName by rememberSaveable { mutableStateOf(initial?.sessionName.orEmpty()) } + var password by remember { mutableStateOf("") } + val endpoint = runCatching { parseSshEndpoint(address, user) }.getOrNull() + val originalEndpoint = initial?.let { runCatching { parseSshEndpoint(it.address, it.user) }.getOrNull() } + val passwordIsSaved = passwordSaved && initial != null && originalEndpoint == endpoint && + initial.port == port.toIntOrNull() + var rememberPassword by rememberSaveable(initial?.id) { mutableStateOf(passwordSaved || initial == null) } + val valid = name.isNotBlank() && endpoint != null && (port.toIntOrNull() ?: 0) in 1..65535 && + (sessionMode == SshSessionMode.SHELL || validRemoteSessionName(sessionMode, sessionName)) + fun profile() = HostProfile(initial?.id ?: UUID.randomUUID().toString(), name.trim(), checkNotNull(endpoint).address, port.toInt(), endpoint.user, + icon = icon, group = group, sessionMode = sessionMode, sessionName = sessionName) + ConnectionForm(stringResource(if (initial == null) R.string.add_ssh else R.string.edit_host), { if (!busy) onCancel() }) { + if (busy) LinearProgressIndicator(Modifier.fillMaxWidth()) + Column(Modifier.padding(top = 4.dp), verticalArrangement = Arrangement.spacedBy(14.dp)) { + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp)) { + HostIconChoice(icon) { icon = it } + ConnectionField(name, { name = it }, R.string.host_name, Modifier.weight(1f), + placeholder = stringResource(R.string.host_name), limit = 40, showLabel = false) + } + Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) { + ConnectionField(address, { value -> + address = value + if ('@' in value) runCatching { parseSshEndpoint(value, user) }.onSuccess { + address = it.address; user = it.user + } + }, R.string.host_address, Modifier.weight(1f), + keyboard = KeyboardType.Uri, placeholder = "server.example.com") + ConnectionField(port, { port = it }, R.string.port, Modifier.width(82.dp), KeyboardType.Number, limit = 5) + } + ConnectionField(user, { user = it }, R.string.username, limit = 40) + SegmentRow(R.string.ssh_terminal_mode) { + ConnectionSegments(SshSessionMode.entries.map { it.id to if (it == SshSessionMode.SHELL) "Shell" else it.id }, + sessionMode.id, { id -> sessionMode = SshSessionMode.entries.first { it.id == id } }, Modifier.weight(1f)) + } + if (sessionMode != SshSessionMode.SHELL) { + ConnectionField(sessionName, { sessionName = it }, R.string.ssh_persistent_session, limit = 64) + HelperText(stringResource(R.string.ssh_multiplexer_hint)) + if (!validRemoteSessionName(sessionMode, sessionName)) Text(stringResource(R.string.ssh_session_name_invalid), + color = MaterialTheme.colorScheme.error, fontSize = 12.sp) + } + SegmentRow(R.string.authentication) { + ConnectionSegments(listOf("auto" to stringResource(R.string.auth_auto), "key" to stringResource(R.string.auth_key)), + "auto", {}, Modifier.weight(1f), disabled = setOf("key")) + } + ConnectionField(password, { password = it }, R.string.credential_password, + keyboard = KeyboardType.Password, placeholder = if (passwordIsSaved) stringResource(R.string.password_saved_placeholder) else "", + transformation = PasswordVisualTransformation(), limit = 1024) + HelperText(stringResource(R.string.ssh_password_optional_hint)) + Column { + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + Checkbox(rememberPassword, { rememberPassword = it }, enabled = !busy) + Text(stringResource(R.string.save_password), fontSize = 13.sp, modifier = Modifier.weight(1f)) + if (passwordIsSaved) { + TextButton({ + password = "" + onClearPassword() + }, enabled = !busy) { Text(stringResource(R.string.clear_saved_password), fontSize = 12.sp) } + } + } + HelperText(stringResource( + when { + passwordIsSaved && rememberPassword -> R.string.password_saved_hint + rememberPassword -> R.string.password_will_save_hint + else -> R.string.password_not_saved_hint + }, + ), Modifier.padding(start = 12.dp)) + } + SegmentRow(R.string.host_group) { + ConnectionSegments(listOf("production" to stringResource(R.string.group_production), "development" to stringResource(R.string.group_development)), + group, { group = it }, Modifier.weight(1f)) + } + } + Spacer(Modifier.height(24.dp)) + fun submit(connect: Boolean) { + val secret = password.takeIf { it.isNotEmpty() }?.toCharArray() + onSave(profile(), secret, rememberPassword, connect) + } + Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { + OutlinedButton({ submit(false) }, enabled = valid && !busy, + modifier = Modifier.weight(1f).heightIn(min = 48.dp), shape = MaterialTheme.shapes.medium) { + Text(stringResource(R.string.save)) + } + Button({ submit(true) }, enabled = valid && !busy, + modifier = Modifier.weight(1.6f).heightIn(min = 48.dp), shape = MaterialTheme.shapes.medium) { + Text(stringResource(R.string.save_connect)) + } + } + } +} + +@Composable +private fun SegmentRow(label: Int, content: @Composable RowScope.() -> Unit) { + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) { + Text(stringResource(label), fontSize = 13.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.widthIn(min = 76.dp, max = 106.dp)) + content() + } +} + +@Composable +fun LoginForm(host: HostProfile, onCancel: () -> Unit, passwordSaved: Boolean, busy: Boolean, + onClearPassword: () -> Unit, onConnect: (CharArray?, Boolean, Boolean, Boolean) -> Unit) { + var password by remember { mutableStateOf("") } + var rememberPassword by rememberSaveable(host.id) { mutableStateOf(true) } + var desktop by remember { mutableStateOf(false) } + var input by remember { mutableStateOf(false) } + ConnectionForm(stringResource(R.string.connect), { if (!busy) onCancel() }) { + if (busy) LinearProgressIndicator(Modifier.fillMaxWidth()) + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(13.dp)) { + HostSymbol(host.icon, Modifier.size(30.dp)) + Column { + Text(host.name, fontSize = 16.sp) + HelperText(host.endpointLabel, Modifier.padding(top = 6.dp)) + } + } + Spacer(Modifier.height(24.dp)) + ConnectionField(password, { password = it }, R.string.password, keyboard = KeyboardType.Password, + placeholder = if (passwordSaved) stringResource(R.string.password_saved_placeholder) else "", + transformation = PasswordVisualTransformation(), limit = 1024) + HelperText(stringResource(R.string.ssh_password_optional_hint)) + if (passwordSaved) { + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + HelperText(stringResource(R.string.password_saved_status), Modifier.weight(1f)) + TextButton(onClearPassword, enabled = !busy) { Text(stringResource(R.string.clear_saved_password)) } + } + } + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox(rememberPassword, { rememberPassword = it }, enabled = !busy) + Text(stringResource(R.string.save_password), fontSize = 13.sp) + } + HelperText(stringResource(if (rememberPassword) R.string.password_will_save_hint else R.string.password_not_saved_hint)) + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox(desktop, { desktop = it }, enabled = !busy) + Text(stringResource(R.string.connect_pebrel), fontSize = 13.sp) + } + if (desktop) { + HelperText(stringResource(R.string.desktop_setup_hint)) + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox(input, { input = it }, enabled = !busy) + Text(stringResource(R.string.allow_input), fontSize = 13.sp) + } + } + Spacer(Modifier.height(20.dp)) + ConnectionButton(stringResource(R.string.connect), enabled = !busy) { + val secret = password.takeIf { it.isNotEmpty() }?.toCharArray() + onConnect(secret, desktop, input, rememberPassword) + } + } +} + +@Composable +fun HostTrustForm(request: TrustRequest, onAnswer: (Boolean) -> Unit) { + AlertDialog(onDismissRequest = { onAnswer(false) }, title = { Text(stringResource(R.string.verify_host)) }, + text = { + Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { + Text(request.host.name) + HelperText("${request.host.address}:${request.host.port}") + HelperText(stringResource(R.string.verify_hint)) + SelectionContainer { + Text(request.fingerprint, fontFamily = LocalTerminalFont.current, fontSize = 12.sp, lineHeight = 20.sp) + } + } + }, confirmButton = { TextButton({ onAnswer(true) }) { Text(stringResource(R.string.trust_connect)) } }, + dismissButton = { TextButton({ onAnswer(false) }) { Text(stringResource(R.string.cancel)) } }) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/HostSymbols.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/HostSymbols.kt new file mode 100644 index 0000000..def6015 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/HostSymbols.kt @@ -0,0 +1,79 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.graphics.Paint +import android.graphics.Rect +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.drawscope.drawIntoCanvas +import androidx.compose.ui.graphics.nativeCanvas +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.stateDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.HostIconSpec +import io.github.kuddev.pebrel.mobile.PebrelApplication +import io.github.kuddev.pebrel.mobile.R + +@Composable +private fun iconLabel(icon: HostIconSpec) = + if (LocalConfiguration.current.locales[0].language == "zh") icon.zh else icon.en + +@Composable +fun HostSymbol(id: String, modifier: Modifier = Modifier) { + val app = LocalContext.current.applicationContext as PebrelApplication + val icon = app.hostIcons.find { it.id == id } ?: app.hostIcons.last { it.id == "term" } + val paint = remember { Paint(Paint.ANTI_ALIAS_FLAG).apply { typeface = app.terminalTypeface } } + val bounds = remember { Rect() } + val color = MaterialTheme.colorScheme.onSurfaceVariant.toArgb() + Canvas(modifier.size(24.dp)) { + paint.color = color + paint.textSize = size.height + paint.getTextBounds(icon.glyph, 0, icon.glyph.length, bounds) + val scale = minOf(size.width / bounds.width().coerceAtLeast(1), size.height / bounds.height().coerceAtLeast(1)) * .85f + paint.textSize *= scale + paint.getTextBounds(icon.glyph, 0, icon.glyph.length, bounds) + drawIntoCanvas { it.nativeCanvas.drawText(icon.glyph, + (size.width - bounds.width()) / 2 - bounds.left, + (size.height - bounds.height()) / 2 - bounds.top, paint) } + } +} + +@Composable +fun HostIconChoice(id: String, onSelect: (String) -> Unit) { + val app = LocalContext.current.applicationContext as PebrelApplication + val icon = app.hostIcons.find { it.id == id } ?: app.hostIcons.last { it.id == "term" } + var expanded by remember { mutableStateOf(false) } + val label = stringResource(R.string.host_icon) + val selectedLabel = iconLabel(icon) + Box { + OutlinedIconButton({ expanded = true }, shape = RoundedCornerShape(14.dp), + colors = IconButtonDefaults.outlinedIconButtonColors(containerColor = MaterialTheme.colorScheme.surface), + border = BorderStroke(.5.dp, MaterialTheme.colorScheme.outlineVariant), + modifier = Modifier.size(48.dp).semantics { + contentDescription = label + stateDescription = selectedLabel + }) { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + HostSymbol(icon.id, Modifier.size(24.dp)) + Glyph(R.drawable.ic_down, Modifier.align(Alignment.BottomEnd).padding(4.dp).size(9.dp)) + } + } + DropdownMenu(expanded, { expanded = false }, Modifier.heightIn(max = 330.dp)) { + app.hostIcons.forEach { entry -> + DropdownMenuItem(text = { Text(iconLabel(entry), fontSize = 13.sp) }, + leadingIcon = { HostSymbol(entry.id) }, onClick = { onSelect(entry.id); expanded = false }) + } + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/InlinePairingScanner.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/InlinePairingScanner.kt new file mode 100644 index 0000000..dd10af9 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/InlinePairingScanner.kt @@ -0,0 +1,180 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.Manifest +import android.app.Activity +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.provider.Settings +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.content.ContextCompat +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.compose.LocalLifecycleOwner +import com.google.zxing.BarcodeFormat +import com.journeyapps.barcodescanner.BarcodeCallback +import com.journeyapps.barcodescanner.BarcodeResult +import com.journeyapps.barcodescanner.BarcodeView +import com.journeyapps.barcodescanner.CameraPreview +import com.journeyapps.barcodescanner.DefaultDecoderFactory +import com.journeyapps.barcodescanner.Size +import io.github.kuddev.pebrel.mobile.R + +@Composable +internal fun InlinePairingScanner(onResult: (String) -> Unit) { + val context = LocalContext.current + val lifecycle = LocalLifecycleOwner.current.lifecycle + val currentResult by rememberUpdatedState(onResult) + fun permitted() = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED + var granted by remember { mutableStateOf(permitted()) } + var openSettings by remember { mutableStateOf(false) } + var captured by remember { mutableStateOf(false) } + var cameraFailed by remember { mutableStateOf(false) } + var previewReady by remember { mutableStateOf(false) } + val hasCamera = remember { context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) } + val permission = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { allowed -> + granted = allowed + openSettings = !allowed && (context as? Activity)?.shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) == false + } + DisposableEffect(lifecycle) { + val observer = LifecycleEventObserver { _, event -> + if (event == Lifecycle.Event.ON_RESUME) granted = permitted() + } + lifecycle.addObserver(observer) + onDispose { lifecycle.removeObserver(observer) } + } + val showCamera = hasCamera && granted && !captured && !cameraFailed + val label = stringResource(R.string.pair_scan_frame) + Box(Modifier.fillMaxWidth().heightIn(min = 258.dp).clip(RoundedCornerShape(14.dp)) + .background(MaterialTheme.colorScheme.surfaceVariant).semantics { contentDescription = label }) { + if (showCamera) { + val framePixels = with(LocalDensity.current) { 152.dp.roundToPx() } + var scanner by remember { mutableStateOf(null) } + Column(Modifier.fillMaxWidth()) { + Box(Modifier.fillMaxWidth().height(226.dp)) { + AndroidView(factory = { viewContext -> + BarcodeView(viewContext).apply { + // TextureView 才能在页内圆角区域裁切,避免相机画面盖到分段按钮上。 + setUseTextureView(true) + decoderFactory = DefaultDecoderFactory(listOf(BarcodeFormat.QR_CODE)) + framingRectSize = Size(framePixels, framePixels) + scanner = this + } + }, modifier = Modifier.matchParentSize(), onReset = null, onRelease = { it.stopDecoding(); it.pause() }) + DisposableEffect(scanner, lifecycle) { + val view = scanner + var accepting = false + var disposed = false + fun stop() { accepting = false; view?.stopDecoding(); view?.pause() } + fun start() { + if (view == null || accepting) return + accepting = true + view.decodeSingle(object : BarcodeCallback { + override fun barcodeResult(result: BarcodeResult) { + if (!accepting || disposed) return + stop() + captured = true + currentResult(result.text) + } + }) + view.resume() + } + view?.addStateListener(object : CameraPreview.StateListener { + override fun previewSized() = Unit + override fun previewStarted() { if (!disposed) previewReady = true } + override fun previewStopped() { if (!disposed) previewReady = false } + override fun cameraClosed() = Unit + override fun cameraError(error: Exception) { if (!disposed) { stop(); cameraFailed = true } } + }) + val observer = LifecycleEventObserver { _, event -> + if (event == Lifecycle.Event.ON_RESUME) start() + if (event == Lifecycle.Event.ON_PAUSE || event == Lifecycle.Event.ON_STOP) stop() + } + lifecycle.addObserver(observer) + if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) start() + onDispose { + // 分段切换和退页都会销毁预览,同时丢弃已经排队的识别回调。 + disposed = true + lifecycle.removeObserver(observer) + stop() + } + } + ScannerFrame(Modifier.matchParentSize()) + if (!previewReady) CircularProgressIndicator(Modifier.align(Alignment.Center).size(24.dp), strokeWidth = 2.dp) + } + // 提示独立排版,大字体不会盖住取景边界或缩小可见二维码区域。 + Text(stringResource(R.string.pair_scan_target), color = Color.White, fontSize = 12.sp, lineHeight = 20.sp, + textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth() + .background(Color.Black).padding(horizontal = 12.dp, vertical = 6.dp)) + } + } else Column(Modifier.fillMaxWidth().padding(horizontal = 20.dp, vertical = 36.dp), + horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(12.dp)) { + Glyph(R.drawable.ic_qr, Modifier.size(32.dp)) + Text(stringResource(when { + !hasCamera -> R.string.pair_camera_missing + captured -> R.string.pair_scan_captured + cameraFailed -> R.string.pair_camera_failed + else -> R.string.pair_camera_permission + }), fontSize = 14.sp, lineHeight = 22.sp, textAlign = TextAlign.Center) + if (hasCamera) TextButton({ + when { + openSettings && !granted -> context.startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.parse("package:${context.packageName}"))) + !granted -> permission.launch(Manifest.permission.CAMERA) + else -> { captured = false; cameraFailed = false; previewReady = false } + } + }, modifier = Modifier.heightIn(min = 48.dp)) { + Text(stringResource(when { + openSettings && !granted -> R.string.pair_camera_settings + !granted -> R.string.pair_camera_enable + else -> R.string.pair_scan_retry + })) + } + } + } +} + +@Composable +private fun ScannerFrame(modifier: Modifier) { + val accent = MaterialTheme.colorScheme.primary + Canvas(modifier) { + val side = 152.dp.toPx().coerceAtMost(size.width - 32.dp.toPx()) + val left = (size.width - side) / 2 + val top = (size.height - side) / 2 + val shade = Color.Black.copy(alpha = .35f) + drawRect(shade, size = androidx.compose.ui.geometry.Size(size.width, top)) + drawRect(shade, topLeft = Offset(0f, top + side), size = androidx.compose.ui.geometry.Size(size.width, top)) + drawRect(shade, topLeft = Offset(0f, top), size = androidx.compose.ui.geometry.Size(left, side)) + drawRect(shade, topLeft = Offset(left + side, top), size = androidx.compose.ui.geometry.Size(left, side)) + val arm = 26.dp.toPx() + val stroke = 2.dp.toPx() + for (x in listOf(left, left + side)) for (y in listOf(top, top + side)) { + drawLine(Color.White, Offset(x, y), Offset(x + if (x == left) arm else -arm, y), stroke, StrokeCap.Round) + drawLine(Color.White, Offset(x, y), Offset(x, y + if (y == top) arm else -arm), stroke, StrokeCap.Round) + } + drawLine(accent, Offset(left + 8.dp.toPx(), size.height / 2), + Offset(left + side - 8.dp.toPx(), size.height / 2), 1.dp.toPx()) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/NewConnectionSheet.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/NewConnectionSheet.kt new file mode 100644 index 0000000..69e0b91 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/NewConnectionSheet.kt @@ -0,0 +1,100 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.heading +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun NewConnectionSheet( + onDismiss: () -> Unit, + onComputer: () -> Unit, + onSshHost: () -> Unit, + onLocal: () -> Unit, + onDeployRelay: () -> Unit, +) { + val colors = MaterialTheme.colorScheme + val shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp) + val sheet = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val scope = rememberCoroutineScope() + var choosing by remember { mutableStateOf(false) } + fun choose(action: () -> Unit) { + if (choosing) return + choosing = true + scope.launch { + try { + // 先收起面板再打开表单,避免两个模态窗口同时争抢焦点。 + sheet.hide() + onDismiss() + action() + } finally { + choosing = false + } + } + } + // 圆角交给组件内部 Surface;外层描边不会随面板位移,会残留在屏幕顶部。 + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheet, + shape = shape, + containerColor = colors.surface, + contentColor = colors.onSurface, + tonalElevation = 0.dp, + scrimColor = colors.scrim.copy(alpha = .62f), + dragHandle = { + Box(Modifier.fillMaxWidth().padding(top = 10.dp, bottom = 8.dp), contentAlignment = Alignment.Center) { + Box(Modifier.size(32.dp, 4.dp).background(colors.onSurfaceVariant.copy(alpha = .4f), RoundedCornerShape(2.dp))) + } + }, + ) { + Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState()).padding(bottom = 16.dp)) { + Text(stringResource(R.string.new_connection), fontSize = 13.sp, color = colors.onSurfaceVariant, + modifier = Modifier.padding(start = 18.dp, top = 4.dp, bottom = 12.dp).semantics { heading() }) + NewConnectionRow(R.drawable.ic_monitor, stringResource(R.string.computer_connect), + stringResource(R.string.new_computer_hint), !choosing) { choose(onComputer) } + NewConnectionRow(R.drawable.ic_terminal, stringResource(R.string.add_ssh), + stringResource(R.string.new_ssh_hint), !choosing) { choose(onSshHost) } + NewConnectionRow(R.drawable.ic_phone, stringResource(R.string.local_terminal), + enabled = !choosing) { choose(onLocal) } + NewConnectionRow(R.drawable.ic_sliders, stringResource(R.string.new_deploy_relay), + stringResource(R.string.new_deploy_relay_hint), !choosing) { choose(onDeployRelay) } + } + } +} + +@Composable +private fun NewConnectionRow( + @DrawableRes icon: Int, + title: String, + detail: String? = null, + enabled: Boolean, + onClick: () -> Unit, +) { + Row(Modifier.fillMaxWidth().clickable(enabled = enabled, role = Role.Button, onClick = onClick) + .heightIn(min = 60.dp).padding(horizontal = 28.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) { + Glyph(icon, Modifier.size(20.dp)) + Column(Modifier.weight(1f)) { + Text(title, fontSize = 15.sp, lineHeight = 21.sp, fontWeight = FontWeight.Medium) + if (detail != null) Text(detail, fontSize = 12.sp, lineHeight = 18.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(top = 2.dp)) + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/OperationStatus.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/OperationStatus.kt new file mode 100644 index 0000000..beb3b1a --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/OperationStatus.kt @@ -0,0 +1,25 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.DesktopFailureKind + +@Composable +fun operationErrorText(code: String): String { + DesktopFailureKind.entries.firstOrNull { it.code == code }?.let { return desktopFailureText(it) } + return when (code) { + "credential_load_failed" -> stringResource(R.string.credential_load_failed) + "credential_save_failed", "host_save_failed" -> stringResource(R.string.credential_save_failed) + "credential_clear_failed" -> stringResource(R.string.credential_clear_failed) + "credential_missing" -> stringResource(R.string.credential_missing) + "relay_connection_failed" -> desktopFailureText(DesktopFailureKind.UNKNOWN) + "relay_storage_failed" -> stringResource(R.string.relay_storage_failed) + "invalid_relay_invite" -> stringResource(R.string.invalid_relay_invite) + "desktop_read_failed" -> stringResource(R.string.desktop_read_failed) + "delivery_unknown" -> stringResource(R.string.delivery_unknown) + "input_rejected" -> stringResource(R.string.input_rejected) + "tabs_unsupported", "target_not_found", "desktop_session_changed", "input_not_authorized", "file_read_failed" -> tabFailureText(code) + else -> stringResource(R.string.operation_failed_hint) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/PebrelMotion.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/PebrelMotion.kt new file mode 100644 index 0000000..782f5c4 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/PebrelMotion.kt @@ -0,0 +1,104 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.animation.ValueAnimator +import androidx.compose.animation.ContentTransform +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.SizeTransform +import androidx.compose.animation.expandVertically +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkVertically +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.core.FastOutLinearInEasing +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.FiniteAnimationSpec +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.animation.core.snap +import androidx.compose.animation.core.tween +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.remember +import androidx.compose.ui.unit.IntSize + +enum class PebrelNavigationDirection { + Forward, + Backward, +} + +/** + * Small, shared motion vocabulary for the native shell. + * + * Compose applies the platform motion duration scale to these specs. The + * Android animator switch is checked separately so an accessibility setting of + * zero returns the Compose no-op variant instead of leaving a partial layout. + */ +@Immutable +data class PebrelMotion( + val animationsEnabled: Boolean, +) { + @OptIn(ExperimentalAnimationApi::class) + fun pageTransition(direction: PebrelNavigationDirection): ContentTransform { + if (!animationsEnabled) return ContentTransform(EnterTransition.None, ExitTransition.None, sizeTransform = null) + val forward = direction == PebrelNavigationDirection.Forward + val enterOffset: (Int) -> Int = { width -> if (forward) width / 6 else -width / 6 } + val exitOffset: (Int) -> Int = { width -> if (forward) -width / 6 else width / 6 } + val enter = fadeIn( + animationSpec = tween(220, easing = FastOutSlowInEasing), + ) + slideInHorizontally( + animationSpec = tween(260, easing = FastOutSlowInEasing), + initialOffsetX = enterOffset, + ) + val exit = fadeOut( + animationSpec = tween(170, easing = FastOutLinearInEasing), + ) + slideOutHorizontally( + animationSpec = tween(210, easing = FastOutLinearInEasing), + targetOffsetX = exitOffset, + ) + return ContentTransform( + targetContentEnter = enter, + initialContentExit = exit, + sizeTransform = SizeTransform(clip = false), + ) + } + + @OptIn(ExperimentalAnimationApi::class) + fun collectionTransition(): ContentTransform { + if (!animationsEnabled) return ContentTransform(EnterTransition.None, ExitTransition.None, sizeTransform = null) + val enter = fadeIn( + animationSpec = tween(180, easing = FastOutSlowInEasing), + ) + expandVertically( + animationSpec = tween(230, easing = FastOutSlowInEasing), + ) + val exit = fadeOut( + animationSpec = tween(130, easing = FastOutLinearInEasing), + ) + shrinkVertically( + animationSpec = tween(190, easing = FastOutLinearInEasing), + ) + return ContentTransform( + targetContentEnter = enter, + initialContentExit = exit, + sizeTransform = SizeTransform(clip = false), + ) + } + + fun tweenOrSnap(durationMillis: Int): FiniteAnimationSpec = + if (animationsEnabled) tween(durationMillis) else snap() + + fun contentSizeSpec(): FiniteAnimationSpec { + if (!animationsEnabled) return snap() + return spring( + dampingRatio = Spring.DampingRatioNoBouncy, + stiffness = Spring.StiffnessMediumLow, + ) + } +} + +@Composable +fun rememberPebrelMotion(): PebrelMotion { + val enabled = ValueAnimator.areAnimatorsEnabled() + return remember(enabled) { PebrelMotion(enabled) } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/PebrelTheme.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/PebrelTheme.kt new file mode 100644 index 0000000..7d5cf16 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/PebrelTheme.kt @@ -0,0 +1,89 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.luminance +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.core.view.WindowCompat +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import io.github.kuddev.pebrel.mobile.PebrelApplication +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import org.json.JSONObject + +val LocalTerminalFont = staticCompositionLocalOf { FontFamily.Monospace } + +private fun JSONObject.color(key: String): Color { + val value = getJSONArray(key) + return Color(value.getInt(0), value.getInt(1), value.getInt(2), if (value.length() == 4) value.getInt(3) else 255) +} + +@Composable +fun PebrelTheme(content: @Composable () -> Unit) { + val application = LocalContext.current.applicationContext as PebrelApplication + val catalog = application.themes + val preferences by application.sessions.display.state.collectAsStateWithLifecycle() + val terminalFont = remember(application, preferences.fontFamily) { + FontFamily(application.terminalTypeface(preferences.fontFamily)) + } + val choice by application.sessions.theme.collectAsStateWithLifecycle() + val systemDark = isSystemInDarkTheme() + val name = if (catalog.has(choice)) choice else if (systemDark) "Nord" else "Paper" + val palette = remember(name) { catalog.getJSONObject(name) } + val background = palette.color("background") + val dark = background.luminance() < 0.5f + val scheme = (if (dark) darkColorScheme() else lightColorScheme()).copy( + primary = palette.color("accent"), onPrimary = background, + tertiary = palette.color("green"), onTertiary = background, + background = background, surface = palette.color("shell"), + onBackground = palette.color("foreground"), onSurface = palette.color("foreground"), + onSurfaceVariant = palette.color("muted"), outline = palette.color("frame"), + outlineVariant = palette.color("line"), secondaryContainer = palette.color("selected"), + surfaceVariant = palette.color("selected"), + onSecondaryContainer = palette.color("foreground"), error = palette.color("red"), + surfaceContainer = background, surfaceContainerLow = background, + surfaceContainerLowest = background, surfaceContainerHighest = palette.color("selected"), + surfaceContainerHigh = palette.color("shell"), surfaceTint = Color.Transparent, + ) + val view = LocalView.current + SideEffect { + (view.context as? android.app.Activity)?.window?.let { window -> + WindowCompat.getInsetsController(window, view).apply { + isAppearanceLightStatusBars = !dark + isAppearanceLightNavigationBars = !dark + } + } + } + LaunchedEffect(name) { + fun argb(role: String): Int { + val rgb = palette.getJSONArray(role) + return android.graphics.Color.rgb(rgb.getInt(0), rgb.getInt(1), rgb.getInt(2)) + } + val roles = listOf("background", "red", "green", "yellow", "blue", "purple", "cyan", "foreground") + application.sessions.setTerminalColors(intArrayOf(argb("foreground"), argb("background"), argb("accent")) + + (roles + roles).map(::argb).toIntArray()) + } + MaterialTheme(colorScheme = scheme, typography = Typography( + bodyLarge = TextStyle(fontSize = 15.sp, lineHeight = 23.sp), + bodyMedium = TextStyle(fontSize = 14.sp, lineHeight = 21.sp), + bodySmall = TextStyle(fontSize = 12.sp, lineHeight = 19.sp), + labelLarge = TextStyle(fontSize = 13.sp, lineHeight = 20.sp), + ), shapes = Shapes( + extraSmall = RoundedCornerShape(8.dp), small = RoundedCornerShape(14.dp), + medium = RoundedCornerShape(18.dp), large = RoundedCornerShape(20.dp), + extraLarge = RoundedCornerShape(24.dp), + )) { + CompositionLocalProvider(LocalTerminalFont provides terminalFont, content = content) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderClipboard.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderClipboard.kt new file mode 100644 index 0000000..17c6206 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderClipboard.kt @@ -0,0 +1,34 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import androidx.compose.runtime.* +import androidx.compose.ui.platform.LocalContext +import kotlinx.coroutines.* + +internal class ReaderClipboard(private val context: Context, private val label: String, + private val scope: CoroutineScope, private val onFailure: () -> Unit) { + var copied by mutableStateOf(null) + private set + private var reset: Job? = null + fun copy(text: String, index: Int) { + try { + context.getSystemService(ClipboardManager::class.java).setPrimaryClip(ClipData.newPlainText(label, text)) + copied = index + reset?.cancel() + reset = scope.launch { delay(1500); copied = null } + } catch (_: Exception) { onFailure() } + } + fun close() { reset?.cancel() } +} + +@Composable +internal fun rememberReaderClipboard(label: String, onFailure: () -> Unit): ReaderClipboard { + val context = LocalContext.current + val scope = rememberCoroutineScope() + val failure = rememberUpdatedState(onFailure) + val feedback = remember(context, label) { ReaderClipboard(context, label, scope) { failure.value() } } + DisposableEffect(feedback) { onDispose { feedback.close() } } + return feedback +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderColors.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderColors.kt new file mode 100644 index 0000000..64d849b --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderColors.kt @@ -0,0 +1,18 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.material3.ColorScheme +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb + +internal data class ReaderColors(val background: String, val foreground: String, val muted: String, + val surface: String, val border: String, val accent: String, + val codeSurface: String = surface) + +internal fun ColorScheme.readerColors() = ReaderColors( + background.readerHex(), onBackground.readerHex(), onSurfaceVariant.readerHex(), + surfaceContainer.readerHex(), outlineVariant.readerHex(), primary.readerHex(), + // 正文容器在 Nord 等主题里等于背景;代码使用独立、已定义的抬高色面。 + codeSurface = surfaceContainerHighest.readerHex(), +) + +internal fun Color.readerHex(): String = "#%06x".format(toArgb() and 0xffffff) diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderDocument.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderDocument.kt new file mode 100644 index 0000000..5f4e4c0 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderDocument.kt @@ -0,0 +1,136 @@ +package io.github.kuddev.pebrel.mobile.ui + +import org.commonmark.Extension +import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension +import org.commonmark.ext.gfm.tables.TablesExtension +import org.commonmark.ext.task.list.items.TaskListItemsExtension +import org.commonmark.node.* +import org.commonmark.parser.Parser +import org.commonmark.renderer.html.CoreHtmlNodeRenderer +import org.commonmark.renderer.html.HtmlRenderer + +internal data class ReaderHeading(val id: String, val text: String, val level: Int) +internal data class ReaderDocument(val html: String, val code: List, val links: List, val headings: List, + val body: String, val token: String) + +/** CommonMark owns parsing; both file and conversation views share these local actions. */ +internal fun prepareReader(source: String, path: String, preview: Boolean, colors: ReaderColors, + copyLabel: String, imageLabel: String): ReaderDocument { + val markup = ReaderMarkup(copyLabel, imageLabel) + val body = if (preview) markup.markdown(source) else { + val lineNumbers = (1..(source.count { it == '\n' } + 1)).joinToString("\n") + """
    ${readerEscape(source)}
    """ + } + return markup.document(body, colors) +} + +internal class ReaderMarkup(private val copyLabel: String, private val imageLabel: String) { + private val code = mutableListOf() + private val links = mutableListOf() + private val headings = mutableListOf() + private val extensions: List = listOf(TablesExtension.create(), StrikethroughExtension.create(), TaskListItemsExtension.create()) + + fun markdown(source: String): String { + val tree = Parser.builder().extensions(extensions) + .customInlineContentParserFactory(ReaderInlineMathFactory) + .customBlockParserFactory(ReaderBlockMathFactory).build().parse(source) + return HtmlRenderer.builder().extensions(extensions).escapeHtml(true).sanitizeUrls(true) + .nodeRendererFactory { context -> object : org.commonmark.renderer.NodeRenderer { + override fun getNodeTypes(): Set> = setOf(ReaderInlineMath::class.java, ReaderBlockMath::class.java) + override fun render(node: Node) { + when (node) { + is ReaderInlineMath -> context.writer.raw(math(node.source, node.display)) + is ReaderBlockMath -> if (node.closed) context.writer.raw(math(node.source.toString(), true)) + else context.writer.text(node.delimiter + node.source) + } + } + } } + .nodeRendererFactory { context -> object : CoreHtmlNodeRenderer(context) { + override fun visit(block: FencedCodeBlock) { context.writer.raw(codeBlock(block.literal, block.info.substringBefore(' '))) } + override fun visit(block: IndentedCodeBlock) { context.writer.raw(codeBlock(block.literal, "plaintext")) } + + override fun visit(link: Link) { + val index = links.size + links += link.destination + context.writer.tag("a", mapOf("href" to "pebrel-link:$index")) + visitChildren(link) + context.writer.tag("/a") + } + + override fun visit(image: Image) { + val index = links.size + links += image.destination + context.writer.tag("a", mapOf("class" to "image-link", "href" to "pebrel-link:$index")) + context.writer.text("$imageLabel · ${nodeText(image).ifBlank { image.destination }}") + context.writer.tag("/a") + } + + override fun visit(heading: Heading) { + val text = nodeText(heading) + val slug = text.lowercase().trim().replace(Regex("[^\\p{L}\\p{N}_ -]"), "").replace(' ', '-') + val base = slug.ifBlank { "heading" } + var id = base + var suffix = 1 + while (headings.any { it.id == id }) { id = "$base-${suffix++}" } + headings += ReaderHeading(id, text, heading.level) + context.writer.tag("h${heading.level}", mapOf("id" to id)) + visitChildren(heading) + context.writer.tag("/h${heading.level}") + } + } }.build().render(tree) + } + + fun codeBlock(literal: String, language: String = "plaintext"): String { + if (language.lowercase() in setOf("math", "latex", "tex")) return math(literal, true) + val index = code.size + code += literal + val safeLanguage = language.takeIf { it.matches(Regex("[a-zA-Z0-9_+-]{1,40}")) } ?: "plaintext" + if (language.equals("mermaid", true)) { + return """
    Mermaid
    ${readerEscape(copyLabel)}
    ${readerEscape(literal)}
    """ + } + return """
    ${readerEscape(copyLabel)}
    ${readerEscape(literal)}
    ${readerEscape(language)}
    """ + } + + private fun math(source: String, display: Boolean): String { + val index = code.size + code += source + return """${readerEscape(source)}${readerEscape(copyLabel)} LaTeX""" + } + + fun document(body: String, colors: ReaderColors): ReaderDocument { + val token = body.hashCode().toUInt().toString(16) + // 自动刷新会重新编号链接;旧页面的迟到点击只属于它自己的文档。 + val guarded = body.replace("href=\"pebrel-copy:", "href=\"pebrel-copy:$token:") + .replace("href=\"pebrel-link:", "href=\"pebrel-link:$token:") + val html = """
    $guarded
    """ + return ReaderDocument(html, code, links, headings, guarded, token) + } +} + +private fun nodeText(node: Node): String = buildString { + node.accept(object : AbstractVisitor() { + override fun visit(text: Text) { append(text.literal) } + override fun visit(code: Code) { append(code.literal) } + override fun visit(line: SoftLineBreak) { append(' ') } + }) +} + +internal fun readerLanguage(path: String): String = when (path.substringAfterLast('.').lowercase()) { + "rs" -> "rust" + "kt", "kts" -> "kotlin" + "js", "jsx", "mjs", "cjs" -> "javascript" + "ts", "tsx" -> "typescript" + "py" -> "python" + "md", "markdown" -> "markdown" + "c", "h", "cpp", "hpp", "cc" -> "cpp" + "cs" -> "csharp" + "sh", "bash", "zsh" -> "bash" + "ps1" -> "powershell" + "yml", "yaml" -> "yaml" + "html", "xml", "svg" -> "xml" + "json", "css", "go", "java", "sql", "swift", "ruby", "toml", "ini", "diff" -> path.substringAfterLast('.').lowercase() + else -> "plaintext" +} + +internal fun readerEscape(text: String): String = text.replace("&", "&").replace("<", "<") + .replace(">", ">").replace("\"", """).replace("'", "'") diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderMath.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderMath.kt new file mode 100644 index 0000000..62df379 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderMath.kt @@ -0,0 +1,83 @@ +package io.github.kuddev.pebrel.mobile.ui + +import org.commonmark.node.CustomBlock +import org.commonmark.node.CustomNode +import org.commonmark.parser.SourceLine +import org.commonmark.parser.beta.InlineContentParser +import org.commonmark.parser.beta.InlineContentParserFactory +import org.commonmark.parser.beta.ParsedInline +import org.commonmark.parser.beta.Scanner +import org.commonmark.parser.block.* + +internal class ReaderInlineMath(val source: String, val display: Boolean) : CustomNode() +internal class ReaderBlockMath(val delimiter: String) : CustomBlock() { + val source = StringBuilder() + var closed = false +} + +/** 在 CommonMark 的解析入口保护公式,避免下划线、反斜线先被 Markdown 改写。 */ +internal object ReaderInlineMathFactory : InlineContentParserFactory { + override fun getTriggerCharacters() = setOf('$', '\\') + override fun create() = InlineContentParser { state -> + val scanner = state.scanner() + val dollar = scanner.peek() == '$' + val display: Boolean + val closer: String + if (dollar) { + scanner.next() + display = scanner.next('$') + if (scanner.peek() == '$') return@InlineContentParser ParsedInline.none() + closer = if (display) "\$\$" else "\$" + } else { + scanner.next() + display = when { + scanner.next('[') -> true + scanner.next('(') -> false + else -> return@InlineContentParser ParsedInline.none() + } + closer = if (display) "\\]" else "\\)" + } + if (!display && (scanner.peek().isWhitespace() || scanner.peek() == Scanner.END)) { + return@InlineContentParser ParsedInline.none() + } + val start = scanner.position() + var count = 0 + while (scanner.hasNext() && count++ < 32_768) { + val end = scanner.position() + if (scanner.next(closer)) { + val source = scanner.getSource(start, end).content + // 与货币文本区分;失败时由 CommonMark 回到原位置继续普通文本解析。 + if (source.isBlank() || (!display && (source.last().isWhitespace() || (dollar && scanner.peek().isDigit())))) { + return@InlineContentParser ParsedInline.none() + } + return@InlineContentParser ParsedInline.of(ReaderInlineMath(source, display), scanner.position()) + } + if (!display && (scanner.peek() == '\n' || scanner.peek() == '`')) break + if (scanner.next('\\') && scanner.hasNext()) scanner.next() else scanner.next() + } + ParsedInline.none() + } +} + +internal object ReaderBlockMathFactory : AbstractBlockParserFactory() { + override fun tryStart(state: ParserState, matchedBlockParser: MatchedBlockParser): BlockStart? { + if (state.indent >= 4) return BlockStart.none() + val line = state.line.content.toString().substring(state.nextNonSpaceIndex).trimEnd() + if (line != "\$\$" && line != "\\[") return BlockStart.none() + return BlockStart.of(MathBlockParser(line)).atIndex(state.line.content.length) + } +} + +private class MathBlockParser(delimiter: String) : AbstractBlockParser() { + private val math = ReaderBlockMath(delimiter) + override fun getBlock() = math + override fun addLine(line: SourceLine) { math.source.append(line.content).append('\n') } + override fun tryContinue(state: ParserState): BlockContinue? { + val closer = if (math.delimiter == "\$\$") "\$\$" else "\\]" + if (state.line.content.toString().substring(state.nextNonSpaceIndex).trimEnd() == closer) { + math.closed = true + return BlockContinue.finished() + } + return BlockContinue.atIndex(state.index) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderWebView.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderWebView.kt new file mode 100644 index 0000000..f97e7a8 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ReaderWebView.kt @@ -0,0 +1,122 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.graphics.Color +import android.net.Uri +import android.view.ViewOutlineProvider +import android.webkit.WebResourceRequest +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.viewinterop.AndroidView +import org.json.JSONObject +import java.io.ByteArrayInputStream +import io.github.kuddev.pebrel.mobile.R + +private val readerAssetFiles = setOf("reader/reader.css", "reader/highlight.min.js", "reader/reader.js", + "reader/extensions.js", "reader/reader-compat.js", "reader/katex/katex.min.js", "reader/katex/katex.min.css", "reader/mermaid/mermaid.min.js") +private val readerFontAsset = Regex("reader/katex/fonts/KaTeX_[A-Za-z-]+\\.woff2") + +private class ReaderView(context: android.content.Context) : WebView(context) { + var readerReady = false + var readerColors: ReaderColors? = null +} + +@Composable +internal fun ReaderWebView(document: ReaderDocument, colors: ReaderColors, copied: Int?, copyLabel: String, + copiedLabel: String, heading: String?, onCopy: (Int) -> Unit, + onLink: (String) -> Unit, modifier: Modifier = Modifier, followUpdates: Boolean = false) { + val currentCopy = rememberUpdatedState(onCopy) + val currentLink = rememberUpdatedState(onLink) + val currentDocument = rememberUpdatedState(document) + val follow = rememberUpdatedState(followUpdates) + val textScale = LocalDensity.current.fontScale + var pageLoading by remember { mutableStateOf(true) } + Box(modifier) { + AndroidView(modifier = Modifier.fillMaxSize(), factory = { context -> + ReaderView(context).apply { + // AndroidView 默认不裁剪原生绘制;限制 WebView 的合成层,避免覆盖标题栏和输入区。 + outlineProvider = ViewOutlineProvider.BOUNDS + clipToOutline = true + settings.javaScriptEnabled = true + settings.allowFileAccess = false + settings.allowContentAccess = false + settings.blockNetworkLoads = true + settings.domStorageEnabled = false + settings.setSupportMultipleWindows(false) + settings.setSupportZoom(true) + settings.builtInZoomControls = true + settings.displayZoomControls = false + webViewClient = object : WebViewClient() { + override fun onPageFinished(view: WebView, url: String) { + (view as ReaderView).readerReady = true + pageLoading = false + view.evaluateJavascript("window.pebrelSetReaderLabels(${JSONObject.quote(context.getString(R.string.reader_extension_error))},${JSONObject.quote(context.getString(R.string.reader_extension_loading))});", null) + if (follow.value) view.evaluateJavascript("window.pebrelRender(${JSONObject.quote(currentDocument.value.body)},true);", null) + } + override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse { + val uri = request.url + val asset = uri.path.orEmpty().removePrefix("/") + // 文档不拥有网络或本地文件权限;唯一可加载资源是 APK 中固定的阅读器资产。 + if (uri.scheme == "https" && uri.host == "reader.pebrel.local" && + (asset in readerAssetFiles || readerFontAsset.matches(asset))) { + val mime = when { asset.endsWith(".css") -> "text/css"; asset.endsWith(".woff2") -> "font/woff2"; else -> "application/javascript" } + return WebResourceResponse(mime, if (asset.endsWith(".woff2")) null else "UTF-8", context.assets.open(asset)) + } + return WebResourceResponse("text/plain", "UTF-8", ByteArrayInputStream(ByteArray(0))) + } + + override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { + val uri = request.url + val parts = uri.schemeSpecificPart.split(':', limit = 2) + if (parts.firstOrNull() != currentDocument.value.token) return true + val index = parts.getOrNull(1)?.toIntOrNull() + when (uri.scheme) { + "pebrel-copy" -> index?.takeIf { it in currentDocument.value.code.indices }?.let { currentCopy.value(it) } + "pebrel-link" -> index?.let { currentDocument.value.links.getOrNull(it) }?.let { currentLink.value(it) } + } + return true + } + } + } + }, update = { view -> + view.setBackgroundColor(Color.parseColor(colors.background)) + view.settings.textZoom = (100 * textScale).toInt() + if (view.tag != document.html) { + view.tag = document.html + if (followUpdates && view.readerReady && view.readerColors == colors) { + view.evaluateJavascript("window.pebrelRender(${JSONObject.quote(document.body)},false);", null) + } else { + pageLoading = true + view.readerReady = false + view.readerColors = colors + view.loadDataWithBaseURL("https://reader.pebrel.local/", document.html, "text/html", "UTF-8", null) + } + } + val label = JSONObject.quote(copyLabel) + val success = JSONObject.quote(copiedLabel) + view.evaluateJavascript("document.querySelectorAll('[data-copy]').forEach(function(a){a.textContent=Number(a.dataset.copy)===${copied ?: -1}?$success:$label;});", null) + if (heading != null) { + val id = JSONObject.quote(Uri.decode(heading.removePrefix("#"))) + view.evaluateJavascript("(function(){var h=document.getElementById($id);if(h)h.scrollIntoView();})();", null) + } + }, onRelease = { view -> + view.stopLoading() + view.destroy() + }) + // 页面完成前保留就地进度,避免文件已到达但 WebView 仍空白时没有反馈。 + if (pageLoading) LinearProgressIndicator(Modifier.fillMaxWidth().align(Alignment.TopCenter)) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/RelayDeploymentFlow.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/RelayDeploymentFlow.kt new file mode 100644 index 0000000..588be37 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/RelayDeploymentFlow.kt @@ -0,0 +1,237 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import kotlinx.coroutines.* +import java.util.UUID + +/** Native service management is separate from terminal and PC-pairing lifecycles. */ +@Composable +fun RelayDeploymentFlow(repository: SessionRepository, onCancel: () -> Unit) { + val context = LocalContext.current + val hosts by repository.hosts.collectAsStateWithLifecycle() + val credentials by repository.savedCredentials.collectAsStateWithLifecycle() + val scope = rememberCoroutineScope() + val owner = remember { repository.beginSshOperation() } + DisposableEffect(owner) { onDispose { repository.endSshOperation(owner) } } + var selectedId by remember { mutableStateOf(hosts.firstOrNull()?.id) } + var address by remember { mutableStateOf("") } + var user by remember { mutableStateOf("root") } + var password by remember { mutableStateOf("") } + var sshPort by remember { mutableStateOf("22") } + var servicePort by remember { mutableStateOf("443") } + var advertisedAddress by remember { mutableStateOf("") } + var advanced by remember { mutableStateOf(false) } + var manual by remember { mutableStateOf(false) } + var choosing by remember { mutableStateOf(false) } + var result by remember { mutableStateOf(null) } + var stage by remember { mutableStateOf(null) } + var installProgress by remember { mutableStateOf(null) } + var failure by remember { mutableStateOf(null) } + var job by remember { mutableStateOf(null) } + var running by remember { mutableStateOf(false) } + var operation by remember { mutableStateOf(null) } + var confirmRemove by remember { mutableStateOf(false) } + var purge by remember { mutableStateOf(false) } + var exportFeedback by remember { mutableStateOf(null) } + var exportText by remember { mutableStateOf("") } + val busy = running + val host = hosts.find { it.id == selectedId } + val endpoint = runCatching { parseSshEndpoint(address, user) }.getOrNull() + val savedPassword = credentials.isNotEmpty() && host != null && repository.hasSavedPassword(host) + val valid = (host != null || endpoint != null && (sshPort.toIntOrNull() ?: 0) in 1..65535) && + (password.isNotEmpty() || savedPassword) && (servicePort.toIntOrNull() ?: 0) in 1..65535 + val saveFile = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri -> + val text = exportText + exportText = "" + if (uri != null) scope.launch { + exportFeedback = try { + withContext(Dispatchers.IO) { checkNotNull(context.contentResolver.openOutputStream(uri)).use { it.write(text.toByteArray()) } } + R.string.deploy_export_saved + } catch (cancelled: CancellationException) { throw cancelled } + catch (_: Exception) { R.string.deploy_export_failed } + } + } + fun execute(action: RelayServiceAction) { + if (!valid || busy) return + val selected = host ?: HostProfile(UUID.randomUUID().toString(), address, checkNotNull(endpoint).address, sshPort.toInt(), endpoint.user) + val entered = password.takeIf(String::isNotEmpty)?.toCharArray() + val relayAddress = advertisedAddress.ifBlank { selected.address } + val port = servicePort.toInt() + val removeConfiguration = purge + password = "" + failure = null + stage = "connecting" + installProgress = if (action == RelayServiceAction.INSTALL) RelayInstallProgress() else null + running = true + val token = Any() + operation = token + job = scope.launch { + var secret = entered + try { + if (secret == null) secret = repository.loadSavedPassword(selected) + if (secret == null) throw RelayServiceFailure("missing_credentials") + result = NativeRelayDeployment.execute(context, selected, checkNotNull(secret), + { h, fingerprint -> repository.verifySshOperation(owner, h, fingerprint) }, + action, relayAddress, port, removeConfiguration) { update -> + scope.launch { if (operation === token) { + stage = update.stage + installProgress = installProgress?.advance(update) + } } + } + installProgress = installProgress?.copy(finished = true) + stage = when { + result?.state?.ready == true -> "ready" + result?.state?.running == true -> "not_ready" + result?.state?.installed == true -> "stopped" + action == RelayServiceAction.UNINSTALL -> "uninstalled" + else -> "not_installed" + } + } catch (_: TimeoutCancellationException) { + failure = R.string.ssh_error_timeout; stage = "failed" + installProgress = installProgress?.copy(failed = true) + } + catch (cancelled: CancellationException) { + installProgress = installProgress?.copy(cancelled = true) + throw cancelled + } + catch (error: Exception) { + failure = serviceErrorText(error); stage = "failed" + installProgress = installProgress?.copy(failed = true) + } + finally { secret?.fill('\u0000'); operation = null; running = false; job = null } + } + } + fun resetStatus() { result = null; stage = null; installProgress = null; failure = null; exportFeedback = null } + ConnectionForm(stringResource(R.string.pair_deploy_server), onCancel) { + Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { + HelperText(stringResource(R.string.service_intro)) + if (!busy) { + if (hosts.isNotEmpty()) OutlinedButton({ choosing = true }, modifier = Modifier.fillMaxWidth()) { + Text(host?.name ?: stringResource(R.string.deploy_choose_host)) + } + if (host == null) { + ConnectionField(address, { address = it; resetStatus() }, R.string.host_address, keyboard = KeyboardType.Uri) + ConnectionField(user, { user = it; resetStatus() }, R.string.username) + } + ConnectionField(password, { password = it }, R.string.password, keyboard = KeyboardType.Password, + placeholder = if (savedPassword) stringResource(R.string.password_saved_placeholder) else "", + transformation = PasswordVisualTransformation(), limit = 1024) + TextButton({ advanced = !advanced }) { Text(stringResource(R.string.service_advanced)) } + if (advanced) { + if (host == null) ConnectionField(sshPort, { sshPort = it; resetStatus() }, R.string.port, keyboard = KeyboardType.Number, limit = 5) + ConnectionField(servicePort, { servicePort = it; resetStatus() }, R.string.service_port, keyboard = KeyboardType.Number, limit = 5) + ConnectionField(advertisedAddress, { advertisedAddress = it; resetStatus() }, R.string.service_address, keyboard = KeyboardType.Uri, + placeholder = host?.address ?: endpoint?.address.orEmpty()) + HelperText(stringResource(R.string.service_address_hint)) + } + } + Card(Modifier.fillMaxWidth()) { + Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) { + Text(stringResource(R.string.service_title)) + Text(stringResource(serviceStageText(stage)), color = MaterialTheme.colorScheme.onSurfaceVariant) + if (busy && installProgress == null) LinearProgressIndicator(Modifier.fillMaxWidth()) + if (installProgress == null) failure?.let { Text(stringResource(it), color = MaterialTheme.colorScheme.error) } + if (!busy) { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Button({ execute(RelayServiceAction.INSTALL) }, enabled = valid) { Text(stringResource(R.string.service_install)) } + TextButton({ execute(RelayServiceAction.STATUS) }, enabled = valid) { Text(stringResource(R.string.service_check)) } + } + if (result?.state?.installed == true) Row { + TextButton({ execute(if (result?.state?.running == true) RelayServiceAction.STOP else RelayServiceAction.START) }, enabled = valid) { + Text(stringResource(if (result?.state?.running == true) R.string.service_stop else R.string.service_start)) + } + TextButton({ purge = false; confirmRemove = true }, enabled = valid) { Text(stringResource(R.string.service_uninstall)) } + } + } + } + } + HelperText(stringResource(R.string.service_requirements)) + if (result?.access != null) { + HelperText(stringResource(R.string.service_pc_next)) + OutlinedButton({ exportText = result?.access.orEmpty(); saveFile.launch("pebrel-relay-access.json") }) { + Text(stringResource(R.string.deploy_save_pc)) + } + HelperText(stringResource(R.string.service_export_private)) + } + exportFeedback?.let { HelperText(stringResource(it)) } + if (busy) TextButton({ job?.cancel(); stage = "cancelled" }) { Text(stringResource(R.string.cancel)) } + installProgress?.let { RelayInstallSteps(it, failure) } + if (!busy) TextButton({ manual = !manual }) { Text(stringResource(R.string.service_manual_commands)) } + if (manual) { + HelperText(stringResource(R.string.service_manual_hint)) + val relayAddress = runCatching { + NativeRelayDeployment.validatedAddress(advertisedAddress.ifBlank { host?.address ?: endpoint?.address.orEmpty() }) + }.getOrDefault("SERVER_IP") + val relayPort = servicePort.toIntOrNull()?.takeIf { it in 1..65535 }?.toString() ?: "PORT" + SelectionContainer { Text("sh install.sh '$relayAddress' $relayPort\n/opt/pebrel-relay/pebrel-relay service-status", + fontFamily = LocalTerminalFont.current, style = MaterialTheme.typography.bodySmall) } + } + } + } + if (choosing) AlertDialog(onDismissRequest = { choosing = false }, title = { Text(stringResource(R.string.deploy_choose_host)) }, + text = { Column { hosts.forEach { entry -> TextButton({ selectedId = entry.id; password = ""; choosing = false; resetStatus() }) { Text(entry.name) } } + TextButton({ selectedId = null; password = ""; choosing = false; resetStatus() }) { Text(stringResource(R.string.deploy_manual)) } } }, + confirmButton = { TextButton({ choosing = false }) { Text(stringResource(R.string.close)) } }) + if (confirmRemove) AlertDialog(onDismissRequest = { confirmRemove = false }, title = { Text(stringResource(R.string.service_uninstall)) }, + text = { Column { Text(stringResource(R.string.service_uninstall_hint)); Row { Checkbox(purge, { purge = it }); Text(stringResource(R.string.service_purge)) } } }, + confirmButton = { TextButton({ confirmRemove = false; execute(RelayServiceAction.UNINSTALL) }) { Text(stringResource(R.string.service_uninstall)) } }, + dismissButton = { TextButton({ confirmRemove = false }) { Text(stringResource(R.string.cancel)) } }) +} + +internal fun serviceStageText(stage: String?): Int = when (stage) { + "failed" -> R.string.service_operation_failed + "connecting" -> R.string.establishing_connection + "checking" -> R.string.deploy_prerequisites + "uploading" -> R.string.service_uploading + "uploaded" -> R.string.service_uploaded + "installing" -> R.string.service_installing + "initializing" -> R.string.deploy_initializing + "starting" -> R.string.service_starting + "verifying" -> R.string.service_verifying + "ready" -> R.string.deploy_done + "stopping", "removing" -> R.string.service_removing + "stopped" -> R.string.service_stopped + "uninstalled" -> R.string.service_uninstalled + "cancelled" -> R.string.service_cancelled + "not_installed" -> R.string.service_not_installed + "not_ready" -> R.string.service_not_ready + else -> R.string.service_unchecked +} + +internal fun serviceErrorText(error: Exception): Int = when ((error as? RelayServiceFailure)?.code) { + "asset_missing", "binary_integrity_failed" -> R.string.service_asset_error + "linux_systemd_required", "systemd_247_required", "systemd_239_required", "unsupported_arch", "supported_init_required" -> R.string.service_system_error + "openrc_supervisor_required" -> R.string.service_openrc_error + "remote_tools_missing" -> R.string.service_tools_error + "service_command_failed", "service_command_output_limit" -> R.string.service_manager_error + "service_command_timeout", "operation_timeout" -> R.string.ssh_error_timeout + "unprivileged_account_required", "privilege_drop_failed" -> R.string.service_privilege_error + "root_required", "permission_denied" -> R.string.service_permission_error + "managed_file_changed", "installation_conflict", "explicit_update_required", "service_manager_changed", + "configuration_directory_not_empty", "symlink_installation_path", "invalid_ownership_manifest" -> R.string.service_conflict + "file_or_service_not_found" -> R.string.service_missing + "service_not_ready", "port_in_use" -> R.string.service_not_ready + "invalid_address" -> R.string.service_address_error + else -> when (classifySshFailure(error)) { + SshFailureKind.AUTH -> R.string.ssh_error_auth + SshFailureKind.TIMEOUT -> R.string.ssh_error_timeout + SshFailureKind.HOST_KEY_CHANGED -> R.string.ssh_error_host_key + SshFailureKind.TRUST_REJECTED -> R.string.ssh_error_trust + else -> R.string.service_failed + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/RelayForm.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/RelayForm.kt new file mode 100644 index 0000000..5e4a6fa --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/RelayForm.kt @@ -0,0 +1,148 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.RelayProfile +import org.json.JSONObject + +@Composable +fun RelayForm(onCancel: () -> Unit, onConnect: (String) -> Unit, onDeploy: () -> Unit = {}) { + var name by remember { mutableStateOf("") } + var url by remember { mutableStateOf("") } + var device by remember { mutableStateOf("") } + var token by remember { mutableStateOf("") } + var pin by remember { mutableStateOf("") } + var mode by remember { mutableStateOf("lan") } + var pasted by remember { mutableStateOf("") } + var method by remember { mutableStateOf("scan") } + var showManual by remember { mutableStateOf(false) } + var invalid by remember { mutableStateOf(false) } + fun importInvitation(text: String) { + runCatching { RelayProfile.parse(text) }.onSuccess { + invalid = false + onConnect(it.toJson().toString()) + }.onFailure { invalid = true } + } + Dialog(onCancel, properties = DialogProperties(usePlatformDefaultWidth = false, decorFitsSystemWindows = false)) { + Surface(Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { + Column(Modifier.fillMaxSize().systemBarsPadding().imePadding()) { + PageHeader(stringResource(R.string.computer_connect), onCancel) + Column(Modifier.weight(1f).fillMaxWidth().verticalScroll(rememberScrollState()) + .padding(start = 22.dp, end = 22.dp, top = 4.dp, bottom = 32.dp)) { + PairingProgress() + val setupHint = stringResource(R.string.pair_setup_hint) + val settingsPath = stringResource(R.string.pair_settings_path) + Text(buildAnnotatedString { + append(setupHint) + val start = setupHint.indexOf(settingsPath) + if (start >= 0) addStyle(SpanStyle(color = MaterialTheme.colorScheme.primary, fontWeight = FontWeight.Medium), + start, start + settingsPath.length) + }, fontSize = 14.sp, lineHeight = 22.sp, + modifier = Modifier.padding(start = 2.dp, end = 2.dp, bottom = 16.dp)) + ConnectionSegments(listOf("scan" to stringResource(R.string.pair_tab_scan), + "code" to stringResource(R.string.pair_short_code), "paste" to stringResource(R.string.pair_tab_paste)), + method, { method = it; invalid = false }, Modifier.fillMaxWidth()) + Spacer(Modifier.height(12.dp)) + when (method) { + "scan" -> { + InlinePairingScanner(::importInvitation) + HelperText(stringResource(R.string.pair_qr_expiry), Modifier.padding(top = 8.dp, start = 2.dp)) + } + "code" -> ShortCodePairing(onConnect) + "paste" -> { + val pasteLabel = stringResource(R.string.pair_import) + OutlinedTextField(pasted, { if (it.length <= 8192) { pasted = it; invalid = false } }, + modifier = Modifier.fillMaxWidth().heightIn(min = 120.dp).semantics { contentDescription = pasteLabel }, + minLines = 4, maxLines = 6, + shape = RoundedCornerShape(14.dp), + placeholder = { Text(stringResource(R.string.pair_paste_hint), fontSize = 12.sp, lineHeight = 20.sp) }, + textStyle = MaterialTheme.typography.bodySmall.copy(fontFamily = LocalTerminalFont.current), + visualTransformation = PasswordVisualTransformation(), isError = invalid) + Spacer(Modifier.height(12.dp)) + ConnectionButton(stringResource(R.string.pair_import_connect), enabled = pasted.isNotBlank(), primary = false) { + importInvitation(pasted) + } + TextButton({ showManual = !showManual }, modifier = Modifier.fillMaxWidth()) { + Text(stringResource(if (showManual) R.string.pair_hide_manual else R.string.pair_manual_setup)) + } + } + } + if (invalid) Text(stringResource(R.string.pair_invalid), fontSize = 12.sp, lineHeight = 20.sp, + color = MaterialTheme.colorScheme.error, modifier = Modifier.padding(top = 8.dp)) + if (method == "paste" && showManual) Column(Modifier.padding(top = 12.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) { + ConnectionSegments(listOf("lan" to stringResource(R.string.pair_lan), "relay" to stringResource(R.string.pair_relay)), + mode, { mode = it }, Modifier.fillMaxWidth()) + ConnectionField(name, { name = it }, R.string.pair_computer_name, limit = 80) + ConnectionField(url, { url = it }, R.string.pair_server_url, keyboard = KeyboardType.Uri, placeholder = "wss://", limit = 2048) + ConnectionField(device, { device = it }, R.string.pair_device_id, limit = 64) + ConnectionField(token, { token = it }, R.string.pair_access_key, limit = 43, transformation = PasswordVisualTransformation()) + if (mode == "lan" || pin.isNotEmpty()) ConnectionField(pin, { pin = it }, R.string.pair_certificate_pin, limit = 51) + if (mode == "relay") HelperText(stringResource(R.string.relay_trust_hint)) + ConnectionButton(stringResource(R.string.connect), enabled = name.isNotBlank() && url.isNotBlank() && device.isNotBlank() && token.isNotBlank()) { + val data = JSONObject().put("version", 1).put("name", name).put("url", url).put("device", device) + .put("token", token).put("mode", mode).apply { if (pin.isNotBlank()) put("tlsPin", pin) } + runCatching { RelayProfile.parse(data.toString()) }.onSuccess { onConnect(it.toJson().toString()) }.onFailure { invalid = true } + } + } + TextButton(onDeploy, modifier = Modifier.padding(top = 20.dp).heightIn(min = 48.dp), + contentPadding = PaddingValues(horizontal = 2.dp)) { + Text(stringResource(R.string.pair_relay_alternative), fontSize = 12.sp, lineHeight = 20.sp) + } + } + } + } + } +} + +@Composable +private fun PairingProgress() { + BoxWithConstraints(Modifier.fillMaxWidth().padding(start = 2.dp, end = 2.dp, top = 6.dp, bottom = 18.dp)) { + if (maxWidth < 300.dp || LocalDensity.current.fontScale > 1.3f) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + PairingStep(1, stringResource(R.string.pair_step_find), true) + PairingStep(2, stringResource(R.string.pair_step_approve), false) + } + } else Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp)) { + PairingStep(1, stringResource(R.string.pair_step_find), true) + HorizontalDivider(Modifier.weight(1f), color = MaterialTheme.colorScheme.outlineVariant) + PairingStep(2, stringResource(R.string.pair_step_approve), false) + } + } +} + +@Composable +private fun PairingStep(number: Int, label: String, active: Boolean) { + val colors = MaterialTheme.colorScheme + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(7.dp)) { + Box(Modifier.size(20.dp * LocalDensity.current.fontScale) + .background(if (active) colors.primary else colors.background, CircleShape) + .border(1.dp, if (active) colors.primary else colors.outline, CircleShape), contentAlignment = Alignment.Center) { + Text(number.toString(), fontSize = 12.sp, lineHeight = 16.sp, fontWeight = FontWeight.Medium, + color = if (active) colors.onPrimary else colors.onSurfaceVariant) + } + Text(label, fontSize = 12.sp, lineHeight = 18.sp, color = if (active) colors.onSurface else colors.onSurfaceVariant) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/RelayInstallSteps.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/RelayInstallSteps.kt new file mode 100644 index 0000000..b74135e --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/RelayInstallSteps.kt @@ -0,0 +1,62 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.os.SystemClock +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.* +import kotlinx.coroutines.delay + +/** Always retain all four rows, including the precise failed/cancelled step. */ +@Composable +internal fun RelayInstallSteps(progress: RelayInstallProgress, failure: Int?) { + val titles = listOf(R.string.service_step_connect, R.string.service_step_check, + R.string.service_step_upload, R.string.service_step_start) + var seconds by remember(progress) { mutableLongStateOf(0) } + LaunchedEffect(progress) { + val start = SystemClock.elapsedRealtime() + while (!progress.finished && !progress.failed && !progress.cancelled) { + delay(1000) + seconds = (SystemClock.elapsedRealtime() - start) / 1000 + } + } + Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) { + titles.forEachIndexed { index, title -> + val state = progress.state(index + 1) + val label = when (state) { + InstallStepState.WAITING -> R.string.service_step_waiting + InstallStepState.ACTIVE -> R.string.service_step_active + InstallStepState.DONE -> R.string.service_step_done + InstallStepState.FAILED -> R.string.service_operation_failed + InstallStepState.CANCELLED -> R.string.service_step_cancelled + } + val color = when (state) { + InstallStepState.ACTIVE -> MaterialTheme.colorScheme.onSurface + InstallStepState.FAILED -> MaterialTheme.colorScheme.error + else -> MaterialTheme.colorScheme.onSurfaceVariant + } + Text(stringResource(R.string.service_step_row, index + 1, stringResource(title), stringResource(label)), + style = MaterialTheme.typography.bodySmall, color = color) + if (index + 1 == progress.step && !progress.finished) { + val update = progress.update + if (update.total > 0) { + Text(stringResource(R.string.service_upload_bytes, update.sent / 1024, (update.total + 1023) / 1024), + style = MaterialTheme.typography.bodySmall, color = color) + LinearProgressIndicator(progress = { update.sent.toFloat() / update.total }, modifier = Modifier.fillMaxWidth()) + } + if (state == InstallStepState.ACTIVE) { + Text(stringResource(serviceStageText(update.stage)), style = MaterialTheme.typography.bodySmall, color = color) + if (update.total == 0) LinearProgressIndicator(Modifier.fillMaxWidth()) + if (seconds >= 5) Text(stringResource(R.string.service_step_wait_seconds, seconds), + style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant) + } + if (state == InstallStepState.FAILED && failure != null) Text(stringResource(failure), + style = MaterialTheme.typography.bodySmall, color = color) + } + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SettingsComponents.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SettingsComponents.kt new file mode 100644 index 0000000..f079f66 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SettingsComponents.kt @@ -0,0 +1,69 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.toggleable +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R + +/** A settings category owns its outer surface; rows own only their input state. */ +@Composable +internal fun SettingsGroup(title: String, content: @Composable ColumnScope.() -> Unit) { + val shape = RoundedCornerShape(14.dp) + Column(Modifier.fillMaxWidth().padding(top = 20.dp, bottom = 10.dp)) { + Text(title, fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(start = 4.dp, bottom = 10.dp)) + Column(Modifier.fillMaxWidth().clip(shape).background(MaterialTheme.colorScheme.surface) + .border(.5.dp, MaterialTheme.colorScheme.outlineVariant, shape), content = content) + } +} + +@Composable +internal fun SettingDivider() { + HorizontalDivider(Modifier.padding(start = 48.dp), thickness = .5.dp, + color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = .65f)) +} + +@Composable +internal fun SettingsRow(@DrawableRes icon: Int, title: String, detail: String = "", onClick: () -> Unit) { + Row(Modifier.fillMaxWidth().clickable(onClick = onClick).heightIn(min = 60.dp) + .padding(horizontal = 16.dp, vertical = 12.dp), verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp)) { + Glyph(icon) + Text(title, fontSize = 14.sp, modifier = Modifier.weight(1f)) + if (detail.isNotEmpty()) Text(detail, fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.widthIn(max = 130.dp)) + Glyph(R.drawable.ic_chevron, Modifier.size(14.dp)) + } +} + +@Composable +internal fun SettingsChoice(title: String, selected: Boolean, onClick: () -> Unit) { + Row(Modifier.fillMaxWidth().selectable(selected, role = Role.RadioButton, onClick = onClick) + .heightIn(min = 58.dp).padding(horizontal = 16.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically) { + Text(title, Modifier.weight(1f), fontSize = 14.sp) + Box(Modifier.size(20.dp)) { if (selected) Glyph(R.drawable.ic_check, color = MaterialTheme.colorScheme.primary) } + } +} + +@Composable +internal fun TogglePreference(title: String, checked: Boolean, enabled: Boolean = true, onChange: (Boolean) -> Unit) { + Row(Modifier.fillMaxWidth().toggleable(checked, enabled = enabled, role = Role.Switch, onValueChange = onChange) + .heightIn(min = 60.dp).padding(horizontal = 16.dp, vertical = 6.dp), verticalAlignment = Alignment.CenterVertically) { + Text(title, Modifier.weight(1f).padding(end = 12.dp), fontSize = 14.sp) + Switch(checked, onCheckedChange = null, enabled = enabled) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SettingsScreen.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SettingsScreen.kt new file mode 100644 index 0000000..562caf2 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SettingsScreen.kt @@ -0,0 +1,250 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.content.Intent +import android.provider.Settings +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.luminance +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import io.github.kuddev.pebrel.mobile.PebrelApplication +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import org.json.JSONObject + +@Composable +fun SettingsScreen(repository: SessionRepository, onBack: () -> Unit, onComputers: () -> Unit, + onBackground: (Boolean) -> Unit, initial: String = "") { + var page by rememberSaveable { mutableStateOf(initial) } + var direction by remember { mutableStateOf(PebrelNavigationDirection.Forward) } + val prefs by repository.display.state.collectAsStateWithLifecycle() + val theme by repository.theme.collectAsStateWithLifecycle() + val background by repository.backgroundActive.collectAsStateWithLifecycle() + val sessions by repository.sessions.collectAsStateWithLifecycle() + val desktops by repository.desktops.collectAsStateWithLifecycle() + val context = LocalContext.current + val motion = rememberPebrelMotion() + fun open(destination: String) { direction = PebrelNavigationDirection.Forward; page = destination } + fun back() { + if (page.isEmpty() || initial.isNotEmpty()) onBack() + else { direction = PebrelNavigationDirection.Backward; page = "" } + } + BackHandler { back() } + AnimatedContent(page, modifier = Modifier.fillMaxSize(), + transitionSpec = { motion.pageTransition(direction) }, label = "settings_page") { route -> + val title = when (route) { + "theme" -> R.string.theme_settings + "font" -> R.string.terminal_font_and_size + "cursor" -> R.string.terminal_cursor + "gestures" -> R.string.terminal_gestures + "input" -> R.string.input_settings + "completion" -> R.string.completion_settings + "keepalive" -> R.string.background_title + "notices" -> R.string.notifications + "about" -> R.string.about + else -> R.string.settings + } + Column(Modifier.fillMaxSize()) { + PageHeader(stringResource(title), ::back) + Column(Modifier.weight(1f).fillMaxWidth().verticalScroll(rememberScrollState()) + .padding(horizontal = 22.dp, vertical = 8.dp)) { + when (route) { + "" -> { + SettingsGroup(stringResource(R.string.raw_terminal)) { + SettingsRow(R.drawable.ic_palette, stringResource(R.string.theme_settings), + if (theme == "system") stringResource(R.string.follow_system) else theme) { open("theme") } + SettingDivider() + SettingsRow(R.drawable.ic_font, stringResource(R.string.terminal_font_and_size), + "${terminalFontLabel(prefs.fontFamily)} · ${prefs.fontSize}") { open("font") } + SettingDivider() + SettingsRow(R.drawable.ic_terminal, stringResource(R.string.terminal_cursor), + cursorLabel(prefs.cursorStyle)) { open("cursor") } + } + SettingsGroup(stringResource(R.string.input_group)) { + SettingsRow(R.drawable.ic_keyboard, stringResource(R.string.input_settings), + stringResource(if (prefs.directInput) R.string.direct_input else R.string.local_compose)) { open("input") } + SettingDivider() + SettingsRow(R.drawable.ic_touch, stringResource(R.string.terminal_gestures)) { open("gestures") } + SettingDivider() + SettingsRow(R.drawable.ic_command, stringResource(R.string.completion_settings), + enabledLabel(prefs.suggestions)) { open("completion") } + } + SettingsGroup(stringResource(R.string.connection_group)) { + SettingsRow(R.drawable.ic_monitor, stringResource(R.string.computers), onClick = onComputers) + } + SettingsGroup(stringResource(R.string.background_title)) { + SettingsRow(R.drawable.ic_battery, stringResource(R.string.keep_background), + enabledLabel(background)) { open("keepalive") } + } + SettingsGroup(stringResource(R.string.app_group)) { + SettingsRow(R.drawable.ic_bell, stringResource(R.string.notifications)) { open("notices") } + SettingDivider() + SettingsRow(R.drawable.ic_info, stringResource(R.string.about)) { open("about") } + } + Row(Modifier.fillMaxWidth().padding(vertical = 18.dp), horizontalArrangement = Arrangement.SpaceBetween) { + HelperText("PEBREL") + HelperText(stringResource(R.string.preview_version)) + } + } + "theme" -> ThemeChoices(repository) + "font" -> { + PreviewPanel(prefs.fontSize) + SettingsGroup(stringResource(R.string.font_settings)) { + listOf("maple", "jetbrains", "system").forEachIndexed { index, family -> + if (index > 0) SettingDivider() + SettingsChoice(terminalFontLabel(family), prefs.fontFamily == family) { + repository.display.update { it.copy(fontFamily = family) } + } + } + } + SettingsGroup(stringResource(R.string.font_size)) { + Column(Modifier.padding(horizontal = 18.dp, vertical = 12.dp)) { + Text(stringResource(R.string.terminal_font_size_value, prefs.fontSize), fontSize = 14.sp) + Slider(prefs.fontSize.toFloat(), { value -> repository.display.update { it.copy(fontSize = value.toInt()) } }, + valueRange = 8f..32f, steps = 23) + } + } + HelperText(stringResource(R.string.font_boundary)) + } + "cursor" -> { + PreviewPanel(prefs.fontSize) + SettingsGroup(stringResource(R.string.terminal_cursor_shape)) { + listOf("block", "bar", "underline").forEachIndexed { index, style -> + if (index > 0) SettingDivider() + SettingsChoice(cursorLabel(style), prefs.cursorStyle == style) { + repository.display.update { it.copy(cursorStyle = style) } + } + } + SettingDivider() + TogglePreference(stringResource(R.string.terminal_cursor_blink), prefs.cursorBlink) { + repository.display.update { value -> value.copy(cursorBlink = it) } + } + } + HelperText(stringResource(R.string.terminal_cursor_hint)) + } + "gestures" -> { + SettingsGroup(stringResource(R.string.terminal_gestures)) { + TogglePreference(stringResource(R.string.terminal_pinch_zoom), prefs.pinchZoom) { + repository.display.update { value -> value.copy(pinchZoom = it) } + } + } + HelperText(stringResource(R.string.terminal_pinch_hint)) + } + "input" -> { + SettingsGroup(stringResource(R.string.input_settings)) { + listOf(false, true).forEachIndexed { index, direct -> + if (index > 0) SettingDivider() + SettingsChoice(stringResource(if (direct) R.string.direct_input else R.string.local_compose), prefs.directInput == direct) { + repository.display.update { it.copy(directInput = direct) } + } + } + } + HelperText(stringResource(R.string.input_settings_hint)) + } + "completion" -> { + SettingsGroup(stringResource(R.string.input_group)) { + TogglePreference(stringResource(R.string.completion_settings), prefs.suggestions) { + repository.display.update { value -> value.copy(suggestions = it) } + } + } + HelperText(stringResource(R.string.completion_boundary)) + } + "keepalive" -> { + val available = background || sessions.any { it.status in setOf("ready", "connecting") } || + desktops.any { it.status in setOf("ready", "connecting") } + SettingsGroup(stringResource(R.string.background_title)) { + TogglePreference(stringResource(R.string.keep_background), background, available, onBackground) + } + HelperText(stringResource(R.string.background_boundary)) + } + "notices" -> { + SettingsGroup(stringResource(R.string.notifications)) { + SettingsRow(R.drawable.ic_bell, stringResource(R.string.notification_settings)) { + context.startActivity(Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)) + } + } + HelperText(stringResource(R.string.notice_boundary)) + } + "about" -> { + Text("Pebrel", fontSize = 24.sp, modifier = Modifier.padding(vertical = 20.dp)) + HelperText(stringResource(R.string.ui_preview_engine)) + HelperText(stringResource(R.string.connection_boundary), Modifier.padding(vertical = 20.dp)) + HelperText(stringResource(R.string.features_pending)) + } + } + Spacer(Modifier.height(24.dp)) + } + } + } +} + +@Composable +private fun enabledLabel(enabled: Boolean) = stringResource(if (enabled) R.string.enabled else R.string.disabled) + +@Composable +private fun terminalFontLabel(family: String): String = when (family) { + "jetbrains" -> "JetBrains Mono" + "system" -> stringResource(R.string.terminal_system_font) + else -> "Maple Mono NF CN" +} + +@Composable +private fun cursorLabel(style: String) = stringResource(when (style) { + "bar" -> R.string.terminal_cursor_bar + "underline" -> R.string.terminal_cursor_underline + else -> R.string.terminal_cursor_block +}) + +@Composable +private fun PreviewPanel(fontSize: Int = 16) { + val shape = RoundedCornerShape(14.dp) + Column(Modifier.padding(top = 16.dp).fillMaxWidth().clip(shape) + .background(MaterialTheme.colorScheme.background).border(.5.dp, MaterialTheme.colorScheme.outlineVariant, shape).padding(18.dp)) { + HelperText(stringResource(R.string.theme_preview)) + Text(stringResource(R.string.font_preview), fontSize = fontSize.sp, fontFamily = LocalTerminalFont.current, + modifier = Modifier.padding(top = 18.dp)) + HelperText(stringResource(R.string.terminal_solid_background), Modifier.padding(top = 14.dp)) + } +} + +@Composable +private fun ThemeChoices(repository: SessionRepository) { + val catalog = (LocalContext.current.applicationContext as PebrelApplication).themes + val choice by repository.theme.collectAsStateWithLifecycle() + fun JSONObject.swatch(role: String): Color = getJSONArray(role).let { Color(it.getInt(0), it.getInt(1), it.getInt(2)) } + PreviewPanel() + SettingsGroup(stringResource(R.string.theme_settings)) { + SettingsChoice(stringResource(R.string.follow_system), choice == "system") { repository.selectTheme("system") } + } + listOf(false, true).forEach { dark -> + val names = catalog.keys().asSequence().toList().filter { + (catalog.getJSONObject(it).swatch("background").luminance() < .5f) == dark + } + SettingsGroup(stringResource(if (dark) R.string.dark_themes else R.string.light_themes)) { + names.forEachIndexed { index, name -> + if (index > 0) SettingDivider() + Row(Modifier.fillMaxWidth().clickable { repository.selectTheme(name) }.heightIn(min = 58.dp).padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(5.dp)) { + Text(name, fontSize = 14.sp, modifier = Modifier.weight(1f)) + listOf("background", "foreground", "red", "green", "blue").forEach { role -> + Box(Modifier.size(12.dp).background(catalog.getJSONObject(name).swatch(role), RoundedCornerShape(2.dp))) + } + Box(Modifier.padding(start = 8.dp).size(20.dp)) { if (name == choice) Glyph(R.drawable.ic_check) } + } + } + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SftpBrowserScreen.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SftpBrowserScreen.kt new file mode 100644 index 0000000..7b7a17a --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SftpBrowserScreen.kt @@ -0,0 +1,287 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.net.Uri +import android.provider.OpenableColumns +import android.text.format.Formatter +import androidx.activity.compose.BackHandler +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.session.LocalSession +import io.github.kuddev.pebrel.ssh.NativeSshException +import kotlinx.coroutines.* + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun SftpBrowserScreen(session: LocalSession, initialPath: String, onPath: (String) -> Unit, + onBack: () -> Unit, onFile: (SftpEntry) -> Unit) { + val client = checkNotNull(session.files) + val context = LocalContext.current + val scope = rememberCoroutineScope() + var requested by rememberSaveable(session.id) { mutableStateOf(initialPath) } + var location by rememberSaveable(session.id) { mutableStateOf(initialPath) } + var history by rememberSaveable(session.id) { mutableStateOf(listOf()) } + var rows by remember(session.id) { mutableStateOf(emptyList()) } + var cursor by remember(session.id) { mutableStateOf(null) } + var loading by remember { mutableStateOf(false) } + var failure by remember { mutableStateOf(null) } + var notice by remember { mutableStateOf(null) } + var refresh by remember { mutableIntStateOf(0) } + var showHidden by rememberSaveable { mutableStateOf(false) } + var menu by remember { mutableStateOf(false) } + var addMenu by remember { mutableStateOf(false) } + var naming by remember { mutableStateOf(null) } + var selected by remember { mutableStateOf(null) } + var deleting by remember { mutableStateOf(null) } + var actionBusy by remember { mutableStateOf(false) } + var actionError by remember { mutableStateOf(null) } + var uploading by remember { mutableStateOf(null) } + var cancellingUpload by remember { mutableStateOf(false) } + var paging by remember { mutableStateOf(null) } + var listingGeneration by remember { mutableIntStateOf(0) } + var uploadUri by remember { mutableStateOf(null) } + var uploadName by remember { mutableStateOf("") } + var uploadSize by remember { mutableStateOf(null) } + var transferred by remember { mutableLongStateOf(0L) } + var transferTotal by remember { mutableStateOf(null) } + val ready = session.status == "ready" + val listState = rememberLazyListState() + val pickUpload = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> + if (uri != null) scope.launch { + try { + val metadata = withContext(Dispatchers.IO) { + context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE), null, null, null)?.use { result -> + if (result.moveToFirst()) result.getString(0) to (if (result.isNull(1)) null else result.getLong(1).takeIf { it >= 0 }) else null + } + } + uploadUri = uri + uploadName = metadata?.first?.takeIf { runCatching { sftpChild(location, it) }.isSuccess } ?: "upload.bin" + uploadSize = metadata?.second + naming = "upload" + } catch (cancelled: CancellationException) { throw cancelled } + catch (_: Exception) { failure = "SFTP_LOCAL_FILE" } + } + } + fun navigate(path: String) { + notice = null + if (path == location) { refresh++; return } + history = history + location + requested = path + } + fun back() { + if (history.isEmpty()) onBack() + else { requested = history.last(); history = history.dropLast(1) } + } + BackHandler { back() } + LaunchedEffect(naming) { actionError = null; if (naming != null) notice = null } + LaunchedEffect(requested, refresh, session.status) { + val generation = ++listingGeneration + paging?.cancel(); paging = null + if (!ready) return@LaunchedEffect + cursor = null + loading = true; failure = null + try { + val result = client.list(requested) + ensureActive() + rows = result.entries; cursor = result.cursor; location = result.path + onPath(result.path) + if (result.skipped > 0) notice = R.string.sftp_skipped + listState.scrollToItem(0) + } catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = sftpFailureCode(error) } + finally { if (generation == listingGeneration) loading = false } + } + LaunchedEffect(client) { + try { awaitCancellation() } + finally { + withContext(NonCancellable) { + paging?.cancelAndJoin() + uploading?.cancelAndJoin() + cursor?.let { id -> runCatching { client.closeList(id) } } + } + } + } + fun open(entry: SftpEntry) { + if (entry.directory) navigate(entry.path) + else if (entry.kind == "symlink") scope.launch { + try { val resolved = client.stat(entry.path); if (resolved.directory) navigate(resolved.path) else onFile(resolved) } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = sftpFailureCode(error) } + } else onFile(entry) + } + Column(Modifier.fillMaxSize()) { + Row(Modifier.fillMaxWidth().heightIn(min = 48.dp), verticalAlignment = Alignment.CenterVertically) { + GlyphButton(R.drawable.ic_back, stringResource(R.string.back), ::back) + Column(Modifier.weight(1f).clickable { naming = "path" }.padding(vertical = 6.dp)) { + Text(location.trimEnd('/').substringAfterLast('/').ifBlank { "/" }, maxLines = 1, overflow = TextOverflow.Ellipsis, fontSize = 14.sp) + Text("SFTP · ${session.host?.name ?: session.title}", color = MaterialTheme.colorScheme.onSurfaceVariant, fontSize = 11.sp, maxLines = 1) + } + Box { + GlyphButton(R.drawable.ic_plus, stringResource(R.string.sftp_add), { addMenu = true }, ready && uploading == null) + DropdownMenu(addMenu, { addMenu = false }) { + DropdownMenuItem({ Text(stringResource(R.string.sftp_upload)) }, { addMenu = false; pickUpload.launch(arrayOf("*/*")) }) + DropdownMenuItem({ Text(stringResource(R.string.sftp_new_folder)) }, { addMenu = false; naming = "mkdir" }) + } + } + Box { + GlyphButton(R.drawable.ic_more, stringResource(R.string.more_actions), { menu = true }) + DropdownMenu(menu, { menu = false }) { + DropdownMenuItem({ Text(stringResource(R.string.sftp_refresh)) }, { menu = false; notice = null; refresh++ }, enabled = ready && !loading) + DropdownMenuItem({ Text(stringResource(R.string.sftp_go_path)) }, { menu = false; naming = "path" }) + DropdownMenuItem({ Text(stringResource(if (showHidden) R.string.sftp_hide_hidden else R.string.sftp_show_hidden)) }, { menu = false; showHidden = !showHidden }) + DropdownMenuItem({ Text(stringResource(R.string.chat_terminal)) }, { menu = false; onBack() }) + } + } + } + if (!ready) HelperText(stringResource(R.string.sftp_disconnected), Modifier.padding(16.dp)) + if (loading) LinearProgressIndicator(Modifier.fillMaxWidth()) + if (uploading != null) SftpTransferProgress(transferred, transferTotal, cancellingUpload) { + cancellingUpload = true; uploading?.cancel() + } + notice?.let { HelperText(stringResource(it), Modifier.padding(horizontal = 16.dp, vertical = 6.dp)) } + failure?.let { SftpFailureRow(it, if (ready && !loading) ({ notice = null; refresh++ }) else null) } + val visible = remember(rows, showHidden) { + rows.filter { showHidden || !it.name.startsWith('.') }.sortedWith(compareBy { !it.directory }.thenBy { it.name.lowercase() }) + } + LazyColumn(Modifier.weight(1f).fillMaxWidth(), state = listState, contentPadding = PaddingValues(horizontal = 16.dp, vertical = 4.dp)) { + if (location != "/" && location != ".") item { + NavigationRow(R.drawable.ic_up, "..", stringResource(R.string.sftp_parent)) { navigate(sftpParent(location)) } + } + items(visible, key = { it.path }) { entry -> + Row(Modifier.fillMaxWidth().heightIn(min = 60.dp) + .combinedClickable(enabled = ready && !loading, onClick = { open(entry) }, onLongClick = { selected = entry }), + verticalAlignment = Alignment.CenterVertically) { + Glyph(if (entry.directory) R.drawable.ic_git_folder else if (isSftpImage(entry.path)) R.drawable.ic_image else R.drawable.ic_git_file, + Modifier.padding(end = 12.dp).size(20.dp)) + Column(Modifier.weight(1f).padding(vertical = 8.dp)) { + Text(entry.name, fontSize = 14.sp, maxLines = 2, overflow = TextOverflow.Ellipsis) + val detail = when { + entry.directory -> stringResource(R.string.sftp_folder) + entry.kind == "symlink" -> stringResource(R.string.sftp_symlink) + entry.size != null -> Formatter.formatShortFileSize(context, entry.size) + else -> stringResource(R.string.sftp_file) + } + HelperText(detail) + } + GlyphButton(R.drawable.ic_more, stringResource(R.string.sftp_file_actions, entry.name), { selected = entry }, ready) + } + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) + } + if (visible.isEmpty() && !loading && failure == null) item { + HelperText(stringResource(R.string.sftp_empty), Modifier.padding(vertical = 28.dp)) + } + if (cursor != null) item { + TextButton({ + if (loading) return@TextButton + loading = true; failure = null + val generation = listingGeneration + val path = location + val ownedCursor = cursor + paging = scope.launch { + try { + val result = client.list(path, ownedCursor) + if (generation != listingGeneration || result.path != location) return@launch + if (rows.size + result.entries.size > 8192) throw NativeSshException("SFTP_DIRECTORY_LIMIT") + rows = (rows + result.entries).distinctBy { it.path }; cursor = result.cursor + } catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { + failure = sftpFailureCode(error) + // 闲置游标已由 SSH 层回收,继续提交同一游标只会重复失败;保留列表供刷新恢复。 + if (failure == "SFTP_STALE") cursor = null + } + finally { if (generation == listingGeneration) { loading = false; paging = null } } + } + }, Modifier.fillMaxWidth(), enabled = !loading && ready) { Text(stringResource(R.string.sftp_more)) } + } + } + } + selected?.takeIf { naming != "rename" }?.let { entry -> AlertDialog(onDismissRequest = { selected = null }, title = { Text(entry.name) }, + text = { Column { + TextButton({ selected = null; open(entry) }, Modifier.fillMaxWidth()) { Text(stringResource(R.string.sftp_open)) } + TextButton({ naming = "rename" }, Modifier.fillMaxWidth()) { Text(stringResource(R.string.sftp_rename)) } + TextButton({ selected = null; deleting = entry }, Modifier.fillMaxWidth()) { Text(stringResource(R.string.sftp_delete), color = MaterialTheme.colorScheme.error) } + } }, confirmButton = { TextButton({ selected = null }) { Text(stringResource(R.string.close)) } }) } + naming?.let { action -> + val title = stringResource(when (action) { "path" -> R.string.sftp_go_path; "mkdir" -> R.string.sftp_new_folder; "rename" -> R.string.sftp_rename; else -> R.string.sftp_upload }) + SftpNameDialog(title, when (action) { "path" -> location; "rename" -> selected?.name.orEmpty(); "upload" -> uploadName; else -> "" }, actionBusy, actionError, + { naming = null; if (action == "rename") selected = null }, { name -> + if (action == "path") { naming = null; navigate(name); return@SftpNameDialog } + val path = runCatching { sftpChild(location, name) }.getOrElse { actionError = "INVALID_INPUT"; return@SftpNameDialog } + if (action == "upload") { + val uri = uploadUri ?: return@SftpNameDialog + naming = null; transferred = 0; transferTotal = uploadSize; notice = null; failure = null; cancellingUpload = false + uploading = scope.launch { + try { + withContext(Dispatchers.IO) { + checkNotNull(context.contentResolver.openInputStream(uri)).use { stream -> + client.upload(path, stream, uploadSize) { count, total -> transferred = count; transferTotal = total } + } + } + notice = R.string.sftp_uploaded; refresh++ + } catch (cancelled: CancellationException) { notice = R.string.sftp_cancelled; throw cancelled } + catch (error: Exception) { failure = sftpFailureCode(error) } + finally { uploading = null; uploadUri = null; cancellingUpload = false } + } + } else { + actionBusy = true; actionError = null + scope.launch { + try { + if (action == "mkdir") client.mkdir(path) else client.rename(checkNotNull(selected), path) + naming = null; selected = null; refresh++ + } catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { actionError = sftpFailureCode(error) } + finally { actionBusy = false } + } + } + }) + } + deleting?.let { entry -> SftpDeleteDialog(entry, client, { deleting = null }) { deleting = null; refresh++ } } +} + +@Composable +internal fun SftpNameDialog(title: String, initial: String, busy: Boolean, error: String?, onDismiss: () -> Unit, onSubmit: (String) -> Unit) { + var name by remember(title, initial) { mutableStateOf(initial) } + AlertDialog(onDismissRequest = { if (!busy) onDismiss() }, title = { Text(title) }, + text = { Column { + OutlinedTextField(name, { name = it }, singleLine = true, enabled = !busy, label = { Text(title) }, isError = error != null, + supportingText = error?.let { { Text(sftpFailureText(it)) } }) + if (busy) LinearProgressIndicator(Modifier.fillMaxWidth()) + } }, + confirmButton = { TextButton({ onSubmit(name) }, enabled = name.isNotBlank() && !busy) { Text(stringResource(R.string.sftp_confirm)) } }, + dismissButton = { TextButton(onDismiss, enabled = !busy) { Text(stringResource(R.string.cancel)) } }) +} + +@Composable +private fun SftpDeleteDialog(entry: SftpEntry, client: SftpClient, onDismiss: () -> Unit, onDeleted: () -> Unit) { + val scope = rememberCoroutineScope() + var busy by remember { mutableStateOf(false) } + var error by remember { mutableStateOf(null) } + AlertDialog(onDismissRequest = { if (!busy) onDismiss() }, title = { Text(stringResource(R.string.sftp_delete)) }, + text = { Column { Text(stringResource(R.string.sftp_delete_confirm, entry.name)); error?.let { Text(sftpFailureText(it), color = MaterialTheme.colorScheme.error) } } }, + confirmButton = { TextButton({ + busy = true + scope.launch { + try { client.remove(entry); onDeleted() } + catch (cancelled: CancellationException) { throw cancelled } + catch (failure: Exception) { error = sftpFailureCode(failure) } + finally { busy = false } + } + }, enabled = !busy) { Text(stringResource(R.string.sftp_delete), color = MaterialTheme.colorScheme.error) } }, + dismissButton = { TextButton(onDismiss, enabled = !busy) { Text(stringResource(R.string.cancel)) } }) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SftpFileScreen.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SftpFileScreen.kt new file mode 100644 index 0000000..34a2ea6 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SftpFileScreen.kt @@ -0,0 +1,186 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.content.Intent +import android.net.Uri +import android.provider.DocumentsContract +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.session.LocalSession +import io.github.kuddev.pebrel.ssh.NativeSshException +import kotlinx.coroutines.* +import java.nio.ByteBuffer +import java.nio.charset.CodingErrorAction + +@Composable +fun SftpFileScreen(session: LocalSession, tab: SftpTab, onBack: () -> Unit, onTabs: () -> Unit, + onOpen: (SftpEntry) -> Unit, onClose: () -> Unit) { + val client = checkNotNull(session.files) + val context = LocalContext.current + val scope = rememberCoroutineScope() + var content by remember(tab.id) { mutableStateOf(null) } + var failure by remember(tab.id) { mutableStateOf(null) } + var notice by remember(tab.id) { mutableStateOf(null) } + var loading by remember(tab.id) { mutableStateOf(false) } + var refresh by remember(tab.id) { mutableIntStateOf(0) } + var preview by rememberSaveable(tab.id) { mutableStateOf(true) } + var menu by remember { mutableStateOf(false) } + var details by remember { mutableStateOf(false) } + var outline by remember { mutableStateOf(false) } + var heading by remember { mutableStateOf(null) } + var imageReset by remember { mutableIntStateOf(0) } + var download by remember(tab.id) { mutableStateOf(null) } + var cancelling by remember { mutableStateOf(false) } + var transferred by remember { mutableLongStateOf(0) } + var transferTotal by remember { mutableStateOf(null) } + val ready = session.status == "ready" + val file = content?.file ?: tab.file + val image = isSftpImage(file.path) + val markdown = file.path.substringAfterLast('.').lowercase() in setOf("md", "markdown") + val clipboard = rememberReaderClipboard(file.name) { failure = "clipboard_failed" } + val copyLabel = stringResource(R.string.reader_copy) + val copiedLabel = stringResource(R.string.reader_copied) + val imageLabel = stringResource(R.string.reader_open_image) + val colors = MaterialTheme.colorScheme.readerColors() + LaunchedEffect(client, tab.id, session.status, refresh) { + if (!ready) return@LaunchedEffect + loading = true; failure = null + try { content = client.preview(tab.file.path) } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = sftpFailureCode(error) } + finally { loading = false } + } + LaunchedEffect(heading) { if (heading != null) { delay(200); heading = null } } + val text by produceState(null, content, image) { + value = null + val bytes = content?.bytes + if (bytes != null && !image) { + try { value = withContext(Dispatchers.Default) { + if (bytes.any { it == 0.toByte() }) throw NativeSshException("SFTP_BINARY") + Charsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPORT).decode(ByteBuffer.wrap(bytes)).toString() + } } + catch (cancelled: CancellationException) { throw cancelled } + catch (_: Exception) { failure = "SFTP_BINARY" } + } + } + val document by produceState(null, text, preview, colors, copyLabel, imageLabel) { + value = text?.let { source -> withContext(Dispatchers.Default) { prepareReader(source, file.path, markdown && preview, colors, copyLabel, imageLabel) } } + } + val bitmap by produceState(null, content, image) { + value = null + val bytes = content?.bytes + if (bytes != null && image) { + try { value = withContext(Dispatchers.IO) { decodeReaderImage(bytes) } } + catch (cancelled: CancellationException) { throw cancelled } + catch (_: Exception) { failure = "image_decode_failed" } + } + } + val saveDocument = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/octet-stream")) { uri -> + if (uri != null) { + transferred = 0; transferTotal = file.size; cancelling = false; failure = null; notice = null + download = scope.launch { + var complete = false + try { + withContext(Dispatchers.IO) { + checkNotNull(context.contentResolver.openOutputStream(uri, "w")).use { output -> + client.download(file.path, output) { count, total -> transferred = count; transferTotal = total } + } + } + complete = true; notice = R.string.sftp_downloaded + } catch (cancelled: CancellationException) { notice = R.string.sftp_cancelled; throw cancelled } + catch (error: Exception) { failure = sftpFailureCode(error) } + finally { + // 只清理这次 CreateDocument 新建的未完成文件,不触碰其他本地文件。 + if (!complete) withContext(NonCancellable + Dispatchers.IO) { + runCatching { DocumentsContract.deleteDocument(context.contentResolver, uri) } + } + download = null; cancelling = false + } + } + } + } + fun openLink(link: String) { + if (link.startsWith('#')) { heading = link; return } + val uri = Uri.parse(link) + if (uri.scheme in setOf("https", "http", "mailto")) { + try { context.startActivity(Intent(Intent.ACTION_VIEW, uri)) } + catch (_: Exception) { failure = "link_open_failed" } + return + } + val path = resolveSftpLink(file.path, link) ?: run { failure = "link_open_failed"; return } + scope.launch { + try { + val entry = client.stat(path) + if (entry.directory) failure = "SFTP_FILE_TYPE" else onOpen(entry) + } catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = sftpFailureCode(error) } + } + } + Column(Modifier.fillMaxSize()) { + Row(Modifier.fillMaxWidth().heightIn(min = 48.dp), verticalAlignment = Alignment.CenterVertically) { + GlyphButton(R.drawable.ic_back, stringResource(R.string.back), onBack) + Row(Modifier.weight(1f).heightIn(min = 48.dp).clickable(onClick = onTabs), verticalAlignment = Alignment.CenterVertically) { + Glyph(if (image) R.drawable.ic_image else R.drawable.ic_git_file, Modifier.size(18.dp)) + Text(file.name, Modifier.weight(1f).padding(horizontal = 8.dp), maxLines = 1, overflow = TextOverflow.Ellipsis, fontSize = 14.sp) + Glyph(R.drawable.ic_down, Modifier.size(12.dp)) + } + Box { + GlyphButton(R.drawable.ic_more, stringResource(R.string.more_actions), { menu = true }) + DropdownMenu(menu, { menu = false }) { + DropdownMenuItem({ Text(stringResource(R.string.reader_refresh)) }, { menu = false; refresh++ }, enabled = ready && !loading) + DropdownMenuItem({ Text(stringResource(R.string.sftp_download)) }, { menu = false; saveDocument.launch(file.name) }, enabled = ready && download == null) + if (markdown && preview) DropdownMenuItem({ Text(stringResource(R.string.reader_outline)) }, { menu = false; outline = true }, enabled = !document?.headings.isNullOrEmpty()) + if (text != null) DropdownMenuItem({ Text(stringResource(R.string.reader_copy_source)) }, { menu = false; text?.let { clipboard.copy(it, -1) } }) + if (image) DropdownMenuItem({ Text(stringResource(R.string.reader_reset_zoom)) }, { menu = false; imageReset++ }) + DropdownMenuItem({ Text(stringResource(R.string.reader_details)) }, { menu = false; details = true }) + DropdownMenuItem({ Text(stringResource(R.string.tab_close)) }, { menu = false; onClose() }) + } + } + } + if (markdown) TabRow(if (preview) 0 else 1, containerColor = MaterialTheme.colorScheme.background) { + Tab(preview, { preview = true }, text = { Text(stringResource(R.string.reader_preview)) }) + Tab(!preview, { preview = false }, text = { Text(stringResource(R.string.reader_source)) }) + } + if (!ready) HelperText(stringResource(R.string.sftp_disconnected), Modifier.padding(horizontal = 16.dp, vertical = 6.dp)) + if (clipboard.copied == -1) HelperText(copiedLabel, Modifier.padding(horizontal = 16.dp, vertical = 6.dp)) + notice?.let { HelperText(stringResource(it), Modifier.padding(horizontal = 16.dp, vertical = 6.dp)) } + if (loading) LinearProgressIndicator(Modifier.fillMaxWidth()) + if (download != null) SftpTransferProgress(transferred, transferTotal, cancelling) { cancelling = true; download?.cancel() } + failure?.let { code -> SftpFailureRow(code, if (ready && !loading) ({ refresh++ }) else null) } + Box(Modifier.weight(1f).fillMaxWidth()) { + val prepared = document + val loadedImage = bitmap + when { + prepared != null -> ReaderWebView(prepared, colors, clipboard.copied, copyLabel, copiedLabel, heading, + { clipboard.copy(prepared.code[it], it) }, ::openLink, Modifier.fillMaxSize()) + loadedImage != null -> ReaderImage(loadedImage, file.name, imageReset) + !loading && failure != null -> TextButton({ saveDocument.launch(file.name) }, Modifier.align(Alignment.Center), enabled = ready && download == null) { + Text(stringResource(R.string.sftp_download)) + } + } + } + } + if (details) AlertDialog(onDismissRequest = { details = false }, title = { Text(file.name) }, + text = { androidx.compose.foundation.text.selection.SelectionContainer { Text("${session.host?.name.orEmpty()}\n${file.path}") } }, + confirmButton = { TextButton({ details = false }) { Text(stringResource(R.string.close)) } }) + if (outline) AlertDialog(onDismissRequest = { outline = false }, title = { Text(stringResource(R.string.reader_outline)) }, + text = { Column(Modifier.verticalScroll(rememberScrollState())) { + document?.headings?.forEach { item -> TextButton({ heading = item.id; outline = false }, Modifier.fillMaxWidth().padding(start = ((item.level - 1) * 10).dp)) { + Text(item.text, Modifier.fillMaxWidth()) + } } + } }, confirmButton = { TextButton({ outline = false }) { Text(stringResource(R.string.close)) } }) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SftpPresentation.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SftpPresentation.kt new file mode 100644 index 0000000..3f52f0b --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SftpPresentation.kt @@ -0,0 +1,100 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.net.Uri +import android.text.format.Formatter +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.text.style.TextOverflow +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.SftpTab +import io.github.kuddev.pebrel.ssh.NativeSshException + +@Composable +internal fun SftpTabRow(tab: SftpTab, host: String, selected: Boolean, onOpen: () -> Unit, onClose: () -> Unit) { + Row(Modifier.fillMaxWidth().heightIn(min = 56.dp) + .background(if (selected) MaterialTheme.colorScheme.surfaceVariant else MaterialTheme.colorScheme.surface), verticalAlignment = Alignment.CenterVertically) { + Row(Modifier.weight(1f).heightIn(min = 56.dp).clickable(onClick = onOpen).padding(start = 12.dp), verticalAlignment = Alignment.CenterVertically) { + Glyph(if (isSftpImage(tab.file.path)) R.drawable.ic_image else R.drawable.ic_git_file, Modifier.size(18.dp)) + Column(Modifier.weight(1f).padding(horizontal = 12.dp, vertical = 8.dp)) { + Text(tab.file.name, fontSize = 14.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) + Text("SFTP · $host", color = MaterialTheme.colorScheme.onSurfaceVariant, fontSize = 12.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) + } + } + GlyphButton(R.drawable.ic_close, stringResource(R.string.tab_close_named, tab.file.name), onClose) + } +} + +@Composable +internal fun SftpTransferProgress(count: Long, total: Long?, cancelling: Boolean = false, onCancel: () -> Unit) { + val context = LocalContext.current + Column(Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 4.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { + val amount = Formatter.formatShortFileSize(context, count) + HelperText(if (cancelling) stringResource(R.string.sftp_cancelling) + else if (total != null) "$amount / ${Formatter.formatShortFileSize(context, total)}" else amount, Modifier.weight(1f)) + TextButton(onCancel, enabled = !cancelling) { Text(stringResource(R.string.cancel)) } + } + if (total != null) LinearProgressIndicator(progress = { if (total == 0L) 1f else (count.toFloat() / total).coerceIn(0f, 1f) }, modifier = Modifier.fillMaxWidth()) + else LinearProgressIndicator(Modifier.fillMaxWidth()) + } +} + +@Composable +internal fun SftpFailureRow(code: String, onRetry: (() -> Unit)? = null) { + Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 6.dp), verticalAlignment = Alignment.CenterVertically) { + Text(sftpFailureText(code), Modifier.weight(1f), color = MaterialTheme.colorScheme.error, fontSize = 13.sp) + if (onRetry != null) TextButton(onRetry) { Text(stringResource(R.string.sftp_refresh)) } + } +} + +internal fun sftpFailureCode(error: Exception): String = when (error) { + is NativeSshException -> error.code + is IllegalArgumentException -> "INVALID_INPUT" + is java.io.IOException, is SecurityException -> "SFTP_LOCAL_FILE" + else -> "SFTP_OPERATION" +} + +@Composable +internal fun sftpFailureText(code: String): String = stringResource(when (code) { + "SFTP_NOT_FOUND" -> R.string.sftp_not_found + "SFTP_PERMISSION" -> R.string.sftp_permission + "SFTP_EXISTS" -> R.string.sftp_exists + "SFTP_UNSUPPORTED" -> R.string.sftp_unsupported + "SFTP_STALE", "SFTP_CHANGED" -> R.string.sftp_changed + "SFTP_CLOSED", "CLOSED", "NETWORK" -> R.string.sftp_disconnected + "SFTP_TIMEOUT", "TIMEOUT" -> R.string.sftp_timeout + "SFTP_LIMIT", "SFTP_DIRECTORY_LIMIT" -> R.string.sftp_limit + "SFTP_TOO_LARGE" -> R.string.sftp_too_large + "SFTP_FILE_TYPE", "SFTP_BINARY" -> R.string.sftp_binary + "SFTP_LOCAL_FILE" -> R.string.sftp_local_file + "INVALID_INPUT" -> R.string.sftp_invalid_path + "image_decode_failed" -> R.string.reader_image_failed + "clipboard_failed" -> R.string.reader_copy_failed + "link_open_failed" -> R.string.reader_link_failed + else -> R.string.sftp_operation_failed +}) + +internal fun isSftpImage(path: String): Boolean = path.substringAfterLast('.').lowercase() in setOf("png", "jpg", "jpeg", "webp", "gif", "bmp") + +internal fun resolveSftpLink(base: String, link: String): String? { + if (Uri.parse(link).scheme != null) return null + val path = Uri.decode(link.substringBefore('#')) + if (path.isBlank() || path.any(Char::isISOControl)) return null + val absolute = if (path.startsWith('/')) path else "${base.substringBeforeLast('/')}/$path" + val parts = mutableListOf() + for (part in absolute.split('/')) when (part) { + "", "." -> Unit + ".." -> if (parts.isNotEmpty()) parts.removeAt(parts.lastIndex) + else -> parts += part + } + return "/" + parts.joinToString("/") +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SharedStorageAccessCard.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SharedStorageAccessCard.kt new file mode 100644 index 0000000..9a18190 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SharedStorageAccessCard.kt @@ -0,0 +1,187 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.session.SharedStorageAccess +import java.io.File + +/** Inline gate for the local-terminal entry point; callers decide where to place it. */ +@Composable +fun LocalTerminalStorageCard(modifier: Modifier = Modifier, onReady: () -> Unit = {}) { + val context = LocalContext.current + var state by remember { mutableStateOf(SharedStorageAccess.state(context)) } + fun refresh() { + state = SharedStorageAccess.state(context) + if (state is SharedStorageAccess.State.Ready) onReady() + } + val runtimePermissionLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.RequestMultiplePermissions(), + ) { refresh() } + val settingsLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.StartActivityForResult(), + ) { refresh() } + + Column( + modifier.fillMaxWidth().workspaceFrame().padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(stringResource(R.string.shared_storage_title), fontSize = 16.sp) + when (val current = state) { + is SharedStorageAccess.State.Ready -> { + Text(stringResource(R.string.shared_storage_ready), color = MaterialTheme.colorScheme.primary, fontSize = 13.sp) + Text( + stringResource(R.string.shared_storage_location, current.directory.absolutePath), + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontSize = 12.sp, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + ) + } + is SharedStorageAccess.State.RuntimePermissionRequired -> { + HelperText(stringResource(R.string.shared_storage_required)) + HelperText(stringResource(R.string.shared_storage_explanation)) + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Button(onClick = { runtimePermissionLauncher.launch(current.permissions.toTypedArray()) }) { + Text(stringResource(R.string.shared_storage_grant)) + } + TextButton(onClick = { settingsLauncher.launch(SharedStorageAccess.appDetailsIntent(context)) }) { + Text(stringResource(R.string.shared_storage_open_settings)) + } + } + } + is SharedStorageAccess.State.AllFilesAccessRequired -> { + HelperText(stringResource(R.string.shared_storage_required)) + HelperText(stringResource(R.string.shared_storage_all_files_explanation)) + val intent = SharedStorageAccess.allFilesAccessIntent(context) + Button( + enabled = intent != null, + onClick = { intent?.let { settingsLauncher.launch(it) } }, + ) { Text(stringResource(R.string.shared_storage_grant)) } + } + is SharedStorageAccess.State.Unavailable -> { + val message = when (current.reason) { + SharedStorageAccess.Reason.MEDIA_UNAVAILABLE -> R.string.shared_storage_unavailable + SharedStorageAccess.Reason.DIRECTORY_NOT_WRITABLE, + SharedStorageAccess.Reason.DIRECTORY_CREATION_FAILED -> R.string.shared_storage_directory_error + } + HelperText(stringResource(message)) + TextButton(onClick = ::refresh) { Text(stringResource(R.string.shared_storage_check_again)) } + } + } + } +} + +/** + * Returns a click handler for a local terminal. The handler supplies a verified shared + * directory immediately, or opens the permission gate and waits for the user's decision. + */ +@Composable +fun rememberLocalTerminalLauncher(onReady: (File) -> Unit): () -> Unit { + val context = LocalContext.current + var gateVisible by remember { mutableStateOf(false) } + if (gateVisible) { + SharedStorageAccessDialog( + onReady = { directory -> + gateVisible = false + onReady(directory) + }, + onDismiss = { gateVisible = false }, + ) + } + return { + when (val current = SharedStorageAccess.state(context)) { + is SharedStorageAccess.State.Ready -> onReady(current.directory) + else -> gateVisible = true + } + } +} + +/** Permission gate used by [rememberLocalTerminalLauncher]; denied requests remain visible. */ +@Composable +fun SharedStorageAccessDialog(onReady: (File) -> Unit, onDismiss: () -> Unit) { + val context = LocalContext.current + var state by remember { mutableStateOf(SharedStorageAccess.state(context)) } + fun refresh() { + state = SharedStorageAccess.state(context) + } + val runtimePermissionLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.RequestMultiplePermissions(), + ) { refresh() } + val settingsLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.StartActivityForResult(), + ) { refresh() } + + LaunchedEffect(state) { + (state as? SharedStorageAccess.State.Ready)?.directory?.let(onReady) + } + val current = state + if (current !is SharedStorageAccess.State.Ready) AlertDialog( + onDismissRequest = onDismiss, + title = { Text(stringResource(R.string.shared_storage_title)) }, + text = { + Column(verticalArrangement = Arrangement.spacedBy(10.dp)) { + when (current) { + is SharedStorageAccess.State.RuntimePermissionRequired -> { + HelperText(stringResource(R.string.shared_storage_required)) + HelperText(stringResource(R.string.shared_storage_explanation)) + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { runtimePermissionLauncher.launch(current.permissions.toTypedArray()) }, + ) { Text(stringResource(R.string.shared_storage_grant)) } + TextButton( + modifier = Modifier.fillMaxWidth(), + onClick = { settingsLauncher.launch(SharedStorageAccess.appDetailsIntent(context)) }, + ) { Text(stringResource(R.string.shared_storage_open_settings)) } + } + is SharedStorageAccess.State.AllFilesAccessRequired -> { + HelperText(stringResource(R.string.shared_storage_required)) + HelperText(stringResource(R.string.shared_storage_all_files_explanation)) + val intent = SharedStorageAccess.allFilesAccessIntent(context) + Button( + modifier = Modifier.fillMaxWidth(), + enabled = intent != null, + onClick = { intent?.let { settingsLauncher.launch(it) } }, + ) { Text(stringResource(R.string.shared_storage_grant)) } + } + is SharedStorageAccess.State.Unavailable -> { + val message = when (current.reason) { + SharedStorageAccess.Reason.MEDIA_UNAVAILABLE -> R.string.shared_storage_unavailable + SharedStorageAccess.Reason.DIRECTORY_NOT_WRITABLE, + SharedStorageAccess.Reason.DIRECTORY_CREATION_FAILED -> R.string.shared_storage_directory_error + } + HelperText(stringResource(message)) + TextButton( + modifier = Modifier.fillMaxWidth(), + onClick = ::refresh, + ) { Text(stringResource(R.string.shared_storage_check_again)) } + } + is SharedStorageAccess.State.Ready -> Unit + } + } + }, + confirmButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) } }, + ) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ShortCodePairing.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ShortCodePairing.kt new file mode 100644 index 0000000..43a8fdd --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/ShortCodePairing.kt @@ -0,0 +1,139 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.foundation.border +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsFocusedAsState +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.PairingCodeLookup +import io.github.kuddev.pebrel.mobile.connection.PairingCodeRejected +import io.github.kuddev.pebrel.mobile.connection.PairingDiscovery +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +@Composable +internal fun ShortCodePairing(onConnect: (String) -> Unit) { + val context = LocalContext.current + var attempt by remember { mutableIntStateOf(0) } + val discovery = remember(attempt) { PairingDiscovery(context) } + val state by discovery.state.collectAsState() + var selected by remember { mutableStateOf(null) } + var code by remember { mutableStateOf("") } + var busy by remember { mutableStateOf(false) } + var waited by remember(attempt) { mutableStateOf(false) } + var error by remember { mutableStateOf(null) } + val scope = rememberCoroutineScope() + var request by remember { mutableStateOf(null) } + val connect by rememberUpdatedState(onConnect) + DisposableEffect(discovery) { + discovery.start() + onDispose { discovery.close() } + } + LaunchedEffect(discovery) { delay(8_000); waited = true } + LaunchedEffect(state.computers) { + if (state.computers.none { it.id == selected }) selected = state.computers.singleOrNull()?.id + } + Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp)) { + Column(Modifier.fillMaxWidth().workspaceFrame().selectableGroup()) { + for ((index, computer) in state.computers.withIndex()) { + if (index > 0) HorizontalDivider(Modifier.padding(start = 16.dp), color = MaterialTheme.colorScheme.outlineVariant) + Row(Modifier.fillMaxWidth().heightIn(min = 56.dp) + .selectable(selected == computer.id, enabled = !busy, role = Role.RadioButton, + onClick = { selected = computer.id; error = null }) + .padding(horizontal = 16.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically) { + RadioButton(selected == computer.id, onClick = null, enabled = !busy) + Column(Modifier.weight(1f).padding(start = 10.dp)) { + Text(computer.name, fontSize = 15.sp, lineHeight = 21.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) + Text("${computer.address.host} · ${stringResource(R.string.pair_lan)}", fontSize = 12.sp, lineHeight = 18.sp, + fontFamily = LocalTerminalFont.current, color = MaterialTheme.colorScheme.onSurfaceVariant) + } + } + } + if (state.computers.isEmpty()) { + HelperText(stringResource(if (state.failed || waited) R.string.pair_discovery_empty else R.string.pair_discovery_searching), + Modifier.padding(16.dp)) + } + } + HelperText(stringResource(R.string.pair_code_network_hint), Modifier.padding(horizontal = 2.dp)) + if (!busy) TextButton({ attempt++; selected = null; error = null }, + modifier = Modifier.heightIn(min = 48.dp)) { Text(stringResource(R.string.pair_discovery_retry)) } + PairingCodeField(code, { code = it; error = null }, enabled = !busy, isError = error == R.string.pair_code_rejected) + Text(stringResource(R.string.pair_code_digits_hint), fontSize = 12.sp, lineHeight = 20.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.align(Alignment.CenterHorizontally)) + error?.let { Text(stringResource(it), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.error) } + val selectedComputer = state.computers.firstOrNull { it.id == selected } + val connectLabel = when { + busy -> stringResource(R.string.pair_code_connecting) + selectedComputer != null -> stringResource(R.string.pair_code_connect_computer, selectedComputer.name) + else -> stringResource(R.string.pair_code_connect) + } + ConnectionButton(connectLabel, + enabled = !busy && code.length == 8 && state.computers.any { it.id == selected }) { + val computer = state.computers.firstOrNull { it.id == selected } ?: return@ConnectionButton + busy = true + error = null + request = scope.launch { + try { connect(PairingCodeLookup.redeem(computer, code).toJson().toString()) } + catch (cancelled: CancellationException) { throw cancelled } + catch (_: PairingCodeRejected) { error = R.string.pair_code_rejected } + catch (_: Exception) { error = R.string.pair_code_failed } + finally { busy = false } + } + } + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { + if (busy) TextButton({ request?.cancel() }) { Text(stringResource(R.string.cancel)) } + } + } +} + +@Composable +internal fun PairingCodeField(value: String, onChange: (String) -> Unit, enabled: Boolean = true, isError: Boolean = false) { + val interactions = remember { MutableInteractionSource() } + val focused by interactions.collectIsFocusedAsState() + val colors = MaterialTheme.colorScheme + val label = stringResource(R.string.pair_short_code) + BasicTextField(value, { onChange(it.filter { digit -> digit in '0'..'9' }.take(8)) }, enabled = enabled, + singleLine = true, interactionSource = interactions, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp).semantics { contentDescription = label }, + decorationBox = { field -> + Box { + // 保留一个真实输入框负责粘贴、选择和无障碍,不创建八个互相抢焦点的字段。 + Box(Modifier.matchParentSize().alpha(0f)) { field() } + Row(Modifier.fillMaxWidth().clearAndSetSemantics { }, verticalAlignment = Alignment.CenterVertically) { + repeat(8) { index -> + if (index > 0) Spacer(Modifier.width(if (index == 4) 12.dp else 6.dp)) + val active = focused && index == value.length.coerceAtMost(7) + Box(Modifier.weight(1f).heightIn(min = 46.dp) + .border(1.dp, when { isError -> colors.error; active -> colors.primary; else -> colors.outlineVariant }, RoundedCornerShape(10.dp)) + .padding(vertical = 8.dp), contentAlignment = Alignment.Center) { + Text(value.getOrNull(index)?.toString().orEmpty(), fontSize = 19.sp, lineHeight = 26.sp, + fontFamily = LocalTerminalFont.current, color = if (enabled) colors.onSurface else colors.onSurfaceVariant) + } + } + } + } + }) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SshConnectionStatus.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SshConnectionStatus.kt new file mode 100644 index 0000000..7d0cc4a --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/SshConnectionStatus.kt @@ -0,0 +1,102 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.* +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.runtime.remember +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.SshFailureKind +import io.github.kuddev.pebrel.mobile.connection.SshStage +import io.github.kuddev.pebrel.mobile.session.LocalSession +import io.github.kuddev.pebrel.mobile.session.TrustRequest + +@Composable +fun SshConnectionStatus(session: LocalSession, onCancel: () -> Unit, onRetry: () -> Unit, onEdit: () -> Unit, + trust: TrustRequest? = null, onTrust: (Boolean) -> Unit = {}) { + BackHandler(onBack = onCancel) + val connecting = session.status == "connecting" + Box(Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background.copy(alpha = .22f)) + .clickable(remember { MutableInteractionSource() }, indication = null) {}, contentAlignment = Alignment.Center) { + ElevatedCard(Modifier.padding(24.dp).widthIn(max = 420.dp).fillMaxWidth(), + colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surface), + elevation = CardDefaults.elevatedCardElevation(8.dp)) { + Column(Modifier.padding(20.dp).verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(14.dp)) { + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) { + if (connecting && trust == null) CircularProgressIndicator(Modifier.size(22.dp), strokeWidth = 2.dp) + else Glyph(if (trust != null) R.drawable.ic_server else R.drawable.ic_info, Modifier.size(23.dp)) + Column(Modifier.weight(1f)) { + Text(session.title, fontSize = 15.sp, fontWeight = FontWeight.Medium) + Text(stringResource(if (trust != null) R.string.verify_host else if (connecting) R.string.establishing_connection else R.string.connection_failed), + fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) + } + } + if (trust != null) { + HelperText("${trust.host.address}:${trust.host.port}") + HelperText(stringResource(R.string.verify_hint)) + SelectionContainer { + Text(trust.fingerprint, fontSize = 12.sp, lineHeight = 20.sp, fontFamily = LocalTerminalFont.current) + } + ConnectionButton(stringResource(R.string.trust_connect)) { onTrust(true) } + TextButton({ onTrust(false) }) { Text(stringResource(R.string.cancel)) } + } else { + Text(stringResource(if (connecting) stageText(session.stage) else failureText(session.failure)), + fontSize = 12.sp, lineHeight = 20.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.semantics { liveRegion = LiveRegionMode.Polite }) + if (connecting) TextButton(onCancel) { Text(stringResource(R.string.cancel)) } + else { + ConnectionButton(stringResource(R.string.retry), onClick = onRetry) + TextButton(onEdit) { Text(stringResource(R.string.back_edit)) } + } + } + } + } + } +} + +@Composable +fun TerminalDisconnected(session: LocalSession, onRetry: (() -> Unit)?) { + Surface(color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = .45f)) { + Column(Modifier.fillMaxWidth().padding(horizontal = 20.dp, vertical = 12.dp)) { + HelperText(stringResource(if (session.status == "ended") R.string.session_ended_hint else failureText(session.failure))) + onRetry?.let { retry -> TextButton(retry) { Text(stringResource(R.string.retry)) } } + } + } +} + +private fun stageText(stage: SshStage): Int = when (stage) { + SshStage.NETWORK -> R.string.ssh_stage_network + SshStage.VERIFYING -> R.string.ssh_stage_verifying + SshStage.AUTHENTICATING -> R.string.ssh_stage_auth + SshStage.OPENING_SHELL -> R.string.ssh_stage_shell +} + +private fun failureText(kind: SshFailureKind?): Int = when (kind) { + SshFailureKind.UNKNOWN_HOST -> R.string.ssh_error_dns + SshFailureKind.TIMEOUT -> R.string.ssh_error_timeout + SshFailureKind.REFUSED -> R.string.ssh_error_refused + SshFailureKind.AUTH -> R.string.ssh_error_auth + SshFailureKind.HOST_KEY_CHANGED -> R.string.ssh_error_host_key + SshFailureKind.TRUST_REJECTED -> R.string.ssh_error_trust + SshFailureKind.CHANNEL -> R.string.ssh_error_channel + SshFailureKind.NETWORK -> R.string.ssh_error_network + SshFailureKind.CRYPTO -> R.string.ssh_error_crypto + SshFailureKind.NEGOTIATION -> R.string.ssh_error_negotiation + else -> R.string.ssh_error_unknown +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalAttachmentFlow.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalAttachmentFlow.kt new file mode 100644 index 0000000..c22860d --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalAttachmentFlow.kt @@ -0,0 +1,157 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.content.ClipData +import android.content.ClipboardManager +import android.net.Uri +import android.widget.Toast +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.session.LocalSession +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import kotlinx.coroutines.* + +data class TerminalAttachmentAction(val pick: () -> Unit, val busy: Boolean) + +/** File selection is owned by the visible terminal; it never sends a shell command. */ +@Composable +fun rememberTerminalAttachmentAction(session: LocalSession, repository: SessionRepository, + onCompose: () -> Unit): TerminalAttachmentAction { + val context = LocalContext.current + val scope = rememberCoroutineScope() + val trust by repository.trust.collectAsStateWithLifecycle() + var selectedFor by remember { mutableStateOf(null) } + var waitingFile by remember(session.id) { mutableStateOf(null) } + var password by remember(session.id) { mutableStateOf("") } + var busy by remember(session.id) { mutableStateOf(false) } + var job by remember(session.id) { mutableStateOf(null) } + var owner by remember(session.id) { mutableStateOf(null) } + var failure by remember(session.id) { mutableStateOf(null) } + var uninsertedPath by remember(session.id) { mutableStateOf(null) } + val currentId by rememberUpdatedState(session.id) + val compose by rememberUpdatedState(onCompose) + DisposableEffect(session.id) { + onDispose { job?.cancel(); owner?.let(repository::endSshOperation) } + } + + fun start(uri: Uri, entered: CharArray? = null) { + if (busy || session.status != "ready") { entered?.fill('\u0000'); return } + val sessionId = session.id + val originalHost = session.host + val host = originalHost?.let { old -> repository.hosts.value.firstOrNull { + it.id == old.id && it.port == old.port && runCatching { + parseSshEndpoint(it.address, it.user) == parseSshEndpoint(old.address, old.user) + }.getOrDefault(false) + } ?: old } + busy = true + failure = null + job = scope.launch { + var secret = entered + var operation: String? = null + try { + if (host != null && secret == null) secret = repository.loadSavedPassword(host) + if (host != null && secret == null) { waitingFile = uri; return@launch } + val path = if (host == null) TerminalAttachments.importLocal(context, uri) else { + operation = repository.beginSshOperation() + owner = operation + TerminalAttachments.uploadSsh(context, uri, host, checkNotNull(secret)) { server, fingerprint -> + repository.verifySshOperation(checkNotNull(operation), server, fingerprint) + } + } + ensureActive() + if (currentId != sessionId || repository.sessions.value.none { it.id == sessionId && it.status == "ready" }) return@launch + val quoted = "'" + path.replace("'", "'\\''") + "'" + val previous = repository.drafts.value[sessionId].orEmpty() + val next = previous + (if (previous.isNotEmpty() && !previous.last().isWhitespace()) " " else "") + quoted + " " + if (next.length > 8192) uninsertedPath = path + else { + repository.setDraft(sessionId, next) + compose() + Toast.makeText(context, R.string.attachment_added, Toast.LENGTH_SHORT).show() + } + } catch (_: TimeoutCancellationException) { failure = R.string.ssh_error_timeout } + catch (cancelled: CancellationException) { throw cancelled } + catch (error: Exception) { failure = attachmentErrorText(error) } + finally { + secret?.fill('\u0000') + operation?.let(repository::endSshOperation) + owner = null + busy = false + } + } + } + + val picker = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> + val belongsTo = selectedFor + selectedFor = null + if (uri != null && belongsTo == session.id) start(uri) + } + if (waitingFile != null) AlertDialog(onDismissRequest = { waitingFile = null; password = "" }, + title = { Text(stringResource(R.string.attachment_auth_title)) }, + text = { + Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { + HelperText(stringResource(R.string.attachment_auth_hint)) + ConnectionField(password, { password = it }, R.string.password, keyboard = KeyboardType.Password, + transformation = PasswordVisualTransformation(), limit = 1024) + } + }, confirmButton = { + TextButton({ + val uri = waitingFile + val secret = password.toCharArray() + password = ""; waitingFile = null + if (uri != null) start(uri, secret) else secret.fill('\u0000') + }, enabled = password.isNotEmpty()) { Text(stringResource(R.string.attachment_upload)) } + }, dismissButton = { TextButton({ waitingFile = null; password = "" }) { Text(stringResource(R.string.cancel)) } }) + if (busy && (owner == null || trust?.ownerId != owner)) AlertDialog(onDismissRequest = { job?.cancel() }, + title = { Text(stringResource(if (session.host == null) R.string.attachment_importing else R.string.attachment_uploading)) }, + text = { LinearProgressIndicator(Modifier.fillMaxWidth()) }, + confirmButton = { TextButton({ job?.cancel() }) { Text(stringResource(R.string.cancel)) } }) + failure?.let { message -> AlertDialog(onDismissRequest = { failure = null }, + title = { Text(stringResource(R.string.attachment_failed)) }, text = { Text(stringResource(message)) }, + confirmButton = { TextButton({ failure = null }) { Text(stringResource(R.string.close)) } }) } + uninsertedPath?.let { path -> AlertDialog(onDismissRequest = { uninsertedPath = null }, + title = { Text(stringResource(R.string.attachment_draft_full)) }, + text = { SelectionContainer { Text(path, fontFamily = LocalTerminalFont.current) } }, + confirmButton = { TextButton({ + context.getSystemService(ClipboardManager::class.java).setPrimaryClip(ClipData.newPlainText("Pebrel", path)) + uninsertedPath = null + }) { Text(stringResource(R.string.attachment_copy_path)) } }, + dismissButton = { TextButton({ uninsertedPath = null }) { Text(stringResource(R.string.close)) } }) } + return TerminalAttachmentAction(pick = { + if (!busy && session.status == "ready") { + selectedFor = session.id + runCatching { picker.launch(arrayOf("*/*")) }.onFailure { selectedFor = null; failure = R.string.attachment_unreadable } + } + }, busy = busy) +} + +private fun attachmentErrorText(error: Exception): Int = when (error) { + is AttachmentException -> error.sshFailure?.let(::attachmentSshErrorText) ?: when (error.code) { + AttachmentError.TOO_LARGE -> R.string.attachment_too_large + AttachmentError.UNREADABLE -> R.string.attachment_unreadable + AttachmentError.STORAGE -> R.string.attachment_storage + AttachmentError.REMOTE_TOOLS -> R.string.attachment_remote_tools + else -> R.string.attachment_transfer_failed + } + else -> attachmentSshErrorText(classifySshFailure(error)) +} + +private fun attachmentSshErrorText(kind: SshFailureKind): Int = when (kind) { + SshFailureKind.AUTH -> R.string.ssh_error_auth + SshFailureKind.HOST_KEY_CHANGED -> R.string.ssh_error_host_key + SshFailureKind.TRUST_REJECTED -> R.string.ssh_error_trust + SshFailureKind.TIMEOUT -> R.string.ssh_error_timeout + else -> R.string.attachment_transfer_failed +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalScreen.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalScreen.kt new file mode 100644 index 0000000..1a814a0 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalScreen.kt @@ -0,0 +1,283 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.view.KeyEvent +import androidx.annotation.DrawableRes +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.repeatOnLifecycle +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.connection.DesktopPane +import io.github.kuddev.pebrel.mobile.connection.DesktopTab +import io.github.kuddev.pebrel.mobile.connection.DesktopReconnect +import io.github.kuddev.pebrel.mobile.connection.SshSessionMode +import io.github.kuddev.pebrel.mobile.session.DesktopWorkspace +import io.github.kuddev.pebrel.mobile.session.LocalSession +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import kotlinx.coroutines.delay + +@Composable +fun LocalTerminalScreen(session: LocalSession, repository: SessionRepository, onBack: () -> Unit, onSessions: () -> Unit, + onRetry: () -> Unit, onEdit: () -> Unit, onClose: () -> Unit, onFiles: (() -> Unit)? = null) { + val prefs by repository.display.state.collectAsStateWithLifecycle() + var direct by rememberSaveable(session.id, prefs.directInput) { mutableStateOf(prefs.directInput) } + val attachments = rememberTerminalAttachmentAction(session, repository) { direct = false } + var closing by remember { mutableStateOf(false) } + var focused by rememberSaveable(session.id) { mutableStateOf(false) } + var keyboardRequest by remember(session.id) { mutableIntStateOf(0) } + val trust by repository.trust.collectAsStateWithLifecycle() + if (!focused) TerminalHeader(session.title, + if (session.source == "Local") stringResource(R.string.local_device) else session.source, + session.status, onBack, onSessions, { closing = true }, onFiles = onFiles) + Column(Modifier.fillMaxSize()) { + if (session.status == "ended" || (session.status == "failed" && session.hasConnected)) TerminalDisconnected(session, if (session.host != null) onRetry else null) + Box(Modifier.weight(1f).fillMaxWidth()) { + key(session.id) { + TerminalSurface(session, repository, Modifier.fillMaxSize(), direct, prefs.fontSize, keyboardRequest) + } + if (session.host != null && (session.status == "connecting" || (session.status == "failed" && !session.hasConnected))) { + SshConnectionStatus(session, onClose, onRetry, onEdit, + trust = trust?.takeIf { it.ownerId == session.id }, onTrust = repository::answerTrust) + } + } + CommandComposer(session.id, repository, session.status == "ready", direct, { + direct = it + if (it) keyboardRequest++ + }, { label -> + val key = when (label) { + "Ctrl+B" -> KeyEvent.KEYCODE_B + "Ctrl+C" -> KeyEvent.KEYCODE_C + "Esc" -> KeyEvent.KEYCODE_ESCAPE + "Tab" -> KeyEvent.KEYCODE_TAB + "←" -> KeyEvent.KEYCODE_DPAD_LEFT + "→" -> KeyEvent.KEYCODE_DPAD_RIGHT + "↑" -> KeyEvent.KEYCODE_DPAD_UP + else -> KeyEvent.KEYCODE_DPAD_DOWN + } + val control = label in setOf("Ctrl+C", "Ctrl+B") + val letter = if (control) label.last().lowercase() else "" + if (!session.terminal.key(key, if (control) 2 else 0, + text = letter, unshifted = letter.firstOrNull()?.code ?: 0)) repository.error.value = "input_rejected" + }, onKeyboard = { keyboardRequest++ }, focused = focused, onToggleFocus = { focused = !focused }, + onAttach = attachments.pick, attachmentBusy = attachments.busy, + extraShortcuts = if (session.host?.sessionMode in setOf(SshSessionMode.TMUX, SshSessionMode.HERDR)) listOf("Ctrl+B") else emptyList()) { command -> + val bytes = (command + "\r").toByteArray() + session.terminal.tryWrite(bytes, 0, bytes.size) + } + } + if (closing) AlertDialog(onDismissRequest = { closing = false }, title = { Text(stringResource(R.string.close_session)) }, + text = { Text(stringResource(R.string.close_session_confirm, session.title)) }, + confirmButton = { TextButton(onClose) { Text(stringResource(R.string.close_session)) } }, + dismissButton = { TextButton({ closing = false }) { Text(stringResource(R.string.cancel)) } }) +} + +@Composable +fun DesktopTerminalScreen(desktop: DesktopWorkspace, pane: DesktopPane, repository: SessionRepository, onBack: () -> Unit, onSessions: () -> Unit, + onGit: (() -> Unit)? = null, onConversation: (() -> Unit)? = null) { + val output by repository.output.collectAsStateWithLifecycle() + val prefs by repository.display.state.collectAsStateWithLifecycle() + val lifecycle = LocalLifecycleOwner.current + val identity = "${desktop.id}:${pane.window}:${pane.id}" + var direct by rememberSaveable(identity, prefs.directInput) { mutableStateOf(prefs.directInput) } + var keyboardRequest by remember(identity) { mutableIntStateOf(0) } + var focused by rememberSaveable(identity) { mutableStateOf(false) } + var showPermission by remember(identity) { mutableStateOf(false) } + var showDetails by remember(identity) { mutableStateOf(false) } + var wrapLines by rememberSaveable(identity) { mutableStateOf(true) } + val enabled = desktop.allowInput && desktop.status == "ready" + LaunchedEffect(enabled) { if (enabled) showPermission = false } + val input = remember(identity, enabled, desktop.connectionGeneration) { repository.desktopInput(desktop.id, pane) } + DisposableEffect(input) { onDispose { input.close() } } + LaunchedEffect(identity, desktop.status, desktop.connectionGeneration) { + if (desktop.status == "ready") lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + repository.watchDesktop(desktop.id, pane) + } + } + DisposableEffect(identity) { onDispose { repository.leaveDesktopPane() } } + DesktopTerminalTheme(if (output.target == identity) output.frame else null) { + if (!focused) TerminalHeader(pane.displayTitle, desktop.host.name, desktop.status, onBack, onSessions, + onGit = onGit.takeIf { pane.sshDestination == null }, gitEnabled = desktop.status == "ready", + onDetails = { showDetails = true }, onConversation = onConversation.takeIf { pane.agent != null }, + wrapLines = wrapLines, onToggleWrap = { wrapLines = !wrapLines }) + Column(Modifier.fillMaxSize()) { + if (desktop.status != "ready") Row(Modifier.fillMaxWidth().padding(horizontal = 12.dp), + verticalAlignment = Alignment.CenterVertically) { + val recovering = desktop.hasConnected && desktop.relayProfile != null && DesktopReconnect.retryable(desktop.failure) + HelperText(if (recovering) stringResource(R.string.desktop_reconnecting) + else desktop.failure?.let { desktopFailureText(it) } ?: statusLabel(desktop.status), Modifier.weight(1f)) + if (desktop.status != "connecting") desktop.relayProfile?.let { profile -> + TextButton({ repository.connectRelay(profile) }) { Text(stringResource(R.string.retry)) } + } + } + else if (!desktop.allowInput) Row(Modifier.fillMaxWidth().padding(horizontal = 12.dp), + verticalAlignment = Alignment.CenterVertically) { + HelperText(stringResource(R.string.composer_pc_read_only_short), Modifier.weight(1f)) + TextButton({ showPermission = true }) { Text(stringResource(R.string.composer_pc_enable_input)) } + } + DesktopOutputSurface(identity, if (output.target == identity) output.text else "", prefs.fontSize, + prefs.pinchZoom, { size -> repository.display.update { it.copy(fontSize = size) } }, + Modifier.weight(1f).fillMaxWidth(), frame = if (output.target == identity) output.frame else null, + inputTarget = input.takeIf { enabled && direct }, keyboardRequest = keyboardRequest, + loading = output.loading, connected = desktop.status == "ready", wrapLines = wrapLines) + if (output.loading && output.text.isBlank()) LinearProgressIndicator(Modifier.fillMaxWidth()) + CommandComposer(identity, repository, enabled, direct, { + direct = it + if (it) keyboardRequest++ + }, { label -> + val code = when (label) { + "Ctrl+C" -> KeyEvent.KEYCODE_C + "Esc" -> KeyEvent.KEYCODE_ESCAPE + "Tab" -> KeyEvent.KEYCODE_TAB + "←" -> KeyEvent.KEYCODE_DPAD_LEFT + "→" -> KeyEvent.KEYCODE_DPAD_RIGHT + "↑" -> KeyEvent.KEYCODE_DPAD_UP + else -> KeyEvent.KEYCODE_DPAD_DOWN + } + input.key(code, if (label == "Ctrl+C") 2 else 0) + }, onKeyboard = { keyboardRequest++ }, focused = focused, onToggleFocus = { focused = !focused }, + send = input::submit) + } + if (showPermission) AlertDialog(onDismissRequest = { showPermission = false }, + title = { Text(stringResource(R.string.composer_pc_enable_input)) }, + text = { Text(stringResource(R.string.composer_pc_read_only)) }, + confirmButton = { TextButton({ showPermission = false }) { Text(stringResource(R.string.close)) } }) + if (showDetails) DesktopPaneDetails(desktop, pane, output.target == identity && output.frame != null) { showDetails = false } + } +} + +@Composable +private fun DesktopPaneDetails(desktop: DesktopWorkspace, pane: DesktopPane, colorGrid: Boolean, onDismiss: () -> Unit) { + AlertDialog(onDismissRequest = onDismiss, title = { Text(stringResource(R.string.terminal_details)) }, + text = { + SelectionContainer { + Column(Modifier.verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(12.dp)) { + Column { + HelperText(stringResource(R.string.terminal_full_title)) + Text(pane.title.ifBlank { pane.displayTitle }) + } + if (pane.cwd.isNotBlank()) Column { + HelperText(stringResource(R.string.terminal_directory)) + Text(pane.cwd) + } + Column { + HelperText(stringResource(R.string.terminal_connection)) + Text("${desktop.host.name} · ${statusLabel(desktop.status)}") + Text("${desktop.transport} · ${desktop.host.address}") + } + Column { + HelperText(stringResource(R.string.terminal_display_mode)) + Text(stringResource(if (colorGrid) R.string.terminal_color_grid else R.string.terminal_plain_text)) + if (!colorGrid) HelperText(stringResource(R.string.desktop_legacy_text)) + } + } + } + }, confirmButton = { TextButton(onDismiss) { Text(stringResource(R.string.close)) } }) +} + +@Composable +internal fun TerminalHeader(title: String, endpoint: String, status: String, onBack: () -> Unit, onSessions: () -> Unit, + onClose: (() -> Unit)? = null, onGit: (() -> Unit)? = null, gitEnabled: Boolean = true, + onDetails: (() -> Unit)? = null, onConversation: (() -> Unit)? = null, conversationActive: Boolean = false, + onFiles: (() -> Unit)? = null, wrapLines: Boolean = false, onToggleWrap: (() -> Unit)? = null) { + var menu by remember { mutableStateOf(false) } + val connection = "$endpoint · ${statusLabel(status)}" + Row(Modifier.fillMaxWidth().height(48.dp).background(MaterialTheme.colorScheme.background), verticalAlignment = Alignment.CenterVertically) { + GlyphButton(R.drawable.ic_back, stringResource(R.string.back), onBack) + Row(Modifier.weight(1f).heightIn(min = 48.dp) + .clickable(onClickLabel = stringResource(R.string.switch_session), onClick = onSessions) + .semantics { contentDescription = connection }, + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(7.dp)) { + if (status == "connecting") CircularProgressIndicator(Modifier.size(10.dp), strokeWidth = 1.5.dp) + else Glyph(if (status == "ready") R.drawable.ic_terminal else R.drawable.ic_info, Modifier.size(15.dp), + if (status == "failed") MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary) + Text(title, fontSize = 13.sp, fontFamily = LocalTerminalFont.current, maxLines = 1, + overflow = TextOverflow.Ellipsis, modifier = Modifier.weight(1f, fill = false)) + Glyph(R.drawable.ic_down, Modifier.size(12.dp)) + } + Row(Modifier.padding(horizontal = 4.dp), verticalAlignment = Alignment.CenterVertically) { + if (onGit != null) TerminalHeaderAction(R.drawable.ic_github, stringResource(R.string.git_title), onGit, enabled = gitEnabled) + if (onConversation != null) TerminalHeaderAction(if (conversationActive) R.drawable.ic_terminal else R.drawable.ic_chat, + stringResource(if (conversationActive) R.string.chat_terminal else R.string.chat_title), onConversation) + if (onFiles != null) TerminalHeaderAction(R.drawable.ic_git_folder, stringResource(R.string.sftp_title), onFiles, enabled = status == "ready") + if (onClose != null || onDetails != null || onToggleWrap != null) Box { + TerminalHeaderAction(R.drawable.ic_more, stringResource(R.string.more_actions), { menu = true }) + DropdownMenu(menu, { menu = false }) { + if (onToggleWrap != null) DropdownMenuItem( + text = { Text(stringResource(R.string.terminal_wrap_lines)) }, + trailingIcon = { Checkbox(checked = wrapLines, onCheckedChange = null) }, + onClick = { menu = false; onToggleWrap() }) + if (onDetails != null) DropdownMenuItem(text = { Text(stringResource(R.string.terminal_details)) }, onClick = { menu = false; onDetails() }) + if (onClose != null) DropdownMenuItem(text = { Text(stringResource(R.string.close_session)) }, onClick = { menu = false; onClose() }) + } + } + } + } +} + +@Composable +private fun TerminalHeaderAction(@DrawableRes icon: Int, label: String, onClick: () -> Unit, enabled: Boolean = true) { + // 只收紧顶栏占位,保留原生触摸扩展;局部覆盖避免把弹出菜单和其他页面也压窄。 + CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 36.dp) { + GlyphButton(icon, label, onClick, enabled, modifier = Modifier.width(36.dp)) + } +} + +@Composable +fun DesktopScreen(desktop: DesktopWorkspace?, onPane: (DesktopPane) -> Unit, onRetry: (() -> Unit)? = null, + onTab: ((DesktopTab) -> Unit)? = null, onCloseTab: ((DesktopTab) -> Unit)? = null, onDisconnect: () -> Unit) { + Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 22.dp, vertical = 12.dp)) { + Row(Modifier.padding(vertical = 20.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(15.dp)) { + Glyph(R.drawable.ic_monitor, Modifier.size(30.dp), MaterialTheme.colorScheme.primary) + Column { + Text(desktop?.host?.name.orEmpty(), fontSize = 19.sp) + Box(Modifier.padding(top = 7.dp)) { StatusCaption(desktop?.status ?: "disconnected", "${desktop?.transport.orEmpty()} · ") } + } + } + if (desktop?.status == "connecting") LinearProgressIndicator(Modifier.fillMaxWidth()) + desktop?.pairingApproval?.let { approval -> + Column(Modifier.fillMaxWidth().workspaceFrame().padding(20.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) { + Text(stringResource(R.string.pair_awaiting_approval), fontSize = 18.sp, fontWeight = FontWeight.Medium) + Text(approval.code.chunked(3).joinToString(" "), fontSize = 28.sp, fontFamily = LocalTerminalFont.current) + HelperText(stringResource(R.string.pair_compare_code)) + } + } + GroupHeading(stringResource(R.string.computer_tabs), desktop?.tabs?.size?.takeIf { it > 0 } ?: desktop?.panes?.size ?: 0) + if (desktop != null && desktop.tabs.isNotEmpty() && onTab != null) DesktopTabRows(desktop, onTab, onPane, onCloseTab) + else desktop?.panes?.forEach { pane -> + Row(Modifier.fillMaxWidth().padding(bottom = 10.dp).workspaceFrame() + .clickable(enabled = desktop.status == "ready") { onPane(pane) }.padding(horizontal = 14.dp, vertical = 18.dp), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) { + Glyph(R.drawable.ic_terminal) + Column(Modifier.weight(1f)) { + Text(pane.displayTitle, fontSize = 14.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) + Text(pane.task.ifBlank { pane.cwd }, fontSize = 12.sp, lineHeight = 18.sp, fontFamily = LocalTerminalFont.current, + color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.padding(top = 6.dp)) + } + Text(statusLabel(pane.state), fontSize = 12.sp, lineHeight = 18.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) + } + } + if (desktop?.tabs.isNullOrEmpty() && desktop?.panes.isNullOrEmpty() && desktop?.status == "ready") HelperText(stringResource(R.string.no_panes), Modifier.padding(vertical = 20.dp)) + desktop?.failure?.let { HelperText(desktopFailureText(it), Modifier.padding(vertical = 12.dp)) } + if (desktop?.status !in listOf("ready", "connecting", "approval")) HelperText(stringResource(R.string.device_unavailable), Modifier.padding(vertical = 12.dp)) + if (onRetry != null && desktop?.status !in listOf("ready", "connecting", "approval")) { + Button(onRetry, modifier = Modifier.padding(top = 12.dp)) { Text(stringResource(R.string.retry)) } + } + OutlinedButton(onDisconnect, modifier = Modifier.padding(top = 20.dp)) { Text(stringResource(R.string.disconnect)) } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalSurface.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalSurface.kt new file mode 100644 index 0000000..0a7af58 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalSurface.kt @@ -0,0 +1,81 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.view.inputmethod.InputMethodManager +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import io.github.kuddev.pebrel.mobile.PebrelApplication +import io.github.kuddev.pebrel.mobile.session.LocalSession +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import io.github.kuddev.pebrel.mobile.session.TerminalPreferences +import io.github.kuddev.pebrel.terminal.GhosttyView + +@Composable +fun TerminalSurface(session: LocalSession, repository: SessionRepository, modifier: Modifier, direct: Boolean, fontSize: Int, keyboardRequest: Int = 0) { + val stored by repository.display.state.collectAsStateWithLifecycle() + TerminalSurface( + session = session, + repository = repository, + modifier = modifier, + preferences = stored.copy(fontSize = fontSize, directInput = direct), + keyboardRequest = keyboardRequest, + onPreferencesChanged = { value -> + repository.display.update { current -> current.copy(fontSize = value.fontSize) } + }, + ) +} + +@Composable +fun TerminalSurface( + session: LocalSession, + repository: SessionRepository, + modifier: Modifier, + preferences: TerminalPreferences, + onPreferencesChanged: (TerminalPreferences) -> Unit = {}, + keyboardRequest: Int = 0, +) { + val renderToken = remember(session.id) { Any() } + val lastKeyboardRequest = remember(session.id) { mutableIntStateOf(keyboardRequest) } + val zoomFeedback = rememberTerminalZoomFeedback(session.id) + DisposableEffect(session.id, renderToken) { onDispose { repository.detachRenderer(session.id, renderToken) } } + Box(modifier.clipToBounds()) { + AndroidView(modifier = Modifier.fillMaxSize(), factory = { context -> GhosttyView(context) }, + onRelease = { it.onZoomChanged = null }, update = { view -> + val app = view.context.applicationContext as PebrelApplication + view.onZoomChanged = zoomFeedback::update + view.setTerminalPreferences( + typeface = app.terminalTypeface(preferences.fontFamily), + fontSize = preferences.fontSize, + cursorStyle = preferences.cursorStyle, + cursorBlink = preferences.cursorBlink, + pinchZoom = preferences.pinchZoom, + onFontSizeChanged = { size -> + if (size != preferences.fontSize) onPreferencesChanged(preferences.copy(fontSize = size)) + }, + ) + view.directInput = preferences.directInput + view.session = session.terminal + repository.attachRenderer(session.id, renderToken) { view.onScreenUpdated() } + if (keyboardRequest != lastKeyboardRequest.intValue) { + lastKeyboardRequest.intValue = keyboardRequest + if (preferences.directInput) { + view.requestFocus() + view.post { + if (view.isAttachedToWindow && view.directInput) { + view.context.getSystemService(InputMethodManager::class.java).showSoftInput(view, 0) + } + } + } + } + }) + TerminalZoomIndicator(zoomFeedback) + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalZoomFeedback.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalZoomFeedback.kt new file mode 100644 index 0000000..b0e8f00 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/TerminalZoomFeedback.kt @@ -0,0 +1,74 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.view.HapticFeedbackConstants +import android.view.View +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R +import kotlinx.coroutines.delay + +/** 字号反馈属于当前视图;不写终端输入,也不替渲染器持久化设置。 */ +internal class TerminalZoomFeedback(private val view: View) { + var size by mutableIntStateOf(0) + private set + var visible by mutableStateOf(false) + private set + var gesturing by mutableStateOf(false) + private set + + fun update(size: Int, gesturing: Boolean) { + // 只在跨过字号档位时轻触一次;遵守系统触感开关,不逐帧启动振动器。 + if (gesturing && this.gesturing && this.size != size) { + view.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK) + } + this.size = size + this.gesturing = gesturing + visible = true + } + + fun dismiss() { visible = false } +} + +@Composable +internal fun rememberTerminalZoomFeedback(identity: Any): TerminalZoomFeedback { + val view = LocalView.current + val feedback = remember(identity, view) { TerminalZoomFeedback(view) } + LaunchedEffect(feedback, feedback.size, feedback.gesturing, feedback.visible) { + if (feedback.visible && !feedback.gesturing) { + delay(650) + feedback.dismiss() + } + } + return feedback +} + +@Composable +internal fun BoxScope.TerminalZoomIndicator(feedback: TerminalZoomFeedback) { + val motion = rememberPebrelMotion() + // 仅覆盖绘制,不占正文布局,也不拦截双指或终端输入。 + AnimatedVisibility(feedback.visible, modifier = Modifier.align(Alignment.Center), + enter = fadeIn(motion.tweenOrSnap(90)), exit = fadeOut(motion.tweenOrSnap(130))) { + Box(Modifier.testTag("terminal-zoom-feedback") + .background(MaterialTheme.colorScheme.inverseSurface, RoundedCornerShape(8.dp)) + .padding(horizontal = 16.dp, vertical = 10.dp)) { + Text(stringResource(R.string.terminal_zoom_size, feedback.size), + color = MaterialTheme.colorScheme.inverseOnSurface, fontSize = 14.sp) + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/WorkspaceComponents.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/WorkspaceComponents.kt new file mode 100644 index 0000000..c05ba86 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/ui/WorkspaceComponents.kt @@ -0,0 +1,125 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.animation.animateContentSize +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.kuddev.pebrel.mobile.R + +/** One outlined surface treatment for hosts, devices and terminal workspaces. */ +@Composable +fun Modifier.workspaceFrame(): Modifier { + val shape = RoundedCornerShape(14.dp) + val colors = MaterialTheme.colorScheme + return clip(shape).background(colors.surface).border(1.dp, colors.outlineVariant, shape) +} + +@Composable +fun WorkspaceSymbol(content: @Composable () -> Unit) { + Box(Modifier.size(44.dp).clip(RoundedCornerShape(11.dp)) + .background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = .45f)), + contentAlignment = Alignment.Center) { content() } +} + +/** Geometry shared by the native pages, matching the approved mobile prototype. */ +@Composable +fun Glyph(@DrawableRes icon: Int, modifier: Modifier = Modifier, color: Color = MaterialTheme.colorScheme.onSurfaceVariant) { + Icon(painterResource(icon), contentDescription = null, modifier = modifier.size(20.dp), tint = color) +} + +@Composable +fun GlyphButton(@DrawableRes icon: Int, label: String, onClick: () -> Unit, enabled: Boolean = true, + modifier: Modifier = Modifier) { + IconButton(onClick, enabled = enabled, modifier = modifier.size(48.dp)) { + Icon(painterResource(icon), label, modifier = Modifier.size(20.dp)) + } +} + +@Composable +fun PageHeader(title: String, onBack: () -> Unit, actions: @Composable RowScope.() -> Unit = {}) { + Row(Modifier.fillMaxWidth().height(67.dp).background(MaterialTheme.colorScheme.surface).padding(horizontal = 9.dp), + verticalAlignment = Alignment.CenterVertically) { + GlyphButton(R.drawable.ic_back, stringResource(R.string.back), onBack) + Text(title, modifier = Modifier.weight(1f).padding(start = 4.dp), fontSize = 20.sp, + fontWeight = FontWeight.Medium, maxLines = 1, overflow = TextOverflow.Ellipsis) + actions() + } +} + +@Composable +fun GroupHeading(title: String, count: Int? = null, action: String? = null, onAction: () -> Unit = {}) { + Row(Modifier.fillMaxWidth().heightIn(min = 48.dp), verticalAlignment = Alignment.CenterVertically) { + Text(title, fontSize = 13.sp, fontWeight = FontWeight.Medium, color = MaterialTheme.colorScheme.onSurfaceVariant) + if (count != null) Text(" $count", fontSize = 12.sp, lineHeight = 18.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) + Spacer(Modifier.weight(1f)) + if (action != null) TextButton(onAction, contentPadding = PaddingValues(start = 8.dp)) { + Text(action, fontSize = 12.sp, lineHeight = 18.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) + Glyph(R.drawable.ic_chevron, Modifier.padding(start = 5.dp).size(14.dp)) + } + } +} + +@Composable +fun statusLabel(status: String): String = stringResource(when (status) { + "ready" -> R.string.state_ready + "connecting" -> R.string.state_connecting + "approval" -> R.string.pair_awaiting_approval + "ended" -> R.string.state_ended + "failed" -> R.string.state_failed + "disconnected" -> R.string.state_disconnected + "finished" -> R.string.state_finished + "waiting_input", "attention" -> R.string.state_attention + "running" -> R.string.state_running + "idle" -> R.string.state_idle + else -> R.string.state_unknown +}) + +@Composable +fun StatusCaption(status: String, prefix: String = "") { + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp)) { + val color = when (status) { + "ready", "finished" -> MaterialTheme.colorScheme.primary + "failed" -> MaterialTheme.colorScheme.error + else -> MaterialTheme.colorScheme.onSurfaceVariant + } + if (status == "connecting") CircularProgressIndicator(Modifier.size(10.dp), strokeWidth = 1.5.dp) + else Box(Modifier.size(6.dp).background(color, CircleShape)) + Text(prefix + statusLabel(status), color = MaterialTheme.colorScheme.onSurfaceVariant, + fontSize = 12.sp, lineHeight = 18.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) + } +} + +@Composable +fun NavigationRow(@DrawableRes icon: Int, title: String, detail: String = "", onClick: () -> Unit) { + val motion = rememberPebrelMotion() + Row(Modifier.fillMaxWidth().animateContentSize(motion.contentSizeSpec()).padding(vertical = 4.dp).workspaceFrame().clickable(onClick = onClick) + .heightIn(min = 64.dp).padding(horizontal = 14.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(14.dp)) { + Glyph(icon) + Text(title, fontSize = 14.sp, modifier = Modifier.weight(1f)) + if (detail.isNotEmpty()) Text(detail, fontSize = 12.sp, lineHeight = 18.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.widthIn(max = 125.dp)) + Glyph(R.drawable.ic_chevron, Modifier.size(14.dp)) + } +} + +@Composable +fun HelperText(text: String, modifier: Modifier = Modifier) { + Text(text, modifier, fontSize = 12.sp, lineHeight = 20.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/voice/PcmRecorder.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/voice/PcmRecorder.kt new file mode 100644 index 0000000..6d588de --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/voice/PcmRecorder.kt @@ -0,0 +1,54 @@ +package io.github.kuddev.pebrel.mobile.voice + +import android.annotation.SuppressLint +import android.media.AudioFormat +import android.media.AudioRecord +import android.media.MediaRecorder +import kotlinx.coroutines.* +import java.io.IOException +import kotlin.math.sqrt + +internal fun interface VoiceRecorder { + suspend fun capture(holding: () -> Boolean, level: (Float, Int) -> Unit): FloatArray +} + +internal object PcmRecorder : VoiceRecorder { + @SuppressLint("MissingPermission") // Runtime grant checked at the UI boundary; revocation still fails closed. + override suspend fun capture(holding: () -> Boolean, level: (Float, Int) -> Unit): FloatArray = withContext(Dispatchers.IO) { + val minimum = AudioRecord.getMinBufferSize(16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT) + if (minimum <= 0) throw IOException("microphone_unavailable") + val recorder = AudioRecord.Builder().setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION) + .setAudioFormat(AudioFormat.Builder().setEncoding(AudioFormat.ENCODING_PCM_16BIT) + .setSampleRate(16000).setChannelMask(AudioFormat.CHANNEL_IN_MONO).build()) + .setBufferSizeInBytes(maxOf(minimum * 2, 6400)).build() + val samples = FloatArray(16000 * 60) + val buffer = ShortArray(800) + var used = 0 + try { + if (recorder.state != AudioRecord.STATE_INITIALIZED) throw IOException("microphone_unavailable") + ensureActive() + if (!holding()) return@withContext FloatArray(0) + recorder.startRecording() + if (recorder.recordingState != AudioRecord.RECORDSTATE_RECORDING) throw IOException("microphone_busy") + while (holding() && used < samples.size) { + ensureActive() + val read = recorder.read(buffer, 0, minOf(buffer.size, samples.size - used), AudioRecord.READ_NON_BLOCKING) + if (read < 0) throw IOException("microphone_read") + if (read == 0) { delay(10); continue } + var energy = 0f + for (i in 0 until read) { + val value = buffer[i] / 32768f + samples[used++] = value + energy += value * value + } + level(sqrt(energy / read).coerceIn(0f, 1f), used / 16000) + } + samples.copyOf(used) + } finally { + runCatching { recorder.stop() } + recorder.release() + buffer.fill(0) + samples.fill(0f) + } + } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/voice/VoiceInputController.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/voice/VoiceInputController.kt new file mode 100644 index 0000000..175c504 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/voice/VoiceInputController.kt @@ -0,0 +1,126 @@ +package io.github.kuddev.pebrel.mobile.voice + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update + +internal enum class VoicePhase { Checking, Missing, Ready, Downloading, Recording, Transcribing } +internal enum class VoiceError { Permission, Failed, Empty, Short, Model, Engine } +internal data class VoiceState(val phase: VoicePhase = VoicePhase.Checking, val error: VoiceError? = null, + val progress: Float = 0f, val level: Float = 0f, val seconds: Int = 0, val cancelArmed: Boolean = false) + +/** Owned by one composer/session. Never knows how to execute or send a command. */ +internal class VoiceInputController( + private val scope: CoroutineScope, + private val backend: VoiceBackend, + private val recorder: VoiceRecorder = PcmRecorder, + private val onTranscript: (String) -> Unit, +) { + private val mutable = MutableStateFlow(VoiceState()) + val state = mutable.asStateFlow() + @Volatile private var generation = 0L + private var job: Job? = null + private var modelReady = false + @Volatile private var holding = false + + init { refresh() } + + fun refresh() { + cancel() + val token = generation + mutable.value = VoiceState(VoicePhase.Checking) + job = scope.launch { + val ready = runCatching { backend.hasModel() }.getOrDefault(false) + ensureActive() + if (generation == token) { modelReady = ready; mutable.value = idle() } + } + } + private fun idle(error: VoiceError? = null) = VoiceState(if (modelReady) VoicePhase.Ready else VoicePhase.Missing, error) + + fun download() { + if (state.value.phase !in setOf(VoicePhase.Missing, VoicePhase.Ready)) return + val token = ++generation + mutable.value = VoiceState(VoicePhase.Downloading) + job = scope.launch { + try { + backend.download { progress -> if (generation == token) mutable.update { it.copy(progress = progress) } } + ensureActive() + if (generation == token) { modelReady = true; mutable.value = idle() } + } catch (error: CancellationException) { throw error } + catch (_: Exception) { if (generation == token) mutable.value = idle(VoiceError.Model) } + } + } + + fun remove() { + if (state.value.phase != VoicePhase.Ready) return + val token = ++generation + mutable.value = VoiceState(VoicePhase.Checking) + job = scope.launch { + try { + backend.remove() + ensureActive() + if (generation == token) { modelReady = false; mutable.value = idle() } + } catch (error: CancellationException) { throw error } + catch (_: Exception) { if (generation == token) mutable.value = idle(VoiceError.Model) } + } + } + + fun permissionDenied() { mutable.value = idle(VoiceError.Permission) } + + fun begin(): Boolean { + if (state.value.phase != VoicePhase.Ready) return false + val token = ++generation + holding = true + mutable.value = VoiceState(VoicePhase.Recording) + job = scope.launch { + var audio: FloatArray? = null + try { + audio = recorder.capture({ holding && generation == token }) { level, seconds -> + if (generation == token) mutable.update { it.copy(level = level, seconds = seconds) } + } + ensureActive() + if (generation != token) return@launch + if (state.value.cancelArmed) { mutable.value = idle(); return@launch } + if (audio.size < 4800) { mutable.value = idle(VoiceError.Short); return@launch } + if (audio.none { kotlin.math.abs(it) > .008f }) { mutable.value = idle(VoiceError.Empty); return@launch } + holding = false + mutable.value = VoiceState(VoicePhase.Transcribing) + val text = withTimeout(120_000) { backend.transcribe(audio) }.trim() + ensureActive() + if (generation == token) { + mutable.value = idle(if (text.isBlank()) VoiceError.Empty else null) + if (text.isNotBlank()) onTranscript(text) + } + } catch (_: TimeoutCancellationException) { if (generation == token) mutable.value = idle(VoiceError.Failed) } + catch (error: CancellationException) { throw error } + catch (_: LinkageError) { if (generation == token) mutable.value = idle(VoiceError.Engine) } + catch (_: SecurityException) { if (generation == token) mutable.value = idle(VoiceError.Permission) } + catch (_: Exception) { if (generation == token) mutable.value = idle(VoiceError.Failed) } + finally { audio?.fill(0f); if (generation == token) holding = false } + } + return true + } + + fun drag(cancel: Boolean) { + if (state.value.phase == VoicePhase.Recording) mutable.update { it.copy(cancelArmed = cancel) } + } + fun release() { + if (state.value.phase != VoicePhase.Recording) return + if (state.value.cancelArmed) cancel() else holding = false + } + fun cancel() { + generation++ + holding = false + job?.cancel() + mutable.value = idle() + } +} + +/** Preserve the latest draft, reject overflow, and never interpret transcription as input keys. */ +internal fun appendVoiceDraft(draft: String, spoken: String, limit: Int = 8192): String? { + val text = spoken.trim() + if (text.isEmpty()) return draft + val result = draft + (if (draft.isNotEmpty() && !draft.last().isWhitespace()) " " else "") + text + return result.takeIf { it.length <= limit } +} diff --git a/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/voice/VoiceModelStore.kt b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/voice/VoiceModelStore.kt new file mode 100644 index 0000000..99fe952 --- /dev/null +++ b/mobile/android/app/src/main/java/io/github/kuddev/pebrel/mobile/voice/VoiceModelStore.kt @@ -0,0 +1,114 @@ +package io.github.kuddev.pebrel.mobile.voice + +import android.content.Context +import io.github.kuddev.pebrel.voice.LocalWhisper +import kotlinx.coroutines.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import okhttp3.OkHttpClient +import okhttp3.Request +import java.io.File +import java.io.IOException +import java.nio.file.Files +import java.nio.file.StandardCopyOption +import java.security.MessageDigest +import java.util.concurrent.TimeUnit + +internal interface VoiceBackend { + suspend fun hasModel(): Boolean + suspend fun download(progress: (Float) -> Unit) + suspend fun remove() + suspend fun transcribe(samples: FloatArray): String +} + +/** Download is an explicit UI action. No constructor/startup path performs network IO. */ +internal class VoiceModelStore(context: Context) : VoiceBackend { + private val directory = File(context.noBackupFilesDir, "voice") + private val model = File(directory, "ggml-base.bin") + companion object { + const val MODEL_BYTES = 147951465L + const val MODEL_SHA256 = "60ed5bc3dd14eea856493d334349b405782ddcaf0028d4b5df4088345fba2efe" + private const val URL = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin" + private val ownership = Mutex() + private val client = OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(20, TimeUnit.SECONDS).callTimeout(10, TimeUnit.MINUTES).build() + } + + override suspend fun hasModel(): Boolean = withContext(Dispatchers.IO) { + model.isFile && model.length() == MODEL_BYTES + } + + override suspend fun download(progress: (Float) -> Unit): Unit = ownership.withLock { + coroutineScope { + val call = client.newCall(Request.Builder().url(URL).build()) + val cancellation = launch(start = CoroutineStart.UNDISPATCHED) { + try { awaitCancellation() } finally { call.cancel() } + } + try { + withContext(Dispatchers.IO) { + if (!directory.isDirectory && !directory.mkdirs()) throw IOException("model_storage") + val temporary = File.createTempFile("base-", ".part", directory) + try { + val digest = MessageDigest.getInstance("SHA-256") + call.execute().use { response -> + if (!response.isSuccessful) throw IOException("model_download") + val body = response.body ?: throw IOException("model_download") + val advertised = body.contentLength() + if (advertised != -1L && advertised != MODEL_BYTES) throw IOException("model_size") + var received = 0L + var lastPercent = -1 + temporary.outputStream().use { output -> + body.byteStream().use { input -> + val buffer = ByteArray(64 * 1024) + while (true) { + ensureActive() + val count = input.read(buffer) + if (count < 0) break + received += count + if (received > MODEL_BYTES) throw IOException("model_size") + output.write(buffer, 0, count) + digest.update(buffer, 0, count) + val percent = (received * 100 / MODEL_BYTES).toInt() + if (percent != lastPercent) { lastPercent = percent; progress(received.toFloat() / MODEL_BYTES) } + } + } + output.fd.sync() + } + if (received != MODEL_BYTES || digest.hex() != MODEL_SHA256) throw IOException("model_checksum") + } + ensureActive() + Files.move(temporary.toPath(), model.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING) + Unit + } finally { temporary.delete() } + } + } finally { cancellation.cancel() } + } + } + + override suspend fun remove() = ownership.withLock { + withContext(Dispatchers.IO) { + // Only this one app-owned model. Never recurse or remove a user-selected directory. + if (model.exists() && !model.delete()) throw IOException("model_remove") + } + } + + override suspend fun transcribe(samples: FloatArray): String = ownership.withLock { + withContext(Dispatchers.IO) { + if (!hasModel()) throw IOException("model_missing") + val digest = MessageDigest.getInstance("SHA-256") + model.inputStream().use { input -> + val buffer = ByteArray(64 * 1024) + while (true) { + ensureActive() + val count = input.read(buffer) + if (count < 0) break + digest.update(buffer, 0, count) + } + } + if (digest.hex() != MODEL_SHA256) throw IOException("model_checksum") + LocalWhisper.transcribe(model, samples) + } + } + + private fun MessageDigest.hex() = digest().joinToString("") { "%02x".format(it) } +} diff --git a/mobile/android/app/src/main/res/drawable/ic_back.xml b/mobile/android/app/src/main/res/drawable/ic_back.xml new file mode 100644 index 0000000..a929d49 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_back.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_battery.xml b/mobile/android/app/src/main/res/drawable/ic_battery.xml new file mode 100644 index 0000000..9a95cf3 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_battery.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_bell.xml b/mobile/android/app/src/main/res/drawable/ic_bell.xml new file mode 100644 index 0000000..f83ea3b --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_bell.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_chat.xml b/mobile/android/app/src/main/res/drawable/ic_chat.xml new file mode 100644 index 0000000..5f47f6a --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_chat.xml @@ -0,0 +1,4 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_check.xml b/mobile/android/app/src/main/res/drawable/ic_check.xml new file mode 100644 index 0000000..9eca253 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_check.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_chevron.xml b/mobile/android/app/src/main/res/drawable/ic_chevron.xml new file mode 100644 index 0000000..47f5f14 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_chevron.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_close.xml b/mobile/android/app/src/main/res/drawable/ic_close.xml new file mode 100644 index 0000000..831af3e --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_close.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_cloud.xml b/mobile/android/app/src/main/res/drawable/ic_cloud.xml new file mode 100644 index 0000000..2b267f4 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_cloud.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_command.xml b/mobile/android/app/src/main/res/drawable/ic_command.xml new file mode 100644 index 0000000..e75c42b --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_command.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_compose_bubble.xml b/mobile/android/app/src/main/res/drawable/ic_compose_bubble.xml new file mode 100644 index 0000000..26e313b --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_compose_bubble.xml @@ -0,0 +1,7 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_down.xml b/mobile/android/app/src/main/res/drawable/ic_down.xml new file mode 100644 index 0000000..ec01ba4 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_down.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_edit.xml b/mobile/android/app/src/main/res/drawable/ic_edit.xml new file mode 100644 index 0000000..99c4703 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_expand.xml b/mobile/android/app/src/main/res/drawable/ic_expand.xml new file mode 100644 index 0000000..5b9803b --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_expand.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_focus.xml b/mobile/android/app/src/main/res/drawable/ic_focus.xml new file mode 100644 index 0000000..89ef6a8 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_focus.xml @@ -0,0 +1,7 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_font.xml b/mobile/android/app/src/main/res/drawable/ic_font.xml new file mode 100644 index 0000000..0c30544 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_font.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_git_branch.xml b/mobile/android/app/src/main/res/drawable/ic_git_branch.xml new file mode 100644 index 0000000..4cb58ba --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_git_branch.xml @@ -0,0 +1,5 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_git_file.xml b/mobile/android/app/src/main/res/drawable/ic_git_file.xml new file mode 100644 index 0000000..cecf996 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_git_file.xml @@ -0,0 +1,5 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_git_folder.xml b/mobile/android/app/src/main/res/drawable/ic_git_folder.xml new file mode 100644 index 0000000..ef83a9b --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_git_folder.xml @@ -0,0 +1,5 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_git_refresh.xml b/mobile/android/app/src/main/res/drawable/ic_git_refresh.xml new file mode 100644 index 0000000..892f25c --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_git_refresh.xml @@ -0,0 +1,5 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_git_unstage.xml b/mobile/android/app/src/main/res/drawable/ic_git_unstage.xml new file mode 100644 index 0000000..29c17f2 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_git_unstage.xml @@ -0,0 +1,5 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_github.xml b/mobile/android/app/src/main/res/drawable/ic_github.xml new file mode 100644 index 0000000..2bf4359 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_github.xml @@ -0,0 +1,6 @@ + + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_grid.xml b/mobile/android/app/src/main/res/drawable/ic_grid.xml new file mode 100644 index 0000000..ca9f38b --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_grid.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_history.xml b/mobile/android/app/src/main/res/drawable/ic_history.xml new file mode 100644 index 0000000..1fed9d6 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_history.xml @@ -0,0 +1,7 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_image.xml b/mobile/android/app/src/main/res/drawable/ic_image.xml new file mode 100644 index 0000000..0d89d47 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_image.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_info.xml b/mobile/android/app/src/main/res/drawable/ic_info.xml new file mode 100644 index 0000000..8091f38 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_info.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_keyboard.xml b/mobile/android/app/src/main/res/drawable/ic_keyboard.xml new file mode 100644 index 0000000..6810756 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_keyboard.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_microphone.xml b/mobile/android/app/src/main/res/drawable/ic_microphone.xml new file mode 100644 index 0000000..69f6d6e --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_microphone.xml @@ -0,0 +1,7 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_monitor.xml b/mobile/android/app/src/main/res/drawable/ic_monitor.xml new file mode 100644 index 0000000..cc0aafb --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_monitor.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_more.xml b/mobile/android/app/src/main/res/drawable/ic_more.xml new file mode 100644 index 0000000..e365409 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_more.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_notification.xml b/mobile/android/app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000..fb541e3 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,9 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_palette.xml b/mobile/android/app/src/main/res/drawable/ic_palette.xml new file mode 100644 index 0000000..8edd196 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_palette.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_phone.xml b/mobile/android/app/src/main/res/drawable/ic_phone.xml new file mode 100644 index 0000000..c96cf1f --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_phone.xml @@ -0,0 +1,7 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_plus.xml b/mobile/android/app/src/main/res/drawable/ic_plus.xml new file mode 100644 index 0000000..a11fa57 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_plus.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_qr.xml b/mobile/android/app/src/main/res/drawable/ic_qr.xml new file mode 100644 index 0000000..18a35fd --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_qr.xml @@ -0,0 +1,3 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_search.xml b/mobile/android/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000..84335d5 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_send.xml b/mobile/android/app/src/main/res/drawable/ic_send.xml new file mode 100644 index 0000000..ca4c13b --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_send.xml @@ -0,0 +1,7 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_send_up.xml b/mobile/android/app/src/main/res/drawable/ic_send_up.xml new file mode 100644 index 0000000..fc4fd2e --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_send_up.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_server.xml b/mobile/android/app/src/main/res/drawable/ic_server.xml new file mode 100644 index 0000000..2ef3f02 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_server.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_settings.xml b/mobile/android/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 0000000..b086a14 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,6 @@ + + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_sliders.xml b/mobile/android/app/src/main/res/drawable/ic_sliders.xml new file mode 100644 index 0000000..e6ec4fc --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_sliders.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_terminal.xml b/mobile/android/app/src/main/res/drawable/ic_terminal.xml new file mode 100644 index 0000000..8d39706 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_terminal.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_touch.xml b/mobile/android/app/src/main/res/drawable/ic_touch.xml new file mode 100644 index 0000000..3e20ca5 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_touch.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_undo.xml b/mobile/android/app/src/main/res/drawable/ic_undo.xml new file mode 100644 index 0000000..61cf53b --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_undo.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/drawable/ic_up.xml b/mobile/android/app/src/main/res/drawable/ic_up.xml new file mode 100644 index 0000000..3985c5b --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_up.xml @@ -0,0 +1,6 @@ + + + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/attachment_strings.xml b/mobile/android/app/src/main/res/values-zh-rCN/attachment_strings.xml new file mode 100644 index 0000000..cf95c2c --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/attachment_strings.xml @@ -0,0 +1,16 @@ + + 附件已加入草稿 + 上传到当前 SSH 主机 + 请输入登录密码以单独传输文件。此密码仅用于本次上传。 + 上传 + 复制路径 + 正在导入附件… + 正在上传附件… + 无法添加附件 + 文件已保存,请复制路径后缩短草稿。 + 请选择不超过 16 MiB 的文件。 + 无法读取所选文件,请重新选择。 + 请允许访问共享存储,并检查可用空间。 + SSH 主机需提供 POSIX Shell、head 和 base64 才能上传附件。 + 文件传输未完成,请检查连接和可用空间后重试。 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/background_storage.xml b/mobile/android/app/src/main/res/values-zh-rCN/background_storage.xml new file mode 100644 index 0000000..88ca48f --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/background_storage.xml @@ -0,0 +1,21 @@ + + + %1$d 个活动会话 + %1$d 个活动会话 + + 退出 + 获取唤醒锁 + 释放唤醒锁 + 唤醒锁已持有 + 本地终端存储 + 本地终端需要共享存储权限。 + 授予权限后,Pebrel 会将本地终端启动在 /storage/emulated/0/Pebrel,并把该目录作为 HOME。请授予权限后继续;拒绝时不会改用应用私有数据目录。 + Android 需要你为 Pebrel 共享目录授予一次“所有文件访问权限”。 + 授予共享存储权限 + 打开应用设置 + 本地终端使用共享存储。 + 本地终端 HOME:%1$s + 共享存储不可用。请挂载外部存储后重试。 + Pebrel 无法创建共享存储目录。请检查存储权限后重试。 + 重新检查 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/composer_strings.xml b/mobile/android/app/src/main/res/values-zh-rCN/composer_strings.xml new file mode 100644 index 0000000..aa9bfdf --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/composer_strings.xml @@ -0,0 +1,27 @@ + + 仅查看 · 尚未授权终端输入 + 启用输入 + 请在电脑“手机连接”设置中允许操作终端。此页面会自动更新,保持当前连接即可,无需重新扫码。 + 对 Pebrel 说… + 撤销草稿编辑 + 输入操作 + 添加附件 + 语音输入 + 说出命令 + 没有可用的语音输入服务。 + 语音输入失败,请重试。 + 没有识别到语音内容。 + 最近命令 + 命令已填入编辑框 + 直接终端输入 + 本地命令编辑 + 显示辅助按键 + 隐藏辅助按键 + 展开命令编辑框 + 收起命令编辑框 + 进入终端专注模式 + 退出终端专注模式 + 显示键盘 + 隐藏键盘 + 语音内容会超过命令长度限制。 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/computer_management.xml b/mobile/android/app/src/main/res/values-zh-rCN/computer_management.xml new file mode 100644 index 0000000..f3477b4 --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/computer_management.xml @@ -0,0 +1,4 @@ + + 移除电脑 + 从这部手机移除 %1$s 及其连接凭据?电脑上的任务会继续运行,此操作不会卸载中转服务。 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/connection_entry_strings.xml b/mobile/android/app/src/main/res/values-zh-rCN/connection_entry_strings.xml new file mode 100644 index 0000000..51136c5 --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/connection_entry_strings.xml @@ -0,0 +1,19 @@ + + 新建 + 扫码或 8 位配对码 + 填写地址和账号,直接登录 + 通过 SSH 部署中转 + Linux 原生服务,无需 Docker + 先连接什么? + 之后可以随时从首页添加连接。 + 我的电脑 + 跟进 Pebrel 桌面端的终端和 Agent + 其他方式 + SSH 主机 + 手机本地终端 + 在这台手机上运行 Shell + 以后再说 + 还没有会话 + 连接电脑后,这里会列出它正在运行的终端。 + 打开本地终端 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/connection_status_strings.xml b/mobile/android/app/src/main/res/values-zh-rCN/connection_status_strings.xml new file mode 100644 index 0000000..6a78832 --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/connection_status_strings.xml @@ -0,0 +1,28 @@ + + 正在重新连接电脑…草稿已保留。 + 原电脑会话已不可用,请重新选择会话后继续。 + 无法解析电脑或中转服务器地址。请检查地址和网络连接。 + 连接超时。请检查地址和端口;局域网连接时,两台设备应处于同一网络,并保持电脑「设置 → 手机远程」开启。 + 此端口未接受连接。请检查电脑「手机远程」的状态,以及地址、端口和防火墙设置。 + 网络连接中断。请检查网络后重新连接;命令不会自动重发。 + 电脑证书与扫码保存的指纹不一致,连接已阻止。请确认电脑身份后,重新扫描当前二维码。 + 连接证书已过期或尚未生效。请检查两台设备的日期和时间,必要时重新生成配对证书。 + 无法验证加密连接。请检查邀请地址是否与电脑证书匹配,或中转服务器是否使用有效的 HTTPS 证书。 + 配对被拒绝、已过期或已撤销。请在电脑「设置 → 手机远程」换一个二维码后重新配对。 + 此手机连接已被占用。请关闭先前的手机连接,稍等片刻后重试。 + 服务器未接受 WebSocket 连接。请检查中转地址和反向代理配置。 + 电脑尚未就绪或已离线。请保持 Pebrel 运行,并查看电脑「设置 → 手机远程」的状态。 + 电脑会话服务尚未就绪。请打开 Pebrel,检查「设置 → 手机远程」后重新连接。 + 连接返回的数据格式不兼容。请更新两端 Pebrel 和中转服务器后重新连接。 + 电脑连接已关闭。请保持 Pebrel 运行并开启手机远程后重新连接;命令不会自动重发。 + 无法连接电脑。请查看电脑配对页面的状态后重新连接。 + 无法在此设备保存电脑连接邀请,请重试。 + 这不是有效的 Pebrel 连接邀请。请扫描电脑「设置 → 手机远程」当前显示的二维码。 + 无法读取终端输出。请检查电脑连接后重试。 + 无法确认命令是否已送达。请先检查电脑端输出,再决定是否重新发送。 + 输入未被接受。请检查会话连接和输入权限。 + 操作未完成。请检查连接或设置后重试。 + 导入并连接 + 手动配置连接 + 收起手动配置 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/credentials.xml b/mobile/android/app/src/main/res/values-zh-rCN/credentials.xml new file mode 100644 index 0000000..1b480cd --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/credentials.xml @@ -0,0 +1,15 @@ + + 密码 + 保存主机,并选择是否在本机保存密码。 + 保存密码 + 已保存 · 留空保留 + 密码已保存在本机 + 留空会保留已保存密码,输入新密码则替换它。 + 密码会加密保存在本机。 + 密码仅用于本次连接。点击保存会移除原来保存的密码。 + 移除已保存密码 + 无法读取已保存密码,请重新输入并保存。 + 主机或密码保存失败,表单已保留,请重试。 + 未能移除已保存密码,请重试。 + 请输入连接密码。已保存密码仅用于原主机地址、端口和用户名。 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/deployment.xml b/mobile/android/app/src/main/res/values-zh-rCN/deployment.xml new file mode 100644 index 0000000..7aca7b8 --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/deployment.xml @@ -0,0 +1,32 @@ + + + 中转服务已就绪 + 将两份配置文件保存到电脑的连接助手目录。在电脑上启动助手接入 Pebrel 会话,然后在此连接。 + 保存电脑配置 + 保存手机邀请 + 复制电脑启动命令 + 文件已保存 + 文件保存失败,请选择可写位置重试。 + 启动命令已复制 + 服务器可能需要下载容器镜像并申请证书。取消会关闭 SSH 操作,已经启动的服务仍保留在服务器上。 + 已有 SSH 主机 + 手动配置 SSH + 选择 SSH 主机 + 公网域名 + HTTPS 端口 + HTTP 验证端口 + 服务器安装目录 + 需要 Linux、Docker 和 Compose,且 SSH 账户有权运行 Docker。域名需解析到该服务器,公网 80 端口需通向 HTTP 验证端口,同时开放 HTTPS 端口。不会覆盖已有的无关目录。 + 部署并启动 + 正在检查配置… + 正在检查服务器环境… + 正在上传中转源码… + 正在生成连接凭据… + 正在构建并启动服务… + 正在检查 HTTPS 服务… + 请检查域名、端口及专用安装目录。 + 请先安装 Docker 和 Compose,并允许该 SSH 账户使用 Docker。 + 该目录包含无关文件或不支持的部署,请选择新的空目录。 + 服务尚未通过 HTTPS 检查。请检查域名解析、公网端口及服务器证书后重试。 + 部署未完成。请检查服务器权限、剩余空间及容器下载后重试。 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/git_strings.xml b/mobile/android/app/src/main/res/values-zh-rCN/git_strings.xml new file mode 100644 index 0000000..e335e53 --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/git_strings.xml @@ -0,0 +1,54 @@ + + Git + 差异 + 刷新 + 变更 + 历史 + 已暂存(%1$d) + 未暂存(%1$d) + 暂存 + 取消暂存 + 全部暂存 + 取消全部暂存 + 暂存 %1$s + 取消暂存 %1$s + 提交 + 提交说明 + 提交已暂存的更改(%1$d) + 提交说明的 UTF-8 大小需在 16 KiB 以内。 + 抓取 + 拉取(仅快进) + 推送 + 同步(抓取、拉取、推送) + 领先 %1$d · 落后 %2$d + 未配置跟踪分支 + 工作区没有更改 + 合并冲突 + 暂无提交记录。 + 加载更早的提交 + 提交快照 · 只读 + 当前视图没有差异。 + 已完成:%1$s + 当前连接为只读。在电脑端允许输入后即可修改仓库。 + 请连接电脑,再从对应终端重新打开 Git。 + 会话或工作目录已变化,请返回终端重新打开 Git。 + 请更新电脑端 Pebrel,以使用移动端 Git。 + 仓库状态已变化,请刷新并检查变更后再操作。 + 此仓库还有 Git 操作正在执行,请稍后刷新。 + 请先处理冲突并暂存更改,再提交。 + 内容过大或不是 UTF-8 文本,请在电脑端查看。 + 操作已完成,但刷新失败。请刷新确认结果,不要重复执行。 + 操作结果尚未确认。请先在电脑端检查仓库,再决定下一步。 + Git 适用于已连接电脑上的本地终端或 WSL 终端。 + Git 返回错误,请检查电脑上的仓库、身份和远端配置。 + 查看错误详情 + 仓库详情 + 已同步 + ↑ %1$d ↓ %2$d + %1$d 项未暂存 · %2$d 项已暂存 + 未暂存 + 未跟踪文件 + 已暂存 + 已折叠 + 已展开 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/native_service.xml b/mobile/android/app/src/main/res/values-zh-rCN/native_service.xml new file mode 100644 index 0000000..93e73df --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/native_service.xml @@ -0,0 +1,55 @@ + + 选择要使用的服务器,Pebrel 通过 SSH 安装轻量加密中转服务,无需 Docker、Node.js 或域名。 + Pebrel 中转服务 + 手动安装命令 + 先上传并解压配套的离线中转安装包,以 root 进入其中的 pebrel-relay-manual 目录,再执行以下命令。若显示 SERVER_IP 或 PORT,请替换成实际值。脚本显示四个步骤,失败即停止;无需 Docker 或 Node.js。 + 高级连接设置 + 中转端口 + 连接地址覆盖(可选) + 默认沿用 SSH 主机地址。仅当电脑、手机访问此服务器所用地址不同时修改。 + 安装并启动 + 检查状态 + 启动 + 停止 + 卸载 + 停止中转并仅移除 Pebrel 管理的服务文件,已连接的设备会断开。默认保留凭据以便重装,除非你勾选同时删除配置。 + 同时删除配对配置和密钥 + 支持 Linux x64/ARM64、systemd 239+ 或 Alpine OpenRC。管理服务需要 root SSH 权限,中转运行时不使用 root 权限。电脑和手机需能访问中转端口(默认 443);Pebrel 不会修改防火墙。 + 在电脑“设置 → 手机连接 → 中转服务器”选择此 SSH 服务器,点击“使用此中转服务器”,再生成二维码供手机扫描。也可在下方保存私有配置文件备用。 + 此文件包含服务器访问凭据,请私下传给自己的电脑,不要公开。 + 正在把内置服务文件上传到服务器… + 文件已发出,正在等待服务器校验… + 正在校验文件并安装服务… + 连接 SSH + 检查系统与内置文件 + 上传到服务器 + 安装、启动并验证 + 待执行 + 进行中 + ✓ 已完成 + 已取消 + %1$d. %2$s · %3$s + 已交给 SSH 传输:%1$d / %2$d KiB + 已 %1$d 秒没有新进度,正在等待服务器;可取消操作。 + 正在启动服务… + 正在检查服务监听… + 正在停止并移除服务… + 已安装 · 已停止 + 服务已卸载 + 操作已取消,已安装的服务会保留。请先检查状态再重试。 + 尚未安装 + 尚未检查状态 + 缺少经过校验的原生服务包或文件损坏,请安装更新后的 Pebrel 包。 + 需要 Linux x64/ARM64,以及正在运行的 systemd 239+ 或 OpenRC。能登录 SSH 不代表系统已提供服务管理器。 + 操作失败 + OpenRC 缺少 rc-update 或 supervise-daemon,请检查 OpenRC 是否完整安装并已运行。 + 服务器需提供 head、base64、sha256sum、mktemp 和 chmod,用于接收并校验服务文件。 + 系统服务管理器拒绝了操作,请先在服务器检查 pebrel-relay 服务状态后重试。 + 中转未能放弃 root 权限。请检查服务器是否存在非 root 的 nobody 账户;服务不会以 root 身份继续运行。 + 缺少必要的服务文件或服务管理程序,请检查服务状态和服务器环境。 + 当前安装及管理系统服务需要 root SSH 账户。 + 现有服务文件已被修改或属于其他安装,未进行覆盖或删除。请先检查服务器上的安装。 + 服务未能就绪。请检查所选端口是否已被占用,再检查服务状态。 + 请输入服务器 IP 或主机名,不要包含网址协议、路径或登录信息。 + 操作未完成,请检查服务器连接和服务状态后重试。 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/pairing.xml b/mobile/android/app/src/main/res/values-zh-rCN/pairing.xml new file mode 100644 index 0000000..9905307 --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/pairing.xml @@ -0,0 +1,51 @@ + + 连接电脑 + 找到电脑 + 核对并批准 + 在电脑上打开 Pebrel,进入「设置 → 手机远程」,点「开启」。 + 设置 → 手机远程 + 扫码 + 粘贴 + 页内二维码扫描区域 + 对准电脑上的二维码 + 二维码只能用一次,10 分钟后失效。 + 开启相机,在这里扫描电脑上的二维码。 + 开启相机 + 打开应用设置 + 这台设备没有可用相机,请使用配对码或粘贴。 + 相机启动失败,请重试,或使用配对码、粘贴。 + 已读取二维码。请查看配对结果,或重新扫码。 + 重新扫码 + 粘贴电脑上「复制完整配对信息」的内容 + 不在同一个网络?改用自建中转 + 两台设备需连接同一个 Wi-Fi 或局域网。 + 电脑二维码下方的 8 位数字 + 配对 %1$s + 扫描二维码 + 扫描 Pebrel 电脑上显示的配对码 + 粘贴完整配对信息 + 读取配置 + 局域网 + 服务器中转 + 在电脑上打开「设置 → 手机远程」。扫码后核对两端校验码,再在电脑上批准这台手机。 + 等待电脑批准 + 请核对电脑「手机远程」里显示的校验码。两端一致后,再在电脑上点「批准」。 + 电脑名称 + 服务地址 + 设备编号 + 连接密钥 + 证书指纹(可选) + 配对码或连接参数无效,请检查地址和密钥。 + 通过 SSH 部署中转 + 使用 8 位配对码 + 收起配对码 + 8 位配对码 + 和电脑连接同一个 Wi-Fi。在下方选择电脑,输入电脑「设置 → 手机远程」二维码下的 8 位数字。 + 正在查找同一网络中的电脑… + 还没找到电脑。请确认手机远程已开启,且两端连接同一个 Wi-Fi。也可以扫码或粘贴完整配对信息。 + 重新查找 + 配对这台电脑 + 正在连接… + 配对码错误、已过期或已使用。请在电脑上换一个后重试。 + 电脑尚未完成连接。请检查网络和防火墙,再到电脑上刷新配对码后重试。 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/reader_extension_strings.xml b/mobile/android/app/src/main/res/values-zh-rCN/reader_extension_strings.xml new file mode 100644 index 0000000..bf7a86a --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/reader_extension_strings.xml @@ -0,0 +1,4 @@ + + 渲染失败,可展开源码查看。 + 正在渲染… + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/ssh_session_strings.xml b/mobile/android/app/src/main/res/values-zh-rCN/ssh_session_strings.xml new file mode 100644 index 0000000..c74c546 --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/ssh_session_strings.xml @@ -0,0 +1,6 @@ + + 终端 + 持久会话名称 + 在 SSH 主机上运行。名称留空使用默认会话;断开只分离客户端,远端进程继续运行。主机需已安装所选的会话管理程序。 + 名称最多 64 位,使用字母、数字、下划线或短横线;支持句点的会话类型也可使用句点。 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/strings.xml b/mobile/android/app/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000..fdad89d --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,266 @@ + + Pebrel + 返回 + 设置 + 会话 + SSH 主机 + 电脑 + 添加 SSH 主机 + 本地终端 + 关闭会话 + 关闭 + 保存 + 取消 + 发送 + 连接 + 名称 + 主机地址 + 用户名 + 端口 + 密码 + 连接 Pebrel 桌面端 + 允许通过此通道输入命令 + 在本地编辑… + 核对主机身份 + 请与服务器上的指纹核对后再信任。已有主机密钥变化时会拒绝连接。 + 信任并连接 + 操作失败 + 刷新输出 + 读取原 Pane 的近期输出。此预览尚未传输完整终端网格,也不提供独占输入控制。 + 后台与会话 + 后台保持会话 + 正在保持用户开启的会话 + 前台服务会显示常驻通知,可能增加耗电;Android 仍可能终止进程。本地 Shell 不能在强行停止应用后继续运行。 + 停止会话 + 任务通知 + 有任务需要处理或已完成。 + 开启系统通知 + 桌面连接预览 + 电脑需要可连接的 SSH 服务,并运行包含 mobile-bridge 的 Pebrel;命令需在 PATH 中可用。Windows、Linux、macOS 共用同一通道协议。 + 联通预览:本地与 SSH 终端、局域网及中转扫码配对、桌面有界输出读取。通知断线补收、电脑文件传输及 WebDAV 暂未提供。 + 外观 + 跟随系统 + 连接电脑 + 手机连接配置 + 先启动你的中转服务器和电脑连接工具,再粘贴 phone-设备编号.txt 的内容。中转域名需要有效的 HTTPS 证书。 + 此测试版信任你自己的中转服务器,服务器管理者可接触会话数据。连接凭据使用 Android Keystore 保存;电脑端加 --allow-input 才能输入。 + 断开连接 + 全部 + 全部 %1$d + 全部会话 + 还没有打开的会话。选择下方主机或本地终端开始。 + 还没有保存主机。点右下角 + 添加 SSH。 + 连接电脑,接着操作 Pebrel 上的工作。 + 搜索名称、地址或用户名 + 没有匹配的主机 + 编辑主机 + 删除主机 + 主机操作 + 从已保存的主机中移除 %1$s?已打开的会话会继续运行。 + 首次连接时请核对主机指纹。密码可选择保存在本机。 + 已连接 + 连接中… + 已结束 + 失败 + 已断开 + 已完成 + 等待处理 + 运行中 + 空闲 + 未知 + 等待输出 + 切换会话 + 会话操作 + 本机 + Pebrel Tabs + 暂无可用的 Pane + 连接已不可用,请返回电脑列表重新连接。 + 终端 + 最近输出 + 只读 + 本地编辑 + 直接输入 + 点终端区域直接输入 + 输入模式 + 辅助按键 + 长命令编辑 + 保留草稿 + 点击填入,确认后发送 + 字体 + 字号 + 背景 + 纯色 + 输入 + 默认输入方式 + 命令候选 + 仅提供本地命令前缀候选;点击候选不会执行命令。 + 连接 + 应用 + 关于 + 开启 + 关闭 + 主题 + 主题预览 + 中文 · English · 0123 → + 浅色 + 深色 + 尚未实现 + 连接测试版 + 系统通知设置 + 字号设置应用于终端输出,界面文字跟随系统缩放。 + 终端使用所选主题的背景色,图片背景暂未实现。 + 本地编辑先在手机编辑再发送;直接输入用于交互式终端程序。 + 电脑文件传输、WebDAV 和结构化阅读暂未提供。 + 在线电脑任务状态通过系统通知提醒,暂不支持离线补收。 + 已配对的电脑保存在本机,点击即可重新连接。第 2 版连接使用端到端加密。 + 终端详情 + 完整标题 + 工作目录 + 电脑连接 + 通知 + 打开系统设置 + 关闭 %1$s?这将结束终端连接及其本地进程。 + 断线时不可发送输入 + Pebrel Android · 本地终端与 SSH 连接。 + 主机图标 + 认证方式 + 密码 + 自动 + Tailscale SSH 或服务器允许的免密登录可留空密码;其他 SSH 服务器可能仍需密码。 + 密钥文件 + 使用密码登录;密钥文件暂未支持。 + 分组 + 生产环境 + 开发环境 + 跳板机 + 不使用跳板机 + 勾选“保存密码”后,密码会加密保存在本机。 + 连接失败 + 正在建立连接… + 重新连接 + 返回编辑主机 + 会话已结束,仍可查看已有输出。 + 正在连接服务器并协商加密通道… + 正在等待你确认服务器指纹… + 正在验证用户名和密码… + 正在打开远程终端… + 无法解析主机地址,请检查地址和 DNS 网络连接。 + 服务器响应超时,请检查地址、端口和网络后重新连接。 + 无法连接 SSH 端口,请检查 SSH 服务、端口和防火墙。 + 认证失败,请检查用户名、密码,以及服务器是否允许密码登录。 + 主机指纹已变化,连接已阻止。请先核实服务器身份,再删除并重新添加此主机。 + 未确认主机指纹或确认已超时,请重新连接并核对指纹。 + 服务器未能打开终端通道,请检查此账号是否允许 Shell 访问。 + SSH 连接已中断,请检查网络后重新连接。已发命令不会自动重发。 + SSH 通道协商失败,请确认端口提供 SSH 服务,且服务器支持当前 SSH 算法。 + 无法打开终端,请重新连接或检查主机配置。 + 此设备上的 SSH 加密组件初始化失败,请更新应用后重试。 + 保存并连接 + 隐藏终端标题栏 + 显示终端标题栏 + 显示或隐藏键盘 + 字号 %1$d sp + 新增标签 + 新建终端 + 打开文件 + 打开 + 工作目录(可留空) + 电脑上的完整文件路径 + Markdown、代码、文本和图片均以独立标签打开,与电脑同步。 + 关闭标签 + 关闭 %1$s + 关闭电脑上的“%1$s”?手机与电脑会同步移除此标签。 + 管理电脑标签 + 终端 + SSH 终端 + 文档 + 代码 + 图片 + 此标签中仍有任务运行,请在电脑上确认关闭。 + 文件有未保存修改,请先在电脑上保存或确认关闭。 + 电脑正在保存或关闭此标签,请完成后重试。 + 请更新电脑端 Pebrel,以管理标签并阅读文件。 + 文件详情 + 预览 + 源码 + 目录 + 刷新文件 + 复制 + 已复制 + 复制源码 + 复制失败,请选择较小的片段后重试。 + 打开图片 + 重置缩放 + 正在阅读电脑上的未保存草稿 + 电脑上的文件已更新,刷新后查看最新内容。 + 连接已断开 · 正在显示已加载的内容 + 电脑连接已断开,重新连接后继续。 + 文件尚未加载,请检查电脑上的文件状态后重试。 + 文件超过阅读器的 16 MiB 上限,请在电脑上打开。 + 此标签或文件类型暂未接入手机阅读器。 + 图片解码失败,请检查电脑上的图片文件和格式。 + 链接未打开,请使用 HTTP 链接或本地文件路径。 + 对话视图 + 终端视图 + 你 + 工具 + 执行中 + 较长内容仅展示一部分,可在终端查看完整输出。 + 更早消息 + Agent 记录对话后,消息会显示在这里。 + 在终端中处理当前交互 + 更新电脑端后即可使用对话视图。 + Agent 会话已结束或更换,请返回终端重新打开对话。 + 等待当前终端报告 Codex 或 Claude Code 的会话文件。识别期间可继续使用终端。 + 会话正在更新,正在重新读取当前消息。 + 最近消息缓存已满,可在终端查看更早记录。 + 题目已变化,请阅读当前选项后重新选择。 + Agent 正在处理任务或等待交互,草稿已保留。 + 尚未收到发送回执,草稿已保留;再次发送前请先查看终端。 + 允许 + 拒绝 + 刷新对话 + SFTP 文件 + 添加文件或文件夹 + 上传文件 + 下载到手机 + 新建文件夹 + 跳转路径 + 隐藏隐藏文件 + 显示隐藏文件 + 上一级目录 + 文件夹 + 符号链接 + 文件 + %1$s 的操作 + 此文件夹为空。 + 加载更多文件 + 刷新 + 打开 + 重命名 + 删除 + 从主机删除“%1$s”?此操作仅删除该文件、链接本身或空文件夹。 + 确定 + 上传完成。 + 已保存到所选位置。 + 正在取消传输… + 传输已取消。 + 部分文件名含控制字符或路径超出长度限制,已跳过。 + 路径已不存在,请刷新文件夹或检查路径。 + 主机拒绝了此文件操作,请检查当前账户的权限。 + 目标已存在,请使用其他名称;现有文件保持不变。 + 此主机未启用 SFTP,终端仍可继续使用。 + 文件或目录状态已变化,请刷新后继续。 + SSH 已断开,请返回终端重新连接。 + 文件请求超时,请检查连接后重试。 + 已达到当前文件视图的容量限制,请结束传输或进入较小的目录。 + 预览支持最大 16 MiB 的文件,可下载到手机后打开。 + 此文件不属于可预览的 UTF-8 文本或图片,可下载到手机后打开。 + 手机文件服务未完成操作,请重新选择文件或保存位置。 + 请输入有效路径或名称,文件名中不要包含斜杠或控制字符。 + 操作未完成,请检查路径、权限以及文件夹是否为空。 + 修改连接地址 + 电脑地址与端口 + 填写电脑“手机远程”设置中显示的地址。保留原配对和权限,连接成功后才保存新地址。 + 请输入有效的电脑地址和端口。 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/terminal_screen_strings.xml b/mobile/android/app/src/main/res/values-zh-rCN/terminal_screen_strings.xml new file mode 100644 index 0000000..38e662e --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/terminal_screen_strings.xml @@ -0,0 +1,14 @@ + + 当前连接返回纯文本,不包含终端颜色和光标。 + 正在读取终端… + 等待终端输出 + 最新输出 ↓ + 显示方式 + 彩色终端网格 + 纯文本 + 编辑 + 键盘 + 更多快捷键 + 按手机宽度自动换行 + 选择并复制屏幕文字 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/terminal_settings.xml b/mobile/android/app/src/main/res/values-zh-rCN/terminal_settings.xml new file mode 100644 index 0000000..1391da7 --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/terminal_settings.xml @@ -0,0 +1,16 @@ + + 字体与大小 + 字号 · %1$d sp + 系统等宽字体 + 光标 + 光标样式 + 方块 + 竖线 + 下划线 + 光标闪烁 + 应用于本地和 SSH 终端。终端程序仍可隐藏光标;终端不活动时暂停闪烁。 + 手势 + 双指捏合调整字号 + 双指捏合可调整终端字号,并保存到后续会话。单指滑动可滚动,长按可选择文字。 + 主题纯色背景 + diff --git a/mobile/android/app/src/main/res/values-zh-rCN/voice_strings.xml b/mobile/android/app/src/main/res/values-zh-rCN/voice_strings.xml new file mode 100644 index 0000000..68fb0dc --- /dev/null +++ b/mobile/android/app/src/main/res/values-zh-rCN/voice_strings.xml @@ -0,0 +1,18 @@ + + 按住说话;轻点管理语音模型 + 结束并识别 + 正在手机上识别… + 松开取消 + 松开识别 + 上滑取消 · 文字只填入草稿 + %1$d / 60 秒 + 本地多语种识别,支持中文和英文。模型约 148 MB,不会自动下载。录音不离开手机,识别后不会自动执行命令。 + 模型下载中:%1$d%% + 下载模型 · 148 MB + 模型已安装,长按麦克风即可说话。首次允许麦克风权限后,请再次长按开始。 + 删除已下载模型 + 需要麦克风权限,请在应用权限中允许,再次长按说话。 + 录音时间太短,请按住多说一会儿。 + 模型下载、校验或删除失败,请检查网络和存储空间后重试。 + 当前构建的本地语音引擎不可用。 + diff --git a/mobile/android/app/src/main/res/values/attachment_strings.xml b/mobile/android/app/src/main/res/values/attachment_strings.xml new file mode 100644 index 0000000..739b857 --- /dev/null +++ b/mobile/android/app/src/main/res/values/attachment_strings.xml @@ -0,0 +1,16 @@ + + Attachment added to the draft + Upload to this SSH host + Enter the login password for a separate file transfer. This password is used only for this upload. + Upload + Copy path + Importing attachment… + Uploading attachment… + Could not add attachment + File saved. Copy its path to a shorter draft. + Choose a file no larger than 16 MiB. + The selected file could not be read. Choose it again. + Allow shared storage access and check the available space. + Uploading requires a POSIX shell with head and base64 on the SSH host. + The file transfer did not complete. Check the connection and available space, then try again. + diff --git a/mobile/android/app/src/main/res/values/background_storage.xml b/mobile/android/app/src/main/res/values/background_storage.xml new file mode 100644 index 0000000..97ff1f7 --- /dev/null +++ b/mobile/android/app/src/main/res/values/background_storage.xml @@ -0,0 +1,21 @@ + + + %1$d live session + %1$d live sessions + + Exit + Acquire wakelock + Release wakelock + Wakelock held + Local terminal storage + Shared storage access is required for local terminals. + After access is granted, Pebrel starts local terminals in /storage/emulated/0/Pebrel and keeps that directory as HOME. Grant access to continue; it will not fall back to private app data. + Android needs a one-time “All files access” grant for the shared Pebrel folder. + Grant shared storage access + Open app settings + Local terminals use shared storage. + Local terminal home: %1$s + Shared storage is unavailable. Mount external storage and try again. + Pebrel could not create its shared storage folder. Try again after checking storage access. + Check again + diff --git a/mobile/android/app/src/main/res/values/composer_strings.xml b/mobile/android/app/src/main/res/values/composer_strings.xml new file mode 100644 index 0000000..e15b8a8 --- /dev/null +++ b/mobile/android/app/src/main/res/values/composer_strings.xml @@ -0,0 +1,27 @@ + + Read-only · terminal input is not authorized + Enable input + Enable terminal control in the computer’s Phone connection settings. This page updates automatically; keep the current connection. + Write to Pebrel… + Undo draft edit + Input actions + Attach file + Voice input + Speak a command + No speech input service is available. + Speech input failed. Try again. + No speech was recognized. + Recent commands + Command filled into editor + Direct terminal input + Local command editor + Show auxiliary keys + Hide auxiliary keys + Expand command editor + Collapse command editor + Enter terminal focus + Exit terminal focus + Show keyboard + Hide keyboard + Voice input would exceed the command limit. + diff --git a/mobile/android/app/src/main/res/values/computer_management.xml b/mobile/android/app/src/main/res/values/computer_management.xml new file mode 100644 index 0000000..81db17c --- /dev/null +++ b/mobile/android/app/src/main/res/values/computer_management.xml @@ -0,0 +1,4 @@ + + Remove computer + Remove %1$s and its saved connection credentials from this phone? Tasks on the computer will keep running. This does not uninstall the relay service. + diff --git a/mobile/android/app/src/main/res/values/connection_entry_strings.xml b/mobile/android/app/src/main/res/values/connection_entry_strings.xml new file mode 100644 index 0000000..5586a7f --- /dev/null +++ b/mobile/android/app/src/main/res/values/connection_entry_strings.xml @@ -0,0 +1,19 @@ + + New + Scan a QR code or use an 8-digit pairing code + Enter an address and account to connect + Deploy a relay over SSH + Linux native service, no Docker required + What would you like to connect first? + You can add more connections from the home screen anytime. + My computer + Follow terminals and agents in Pebrel desktop + Other ways + SSH host + Phone terminal + Run a shell on this phone + Maybe later + No sessions yet + Connect your computer to see its running terminals here. + Open local terminal + diff --git a/mobile/android/app/src/main/res/values/connection_status_strings.xml b/mobile/android/app/src/main/res/values/connection_status_strings.xml new file mode 100644 index 0000000..8cb80bb --- /dev/null +++ b/mobile/android/app/src/main/res/values/connection_status_strings.xml @@ -0,0 +1,28 @@ + + Reconnecting to the computer… Your draft is kept. + The computer session is no longer available. Choose a session to continue. + The computer or relay address could not be resolved. Check the address and network. + Connection timed out. Check the address and port. For LAN pairing, use the same network and keep Settings → Phone Remote enabled on your computer. + This port did not accept the connection. Check Phone Remote on your computer, then check the address, port and firewall. + The network connection was interrupted. Check the network and reconnect. Commands will not be resent automatically. + The computer certificate no longer matches the scanned invitation. Verify the computer, then scan its current QR code again. + The certificate is expired or not yet valid. Check the clocks on both devices and generate a new pairing certificate if needed. + The encrypted connection could not be verified. Check that the invitation address matches the computer certificate, or that the relay has a valid HTTPS certificate. + Pairing was rejected, expired or revoked. Refresh the QR code in Settings → Phone Remote on your computer and pair again. + This phone connection is already in use. Close the previous phone connection, wait a moment, then retry. + The server rejected the WebSocket connection. Check the relay address and reverse proxy configuration. + The computer did not become ready or went offline. Keep Pebrel running and check Settings → Phone Remote on your computer. + The desktop session service is not ready. Open Pebrel, check Settings → Phone Remote, then reconnect. + The connection returned incompatible data. Update Pebrel on both devices and your relay server, then reconnect. + The computer connection closed. Keep Pebrel running with Phone Remote enabled, then reconnect. Commands will not be resent automatically. + Could not connect to the computer. Check the computer pairing page and reconnect. + The computer invitation could not be saved on this device. Retry saving it. + This is not a supported Pebrel invitation. Scan the current QR code in Settings → Phone Remote on your computer. + Could not read the pane output. Check the computer connection and retry. + Command delivery could not be confirmed. Check the computer output before sending it again. + Input was not accepted. Check the session connection and input permission. + The operation could not be completed. Check the connection or settings and retry. + Import and connect + Configure manually + Hide manual settings + diff --git a/mobile/android/app/src/main/res/values/credentials.xml b/mobile/android/app/src/main/res/values/credentials.xml new file mode 100644 index 0000000..7497001 --- /dev/null +++ b/mobile/android/app/src/main/res/values/credentials.xml @@ -0,0 +1,15 @@ + + Password + Save the host and choose whether to keep its password on this device. + Save password + Saved · leave blank to keep + Password saved on this device + Leave the password blank to keep it. Enter a new password to replace it. + The password will be encrypted and saved on this device. + Used for this connection only. Saving removes any previously stored password. + Remove saved password + The saved password could not be read. Enter it again and save to replace it. + Could not save the host or password. Your form remains open; please try again. + Could not remove the saved password. Please try again. + Enter a password to connect. Saved passwords are specific to the host address, port and username. + diff --git a/mobile/android/app/src/main/res/values/deployment.xml b/mobile/android/app/src/main/res/values/deployment.xml new file mode 100644 index 0000000..7d53c65 --- /dev/null +++ b/mobile/android/app/src/main/res/values/deployment.xml @@ -0,0 +1,32 @@ + + + Relay is ready + Save both configuration files to your PC beside the pairing helper. Start the helper on the PC to attach its Pebrel sessions, then connect here. + Save PC configuration + Save phone invitation + Copy PC startup command + File saved + Could not save the file. Choose a writable location and try again. + Startup command copied + The server may need to download container images and request a certificate. Cancel closes the SSH operation; any service already started remains on your server. + Saved SSH host + Manual SSH + Choose SSH host + Public domain + HTTPS port + HTTP challenge port + Server installation directory + Requires Linux, Docker with Compose, and an SSH account allowed to run Docker. Point this domain to the server. Public port 80 must reach the HTTP challenge port; open the HTTPS port as well. Existing unrelated directories are refused. + Deploy and start + Checking configuration… + Checking server requirements… + Uploading relay source… + Creating connection credentials… + Building and starting the service… + Checking the HTTPS endpoint… + Check the domain, ports and dedicated installation directory. + Docker with Compose must be installed and accessible to this SSH account. + This directory contains unrelated files or an unsupported deployment. Choose a new empty directory. + The service has not passed HTTPS verification. Check domain DNS, public ports and the server certificate, then retry. + Deployment did not finish. Check server access, available space and container downloads, then retry. + diff --git a/mobile/android/app/src/main/res/values/git_strings.xml b/mobile/android/app/src/main/res/values/git_strings.xml new file mode 100644 index 0000000..dae1266 --- /dev/null +++ b/mobile/android/app/src/main/res/values/git_strings.xml @@ -0,0 +1,54 @@ + + Git + Diff + Refresh + Changes + History + Staged (%1$d) + Changes (%1$d) + Stage + Unstage + Stage all + Unstage all + Stage %1$s + Unstage %1$s + Commit + Commit message + Commit staged changes (%1$d) + Keep the message within 16 KiB of UTF-8 text. + Fetch + Pull (fast-forward) + Push + Sync (fetch, pull, push) + %1$d ahead · %2$d behind + No tracking branch + Working tree clean + Merge conflict + No commits to display. + Load earlier commits + Commit snapshot · read-only + No differences in this view. + Completed: %1$s + Read-only connection. Enable input on the computer to change this repository. + Connect to the computer, then reopen Git from its terminal. + The session or working directory changed. Return to the terminal and reopen Git. + Update Pebrel on the computer to use mobile Git. + Repository state changed. Refresh and review the changes before continuing. + Another Git operation is running in this repository. Refresh shortly. + Resolve conflicts and stage changes before committing. + This output is too large or is not UTF-8. View it on the computer. + The operation completed, but refreshing failed. Refresh to check the result; do not repeat the operation. + The operation result is not confirmed. Check the repository on the computer before trying again. + Git uses a local or WSL terminal on the connected computer. + Git returned an error. Check the repository, identity and remote configuration on the computer. + Error details + Repository details + Up to date + ↑ %1$d ↓ %2$d + %1$d changed · %2$d staged + Changes + Untracked files + Staged changes + Collapsed + Expanded + diff --git a/mobile/android/app/src/main/res/values/native_service.xml b/mobile/android/app/src/main/res/values/native_service.xml new file mode 100644 index 0000000..aa3e602 --- /dev/null +++ b/mobile/android/app/src/main/res/values/native_service.xml @@ -0,0 +1,55 @@ + + Choose the server you want to use. Pebrel installs a small encrypted relay over SSH; no Docker, Node.js or domain name is required. + Pebrel relay service + Manual installation commands + Upload and extract the companion offline relay kit, then enter its pebrel-relay-manual directory as root. Run the commands below; replace SERVER_IP or PORT if shown. The script prints four steps and stops on failure. No Docker or Node.js is required. + Advanced connection settings + Relay port + Connection address override (optional) + Defaults to the SSH host address. Change this only when your computer and phone use a different route to this server. + Install and start + Check status + Start + Stop + Uninstall + Stops the relay and removes only Pebrel-managed service files. Connected devices will disconnect. Credentials are kept for reinstallation unless you choose to remove them. + Also remove pairing configuration and keys + Supports Linux x64/ARM64 with systemd 239+ or Alpine OpenRC. Root SSH access is required for management; the relay runs without root privileges. Both devices must reach the relay port (443 by default); Pebrel does not change your firewall. + On your computer, open Settings → Phone connection → Relay, select this SSH server and choose Use this relay server. Then generate a QR code for your phone. You can also save a private configuration file below. + This file contains server access credentials. Transfer it privately to your computer; do not publish it. + Uploading the bundled service to the server… + Upload sent. Waiting for server verification… + Verifying the file and installing the service… + Connect to SSH + Check system and bundled file + Upload to server + Install, start and verify + Waiting + In progress + ✓ Done + Cancelled + %1$d. %2$s · %3$s + Sent to SSH: %1$d / %2$d KiB + No new progress for %1$d s; waiting for the server. You can cancel. + Starting the service… + Checking the service listener… + Stopping and removing the service… + Installed · stopped + Service uninstalled + Operation cancelled. Any installed service is retained; check status before retrying. + Not installed + Status not checked + The verified native service package is missing or invalid. Install an updated Pebrel package. + Requires Linux x64/ARM64 with a running systemd 239+ or OpenRC service manager. SSH login alone does not provide a service manager. + Operation failed + OpenRC is missing rc-update or supervise-daemon. Check that OpenRC is fully installed and running. + The SSH host needs head, base64, sha256sum, mktemp and chmod to receive and verify the service. + The system service manager rejected the operation. Check the pebrel-relay service status on the server before retrying. + The relay could not drop root privileges. Check that the server has a non-root nobody account; the service will not run as root. + A required service file or service-manager executable is missing. Check status and the server environment. + Service installation and management currently require a root SSH account. + Existing service files differ or belong to another installation. They were not overwritten or removed. Check the server installation first. + The service did not become ready. Check whether the selected port is already in use, then check status. + Enter a server IP address or hostname, without a URL scheme, path or credentials. + The operation did not complete. Check the server connection and service status before retrying. + diff --git a/mobile/android/app/src/main/res/values/pairing.xml b/mobile/android/app/src/main/res/values/pairing.xml new file mode 100644 index 0000000..9db74e3 --- /dev/null +++ b/mobile/android/app/src/main/res/values/pairing.xml @@ -0,0 +1,51 @@ + + Connect computer + Find computer + Verify and approve + Open Pebrel on your computer, go to Settings → Phone Remote, and turn it on. + Settings → Phone Remote + Scan + Paste + Inline QR scanner + Point at the QR code on your computer + The QR code is single-use and expires in 10 minutes. + Enable the camera to scan your computer’s QR code here. + Enable camera + Open app settings + No camera is available. Use a pairing code or paste an invitation. + The camera did not start. Try again, use a code, or paste an invitation. + QR code read. Check the pairing result or scan again. + Scan again + Paste the content from Copy complete invitation on your computer + On different networks? Use a self-hosted relay + Both devices need to be on the same Wi-Fi or local network. + The 8 digits below the QR code on your computer + Pair %1$s + Scan QR code + Scan the code shown by your Pebrel computer + Paste invitation + Read invitation + Local network + Relay server + On your computer, open Settings → Phone Remote. Scan the QR code, compare the verification codes, then approve this phone on your computer. + Waiting for computer approval + Check that this code matches the one in Phone Remote on your computer. Approve only when both codes match. + Computer name + Server address + Device ID + Access key + Certificate fingerprint (optional) + Invalid invitation or connection settings. Check the address and keys. + Deploy using SSH + Use an 8-digit pairing code + Hide pairing code + 8-digit pairing code + Connect to the same Wi-Fi as your computer. Select the computer below and enter the code shown under its QR code in Settings → Phone Remote. + Looking for computers on this network… + No computer found. Check that Phone Remote is on and both devices are on the same Wi-Fi. You can also scan the QR code or paste the complete invitation. + Search again + Pair this computer + Connecting… + The code is incorrect, expired or already used. Refresh it on your computer and try again. + The computer did not complete the connection. Check its network and firewall, then refresh the pairing code before retrying. + diff --git a/mobile/android/app/src/main/res/values/reader_extension_strings.xml b/mobile/android/app/src/main/res/values/reader_extension_strings.xml new file mode 100644 index 0000000..b53bf0b --- /dev/null +++ b/mobile/android/app/src/main/res/values/reader_extension_strings.xml @@ -0,0 +1,4 @@ + + Rendering failed. Expand the source to inspect it. + Rendering… + diff --git a/mobile/android/app/src/main/res/values/ssh_session_strings.xml b/mobile/android/app/src/main/res/values/ssh_session_strings.xml new file mode 100644 index 0000000..59882fd --- /dev/null +++ b/mobile/android/app/src/main/res/values/ssh_session_strings.xml @@ -0,0 +1,6 @@ + + Terminal + Persistent session + Runs on the SSH host. Leave the name empty for the default session. Disconnecting detaches the client; remote processes keep running. The host needs the selected session manager installed. + Use up to 64 letters, numbers, underscores or hyphens; dots are also accepted by session types that support them. + diff --git a/mobile/android/app/src/main/res/values/strings.xml b/mobile/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..c98c6c0 --- /dev/null +++ b/mobile/android/app/src/main/res/values/strings.xml @@ -0,0 +1,266 @@ + + Pebrel + Back + Settings + Sessions + SSH hosts + Computers + Add SSH host + Local terminal + Close session + Close + Save + Cancel + Send + Connect + Name + Host address + Username + Port + Password + Connect to Pebrel desktop + Allow command input on this channel + Edit locally… + Verify host identity + Compare this fingerprint with your server before trusting it. A changed key is rejected. + Trust and connect + Operation failed + Refresh output + Recent output from the original pane. This preview does not stream the full terminal grid or claim exclusive input control. + Background and sessions + Keep sessions in background + Keeping user sessions active + A foreground service uses a persistent notification and may consume more power. Android can still terminate the process. Local shells cannot survive force-stop. + Stop sessions + Task notifications + A task needs your attention or has completed. + Enable system notifications + Desktop connection preview + The computer needs a reachable SSH server and a running Pebrel build with mobile-bridge on PATH. Windows, Linux and macOS use the same channel protocol. + Connection preview: local and SSH terminals, LAN and relay QR pairing, and bounded desktop output. Durable notification replay, desktop file transfer and WebDAV are not available yet. + Appearance + Follow system + Connect computer + Private phone invitation + Start your relay and the desktop connector, then paste the contents of phone-DEVICE.txt here. Your relay must have a valid HTTPS certificate. + This preview uses your relay as a trusted endpoint: its operator can access session data. Credentials are stored using Android Keystore. Input requires --allow-input on the computer. + Disconnect + All + All %1$d + All sessions + No open sessions yet. Connect a host or open a local terminal. + No saved hosts. Tap + to add an SSH host. + Connect a computer to continue your Pebrel sessions. + Search name, address or username + No matching hosts + Edit host + Delete host + Host actions + Remove %1$s from saved hosts? Open sessions will keep running. + Verify the host fingerprint on first connection. Passwords can be saved on this device. + Connected + Connecting… + Ended + Failed + Disconnected + Finished + Needs attention + Running + Idle + Unknown + Waiting for output + Switch session + Session actions + This device + Pebrel Tabs + No panes available + Connection is unavailable. Return to Computers to reconnect. + Terminal + Recent output + Read only + Compose + Direct + Tap the terminal to type directly + Input mode + Auxiliary keys + Long command editor + Keep draft + Tap to fill, then send + Font + Font size + Background + Solid color + Input + Default input + Command suggestions + Local command prefixes only. Suggestions never execute a command. + Connections + Application + About + On + Off + Theme + Theme preview + 中文 · English · 0123 → + Light + Dark + Not available yet + Connection preview + System notification settings + Font changes apply to terminal output. Layout follows system text scaling. + The terminal uses the selected theme background. Image backgrounds are not available yet. + Compose edits locally before sending. Direct input is for interactive terminal programs. + Desktop file transfer, WebDAV and structured reading are not available yet. + Online desktop task updates use system notifications. Offline replay is not available yet. + Paired computers are saved on this device. Tap a saved computer to reconnect. Version 2 connections use end-to-end encryption. + Terminal details + Full title + Working directory + Computer connection + Notifications + Open system settings + Close %1$s? This ends the terminal connection and its local process. + Input unavailable while disconnected + Pebrel for Android · Local terminals and SSH connections. + Host icon + Authentication + Password + Automatic + Leave the password blank for Tailscale SSH or another server-approved passwordless login. Other SSH servers may require a password. + Key file + Password login. Key files are not supported yet. + Group + Production + Development + Jump host + No jump host + Choose Save password to keep it encrypted on this device. + Connection failed + Establishing connection… + Retry + Edit host + The remote session has ended. Output remains available to read. + Connecting to the server and negotiating an encrypted channel… + Waiting for your confirmation of the host fingerprint… + Verifying username and password… + Opening the remote terminal… + The host address could not be resolved. Check the address and DNS connection. + The server did not respond in time. Check the address, port and network, then retry. + Could not connect to the SSH port. Check the SSH service, port and firewall. + Authentication failed. Check the username and password, and whether the server allows password login. + The host fingerprint has changed. Connection was blocked. Verify the server identity before removing and adding this host again. + The host fingerprint was not confirmed, or confirmation timed out. Reconnect to verify it again. + The server could not open the terminal channel. Check shell access for this account. + The SSH connection was interrupted. Check the network and reconnect. Commands will not be resent automatically. + SSH channel negotiation failed. Check that this is an SSH port and that the server supports current SSH algorithms. + Could not open this terminal. Retry or check the host configuration. + The SSH cryptographic component failed to initialize on this device. Update the app and retry. + Save and connect + Hide terminal header + Show terminal header + Show or hide keyboard + Font size %1$d sp + New tab + New terminal + Open file + Open + Working directory (optional) + Full path on the computer + Markdown, source code, text and images open as shared tabs. + Close tab + Close %1$s + Close “%1$s” on the computer? This removes the shared tab from both devices. + Manage computer tabs + Terminal + SSH terminal + Document + Source code + Image + A task is still running in this tab. Confirm its closure on the computer. + This file has unsaved changes. Save or close it on the computer first. + The computer is still saving or closing this tab. Try again when it finishes. + Update Pebrel on the computer to manage and read shared file tabs. + File details + Preview + Source + Contents + Refresh file + Copy + Copied + Copy source + Copy failed. Select a smaller section and try again. + Open image + Reset zoom + Reading the computer’s unsaved draft + The file changed on the computer. Refresh to read the latest content. + Disconnected · showing the content already loaded + The computer is disconnected. Reconnect to continue. + The file has not loaded. Check its state on the computer and retry. + This file exceeds the 16 MiB reader limit. Open it on the computer. + This tab or file type has no mobile reader. + Image decoding failed. Check the image format and file on the computer. + This link did not open. Use an HTTP link or a local file path. + Conversation + Terminal view + You + Tool + In progress + Long content is limited here. View the full output in the terminal. + Earlier messages + Messages will appear here when this Agent writes its conversation. + Answer this interaction in the terminal + Update the computer app to use conversation view. + This Agent session ended or changed. Return to the terminal and reopen its conversation. + Waiting for a Codex or Claude Code conversation file reported by this pane. Continue in the terminal while it is being identified. + The conversation is updating. Refreshing its current messages. + The recent-history cache is full. View older history in the terminal. + The question changed. Review its current options before answering. + The Agent is working or awaiting an interaction. Your draft is kept. + No receipt was received. Your draft is kept; check the terminal before sending it again. + Allow + Deny + Refresh chat + SFTP files + Add file or folder + Upload file + Download to device + New folder + Go to path + Hide hidden files + Show hidden files + Parent folder + Folder + Symbolic link + File + Actions for %1$s + This folder is empty. + Load more files + Refresh + Open + Rename + Delete + Delete “%1$s” from this host? Only this file, link or empty folder will be removed. + Confirm + Upload complete. + Saved to the selected location. + Cancelling transfer… + Transfer cancelled. + Some names contain unsupported control characters or exceed the path limit. + This path no longer exists. Refresh the folder or check the path. + The host denied this file operation. Check the account permissions. + The destination already exists. Choose a different name; existing files are kept. + SFTP is not enabled on this host. The terminal remains available. + This file or directory view changed. Refresh before continuing. + SSH is disconnected. Return to the terminal to reconnect. + The file request timed out. Check the connection and retry. + The current file-view limit was reached. Close a transfer or open a smaller folder. + Preview supports files up to 16 MiB. Download this file to read it on your device. + This file is not a supported UTF-8 text or image preview. Download it to open on your device. + The device file provider did not complete the operation. Choose the file or destination again. + Enter a valid path or name. Names must not contain slashes or control characters. + The operation did not finish. Check the path, permissions and whether the folder is empty. + Change connection address + Computer address and port + Use the address shown in the computer’s Phone Remote settings. Pairing and permissions stay unchanged. The address is saved after connecting successfully. + Enter a valid computer address and port. + diff --git a/mobile/android/app/src/main/res/values/styles.xml b/mobile/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..61c7765 --- /dev/null +++ b/mobile/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + diff --git a/mobile/android/app/src/main/res/values/terminal_screen_strings.xml b/mobile/android/app/src/main/res/values/terminal_screen_strings.xml new file mode 100644 index 0000000..8e79a0c --- /dev/null +++ b/mobile/android/app/src/main/res/values/terminal_screen_strings.xml @@ -0,0 +1,14 @@ + + This connection returns plain text without terminal colors or a cursor. + Reading terminal… + Waiting for terminal output + Latest output ↓ + Display + Color terminal grid + Plain text + Edit + Keyboard + More shortcuts + Wrap to phone width + Select and copy screen text + diff --git a/mobile/android/app/src/main/res/values/terminal_settings.xml b/mobile/android/app/src/main/res/values/terminal_settings.xml new file mode 100644 index 0000000..ae2d12a --- /dev/null +++ b/mobile/android/app/src/main/res/values/terminal_settings.xml @@ -0,0 +1,16 @@ + + Font and size + Font size · %1$d sp + System monospace + Cursor + Cursor shape + Block + Bar + Underline + Blink cursor + Applies to local and SSH terminals. Applications can hide the cursor; blinking pauses while the terminal is inactive. + Gestures + Pinch to resize text + Use two fingers to change the terminal font size. The size is saved for future sessions. Use one finger to scroll, or hold to select text. + Solid theme background + diff --git a/mobile/android/app/src/main/res/values/voice_strings.xml b/mobile/android/app/src/main/res/values/voice_strings.xml new file mode 100644 index 0000000..2030e16 --- /dev/null +++ b/mobile/android/app/src/main/res/values/voice_strings.xml @@ -0,0 +1,18 @@ + + Hold to speak; tap for the voice model + Finish and transcribe + Transcribing on this phone… + Release to cancel + Release to transcribe + Slide up to cancel · Text goes into the draft + %1$d / 60 seconds + Multilingual local recognition, including Chinese and English. Model: 148 MB. No automatic downloads. Audio stays on this phone; recognition never runs a command. + Downloading model: %1$d%% + Download model · 148 MB + Model installed. Hold the microphone to speak. After granting microphone permission, hold again to start. + Delete downloaded model + Microphone access is required. Allow it in app permissions, then hold to speak again. + Recording was too short. Hold to speak a little longer. + Could not download, verify or remove the model. Check the connection and available storage, then try again. + The local speech engine is unavailable in this build. + diff --git a/mobile/android/app/src/preview/res/values-zh-rCN/strings.xml b/mobile/android/app/src/preview/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000..26dd387 --- /dev/null +++ b/mobile/android/app/src/preview/res/values-zh-rCN/strings.xml @@ -0,0 +1,3 @@ + + Pebrel + diff --git a/mobile/android/app/src/preview/res/values/strings.xml b/mobile/android/app/src/preview/res/values/strings.xml new file mode 100644 index 0000000..26dd387 --- /dev/null +++ b/mobile/android/app/src/preview/res/values/strings.xml @@ -0,0 +1,3 @@ + + Pebrel + diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopReadSchedulerTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopReadSchedulerTest.kt new file mode 100644 index 0000000..6148fe0 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopReadSchedulerTest.kt @@ -0,0 +1,34 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.* +import kotlinx.coroutines.test.* +import org.junit.Assert.* +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class DesktopReadSchedulerTest { + @Test fun inputWakesIdleImmediatelyWithoutConcurrentOrLostReads() = runTest { + val reader = DesktopReadScheduler() + var reads = 0 + var hold: CompletableDeferred? = null + val job = launch { reader.run { reads++; hold?.await(); false } } + runCurrent() + advanceTimeBy(2000) + runCurrent() + val before = reads + hold = CompletableDeferred() + reader.refresh() + runCurrent() + assertEquals(before + 1, reads) // No clock advance after the keystroke. + repeat(1000) { reader.refresh() } + runCurrent() + assertEquals(before + 1, reads) // Only one request in flight. + hold!!.complete(Unit) + hold = null + runCurrent() + assertEquals(before + 2, reads) // One coalesced follow-up, never 1000. + job.cancelAndJoin() + advanceTimeBy(1000) + assertEquals(before + 2, reads) + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopReconnectTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopReconnectTest.kt new file mode 100644 index 0000000..872cc70 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopReconnectTest.kt @@ -0,0 +1,59 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.* +import kotlinx.coroutines.test.* +import org.junit.Assert.* +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class DesktopReconnectTest { + @Test fun backgroundPausesRetriesForegroundResumesAndClosePreventsResurrection() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + val attempts = mutableListOf() + val reconnect = DesktopReconnect(this) { attempts += it } + try { + reconnect.foreground(true) + repeat(20) { reconnect.schedule("pc") } + runCurrent() + advanceTimeBy(500); runCurrent() + assertEquals(listOf("pc"), attempts) + reconnect.schedule("pc") + reconnect.foreground(false) + advanceTimeBy(60_000); runCurrent() + assertEquals(1, attempts.size) + reconnect.foreground(true) + reconnect.schedule("pc", immediate = true) + advanceTimeBy(1); runCurrent() + assertEquals(2, attempts.size) + reconnect.schedule("pc") + reconnect.forget("pc") + advanceTimeBy(60_000); runCurrent() + assertEquals(2, attempts.size) + } finally { reconnect.clear(); Dispatchers.resetMain() } + } + + @Test fun certificateAndAuthenticationFailuresNeverAutoRetry() { + for (failure in listOf(DesktopFailureKind.AUTHENTICATION, DesktopFailureKind.CERTIFICATE_CHANGED, + DesktopFailureKind.TLS, DesktopFailureKind.PROTOCOL)) assertFalse(DesktopReconnect.retryable(failure)) + for (failure in listOf(DesktopFailureKind.NETWORK, DesktopFailureKind.DISCONNECTED, + DesktopFailureKind.PEER_OFFLINE, DesktopFailureKind.TIMEOUT)) assertTrue(DesktopReconnect.retryable(failure)) + } + + @Test fun startingAnAttemptDoesNotResetTheFailureBackoff() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + var attempts = 0 + val reconnect = DesktopReconnect(this) { attempts++ } + try { + reconnect.foreground(true) + reconnect.schedule("pc") + advanceTimeBy(500); runCurrent() + assertEquals(1, attempts) + reconnect.cancelPending("pc") + reconnect.schedule("pc") + advanceTimeBy(999); runCurrent() + assertEquals(1, attempts) + advanceTimeBy(1); runCurrent() + assertEquals(2, attempts) + } finally { reconnect.clear(); Dispatchers.resetMain() } + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenFrameTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenFrameTest.kt new file mode 100644 index 0000000..4e57c55 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenFrameTest.kt @@ -0,0 +1,59 @@ +package io.github.kuddev.pebrel.mobile.connection + +import org.json.JSONArray +import org.json.JSONObject +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class DesktopScreenFrameTest { + private val theme = intArrayOf(0xffeeeeee.toInt(), 0xff101010.toInt(), 0xffffffff.toInt()) + + IntArray(16) { 0xff000000.toInt() or (it * 0x10101) } + private fun screen() = JSONObject("""{"version":1,"columns":4,"rows":[ + [["中",2,14251863,-258,0],["▛",1,-2,25,1],[" ",1,-257,-26,0]] + ],"cursor":[3,0,1],"palette":[[1,16711680]]}""") + + @Test fun desktopPaletteWinsOverOppositePhoneTheme() { + val snapshot = screen().put("palette", JSONArray("[[256,15787730],[257,660510],[258,16777215],[1,16711680]]")) + val dark = decodeDesktopScreen(snapshot, theme) + val light = decodeDesktopScreen(snapshot, IntArray(19) { 0xffffffff.toInt() }.apply { this[0] = 0xff101010.toInt() }) + assertEquals(0xff0a141e.toInt(), light.background) + assertEquals(dark.background, light.background) + assertEquals(dark.rows[0]!!.cells[21], light.rows[0]!!.cells[21]) + } + + @Test fun preservesCellWidthsForegroundBackgroundPaletteAndCursor() { + val frame = decodeDesktopScreen(screen(), theme) + val row = frame.rows[0]!! + assertEquals(4, frame.columns) + assertEquals(2, row.cells[2]) + assertEquals(0, row.cells[8]) // second half of wide glyph is a spacer + assertEquals(0xffd97757.toInt(), row.cells[3]) + assertEquals(0xffff0000.toInt(), row.cells[15]) + assertEquals(0xff000019.toInt(), row.cells[16]) + assertEquals(0xff005faf.toInt(), row.cells[22]) + assertTrue(frame.cursorVisible) + assertEquals(3, frame.cursorX) + } + + @Test fun rejectsMalformedRowsAndTerminalControlText() { + val tooWide = screen().put("columns", 3) + assertThrows(IllegalArgumentException::class.java) { decodeDesktopScreen(tooWide, theme) } + val control = screen() + control.getJSONArray("rows").getJSONArray(0).getJSONArray(0).put(0, "\u001b]52;clipboard") + assertThrows(IllegalArgumentException::class.java) { decodeDesktopScreen(control, theme) } + val invalidPalette = screen().put("palette", JSONArray("[[269,1]]")) + assertThrows(IllegalArgumentException::class.java) { decodeDesktopScreen(invalidPalette, theme) } + } + + @Test fun rejectsNewVersionsAndOutOfBoundsCursor() { + assertThrows(IllegalArgumentException::class.java) { decodeDesktopScreen(screen().put("version", 2), theme) } + assertThrows(IllegalArgumentException::class.java) { + decodeDesktopScreen(screen().put("cursor", JSONArray("[4,0,1]")), theme) + } + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenStreamTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenStreamTest.kt new file mode 100644 index 0000000..4726294 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenStreamTest.kt @@ -0,0 +1,92 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.* +import kotlinx.coroutines.test.* +import org.json.JSONArray +import org.json.JSONObject +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class DesktopScreenStreamTest { + private fun params(pane: Long = 2) = JSONObject().put("window_id", 1).put("pane_id", pane).put("lines", 100) + private fun event(id: Long, seq: Long, pane: Long = 2) = JSONObject().put("event", "pane.screen") + .put("subscription_id", id).put("sequence", seq).put("data", params(pane).put("screen_seq", seq) + .put("screen", JSONObject().put("version", 1).put("columns", 1) + .put("rows", JSONArray("[[[\"x\",1,0,0,0]]]")).put("cursor", JSONArray("[0,0,1]")) + .put("palette", JSONArray()))) + + @Test fun outputPushesWithoutPollingAndDoesNotWaitForAckRoundTrip() = runTest { + val methods = mutableListOf() + val observed = mutableListOf() + val ack = mutableListOf>() + val stream = DesktopScreenStream({ method, _ -> methods += method; JSONObject().put("subscription_id", 7) }, { + assertEquals(observed.last(), it.getLong("sequence")) // Publish before ACK. + CompletableDeferred().also(ack::add) + }, StandardTestDispatcher(testScheduler)) + val job = launch { stream.run(params()) { observed += it.response.getLong("screen_seq") } } + runCurrent() + repeat(4) { stream.receive(event(7, it + 1L)); runCurrent() } + assertEquals(listOf(1L, 2L, 3L, 4L), observed) + assertEquals(4, ack.size) + assertEquals(listOf("pane.screen.subscribe"), methods) + job.cancelAndJoin() + assertTrue(ack.all { it.isCancelled }) + assertEquals(listOf("pane.screen.subscribe", "pane.screen.unsubscribe"), methods) + } + + @Test fun replacementRejectsOldEventsAndSequenceGapResubscribesToFullSnapshot() = runTest { + var id = 4L + val observed = mutableListOf() + val methods = mutableListOf() + val stream = DesktopScreenStream({ method, _ -> + methods += method + JSONObject().put("subscription_id", id) + }, { CompletableDeferred(Unit) }, StandardTestDispatcher(testScheduler)) + val first = launch { stream.run(params()) { observed += it.response.getLong("screen_seq") } } + runCurrent() + first.cancelAndJoin() + id = 5 + val second = async { runCatching { stream.run(params(3)) { observed += it.response.getLong("screen_seq") } } } + runCurrent() + stream.receive(event(4, 1)) // Retired subscription must not poison the new pane. + stream.receive(event(5, 1, 3)) + runCurrent() + assertEquals(listOf(1L), observed) + stream.receive(event(5, 3, 3)) + runCurrent() + assertEquals(listOf(1L), observed) + id = 6 + advanceTimeBy(100); runCurrent() + stream.receive(event(5, 4, 3)) + stream.receive(event(6, 1, 3)) + runCurrent() + assertEquals(listOf(1L, 1L), observed) + assertEquals(3, methods.count { it == "pane.screen.subscribe" }) + assertFalse(methods.contains("pane.prompt")) + second.cancelAndJoin() + } + + @Test fun malformedStreamsStopAfterTwoReadOnlyRecoveries() = runTest { + var subscriptions = 0L + val stream = DesktopScreenStream({ method, _ -> + if (method == "pane.screen.subscribe") subscriptions++ + JSONObject().put("subscription_id", subscriptions) + }, { CompletableDeferred(Unit) }, StandardTestDispatcher(testScheduler)) + val job = async { runCatching { stream.run(params()) { error("invalid frame published") } } } + repeat(3) { + runCurrent() + stream.receive(event(subscriptions, 3)) + runCurrent() + advanceTimeBy(300) + } + runCurrent() + assertTrue(job.await().isFailure) + assertEquals(3L, subscriptions) + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenSyncTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenSyncTest.kt new file mode 100644 index 0000000..9879610 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopScreenSyncTest.kt @@ -0,0 +1,54 @@ +package io.github.kuddev.pebrel.mobile.connection + +import org.json.JSONArray +import org.json.JSONObject +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class DesktopScreenSyncTest { + private fun full() = JSONObject("""{"screen_seq":1,"screen":{"version":1,"columns":1,"rows":[[["a",1,-257,-258,0]],[["b",1,-257,-258,0]]],"cursor":[0,1,1],"palette":[]}}""") + private fun delta(base: Int = 1) = JSONObject("""{"screen_seq":2,"screen_delta":{"base":$base,"rows":[[1,[["中",1,-257,-258,0]]]],"cursor":[0,1,1],"palette":[]}}""") + @Test fun initialSnapshotThenExactRowsAndUnchangedFrame() { + val sync = DesktopScreenSync() + val first = sync.apply("1:2", full()) + val updated = sync.apply("1:2", delta()) + assertTrue(updated.screenChanged) + val rows = updated.response.getJSONObject("screen").getJSONArray("rows") + assertEquals("a", rows.getJSONArray(0).getJSONArray(0).getString(0)) + assertEquals("中", rows.getJSONArray(1).getJSONArray(0).getString(0)) + assertEquals("b", first.response.getJSONObject("screen").getJSONArray("rows").getJSONArray(1).getJSONArray(0).getString(0)) + val same = delta(2) + same.getJSONObject("screen_delta").put("rows", JSONArray()) + assertFalse(sync.apply("1:2", same).screenChanged) + } + @Test fun wrongBaselinePaneAndRowCannotCorruptTheScreen() { + val sync = DesktopScreenSync() + sync.apply("1:2", full()) + assertThrows(IllegalArgumentException::class.java) { sync.apply("1:2", delta(4)) } + assertThrows(IllegalArgumentException::class.java) { sync.apply("2:2", delta()) } + val invalid = delta() + invalid.getJSONObject("screen_delta").getJSONArray("rows").getJSONArray(0).put(0, 2) + assertThrows(IllegalArgumentException::class.java) { sync.apply("1:2", invalid) } + assertEquals(1L, sync.since("1:2")) + assertEquals(0L, sync.since("2:2")) + sync.reset() + assertEquals(0L, sync.since("1:2")) + assertThrows(IllegalArgumentException::class.java) { sync.apply("1:2", delta()) } + } + + @Test fun wrapMetadataSurvivesDeltasAndCannotChangeWithoutARevision() { + val sync = DesktopScreenSync() + val start = full().apply { getJSONObject("screen").put("wrapped", JSONArray("[true,false]")) } + sync.apply("1:2", start) + val changed = delta().apply { getJSONObject("screen_delta").put("wrapped", JSONArray("[false,false]")) } + val result = sync.apply("1:2", changed) + assertEquals("[false,false]", result.response.getJSONObject("screen").getJSONArray("wrapped").toString()) + val stale = delta(2).apply { getJSONObject("screen_delta").put("rows", JSONArray()).put("wrapped", JSONArray("[true,false]")) } + assertThrows(IllegalArgumentException::class.java) { sync.apply("1:2", stale) } + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopTerminalInputTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopTerminalInputTest.kt new file mode 100644 index 0000000..3f740d6 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/DesktopTerminalInputTest.kt @@ -0,0 +1,127 @@ +package io.github.kuddev.pebrel.mobile.connection + +import android.view.KeyEvent +import kotlinx.coroutines.* +import kotlinx.coroutines.test.* +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class DesktopTerminalInputTest { + @Test fun interruptedAcknowledgementStopsPipelineAndSettlesDraftWithoutReplay() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + val receipts = mutableListOf>() + val rejected = mutableListOf() + val input = DesktopTerminalInput({ _, _ -> error("sequential path") }, { true }, {}, { rejected += it }, + dispatch = { _, _ -> CompletableDeferred().also(receipts::add) }) + try { + val draft = async { input.submit("keep this draft") } + runCurrent() + assertEquals(2, receipts.size) + receipts.first().cancel() + runCurrent() + assertFalse(draft.await()) + assertEquals(listOf(true), rejected) + assertTrue(receipts.all { it.isCancelled }) + assertFalse(input.text("must not replay")) + } finally { input.close(); Dispatchers.resetMain() } + } + + @Test fun highRttInputPipelinesEightInOrderInsteadOfOneKeyPerRoundTrip() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + val writes = mutableListOf() + val receipts = mutableListOf>() + val failures = mutableListOf() + val input = DesktopTerminalInput({ _, _ -> error("sequential path used") }, { true }, {}, { failures += it }, + dispatch = { _, params -> + writes += params.getString("text") + CompletableDeferred().also(receipts::add) + }) + try { + repeat(12) { assertTrue(input.text(it.toString())) } + runCurrent() + assertEquals((0..7).map(Int::toString), writes) + receipts.first().complete(Unit) + runCurrent() + assertEquals((0..8).map(Int::toString), writes) + receipts[1].completeExceptionally(java.io.IOException("unknown delivery")) + runCurrent() + assertEquals(9, writes.size) + assertEquals(listOf(true), failures) + assertTrue(receipts.drop(2).all { it.isCancelled }) + } finally { input.close(); Dispatchers.resetMain() } + } + @Test fun printableTextDoesNotSubmitAndKeysKeepTheirOwnProtocol() { + val command = DesktopTerminalInput.encodeText("你好 😀")!!.single() + assertEquals("pane.prompt", command.method) + assertFalse(command.params.getBoolean("submit")) + val control = DesktopTerminalInput.encodeKey(KeyEvent.KEYCODE_C, 2, "c")!!.single() + assertEquals("c", control.params.getString("key")) + assertTrue(control.params.getJSONObject("modifiers").getBoolean("control")) + assertNull(DesktopTerminalInput.encodeText("\u001b]52;clipboard")) + assertNull(DesktopTerminalInput.encodeText("\n".repeat(129))) + assertNull(DesktopTerminalInput.encodeKey(KeyEvent.KEYCODE_A, 8, "a")) + } + + @Test fun enterPressAndReleaseSubmitOnlyOnceAndSingleLineComposerAddsOneEnter() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + val writes = mutableListOf>() + val input = DesktopTerminalInput({ method, params -> + writes += method to params.optString("key", params.optString("text")) + }, { true }, {}, {}) + try { + assertTrue(input.key(KeyEvent.KEYCODE_ENTER, action = 1)) + assertTrue(input.key(KeyEvent.KEYCODE_ENTER, action = 0)) + runCurrent() + assertEquals(listOf("pane.send_key" to "enter"), writes) + writes.clear() + val submitted = async { input.submit("echo once") } + advanceUntilIdle() + assertTrue(submitted.await()) + assertEquals(listOf("pane.prompt" to "echo once", "pane.send_key" to "enter"), writes) + } finally { input.close(); Dispatchers.resetMain() } + } + + @Test fun orderedQueueStopsAfterUnknownDeliveryAndNeverReplays() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + val writes = mutableListOf() + val failures = mutableListOf() + val gate = CompletableDeferred() + val input = DesktopTerminalInput({ _, params -> + writes += params.getString("text") + gate.await() + throw java.io.IOException("lost acknowledgement") + }, { true }, {}, { failures += it }) + try { + assertTrue(input.text("one")) + assertTrue(input.text("two")) + runCurrent() + assertEquals(listOf("one"), writes) + gate.complete(Unit) + advanceUntilIdle() + assertEquals(listOf("one"), writes) + assertEquals(listOf(true), failures) + assertFalse(input.text("three")) + } finally { input.close(); Dispatchers.resetMain() } + } + + @Test fun readOnlyStaleOwnersAndMultilineClipboardAreRejected() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + var active = true + var count = 0 + val input = DesktopTerminalInput({ _, _ -> count++ }, { active }, {}, {}) + try { + assertFalse(input.paste("rm example\n")) + assertTrue(input.text("queued")) + active = false + advanceUntilIdle() + assertEquals(0, count) + assertFalse(input.text("read only")) + } finally { input.close(); Dispatchers.resetMain() } + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/FrameBoundsTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/FrameBoundsTest.kt new file mode 100644 index 0000000..e96c950 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/FrameBoundsTest.kt @@ -0,0 +1,17 @@ +package io.github.kuddev.pebrel.mobile.connection + +import org.junit.Assert.* +import org.junit.Test + +class FrameBoundsTest { + @Test fun completeFrameLeavesNextFrameUnread() { + val stream = "abc\nnext\n".byteInputStream() + assertEquals("abc\n", readBoundedFrame(stream, 4)!!.toString(Charsets.UTF_8)) + assertEquals("next\n", readBoundedFrame(stream, 5)!!.toString(Charsets.UTF_8)) + assertNull(readBoundedFrame(stream, 5)) + } + @Test fun oversizedAndTruncatedFramesAreRejected() { + assertThrows(java.io.IOException::class.java) { readBoundedFrame("abc\n".byteInputStream(), 3) } + assertThrows(java.io.EOFException::class.java) { readBoundedFrame("abc".byteInputStream(), 8) } + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/NativeRelayDeploymentTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/NativeRelayDeploymentTest.kt new file mode 100644 index 0000000..827e03d --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/NativeRelayDeploymentTest.kt @@ -0,0 +1,117 @@ +package io.github.kuddev.pebrel.mobile.connection + +import org.json.JSONObject +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class NativeRelayDeploymentTest { + @Test fun fourStepsKeepTheExactFailureAndIgnoreLateOrRegressiveProgress() { + var progress = RelayInstallProgress() + assertEquals(InstallStepState.ACTIVE, progress.state(1)) + assertEquals(InstallStepState.WAITING, progress.state(4)) + progress = progress.advance(RelayServiceProgress("checking")) + .advance(RelayServiceProgress("uploading", 32768, 65536)) + assertEquals(InstallStepState.DONE, progress.state(2)) + assertEquals(3, progress.step) + assertEquals(progress, progress.advance(RelayServiceProgress("checking"))) + assertEquals(progress, progress.advance(RelayServiceProgress("uploading", 0, 65536))) + val failed = progress.copy(failed = true) + assertEquals(InstallStepState.FAILED, failed.state(3)) + assertEquals(InstallStepState.WAITING, failed.state(4)) + assertEquals(failed, failed.advance(RelayServiceProgress("ready"))) + val uploaded = progress.advance(RelayServiceProgress("uploaded", 65536, 65536)) + assertEquals(3, uploaded.step) // Remote hash verified; not yet installed. + assertEquals(uploaded, uploaded.advance(RelayServiceProgress("uploading", 65536, 65536))) + val verifying = uploaded.advance(RelayServiceProgress("verifying")) + assertEquals(InstallStepState.ACTIVE, verifying.state(4)) + assertEquals(InstallStepState.CANCELLED, verifying.copy(cancelled = true).state(4)) + assertTrue((1..4).all { verifying.copy(finished = true).state(it) == InstallStepState.DONE }) + } + + @Test fun uploadReportsOnlyWrittenBytesAndStopsCountingOnFailure() { + val bytes = ByteArray(70000) { (it % 251).toByte() } + val written = java.io.ByteArrayOutputStream() + val counts = mutableListOf() + NativeRelayDeployment.writeUpload(written, bytes) { sent, total -> + assertEquals(bytes.size, total) + assertEquals(written.size(), sent) + counts += sent + } + assertArrayEquals(bytes, written.toByteArray()) + assertEquals(listOf(32768, 65536, 70000), counts) + counts.clear() + val broken = object : java.io.OutputStream() { + override fun write(value: Int) = throw java.io.IOException("closed") + } + assertThrows(java.io.IOException::class.java) { + NativeRelayDeployment.writeUpload(broken, bytes) { sent, _ -> counts += sent } + } + assertTrue(counts.isEmpty()) + } + + @Test fun nativeJsonReadsAreBufferedWithoutChangingFrameLimits() { + var reads = 0 + val bytes = ("{\"event\":\"progress\",\"stage\":\"starting\"}\n".repeat(100)).byteInputStream() + val input = object : java.io.InputStream() { + override fun read(): Int = error("must use bulk native reads") + override fun read(b: ByteArray, off: Int, len: Int): Int { reads++; return bytes.read(b, off, len) } + } + var stages = 0 + NativeRelayDeployment.readMessages(input) { stages++ } + assertEquals(100, stages) + assertTrue(reads <= 3) + } + + @Test fun managerFailuresAreNotCollapsedAndUnknownOutputStaysPrivate() { + for (code in listOf("systemd_239_required", "supported_init_required", "openrc_supervisor_required", "service_command_failed", "configuration_directory_not_empty")) { + val error = assertThrows(RelayServiceFailure::class.java) { + NativeRelayDeployment.checkedMessages(1, emptyList(), listOf(JSONObject().put("error", code))) + } + assertEquals(code, error.code) + } + val error = assertThrows(RelayServiceFailure::class.java) { + NativeRelayDeployment.checkedMessages(1, emptyList(), listOf(JSONObject().put("error", "private credentials"))) + } + assertEquals("service_failed", error.code) + assertTrue(NativeRelayDeployment.preflightCommand().contains("/sbin/openrc-run")) + assertTrue(NativeRelayDeployment.preflightCommand().contains("supported_init_required")) + } + private fun access() = JSONObject().put("version", 2).put("url", "wss://192.0.2.10:443") + .put("room", "r".repeat(43)).put("tlsPin", "sha256/" + "A".repeat(43) + "=") + .put("desktopToken", "a".repeat(43)).put("mobileToken", "b".repeat(43)) + + @Test fun ipDeploymentDoesNotRequireDomainAndRejectsShellOrUrlInputs() { + assertEquals("192.0.2.10", NativeRelayDeployment.validatedAddress("192.0.2.10")) + assertEquals("2001:db8::1", NativeRelayDeployment.validatedAddress("[2001:db8::1]")) + listOf("wss://host", "host;touch /tmp/no", "user@host", "-flag", "").forEach { + assertThrows(RelayServiceFailure::class.java) { NativeRelayDeployment.validatedAddress(it) } + } + } + @Test fun exportIsDesktopAccessNotAPhoneInvitation() { + NativeRelayDeployment.validateAccess(access()) + assertThrows(RelayServiceFailure::class.java) { NativeRelayDeployment.validateAccess(access().put("version", 1)) } + assertThrows(RelayServiceFailure::class.java) { NativeRelayDeployment.validateAccess(access().put("url", "wss://user:pass@host")) } + assertThrows(RelayServiceFailure::class.java) { NativeRelayDeployment.validateAccess(access().put("desktopToken", "b".repeat(43))) } + } + @Test fun onlyRealAllowlistedStagesReachProgressAndExportsRemainPrivate() { + val updates = mutableListOf() + val stream = ("{\"event\":\"progress\",\"stage\":\"uploaded\"}\n" + + "{\"event\":\"progress\",\"stage\":\"starting\"}\n" + access().toString(2) + + "\n{\"event\":\"progress\",\"stage\":\"private-server-output\"}\n" + + "{\"installed\":true,\"running\":true,\"ready\":false,\"configuration_retained\":true}\n").byteInputStream() + val results = NativeRelayDeployment.readMessages(stream) { updates += it } + assertEquals(listOf("uploaded", "starting"), updates) + assertEquals(2, results.size) + assertFalse(NativeRelayDeployment.parseState(results.last()).ready) + } + @Test fun unboundedRemoteOutputIsRejected() { + assertThrows(java.io.IOException::class.java) { + NativeRelayDeployment.readMessages(("x".repeat(9000) + "\n").byteInputStream()) {} + } + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/RelayConnectionTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/RelayConnectionTest.kt new file mode 100644 index 0000000..ad2e4f9 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/RelayConnectionTest.kt @@ -0,0 +1,331 @@ +package io.github.kuddev.pebrel.mobile.connection + +import kotlinx.coroutines.* +import okhttp3.* +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import okhttp3.tls.HandshakeCertificates +import okhttp3.tls.HeldCertificate +import org.json.JSONObject +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import java.util.concurrent.atomic.AtomicInteger + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class RelayConnectionTest { + @Test fun discoveredAddressesNeverReplacePinnedIdentityOrInvitationCredentials() { + val pin = "sha256/${"a".repeat(43)}=" + val secure = SecureRelayProfile("b".repeat(43), "device", "c".repeat(43), false) + val saved = RelayProfile("wss://192.0.2.1:4000", "room", "a".repeat(43), "Saved name", pin, "lan", secure) + val candidate = PairingComputer("anything", "Advertised name", "https://192.0.2.2:5000".toHttpUrl(), pin) + val moved = checkNotNull(saved.discoveredAt(candidate)) + assertEquals(saved.id, moved.id) + assertEquals(saved.device, moved.device) + assertEquals(saved.token, moved.token) + assertEquals(saved.name, moved.name) + assertEquals(saved.tlsPin, moved.tlsPin) + assertSame(secure, moved.secure) + assertEquals("wss://192.0.2.2:5000", moved.url) + assertTrue(moved.sameConnection(saved.atLanAddress("192.0.2.2:5000"))) + for (invalid in listOf("ws://192.0.2.2:5000", "user:pass@192.0.2.2:5000", "192.0.2.2:5000/path", "192.0.2.2:5000?token=other")) { + assertThrows(IllegalArgumentException::class.java) { saved.atLanAddress(invalid) } + } + assertNull(saved.discoveredAt(candidate.copy(pin = "sha256/${"d".repeat(43)}="))) + assertNull(saved.discoveredAt(candidate.copy(address = "https://192.0.2.2:5000/other".toHttpUrl()))) + assertNull(moved.discoveredAt(candidate)) + assertNull(RelayProfile(saved.url, saved.device, saved.token, saved.name, pin, "lan").discoveredAt(candidate)) + assertNull(RelayProfile(saved.url, saved.device, saved.token, saved.name, pin, "relay", secure).discoveredAt(candidate)) + val invite = SecureRelayProfile(secure.host, "invite", secure.secret, true, 9999999999) + assertNull(RelayProfile(saved.url, saved.device, saved.token, saved.name, pin, "lan", invite).discoveredAt(candidate)) + } + + @Test fun aComputerIdentitySurvivesAddressRoomAndInvitationChanges() { + fun secure(host: String, grant: String) = SecureRelayProfile(host, grant, "a".repeat(43), false) + val first = RelayProfile("wss://192.0.2.1:4100", "first-room", "a".repeat(43), "PC", + "sha256/${"a".repeat(43)}=", "lan", secure("b".repeat(43), "first-grant")) + val next = RelayProfile("wss://192.0.2.2:4200", "next-room", "c".repeat(43), "Renamed PC", + "sha256/${"d".repeat(43)}=", "lan", secure("b".repeat(43), "next-grant")) + val another = RelayProfile(next.url, next.device, next.token, next.name, next.tlsPin, next.mode, + secure("c".repeat(43), "next-grant")) + assertEquals("routing changes must not create a second computer", first.id, next.id) + assertNotEquals("same address and name do not prove the same computer", next.id, another.id) + val legacy = RelayProfile(first.url, first.device, first.token, first.name) + assertEquals(legacy.legacyId, legacy.id) + assertNotEquals(legacy.id, RelayProfile(next.url, next.device, next.token, next.name).id) + } + + @Test fun savedDuplicatesKeepTheLatestConnectionWithoutMergingOtherComputers() { + fun key(value: Byte) = java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(ByteArray(32) { value }) + fun profile(host: Byte, room: String) = RelayProfile.parse(RelayProfile("wss://example.com", room, key(1), "PC", + "sha256/${"a".repeat(43)}=", "relay", SecureRelayProfile(key(host), "grant-$room", key(2), false)).toJson().toString()) + val old = profile(3, "old") + val other = profile(4, "other") + val latest = profile(3, "new") + val restored = RelayProfile.latestByComputer(listOf(old, other, latest)) + assertEquals(listOf(other, latest), restored) + assertNotEquals(old.legacyId, latest.legacyId) + assertEquals(old.id, latest.id) + assertEquals(restored, RelayProfile.latestByComputer(restored)) + assertTrue(latest.sameConnection(RelayProfile.parse(latest.toJson().toString()))) + assertFalse(old.sameConnection(latest)) + } + + @Test fun desktopTabLabelsAndLegacyBasenamesKeepTheOriginalPaths() { + val path = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" + val pane = DesktopPane(1, 2, path, "C:\\project", "", "idle", 1) + assertEquals("powershell.exe", pane.displayTitle) + assertEquals(path, pane.title) + assertEquals("work", pane.copy(tabLabel = "work").displayTitle) + assertEquals("project", pane.copy(title = "/workspace/project/").displayTitle) + assertEquals("project", pane.copy(title = "").displayTitle) + val snapshot = JSONObject("""{"windows":[{"id":1,"tabs":[{"label":"work","panes":[{"id":2}]}]}]}""") + snapshot.getJSONArray("windows").getJSONObject(0).getJSONArray("tabs").getJSONObject(0) + .getJSONArray("panes").getJSONObject(0).put("title", path) + val parsed = parseDesktopPanes(snapshot).single() + assertEquals("work", parsed.displayTitle) + assertEquals(path, parsed.title) + } + + @Test fun interruptedTlsReadsRetryButHandshakeAndIdentityFailuresStayBlocked() { + val interrupted = javax.net.ssl.SSLException("read interrupted") + assertEquals(DesktopFailureKind.TLS, classifyDesktopFailure(interrupted)) + val network = classifyDesktopFailure(interrupted, tlsEstablished = true) + assertEquals(DesktopFailureKind.NETWORK, network) + assertTrue(DesktopReconnect.retryable(network)) + assertEquals(DesktopFailureKind.TLS, classifyDesktopFailure( + javax.net.ssl.SSLHandshakeException("handshake failed"), tlsEstablished = true)) + assertEquals(DesktopFailureKind.CERTIFICATE_CHANGED, classifyDesktopFailure( + javax.net.ssl.SSLException(PairingCertificateChanged()), tlsEstablished = true)) + assertEquals(DesktopFailureKind.CERTIFICATE_DATE, classifyDesktopFailure( + javax.net.ssl.SSLException(java.security.cert.CertificateExpiredException()), tlsEstablished = true)) + } + + @Test fun aSnapshotCannotPublishASavedComputerBeforeTheProtocolIsValidated() = runBlocking { + var published = 0 + val transport = object : DesktopTransport { + override suspend fun open(allowInput: Boolean, receive: (JSONObject) -> Unit, disconnected: (Throwable?) -> Unit) { + receive(JSONObject().put("event", "runtime.snapshot").put("data", JSONObject().put("process_id", 1))) + receive(JSONObject().put("type", "mobile.ready").put("protocol", "wrong").put("version", 1)) + } + override fun send(frame: JSONObject) = error("invalid protocol must not send") + override fun close() = Unit + } + val client = DesktopRuntimeClient(transport, { published++ }, {}) + try { + assertTrue(runCatching { client.connect(true) }.isFailure) + assertEquals(0, published) + } finally { client.close() } + } + + @Test fun invitationsRequireTlsAndSeparateBoundedCredentials() { + val value = JSONObject().put("version", 1).put("url", "wss://relay.example.com") + .put("device", "computer").put("token", "a".repeat(43)).put("name", "PC") + assertEquals("computer", RelayProfile.parse(value.toString()).device) + for (url in listOf("ws://relay.example.com", "wss://user:pass@relay.example.com", "wss://relay.example.com?token=secret")) { + assertThrows(IllegalArgumentException::class.java) { RelayProfile.parse(value.put("url", url).toString()) } + } + } + + @Test fun tlsWebSocketUsesHeaderCredentialsAndDisconnectSettlesInputWithoutReplay() = runBlocking { + val certificate = HeldCertificate.Builder().commonName("localhost").addSubjectAlternativeName("localhost").build() + val serverTls = HandshakeCertificates.Builder().heldCertificate(certificate).build() + val server = MockWebServer() + server.useHttps(serverTls.sslSocketFactory(), false) + val sent = AtomicInteger() + val disconnected = CompletableDeferred() + val snapshot = CompletableDeferred() + val epoch = "test-link" + fun envelope(body: JSONObject) = JSONObject().put("type", "relay.data").put("link", epoch).put("body", body).toString() + server.enqueue(MockResponse().withWebSocketUpgrade(object : WebSocketListener() { + override fun onOpen(webSocket: WebSocket, response: Response) { + webSocket.send(JSONObject().put("type", "relay.paired").put("link", epoch).toString()) + webSocket.send(envelope(JSONObject().put("type", "mobile.ready").put("protocol", "pebrel.mobile.relay") + .put("version", 1).put("capabilities", JSONObject().put("input", true)))) + } + override fun onMessage(webSocket: WebSocket, text: String) { + val frame = JSONObject(text) + assertEquals(epoch, frame.getString("link")) + val request = frame.getJSONObject("body") + if (request.getString("method") == "events.subscribe") { + webSocket.send(envelope(JSONObject().put("id", request.getString("id")).put("ok", true).put("result", JSONObject()))) + webSocket.send(envelope(JSONObject().put("event", "runtime.snapshot").put("data", JSONObject().put("process_id", 10)))) + } else { + sent.incrementAndGet() + webSocket.close(1012, "test_disconnect") + } + } + })) + server.start() + val profile = RelayProfile.parse(JSONObject().put("version", 1).put("url", "wss://localhost:${server.port}") + .put("device", "computer").put("token", "a".repeat(43)).put("mode", "lan") + .put("tlsPin", CertificatePinner.pin(certificate.certificate)).toString()) + val http = PinnedDesktopTls.client(OkHttpClient(), requireNotNull(profile.tlsPin)) + val client = DesktopRuntimeClient(RelayTransport(profile, http), { snapshot.complete(it) }, { disconnected.complete(Unit) }) + try { + withTimeout(10_000) { + client.connect(true) + assertEquals(10, snapshot.await().getInt("process_id")) + val result = runCatching { client.request("pane.prompt", JSONObject().put("window_id", 1).put("pane_id", 2).put("text", "pwd")) } + assertTrue(result.isFailure) + disconnected.await() + assertEquals(1, sent.get()) + } + val upgrade = server.takeRequest() + assertEquals("Bearer ${profile.token}", upgrade.getHeader("Authorization")) + assertFalse(upgrade.path!!.contains(profile.token)) + } finally { client.close(); http.dispatcher.executorService.shutdown(); http.connectionPool.evictAll(); server.shutdown() } + } + + @Test fun handshakeFailurePreservesTheCauseWithoutExposingRemoteText() = runBlocking { + val expected = DesktopFailureKind.CERTIFICATE_CHANGED + var observed: DesktopFailureKind? = null + val transport = object : DesktopTransport { + override suspend fun open(allowInput: Boolean, receive: (JSONObject) -> Unit, disconnected: (Throwable?) -> Unit) { + disconnected(javax.net.ssl.SSLHandshakeException("private remote detail").apply { + initCause(PairingCertificateChanged()) + }) + } + override fun send(frame: JSONObject) = error("No request may be sent before the handshake") + override fun close() = Unit + } + val client = DesktopRuntimeClient(transport, {}, { observed = it }) + try { + val failure = withTimeout(1000) { runCatching { client.connect(true) }.exceptionOrNull() } + assertTrue(failure is DesktopConnectionFailure) + assertEquals(expected, (failure as DesktopConnectionFailure).kind) + assertEquals(expected, observed) + assertEquals(expected.code, failure.message) + } finally { client.close() } + } + + @Test fun notificationReducerUsesDesktopFinishedStateAndRejectsDuplicateSequences() { + fun snapshot(sequence: Int, state: String): JSONObject = JSONObject("""{ + "process_id":42,"windows":[{"id":1,"tabs":[{"label":"Test","panes":[ + {"id":2,"title":"Test","task_state":"$state","state_change_seq":$sequence} + ]}]}]}""") + val reducer = DesktopTransitions() + assertTrue(reducer.observe(snapshot(1, "running")).isEmpty()) + assertEquals(1, reducer.observe(snapshot(2, "finished")).size) + assertTrue(reducer.observe(snapshot(2, "finished")).isEmpty()) + assertTrue(reducer.observe(snapshot(1, "finished")).isEmpty()) + assertTrue(reducer.observe(snapshot(2, "finished")).isEmpty()) + assertTrue(reducer.observe(snapshot(3, "finished").put("mobile_policy", JSONObject().put("notifications", false))).isEmpty()) + assertTrue(reducer.observe(snapshot(3, "finished").put("mobile_policy", JSONObject().put("notifications", true))).isEmpty()) + assertEquals(1, reducer.observe(snapshot(4, "finished")).size) + } + + @Test fun rejectedSendDisconnectsOnceAndSettlesOtherPendingRequests() = runBlocking { + val pendingSent = CompletableDeferred() + val transportClosed = CompletableDeferred() + val disconnected = AtomicInteger() + val sends = AtomicInteger() + val closes = AtomicInteger() + val transport = object : DesktopTransport { + lateinit var receive: (JSONObject) -> Unit + lateinit var lost: (Throwable?) -> Unit + override suspend fun open(allowInput: Boolean, receive: (JSONObject) -> Unit, disconnected: (Throwable?) -> Unit) { + this.receive = receive + lost = disconnected + receive(JSONObject().put("type", "mobile.ready").put("protocol", "pebrel.mobile.relay").put("version", 1)) + } + override fun send(frame: JSONObject) { + sends.incrementAndGet() + when (frame.getString("method")) { + "events.subscribe" -> { + receive(JSONObject().put("id", frame.getString("id")).put("ok", true)) + receive(JSONObject().put("event", "runtime.snapshot").put("data", JSONObject().put("process_id", 1))) + } + "pane.read" -> pendingSent.complete(Unit) + else -> throw java.io.IOException("send_rejected") + } + } + override fun close() { closes.incrementAndGet(); lost(null); transportClosed.complete(Unit) } + } + val client = DesktopRuntimeClient(transport, {}, { disconnected.incrementAndGet() }) + try { + withTimeout(5000) { + client.connect(true) + val pending = async { runCatching { client.request("pane.read") } } + pendingSent.await() + assertTrue(runCatching { client.request("pane.prompt") }.isFailure) + assertTrue(pending.await().isFailure) + transportClosed.await() + assertTrue(runCatching { client.request("pane.prompt") }.isFailure) + assertEquals(1, disconnected.get()) + assertEquals(1, closes.get()) + assertEquals(3, sends.get()) + } + } finally { client.close() } + } + + @Test fun shortCodeUsesPinnedTlsAndReturnsOnlyAnUnapprovedV2Invitation() = runBlocking { + val certificate = HeldCertificate.Builder().commonName("localhost").addSubjectAlternativeName("localhost").build() + val server = MockWebServer() + server.useHttps(HandshakeCertificates.Builder().heldCertificate(certificate).build().sslSocketFactory(), false) + server.start() + val pin = CertificatePinner.pin(certificate.certificate) + fun key(value: Int) = java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(ByteArray(32) { value.toByte() }) + val invitation = RelayProfile("wss://localhost:${server.port}", "room", key(1), "Computer", pin, "lan", + SecureRelayProfile(key(2), "invite", key(3), true, System.currentTimeMillis() / 1000 + 600)) + val observed = CompletableDeferred() + server.enqueue(MockResponse().withWebSocketUpgrade(object : WebSocketListener() { + override fun onMessage(webSocket: WebSocket, text: String) { + observed.complete(JSONObject(text).getString("code")) + webSocket.send(JSONObject().put("invitation", invitation.toJson().toString()).toString()) + // 与真实短码端点一样,一次响应后主动完成关闭握手。 + webSocket.close(1000, null) + } + })) + server.enqueue(MockResponse().withWebSocketUpgrade(object : WebSocketListener() { + override fun onMessage(webSocket: WebSocket, text: String) { + webSocket.send("{\"error\":\"pairing_code_invalid\"}") + webSocket.close(1000, null) + } + })) + // MockWebServer 会反查回环地址的主机名;本机 hosts 别名不应改变夹具证书的 SAN。 + val address = server.url("/").newBuilder().host("localhost").build() + val computer = PairingComputer("fixture", "Computer", address, pin) + val http = OkHttpClient() + try { + withTimeout(10_000) { + val received = PairingCodeLookup.redeem(computer, "12345678", http) + assertEquals(2, received.version) + assertTrue(received.secure!!.invitation) + assertEquals(invitation.id, received.id) + assertEquals("12345678", observed.await()) + val request = server.takeRequest() + assertEquals("/v2/pair", request.path) + assertNull(request.getHeader("Authorization")) + assertTrue(runCatching { PairingCodeLookup.redeem(computer, "87654321", http) }.exceptionOrNull() is PairingCodeRejected) + } + } finally { http.dispatcher.executorService.shutdown(); http.connectionPool.evictAll(); server.shutdown() } + } + + @Test fun aLivePermissionChangeUpdatesAnIdleWorkspaceWithoutReconnecting() = runBlocking { + val snapshots = mutableListOf() + val transport = object : DesktopTransport { + lateinit var receive: (JSONObject) -> Unit + override suspend fun open(allowInput: Boolean, receive: (JSONObject) -> Unit, disconnected: (Throwable?) -> Unit) { + this.receive = receive + receive(JSONObject().put("type", "mobile.ready").put("protocol", "pebrel.mobile.relay").put("version", 1)) + } + override fun send(frame: JSONObject) { + receive(JSONObject().put("id", frame.getString("id")).put("ok", true)) + receive(JSONObject("""{"event":"runtime.snapshot","data":{"process_id":1,"windows":[],"mobile_policy":{"allow_input":true}}}""")) + } + override fun close() = Unit + } + val client = DesktopRuntimeClient(transport, { snapshots.add(it.getJSONObject("mobile_policy").getBoolean("allow_input")) }, {}) + try { + client.connect(true) + transport.receive(JSONObject("""{"type":"mobile.policy","allow_input":false}""")) + transport.receive(JSONObject("""{"type":"mobile.policy","allow_input":true}""")) + assertEquals(listOf(true, false, true), snapshots) + } finally { client.close() } + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/SecureRelaySessionTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/SecureRelaySessionTest.kt new file mode 100644 index 0000000..02f5bcb --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/SecureRelaySessionTest.kt @@ -0,0 +1,146 @@ +package io.github.kuddev.pebrel.mobile.connection + +import org.json.JSONObject +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import java.util.Base64 + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class SecureRelaySessionTest { + private fun key(value: Int) = Base64.getUrlEncoder().withoutPadding().encodeToString(ByteArray(32) { value.toByte() }) + private fun profile(invitation: Boolean = true) = RelayProfile("wss://fixture.invalid", "room", key(1), "PC", + "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "relay", SecureRelayProfile(key(2), "grant", key(3), invitation, 400)) + + /** Lifecycle fake, not a cryptographic implementation/test. Real Noise is + * exercised by the Rust TLS relay integration tests. */ + private class Cipher : RelayCipher { + var closed = false + override fun hello() = byteArrayOf(99) + override fun finish(bytes: ByteArray) { require(bytes.contentEquals(byteArrayOf(88))) } + override fun verificationCode() = "375219" + override fun seal(bytes: ByteArray) = arrayOf(bytes.copyOf()) + override fun open(bytes: ByteArray) = bytes.copyOf() + override fun close() { closed = true } + } + + @Test fun v2ProfileRoundTripsWithoutLosingSecurityOrChangingIdentityOnEnrollment() { + val profile = profile() + val restored = RelayProfile.parse(profile.toJson().toString()) + assertEquals(2, restored.version) + assertEquals(profile.id, restored.id) + val identity = restored.id + restored.secure = SecureRelayProfile(key(2), "device-grant", key(4), false) + assertEquals(identity, RelayProfile.parse(restored.toJson().toString()).id) + val different = profile.toJson().apply { getJSONObject("secure").put("host", key(5)) } + assertNotEquals(identity, RelayProfile.parse(different.toString()).id) + assertFalse(profile.toString().contains(key(3))) + assertFalse(restored.secure.toString().contains(key(4))) + } + + @Test fun downgradeMissingPinAndNonCanonicalSecretsAreRejected() { + val wrongVersion = profile().toJson().put("version", 1) + assertThrows(IllegalArgumentException::class.java) { RelayProfile.parse(wrongVersion.toString()) } + val missingPin = profile().toJson().apply { remove("tlsPin") } + assertThrows(IllegalArgumentException::class.java) { RelayProfile.parse(missingPin.toString()) } + val badSecret = profile().toJson().apply { getJSONObject("secure").put("secret", "a".repeat(43)) } + assertThrows(IllegalArgumentException::class.java) { RelayProfile.parse(badSecret.toString()) } + } + + @Test fun encryptedEnrollmentRotatesTheQrSecretBeforeRuntimeIsAllowed() { + val profile = profile() + val sent = mutableListOf() + val received = mutableListOf() + val cipher = Cipher() + var context = "" + val session = SecureRelaySession(profile, { sent.add(it) }, { received.add(it) }, { _, _, transcript -> + context = transcript; cipher + }, { 101 }) + assertThrows(IllegalStateException::class.java) { session.runtime(JSONObject()) } + session.paired("epoch") + assertEquals("pebrel.mobile.v2\n${key(2)}\ngrant\ninvite\nepoch", context) + assertEquals(1.toByte(), sent[0][0]) + assertFalse(String(sent[0]).contains(key(3))) + session.binary(byteArrayOf(88)) + assertEquals("secure.connect", JSONObject(String(sent.last())).getString("type")) + session.binary(JSONObject().put("type", "secure.enrolled").put("grant", "new-device") + .put("secret", key(4)).toString().toByteArray()) + assertEquals("secure.ack", JSONObject(String(sent.last())).getString("type")) + assertEquals("new-device", profile.secure!!.grant) + assertFalse(profile.secure!!.invitation) + assertTrue(received.isEmpty()) + session.binary("{\"type\":\"mobile.ready\"}".toByteArray()) + assertEquals("mobile.ready", received.single().getString("type")) + session.runtime(JSONObject().put("id", "once")) + session.close() + assertTrue(cipher.closed) + assertThrows(IllegalStateException::class.java) { session.runtime(JSONObject()) } + } + + @Test fun expiredQrNeverStartsCryptoAndASecondPeerCannotReuseTheChannel() { + var created = 0 + val expired = SecureRelaySession(profile(), {}, {}, { _, _, _ -> created++; Cipher() }, { 400 }) + assertThrows(IllegalArgumentException::class.java) { expired.paired("epoch") } + assertEquals(0, created) + val active = SecureRelaySession(profile(), {}, {}, { _, _, _ -> Cipher() }, { 101 }) + active.paired("first") + assertThrows(IllegalStateException::class.java) { active.paired("second") } + active.close() + } + + @Test fun lanUsesV2AndApprovalShowsTheLocallyDerivedCodeWithoutOpeningRuntime() { + assertEquals("lan", RelayProfile.parse(profile().toJson().put("mode", "lan").toString()).mode) + val received = mutableListOf() + val session = SecureRelaySession(profile(), {}, { received.add(it) }, { _, _, _ -> Cipher() }, { 101 }) + session.paired("epoch") + session.binary(byteArrayOf(88)) + session.binary("""{"type":"secure.approval","code":"375219","expiresAt":221}""".toByteArray()) + assertEquals("mobile.approval", received.single().getString("type")) + assertEquals("375219", received.single().getString("code")) + assertThrows(IllegalStateException::class.java) { session.runtime(JSONObject()) } + session.close() + val changed = SecureRelaySession(profile(), {}, {}, { _, _, _ -> Cipher() }, { 101 }) + changed.paired("epoch") + changed.binary(byteArrayOf(88)) + assertThrows(IllegalArgumentException::class.java) { + changed.binary("""{"type":"secure.approval","code":"111111","expiresAt":221}""".toByteArray()) + } + changed.close() + } + + @Test fun anExistingDeviceRejectsChangedGrantAndNoPlaintextReadyIsPublished() { + val received = mutableListOf() + val session = SecureRelaySession(profile(false), {}, { received.add(it) }, { _, _, _ -> Cipher() }) + session.paired("epoch") + session.binary(byteArrayOf(88)) + assertThrows(IllegalStateException::class.java) { + session.binary("{\"type\":\"secure.accepted\",\"grant\":\"other\"}".toByteArray()) + } + assertTrue(received.isEmpty()) + session.close() + } + + @Test fun approvalClockSkewKeepsLocalWaitBoundedWithoutGrantingRuntimeAccess() { + for (hostDeadline in listOf(31L, 281L, 10_000L)) { + val received = mutableListOf() + val session = SecureRelaySession(profile(), {}, { received.add(it) }, { _, _, _ -> Cipher() }, { 101 }) + session.paired("epoch") + session.binary(byteArrayOf(88)) + session.binary(JSONObject().put("type", "secure.approval").put("code", "375219") + .put("expiresAt", hostDeadline).toString().toByteArray(Charsets.UTF_8)) + assertEquals(221L, received.single().getLong("expiresAt")) + assertThrows(IllegalStateException::class.java) { session.runtime(JSONObject()) } + session.close() + } + val malformed = SecureRelaySession(profile(), {}, {}, { _, _, _ -> Cipher() }, { 101 }) + malformed.paired("epoch") + malformed.binary(byteArrayOf(88)) + assertThrows(IllegalArgumentException::class.java) { + malformed.binary("""{"type":"secure.approval","code":"375219","expiresAt":0}""".toByteArray()) + } + malformed.close() + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/SshStatusTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/SshStatusTest.kt new file mode 100644 index 0000000..4866515 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/connection/SshStatusTest.kt @@ -0,0 +1,57 @@ +package io.github.kuddev.pebrel.mobile.connection + +import java.io.IOException +import java.net.ConnectException +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import io.github.kuddev.pebrel.ssh.NativeSshException +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertFalse +import org.junit.Assert.assertThrows +import org.junit.Test + +class SshStatusTest { + @Test fun persistentSessionsUseVerifiedPtyCommandsAndNeverInterpolateUnvalidatedNames() { + val host = HostProfile("mode", "SSH", "localhost", 22, "user") + assertNull(host.attachCommand()) + assertNull(host.copy(sessionName = "previous.persisted.session").attachCommand()) + assertEquals("exec tmux new-session -A -s 'pebrel'", host.copy(sessionMode = SshSessionMode.TMUX).attachCommand()) + assertEquals("exec herdr", host.copy(sessionMode = SshSessionMode.HERDR).attachCommand()) + assertEquals("exec herdr session attach 'work.dev'", host.copy(sessionMode = SshSessionMode.HERDR, sessionName = "work.dev").attachCommand()) + for (name in listOf("a';echo bad", "$(id)", "../work", "line\nbreak", "-flag", "a".repeat(65))) { + assertFalse(validRemoteSessionName(SshSessionMode.HERDR, name)) + assertThrows(IllegalArgumentException::class.java) { host.copy(sessionMode = SshSessionMode.HERDR, sessionName = name).attachCommand() } + } + } + @Test fun wrappedDnsFailureIsNotReportedAsAuthentication() { + assertEquals(SshFailureKind.UNKNOWN_HOST, classifySshFailure(IOException("transport", UnknownHostException()))) + } + @Test fun nestedTimeoutWinsOverGenericAuthenticationFailure() { + assertEquals(SshFailureKind.TIMEOUT, classifySshFailure(NativeSshException("AUTH", SocketTimeoutException()))) + } + @Test fun rejectedPasswordHasItsOwnActionableCategory() { + assertEquals(SshFailureKind.AUTH, classifySshFailure(NativeSshException("AUTH"))) + } + @Test fun refusedPortAndGenericDisconnectRemainDistinct() { + assertEquals(SshFailureKind.REFUSED, classifySshFailure(ConnectException())) + assertEquals(SshFailureKind.NETWORK, classifySshFailure(IOException())) + } + @Test fun explicitHostKeyRejectionSurvivesTransportWrapping() { + val failure = IOException("outer", SshFailure(SshFailureKind.HOST_KEY_CHANGED, IOException("negotiation"))) + assertEquals(SshFailureKind.HOST_KEY_CHANGED, classifySshFailure(failure)) + } + @Test fun localCryptoFailureIsNotBlamedOnServerAlgorithms() { + assertEquals(SshFailureKind.CRYPTO, classifySshFailure( + IOException(java.security.NoSuchAlgorithmException("local provider")))) + } + @Test fun arbitraryFailureDoesNotBlameCredentials() { + assertEquals(SshFailureKind.UNKNOWN, classifySshFailure(IllegalStateException())) + } + @Test fun nativeErrorsKeepNegotiationChannelAndTrustDistinct() { + listOf(SshFailureKind.NEGOTIATION, SshFailureKind.CHANNEL, SshFailureKind.TRUST_REJECTED).forEach { + assertEquals(it, classifySshFailure(IOException("wrapper", NativeSshException(it.name)))) + } + assertEquals(SshFailureKind.UNKNOWN, classifySshFailure(NativeSshException("FUTURE_CODE"))) + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/git/DesktopGitTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/git/DesktopGitTest.kt new file mode 100644 index 0000000..c454328 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/git/DesktopGitTest.kt @@ -0,0 +1,93 @@ +package io.github.kuddev.pebrel.mobile.git + +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.session.DesktopWorkspace +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.* +import org.json.JSONObject +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class DesktopGitTest { + private val target = DesktopGitTarget("pc", 1, 2, "/repo", 3, 4) + private fun status() = JSONObject("""{"root":"/repo","revision":"${"a".repeat(64)}", + "status":{"branch":"main","head":"abc","upstream":"origin/main","ahead":0,"behind":0, + "entries":[{"path":"src/中文 [1].txt","original":null,"index":"M","worktree":"M","conflict":false}]}}""") + + @Test fun readOnlyAndChangedIdentityRejectWritesBeforeTransport() = runTest { + val calls = mutableListOf() + val controller = DesktopGit(target, this, { method, _ -> calls += method; status() }, { write -> if (write) "input_not_authorized" else null }) + controller.refresh(); runCurrent() + controller.mutate(GitAction.Stage, all = true); runCurrent() + assertEquals(listOf("git.status"), calls) + assertEquals("input_not_authorized", controller.state.value.error?.code) + val host = HostProfile("pc", "Computer", "localhost", user = "test") + val pane = DesktopPane(1, 2, "Shell", "/repo", "", "idle", 0) + val current = DesktopWorkspace("pc", host, listOf(pane), status = "ready", allowInput = true, connectionGeneration = 3, runtimeProcess = 4) + assertNull(target.availability(current, true)) + assertEquals("git_target_changed", target.availability(current.copy(connectionGeneration = 5), true)) + assertEquals("git_target_changed", target.availability(current.copy(panes = listOf(pane.copy(cwd = "/other"))), false)) + assertEquals("git_target_changed", target.availability(current.copy(panes = listOf(pane.copy(sshDestination = "remote"))), false)) + assertEquals("git_disconnected", target.availability(current.copy(status = "disconnected"), false)) + } + + @Test fun successfulCommitAndFailedRefreshKeepReceiptAndNeverRepeatCommit() = runTest { + var reads = 0 + var writes = 0 + val committed = mutableListOf() + val controller = DesktopGit(target, this, { method, _ -> + if (method == "git.status") { + if (++reads > 1) throw DesktopRpcFailure("git_failed", "refresh failed") + status() + } else { writes++; JSONObject("""{"success":true}""") } + }, { null }, committed::add) + controller.refresh(); runCurrent() + controller.mutate(GitAction.Commit, message = "from mobile"); runCurrent() + assertEquals(listOf("from mobile"), committed) + assertEquals(listOf(GitAction.Commit), controller.state.value.completed) + assertEquals("git_refresh_failed", controller.state.value.error?.code) + assertTrue(controller.state.value.needsRefresh) + controller.mutate(GitAction.Commit, message = "from mobile"); runCurrent() + assertEquals(1, writes) + } + + @Test fun unknownDeliveryAndPartialSyncRequireExplicitRefresh() = runTest { + val writes = mutableListOf() + val controller = DesktopGit(target, this, { method, _ -> + when (method) { + "git.status" -> status() + "git.fetch" -> { writes += method; JSONObject("""{"success":true}""") } + else -> { writes += method; throw DesktopConnectionFailure(DesktopFailureKind.NETWORK) } + } + }, { null }) + controller.refresh(); runCurrent() + controller.mutate(GitAction.Sync); runCurrent() + assertEquals(listOf("git.fetch", "git.pull"), writes) + assertEquals(listOf(GitAction.Fetch), controller.state.value.completed) + assertEquals("git_outcome_unknown", controller.state.value.error?.code) + assertTrue(controller.state.value.needsRefresh) + controller.refresh(); runCurrent() + assertEquals(2, writes.size) + } + + @Test fun closingDiffDropsLateResponseAndTreeKeepsExactPaths() = runTest { + val reply = CompletableDeferred() + val controller = DesktopGit(target, this, { _, _ -> reply.await() }, { null }) + controller.openDiff(GitDiffSelection("src/中文 [1].txt")); runCurrent() + controller.closeDiff() + reply.complete(JSONObject().put("text", "+late response")); runCurrent() + assertNull(controller.state.value.diff) + assertNull(controller.state.value.diffText) + val entries = GitSnapshot.parse(status()).entries + assertEquals(listOf("src", "src/中文 [1].txt"), gitTree(entries, emptySet()).map { it.path }) + assertEquals(listOf("src"), gitTree(entries, setOf("src")).map { it.path }) + assertTrue(entries.single().staged && entries.single().unstaged) + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/session/DesktopRecoveryTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/session/DesktopRecoveryTest.kt new file mode 100644 index 0000000..d467a32 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/session/DesktopRecoveryTest.kt @@ -0,0 +1,270 @@ +package io.github.kuddev.pebrel.mobile.session + +import android.os.Looper +import androidx.test.core.app.ApplicationProvider +import io.github.kuddev.pebrel.mobile.connection.* +import kotlinx.coroutines.* +import kotlinx.coroutines.test.* +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.json.JSONObject +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class DesktopRecoveryTest { + @Test fun conversationReadStartedBeforeAnAnswerCannotRestoreOldButtons() = runTest { + fun page(prompt: Boolean) = JSONObject().put("identity", JSONObject().put("kind", "codex").put("session_id", "native").put("epoch", 4)) + .put("unchanged", false).put("messages", org.json.JSONArray()).put("revision", "revision") + .put("before", JSONObject.NULL).put("can_send", !prompt).put("state", if (prompt) "waiting_input" else "finished") + .put("prompt", if (prompt) JSONObject().put("id", "first-question").put("text", "Choose scope") + .put("options", org.json.JSONArray(listOf("Current", "All"))).put("selected", 0) else JSONObject.NULL) + val cache = ConversationCache() + var pendingRead: CompletableDeferred? = null + val writes = mutableListOf() + val client = DesktopConversation(ConversationIdentity("codex", "native"), "view", cache, + request = { method, _ -> + if (method == "conversation.read") pendingRead?.await() ?: page(true) + else { writes += method; JSONObject().put("accepted", true) } + }, availability = { null }) + client.refresh() + val prompt = checkNotNull(client.state.value.prompt) + pendingRead = CompletableDeferred() + val read = launch { client.refresh() } + runCurrent() + client.choose(prompt, 1) + pendingRead!!.complete(page(true)) + read.join() + assertNull(client.state.value.prompt) + assertFalse(client.state.value.canSend) + assertEquals(listOf("conversation.choose"), writes) + assertTrue(runCatching { client.choose(prompt, 0) }.exceptionOrNull() is DesktopRpcFailure) + val restored = checkNotNull(cache.get("view")) + assertNull(restored.identity.epoch) + assertNull(restored.prompt) + assertFalse(restored.canSend) + } + + @Test fun conversationKeyRechecksOwnershipAndNeverReplaysAfterAnUncertainReply() = runTest { + var available: String? = null + val sent = mutableListOf() + val client = DesktopConversation(ConversationIdentity("claude", "native"), "key-view", ConversationCache(), + request = { method, params -> + if (method == "conversation.read") JSONObject("""{"identity":{"kind":"claude","session_id":"native","epoch":8},"messages":[],"unchanged":false,"revision":"r","before":null,"can_send":true,"state":"finished","prompt":null}""") + else { sent += params.getString("key"); throw java.io.IOException("response lost") } + }, availability = { available }) + client.refresh() + assertTrue(runCatching { client.key("Ctrl+C") }.isFailure) + assertFalse(client.state.value.canSend) + available = "conversation_identity_changed" + assertTrue(runCatching { client.key("Esc") }.exceptionOrNull() is DesktopRpcFailure) + assertEquals(listOf("Ctrl+C"), sent) + } + + private class Link(private val autoSnapshot: Boolean = true) : DesktopTransport { + lateinit var receive: (JSONObject) -> Unit + lateinit var lost: (Throwable?) -> Unit + var closed = false + val methods = java.util.concurrent.CopyOnWriteArrayList() + override suspend fun open(allowInput: Boolean, receive: (JSONObject) -> Unit, disconnected: (Throwable?) -> Unit) { + this.receive = receive + lost = disconnected + receive(JSONObject().put("type", "mobile.ready").put("protocol", "pebrel.mobile.relay").put("version", 1) + .put("capabilities", JSONObject().put("input", true))) + } + override fun send(frame: JSONObject) { + val method = frame.getString("method") + methods += method + receive(JSONObject().put("id", frame.getString("id")).put("ok", true).put("result", JSONObject())) + if (method == "events.subscribe" && autoSnapshot) receive(JSONObject("""{ + "event":"runtime.snapshot","data":{"process_id":11,"windows":[{"id":1,"tabs":[ + {"label":"Project","panes":[{"id":2,"title":"Shell","task_state":"idle"}]}]}]}} + """)) + } + override fun close() { closed = true } + } + + private suspend fun TestScope.awaitState(condition: () -> Boolean) { + repeat(200) { + shadowOf(Looper.getMainLooper()).idle() + runCurrent() + if (condition()) return + withContext(Dispatchers.IO) { delay(5) } + } + fail("repository did not reach expected state") + } + + @Test fun foregroundRecoveryKeepsComputerPaneAndDraftAndDoesNotReplayInput() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + val links = mutableListOf() + val repository = SessionRepository(ApplicationProvider.getApplicationContext()) { Link().also(links::add) } + try { + repository.foregroundChanged(true) + val id = repository.connectRelay(RelayProfile("wss://example.com", "pc", "a".repeat(43), "PC")) + awaitState { repository.desktops.value.singleOrNull()?.status == "ready" } + val first = repository.desktops.value.single() + val target = "$id:1:2" + repository.setDraft(target, "unsent draft") + repository.foregroundChanged(false) + links.first().lost(java.io.IOException("network gone")) + awaitState { repository.desktops.value.single().status == "disconnected" } + advanceTimeBy(60_000); runCurrent() + assertEquals(1, links.size) + assertEquals(first.panes, repository.desktops.value.single().panes) + + repository.foregroundChanged(true) + advanceTimeBy(1); runCurrent() + awaitState { repository.desktops.value.single().status == "ready" && links.size == 2 } + val recovered = repository.desktops.value.single() + assertEquals(id, recovered.id) + assertEquals(first.panes, recovered.panes) + assertEquals(first.connectionGeneration + 1, recovered.connectionGeneration) + assertEquals("unsent draft", repository.drafts.value[target]) + links.first().lost(DesktopConnectionFailure(DesktopFailureKind.AUTHENTICATION)) + shadowOf(Looper.getMainLooper()).idle() + assertEquals("ready", repository.desktops.value.single().status) + assertTrue(links.all { link -> link.methods.none { it in setOf("pane.prompt", "pane.send_key") } }) + repository.closeDesktop(id) + advanceTimeBy(60_000); runCurrent() + assertTrue(repository.desktops.value.isEmpty()) + assertEquals(2, links.size) + } finally { repository.foregroundChanged(false); repository.closeAll(); Dispatchers.resetMain() } + } + + @Test fun successfulComputerWithChangedCertificateRequiresUserAction() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + val links = mutableListOf() + val repository = SessionRepository(ApplicationProvider.getApplicationContext()) { Link().also(links::add) } + try { + repository.foregroundChanged(true) + repository.connectRelay(RelayProfile("wss://example.com", "pc", "a".repeat(43), "PC")) + awaitState { repository.desktops.value.singleOrNull()?.status == "ready" } + links.first().lost(DesktopConnectionFailure(DesktopFailureKind.CERTIFICATE_CHANGED)) + awaitState { repository.desktops.value.single().status == "disconnected" } + repository.foregroundChanged(false) + repository.foregroundChanged(true) + advanceTimeBy(60_000); runCurrent() + assertEquals(1, links.size) + assertEquals(DesktopFailureKind.CERTIFICATE_CHANGED, repository.desktops.value.single().failure) + } finally { repository.foregroundChanged(false); repository.closeAll(); Dispatchers.resetMain() } + } + + @Test fun scanningTheSameComputerAgainReusesItsWorkspaceAndSavedRecord() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + val links = mutableListOf() + val repository = SessionRepository(ApplicationProvider.getApplicationContext()) { Link().also(links::add) } + fun profile(address: String, room: String) = RelayProfile(address, room, "a".repeat(43), "PC", + "sha256/${"a".repeat(43)}=", "lan", SecureRelayProfile("b".repeat(43), "grant", "c".repeat(43), false)) + try { + val original = profile("wss://192.0.2.1:4000", "old-room") + val id = repository.connectRelay(original) + awaitState { repository.desktops.value.singleOrNull()?.status == "ready" } + val first = repository.desktops.value.single() + repository.setDraft("$id:1:2", "draft stays with the computer") + links.first().lost(java.io.IOException("network gone")) + awaitState { repository.desktops.value.single().status == "disconnected" } + val replacement = profile("wss://192.0.2.2:5000", "new-room") + assertEquals(id, repository.connectRelay(replacement)) + awaitState { repository.desktops.value.singleOrNull()?.status == "ready" && links.size == 2 } + assertEquals(first.connectionGeneration + 1, repository.desktops.value.single().connectionGeneration) + assertEquals("draft stays with the computer", repository.drafts.value["$id:1:2"]) + assertSame(replacement, repository.relays.value.single()) + links.first().lost(DesktopConnectionFailure(DesktopFailureKind.AUTHENTICATION)) + shadowOf(Looper.getMainLooper()).idle() + assertEquals("ready", repository.desktops.value.single().status) + repository.closeDesktop(id) + assertEquals(id, repository.restoreDesktop("relay:${repository.relays.value.single().legacyId}")) + awaitState { repository.desktops.value.singleOrNull()?.status == "ready" && links.size == 3 } + assertEquals(1, repository.relays.value.size) + } finally { repository.closeAll(); Dispatchers.resetMain() } + } + + @Test fun aNewInvitationReplacesAStalledLinkWithoutOverwritingSavedCredentials() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + val links = mutableListOf() + val repository = SessionRepository(ApplicationProvider.getApplicationContext()) { + Link(autoSnapshot = links.isEmpty()).also(links::add) + } + fun profile(room: String) = RelayProfile("wss://example.com", room, "a".repeat(43), "PC", + "sha256/${"a".repeat(43)}=", "relay", SecureRelayProfile("b".repeat(43), "grant-$room", "c".repeat(43), false)) + try { + val saved = profile("saved") + val id = repository.connectRelay(saved) + awaitState { repository.desktops.value.singleOrNull()?.status == "ready" } + repository.setDraft("$id:1:2", "keep me") + links.first().lost(java.io.IOException("offline")) + awaitState { repository.desktops.value.single().status == "disconnected" } + repository.connectRelay(profile("stalled")) + awaitState { links[1].methods.contains("events.subscribe") } + assertEquals(id, repository.connectRelay(profile("stalled"))) + assertEquals(2, links.size) + assertEquals(id, repository.connectRelay(profile("replacement"))) + awaitState { links[2].methods.contains("events.subscribe") && links[1].closed } + assertSame(saved, repository.relays.value.single()) + links[1].receive(JSONObject("""{"event":"runtime.snapshot","data":{"process_id":99,"windows":[]}}""")) + links[1].lost(DesktopConnectionFailure(DesktopFailureKind.AUTHENTICATION)) + shadowOf(Looper.getMainLooper()).idle() + assertEquals("connecting", repository.desktops.value.single().status) + assertEquals(11L, repository.desktops.value.single().runtimeProcess) + links[2].lost(DesktopConnectionFailure(DesktopFailureKind.AUTHENTICATION)) + awaitState { repository.desktops.value.single().status == "disconnected" } + assertSame(saved, repository.relays.value.single()) + assertEquals("keep me", repository.drafts.value["$id:1:2"]) + } finally { repository.closeAll(); Dispatchers.resetMain() } + } + + @Test fun lanDiscoveryKeepsTheComputerAndSavesTheAddressOnlyAfterAnAuthenticatedSnapshot() = runTest { + Dispatchers.setMain(StandardTestDispatcher(testScheduler)) + val links = mutableListOf() + val repository = SessionRepository(ApplicationProvider.getApplicationContext()) { + Link(autoSnapshot = links.isEmpty()).also(links::add) + } + val pin = "sha256/${"a".repeat(43)}=" + val original = RelayProfile("wss://192.0.2.1:4000", "room", "a".repeat(43), "PC", pin, "lan", + SecureRelayProfile("d".repeat(43), "device-grant", "c".repeat(43), false)) + val moved = PairingComputer("same-computer", "Untrusted advertised name", "https://192.0.2.2:4000".toHttpUrl(), pin) + try { + repository.foregroundChanged(true) + val id = repository.connectRelay(original) + awaitState { repository.desktops.value.singleOrNull()?.status == "ready" } + repository.setDraft("$id:1:2", "unsent") + links.first().lost(java.io.IOException("network changed")) + awaitState { repository.desktops.value.single().status == "disconnected" } + repository.recoverLanComputers(listOf(moved.copy(pin = "sha256/${"b".repeat(43)}="))) + assertEquals(1, links.size) + repository.recoverLanComputers(listOf(moved)) + awaitState { links.size == 2 && links[1].methods.contains("events.subscribe") } + assertEquals(id, repository.desktops.value.single().id) + assertSame(original, repository.relays.value.single()) + assertEquals("unsent", repository.drafts.value["$id:1:2"]) + + links[1].receive(JSONObject("""{"event":"runtime.snapshot","data":{"process_id":11,"windows":[]}}""")) + awaitState { repository.desktops.value.single().status == "ready" } + val saved = repository.relays.value.single() + assertEquals("wss://192.0.2.2:4000", saved.url) + assertEquals(original.id, saved.id) + assertEquals(original.name, saved.name) + assertEquals(original.token, saved.token) + assertSame(original.secure, saved.secure) + assertTrue(links.all { link -> link.methods.none { it in setOf("pane.prompt", "pane.send_key") } }) + links.first().lost(DesktopConnectionFailure(DesktopFailureKind.AUTHENTICATION)) + shadowOf(Looper.getMainLooper()).idle() + assertEquals("ready", repository.desktops.value.single().status) + + repository.foregroundChanged(false) + links[1].lost(java.io.IOException("offline")) + awaitState { repository.desktops.value.single().status == "disconnected" } + repository.recoverLanComputers(listOf(moved.copy(address = "https://192.0.2.3:4000".toHttpUrl()))) + assertEquals(2, links.size) + repository.closeDesktop(id) + repository.foregroundChanged(true) + repository.recoverLanComputers(listOf(moved)) + assertTrue(repository.desktops.value.isEmpty()) + } finally { repository.foregroundChanged(false); repository.closeAll(); Dispatchers.resetMain() } + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/CommandComposerTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/CommandComposerTest.kt new file mode 100644 index 0000000..50c20a8 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/CommandComposerTest.kt @@ -0,0 +1,219 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.* +import androidx.compose.ui.test.* +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.platform.testTag +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.width +import android.graphics.Bitmap +import android.graphics.Canvas +import android.view.View +import java.io.File +import androidx.test.core.app.ApplicationProvider +import io.github.kuddev.pebrel.mobile.PebrelApplication +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.session.SessionRepository +import io.github.kuddev.pebrel.mobile.session.DisplayPreferences +import io.github.kuddev.pebrel.mobile.session.DesktopWorkspace +import io.github.kuddev.pebrel.mobile.connection.HostProfile +import io.github.kuddev.pebrel.mobile.connection.DesktopPane +import org.junit.Assert.* +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class CommandComposerTest { + @Test fun compactShortcutOverflowProvidesATappableMenuAndRespectsInputPermission() { + val context = ApplicationProvider.getApplicationContext() + val sent = mutableListOf() + var allowed by mutableStateOf(true) + compose.setContent { MaterialTheme { + Box(Modifier.width(240.dp)) { + ComposerToolbar(onEdit = {}, keyboardVisible = false, keyboardEnabled = true, + onImeToggle = {}, enabled = allowed, + shortcuts = listOf("Ctrl+C", "Esc", "Tab", "←", "→", "↑", "↓"), onKey = { sent += it }) + } + } } + compose.onNodeWithContentDescription(context.getString(R.string.terminal_shortcuts_more)).performClick() + compose.onAllNodesWithText("↓").onLast().performClick() + assertEquals(listOf("↓"), sent) + compose.runOnIdle { allowed = false } + compose.onNodeWithContentDescription(context.getString(R.string.terminal_shortcuts_more)).performClick() + compose.onAllNodesWithText("↓").onLast().assertIsNotEnabled() + assertEquals(listOf("↓"), sent) + } + @Test fun installFailureShowsAllFourNumberedStepsAndActualUploadCount() { + val context = ApplicationProvider.getApplicationContext() + val progress = io.github.kuddev.pebrel.mobile.connection.RelayInstallProgress() + .advance(io.github.kuddev.pebrel.mobile.connection.RelayServiceProgress("uploading", 32768, 65536)) + .copy(failed = true) + compose.setContent { + renderedView = LocalView.current + MaterialTheme { Box(Modifier.testTag("relay-install-steps")) { RelayInstallSteps(progress, R.string.ssh_error_timeout) } } + } + val titles = listOf(R.string.service_step_connect, R.string.service_step_check, R.string.service_step_upload, R.string.service_step_start) + val states = listOf(R.string.service_step_done, R.string.service_step_done, R.string.service_operation_failed, R.string.service_step_waiting) + titles.forEachIndexed { i, title -> + compose.onNodeWithText(context.getString(R.string.service_step_row, i + 1, context.getString(title), context.getString(states[i]))).assertIsDisplayed() + } + compose.onNodeWithText(context.getString(R.string.service_upload_bytes, 32, 64)).assertIsDisplayed() + compose.onNodeWithText(context.getString(R.string.ssh_error_timeout)).assertIsDisplayed() + saveSurface("relay-install-steps") + } + @Test fun readOnlyPaneExplainsLiveAuthorizationWithoutPairingAgain() { + val context = ApplicationProvider.getApplicationContext() + val repository = SessionRepository(context) + val allowInput = mutableStateOf(false) + compose.setContent { MaterialTheme { DesktopTerminalScreen( + DesktopWorkspace("pc", HostProfile("host", "PC", "192.0.2.1", 22, "root"), status = "ready", allowInput = allowInput.value), + DesktopPane(1, 1, "shell", "", "", "idle", 0), repository, {}, {}) } } + compose.onNodeWithText(context.getString(R.string.composer_pc_read_only_short)).assertExists() + compose.onNodeWithText(context.getString(R.string.composer_pc_enable_input)).performClick() + compose.onNodeWithText(context.getString(R.string.composer_pc_read_only)).assertExists() + compose.onNodeWithText(context.getString(R.string.close)).performClick() + compose.onNodeWithText(context.getString(R.string.composer_pc_read_only)).assertDoesNotExist() + compose.onNodeWithText(context.getString(R.string.composer_pc_enable_input)).performClick() + compose.runOnIdle { allowInput.value = true } + compose.onNodeWithText(context.getString(R.string.composer_pc_read_only)).assertDoesNotExist() + compose.onNodeWithText(context.getString(R.string.composer_pc_read_only_short)).assertDoesNotExist() + } + + @Test fun terminalHeaderUsesTheTabLabelAndAnAccessibleGitIconWithFullPathInDetails() { + val context = ApplicationProvider.getApplicationContext() + val repository = SessionRepository(context) + val path = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" + val cwd = "C:\\workspace\\project" + val ready = mutableStateOf(true) + var opened = 0 + compose.setContent { MaterialTheme { Column { DesktopTerminalScreen( + DesktopWorkspace("pc", HostProfile("host", "PC", "192.0.2.1", 22, "root"), status = if (ready.value) "ready" else "disconnected"), + DesktopPane(1, 1, path, cwd, "", "idle", 0, tabLabel = "work"), repository, {}, {}, onGit = { opened++ }) } } } + compose.onNodeWithText("work").assertIsDisplayed() + compose.onNodeWithText(path).assertDoesNotExist() + compose.onNodeWithText(context.getString(R.string.git_title)).assertDoesNotExist() + compose.onNodeWithContentDescription(context.getString(R.string.git_title)) + // 右上角使用已确认的紧凑排列;高度仍保留 48dp,普通按钮不随之缩小。 + .assertHeightIsAtLeast(48.dp).assertWidthIsEqualTo(36.dp).assertIsEnabled().performClick() + compose.runOnIdle { assertEquals(1, opened); ready.value = false } + compose.onNodeWithContentDescription(context.getString(R.string.git_title)).assertIsNotEnabled() + compose.onNodeWithContentDescription(context.getString(R.string.more_actions)).performClick() + compose.onNodeWithText(context.getString(R.string.terminal_details)).performClick() + compose.onNodeWithText(path).assertIsDisplayed() + compose.onNodeWithText(cwd).assertIsDisplayed() + compose.onNodeWithText(context.getString(R.string.close)).performClick() + compose.onNodeWithText(path).assertDoesNotExist() + } + + @Test fun terminalChromeUsesRemoteDarkAndLightColorsAndFailureEndsProgress() { + val fallback = androidx.compose.material3.lightColorScheme() + for ((bg, fg) in listOf(0xff2e3440.toInt() to 0xffeceff4.toInt(), 0xfffcfbf9.toInt() to 0xff222222.toInt())) { + val frame = io.github.kuddev.pebrel.terminal.TerminalFrame(emptyArray(), intArrayOf(1, 1, 0, 0, 0, bg, fg, 2, fg, 0xffbf616a.toInt())) + val scheme = desktopTerminalColors(frame, fallback) + assertEquals(androidx.compose.ui.graphics.Color(bg), scheme.background) + assertEquals(scheme.background, scheme.surface) + assertEquals(androidx.compose.ui.graphics.Color(fg), scheme.onSurface) + } + assertEquals(fallback, desktopTerminalColors(null, fallback)) + assertEquals(R.string.service_operation_failed, serviceStageText("failed")) + assertEquals(R.string.service_openrc_error, serviceErrorText(io.github.kuddev.pebrel.mobile.connection.RelayServiceFailure("openrc_supervisor_required"))) + } + @get:Rule val compose = createComposeRule() + private var renderedView: View? = null + + @Test fun oneSlotSwitchesModesWithoutLosingDraftOrKeepingAnEmptyToolbar() { + val context = ApplicationProvider.getApplicationContext() + val repository = SessionRepository(context) + compose.setContent { + renderedView = LocalView.current + var direct by remember { mutableStateOf(true) } + MaterialTheme { + CommandComposer("test-session", repository, true, direct, { direct = it }, {}, onKeyboard = {}) { true } + } + } + compose.onNodeWithTag("composer-direct").assertExists() + compose.onNodeWithTag("composer-editor").assertDoesNotExist() + compose.onAllNodes(hasSetTextAction()).assertCountEquals(0) + saveSurface("composer-direct") + compose.onNodeWithContentDescription(context.getString(R.string.composer_mode_edit)).performClick() + compose.onNodeWithTag("composer-direct").assertDoesNotExist() + compose.onNodeWithTag("composer-editor").assertExists() + compose.onAllNodes(hasSetTextAction()).assertCountEquals(1) + saveSurface("composer-editor") + compose.onNode(hasSetTextAction()).performTextInput("echo preserved") + compose.onNodeWithContentDescription(context.getString(R.string.composer_mode_direct)).performClick() + compose.onNodeWithTag("composer-editor").assertDoesNotExist() + compose.onNodeWithTag("composer-direct").assertExists() + compose.onNodeWithContentDescription(context.getString(R.string.composer_mode_edit)).performClick() + compose.onNode(hasSetTextAction()).assertTextEquals("echo preserved") + compose.onNodeWithTag("composer-direct").assertDoesNotExist() + } + + @Test fun desktopScreenStartsCompactAndCanSwitchToOneEditor() { + val context = ApplicationProvider.getApplicationContext() + val repository = SessionRepository(context) + compose.setContent { + MaterialTheme { DesktopTerminalScreen( + DesktopWorkspace("pc", HostProfile("host", "PC", "192.0.2.1", 22, "user")), + DesktopPane(1, 1, "shell", "", "", "idle", 0), repository, {}, {}) } + } + compose.onNodeWithTag("composer-editor").assertDoesNotExist() + compose.onNodeWithTag("composer-direct").assertExists() + compose.onNodeWithContentDescription(context.getString(R.string.composer_mode_edit)).performClick() + compose.onNodeWithTag("composer-direct").assertDoesNotExist() + compose.onAllNodes(hasSetTextAction()).assertCountEquals(1) + } + + @Test fun oldComposerFirstPreferenceMigratesOnceAndLaterChoiceIsPreserved() { + val context = ApplicationProvider.getApplicationContext() + val stored = context.getSharedPreferences("terminal_display", 0) + stored.edit().remove("compact_input_default_v1").putBoolean("direct_input", false).commit() + val display = DisplayPreferences(context) + assertTrue(display.state.value.directInput) + display.update { it.copy(directInput = false) } + assertFalse(DisplayPreferences(context).state.value.directInput) + display.update { it.copy(directInput = true) } + } + + @Test fun servicePageKeepsNetworkInternalsUnderAdvanced() { + val context = ApplicationProvider.getApplicationContext() + val repository = SessionRepository(context) + compose.setContent { MaterialTheme { RelayDeploymentFlow(repository) {} } } + compose.onNodeWithText(context.getString(R.string.service_install)).assertExists().assertIsNotEnabled() + compose.onNodeWithText(context.getString(R.string.deploy_domain)).assertDoesNotExist() + compose.onNodeWithText(context.getString(R.string.deploy_http_port)).assertDoesNotExist() + compose.onNodeWithText(context.getString(R.string.service_port)).assertDoesNotExist() + compose.onNodeWithText(context.getString(R.string.service_advanced)).performClick() + compose.onNodeWithContentDescription(context.getString(R.string.service_port)).assertExists() + compose.onNodeWithContentDescription(context.getString(R.string.service_address)).assertExists() + compose.onNodeWithText(context.getString(R.string.service_manual_commands)).performScrollTo().performClick() + compose.onNodeWithText("sh install.sh 'SERVER_IP' 443\n/opt/pebrel-relay/pebrel-relay service-status").assertExists() + } + + private fun saveSurface(tag: String) { + val file = File("build/reports/composer/$tag.png") + check(file.parentFile!!.isDirectory || file.parentFile!!.mkdirs()) + val bounds = compose.onNodeWithTag(tag).fetchSemanticsNode().boundsInRoot + compose.runOnIdle { + // PixelCopy needs an emulator surface; use the actual Compose view's + // native Canvas in Robolectric, preserving the production layout. + val bitmap = Bitmap.createBitmap(bounds.width.toInt(), bounds.height.toInt(), Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + canvas.translate(-bounds.left, -bounds.top) + checkNotNull(renderedView).draw(canvas) + file.outputStream().use { check(bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)) } + bitmap.recycle() + } + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/DesktopOutputSurfaceTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/DesktopOutputSurfaceTest.kt new file mode 100644 index 0000000..eba950b --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/DesktopOutputSurfaceTest.kt @@ -0,0 +1,247 @@ +package io.github.kuddev.pebrel.mobile.ui + +import android.view.View +import android.view.ViewGroup +import android.view.HapticFeedbackConstants +import android.view.inputmethod.EditorInfo +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.semantics.SemanticsActions +import androidx.compose.ui.semantics.SemanticsProperties +import androidx.compose.ui.test.* +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.unit.dp +import io.github.kuddev.pebrel.terminal.TerminalInputTarget +import io.github.kuddev.pebrel.terminal.TerminalSnapshotView +import org.junit.Assert.* +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class DesktopOutputSurfaceTest { + @get:Rule val compose = createComposeRule() + + @Test fun legacyTextKeepsTheNativeKeyboardTargetAndRevokesStaleInput() { + val sent = mutableListOf() + val keys = mutableListOf() + val target = object : TerminalInputTarget { + override fun text(text: String): Boolean { sent += text; return true } + override fun key(code: Int, modifiers: Int, action: Int, text: String, unshifted: Int): Boolean { + keys += code; return true + } + } + var keyboardRequest by mutableIntStateOf(0) + var allowInput by mutableStateOf(true) + lateinit var root: View + compose.setContent { + root = LocalView.current + MaterialTheme { + DesktopOutputSurface("pc-pane", "Legacy terminal output", 16, true, {}, + Modifier.size(300.dp, 240.dp).testTag("output"), + inputTarget = target.takeIf { allowInput }, keyboardRequest = keyboardRequest) + } + } + compose.waitForIdle() + val view = compose.runOnIdle { + checkNotNull(findInputView(root)) { "Legacy text must retain the native input target" } + .also { it.clearFocus() } + } + compose.runOnIdle { keyboardRequest++ } + compose.waitForIdle() + val ime = compose.runOnIdle { + assertTrue("keyboard action focuses the actual native editor", view.hasFocus()) + checkNotNull(view.onCreateInputConnection(EditorInfo())) + } + compose.runOnIdle { + assertTrue(ime.setComposingText("中", 1)) + assertTrue(sent.isEmpty()) + assertTrue(ime.commitText("中文😀", 1)) + assertEquals(listOf("中文😀"), sent) + view.clearFocus() + } + compose.onNodeWithText("Legacy terminal output").performTouchInput { click() } + compose.runOnIdle { + assertTrue("a tap on the output opens input", view.hasFocus()) + assertTrue("focusing the editor must not submit an empty command", keys.isEmpty()) + assertEquals(listOf("中文😀"), sent) + } + + compose.runOnIdle { allowInput = false } + compose.waitForIdle() + compose.onNodeWithText("Legacy terminal output").performTouchInput { click() } + compose.runOnIdle { + assertFalse(view.hasFocus()) + assertNull(view.onCreateInputConnection(EditorInfo())) + assertFalse(ime.commitText("stale", 1)) + assertEquals(listOf("中文😀"), sent) + } + } + + private fun findInputView(view: View): TerminalSnapshotView? { + if (view is TerminalSnapshotView) return view + if (view is ViewGroup) for (i in 0 until view.childCount) { + findInputView(view.getChildAt(i))?.let { return it } + } + return null + } + + @Test fun legacyZoomScalesLinePitchWithGlyphWidth() { + val output = "MMMMMMMM\nMMMMMMMM\nMMMMMMMM" + compose.setContent { + var size by remember { mutableIntStateOf(16) } + MaterialTheme { + DesktopOutputSurface("pc-pane", output, size, true, { size = it }, + Modifier.size(300.dp, 300.dp).testTag("output")) + } + } + fun layout(): TextLayoutResult { + val result = mutableListOf() + compose.onNodeWithText(output).performSemanticsAction(SemanticsActions.GetTextLayoutResult) { it(result) } + return result.single() + } + val before = layout() + zoom(70f) + val after = layout() + val widthRatio = after.getBoundingBox(7).right / before.getBoundingBox(7).right + val rowRatio = (after.getLineBaseline(1) - after.getLineBaseline(0)) / + (before.getLineBaseline(1) - before.getLineBaseline(0)) + assertTrue("pinch must enlarge glyphs: ratio=$widthRatio, before=${before.layoutInput.style.fontSize}, " + + "after=${after.layoutInput.style.fontSize}", widthRatio > 1.1f) + val fontRatio = after.layoutInput.style.fontSize.value / before.layoutInput.style.fontSize.value + // 原生字号落到物理像素时会取整;按一像素容差核对行距,不以取整后的单字宽充当比例。 + assertEquals("line pitch must scale with glyphs, not stay at the theme's fixed line height", + fontRatio, rowRatio, 1f / (before.getLineBaseline(1) - before.getLineBaseline(0))) + } + + @Test fun successivePinchesStillWorkAfterTheFirstFontPreferenceIsSaved() { + val sizes = mutableListOf() + compose.setContent { + var size by remember { mutableIntStateOf(16) } + MaterialTheme { + DesktopOutputSurface("pc-pane", "Terminal output", size, true, { + sizes += it + size = it + }, Modifier.size(300.dp, 240.dp).testTag("output")) + } + } + zoom(60f) + assertEquals(1, sizes.size) + assertTrue(sizes[0] > 16) + zoom(70f) + assertEquals(2, sizes.size) + assertTrue(sizes[1] > sizes[0]) + assertTrue(sizes[1] <= 32) + } + + @Test fun pinchShowsCenteredSizeTicksAndDismissesAfterReleaseWithoutTakingLayoutSpace() { + lateinit var root: View + compose.setContent { + root = LocalView.current + var size by remember { mutableIntStateOf(16) } + MaterialTheme { + DesktopOutputSurface("pc-pane", "First line\nSecond line\nThird line", size, true, { size = it }, + Modifier.size(300.dp, 300.dp).testTag("output")) + } + } + val bounds = compose.onNodeWithTag("output").fetchSemanticsNode().boundsInRoot + compose.mainClock.autoAdvance = false + compose.onNodeWithTag("output").performTouchInput { + down(0, center + Offset(-50f, 0f)) + down(1, center + Offset(50f, 0f)) + moveTo(0, center + Offset(-80f, 0f)) + moveTo(1, center + Offset(80f, 0f)) + } + compose.waitForIdle() + compose.mainClock.advanceTimeBy(150) + val indicator = compose.onNodeWithTag("terminal-zoom-feedback").fetchSemanticsNode().boundsInRoot + compose.onNodeWithTag("terminal-zoom-feedback").assertIsDisplayed() + assertEquals(bounds.center.x, indicator.center.x, 1f) + assertEquals(bounds.center.y, indicator.center.y, 1f) + assertEquals(bounds, compose.onNodeWithTag("output").fetchSemanticsNode().boundsInRoot) + compose.runOnIdle { assertEquals(HapticFeedbackConstants.CLOCK_TICK, shadowOf(root).lastHapticFeedbackPerformed()) } + compose.onNodeWithTag("output").performTouchInput { up(0); up(1) } + compose.mainClock.advanceTimeBy(150) + compose.onNodeWithTag("terminal-zoom-feedback").assertIsDisplayed() + compose.mainClock.advanceTimeBy(900) + compose.onNodeWithTag("terminal-zoom-feedback").assertDoesNotExist() + compose.mainClock.autoAdvance = true + } + + private fun zoom(distance: Float) { + compose.onNodeWithTag("output").performTouchInput { + down(0, center + Offset(-50f, 0f)) + down(1, center + Offset(50f, 0f)) + moveTo(0, center + Offset(-distance, 0f)) + moveTo(1, center + Offset(distance, 0f)) + up(0) + up(1) + } + compose.waitForIdle() + } + + // Robolectric 的 API 28 原生 Magnifier 没有 SurfaceControl;此处只验证滚动/IME 归属, + // 带放大镜的长按选区另在 API 28 雷电的真实窗口中验证。 + @Config(sdk = [27]) + @Test fun singleFingerScrollingDoesNotChangeFontSize() { + var saves = 0 + lateinit var root: View + val target = object : TerminalInputTarget { + override fun text(text: String) = true + override fun key(code: Int, modifiers: Int, action: Int, text: String, unshifted: Int) = true + } + compose.setContent { + root = LocalView.current + MaterialTheme { + DesktopOutputSurface("pc-pane", (1..80).joinToString("\n") { "Output line $it" }, 16, + true, { saves++ }, Modifier.size(300.dp, 240.dp).testTag("output"), inputTarget = target) + } + } + compose.runOnIdle { checkNotNull(findInputView(root)).clearFocus() } + compose.onNodeWithTag("output").performTouchInput { swipeUp() } + compose.waitForIdle() + assertEquals(0, saves) + val scroll = compose.onNode(SemanticsMatcher.keyIsDefined(SemanticsProperties.VerticalScrollAxisRange)) + .fetchSemanticsNode().config[SemanticsProperties.VerticalScrollAxisRange] + assertTrue(scroll.value() > 0f) + compose.runOnIdle { assertFalse("scroll must not open the IME", checkNotNull(findInputView(root)).hasFocus()) } + compose.onNodeWithTag("output").performTouchInput { longClick() } + compose.runOnIdle { assertFalse("long-press selection must not open the IME", checkNotNull(findInputView(root)).hasFocus()) } + } + + @Test fun legacyOutputStartsAtTheTailAndPreservesAnExplicitScrollUntilLatestIsTapped() { + val original = "\n\n\n" + (1..80).joinToString("\n") { "Output line $it" } + var output by mutableStateOf(original) + compose.setContent { MaterialTheme { + DesktopOutputSurface("pc-pane", output, 16, true, {}, + Modifier.size(300.dp, 240.dp).testTag("output")) + } } + fun range() = compose.onNode(SemanticsMatcher.keyIsDefined(SemanticsProperties.VerticalScrollAxisRange)) + .fetchSemanticsNode().config[SemanticsProperties.VerticalScrollAxisRange] + compose.waitForIdle() + compose.onNodeWithText(original).assertExists() + assertEquals(range().maxValue(), range().value(), 1f) + compose.onNodeWithTag("output").performTouchInput { swipeDown() } + compose.waitForIdle() + val reading = range().value() + assertTrue(reading < range().maxValue()) + compose.runOnIdle { output += "\nNew output" } + compose.waitForIdle() + assertEquals(reading, range().value(), 1f) + compose.onNodeWithText("Latest output ↓").performClick() + compose.waitForIdle() + assertEquals(range().maxValue(), range().value(), 1f) + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/GitScreenTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/GitScreenTest.kt new file mode 100644 index 0000000..e7ffa51 --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/GitScreenTest.kt @@ -0,0 +1,112 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.* +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.unit.dp +import io.github.kuddev.pebrel.mobile.git.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import org.json.JSONObject +import org.junit.After +import org.junit.Assert.* +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28], qualifiers = "w360dp-h640dp-mdpi") +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class GitScreenTest { + @get:Rule val compose = createComposeRule() + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + private val root = "D:/a-very-long-parent/another-folder/work" + private val target = DesktopGitTarget("pc", 1, 2, root, 3, 4) + private val calls = mutableListOf>() + private var draft by mutableStateOf("") + + @After fun close() { scope.cancel() } + + private fun show(readonly: Boolean = false) { + val controller = DesktopGit(target, scope, { method, params -> + calls += method to JSONObject(params.toString()) + when (method) { + "git.status" -> JSONObject("""{"root":"$root","revision":"${"a".repeat(64)}", + "status":{"branch":"main","upstream":"origin/main","ahead":0,"behind":0, + "entries":[ + {"path":"README.md","original":null,"index":".","worktree":"M","conflict":false}, + {"path":"src/Main.kt","original":null,"index":".","worktree":"M","conflict":false}, + {"path":"new file.txt","original":null,"index":"A","worktree":".","conflict":false}]}}""") + "git.history" -> JSONObject("""{"commits":[],"has_more":false}""") + "git.diff" -> JSONObject().put("text", "+changed") + else -> JSONObject().put("success", true) + } + }, { write -> if (write && readonly) "input_not_authorized" else null }) + compose.setContent { + val state by controller.state.collectAsState() + LaunchedEffect(controller) { controller.refresh() } + MaterialTheme { + Box(Modifier.requiredSize(360.dp, 600.dp).testTag("git-page")) { + GitScreenContent(target, "Computer", state, null, !readonly, draft, + { draft = it }, controller, {}) + } + } + } + compose.waitForIdle() + } + + @Test fun compactPageShowsFilesBeforeTheDockedCommitBarAndKeepsFullPathInDetails() { + show() + compose.onNodeWithText("work").assertIsDisplayed() + compose.onNodeWithText(root).assertDoesNotExist() + val file = compose.onNodeWithContentDescription("README.md").assertIsDisplayed().getUnclippedBoundsInRoot() + val input = compose.onNodeWithContentDescription("Commit message").getUnclippedBoundsInRoot() + assertTrue("files must not be hidden below the commit form", file.bottom < input.top) + assertTrue("commit bar is docked, not at the top of the list", input.top > 490.dp) + compose.onNodeWithContentDescription("Refresh").assertHeightIsAtLeast(48.dp).assertWidthIsAtLeast(48.dp) + compose.onNodeWithContentDescription("Stage README.md").assertHeightIsAtLeast(48.dp).assertWidthIsAtLeast(48.dp) + compose.onNodeWithContentDescription("Repository details").performTouchInput { click() } + compose.onNodeWithText(root).assertIsDisplayed() + } + + @Test fun draftAndCollapsedFolderSurviveHistoryAndOnlyCommitButtonSubmits() { + show() + compose.onNodeWithContentDescription("Commit staged changes (1)").assertIsNotEnabled() + compose.onNodeWithContentDescription("Commit message").performTextInput("Review from phone") + assertTrue(calls.none { it.first == "git.commit" }) + compose.onNodeWithContentDescription("src").performTouchInput { click() } + compose.onNodeWithContentDescription("src/Main.kt").assertDoesNotExist() + compose.onNodeWithText("History").performTouchInput { click() } + compose.onNodeWithText("Changes", useUnmergedTree = false).performTouchInput { click() } + compose.onNodeWithContentDescription("src/Main.kt").assertDoesNotExist() + compose.onNodeWithText("Review from phone").assertIsDisplayed() + compose.onNodeWithContentDescription("Commit staged changes (1)").assertIsEnabled().performTouchInput { click() } + compose.runOnIdle { + val request = calls.filter { it.first == "git.commit" }.single().second + assertEquals("Review from phone", request.getString("message")) + assertEquals(root, request.getString("expected_cwd")) + } + } + + @Test fun readOnlyConnectionKeepsDiffReadableButAllMutationEntriesDisabled() { + show(readonly = true) + compose.onNodeWithText("Stage all").assertIsNotEnabled() + compose.onNodeWithText("Unstage all").assertIsNotEnabled() + compose.onNodeWithContentDescription("Stage README.md").assertIsNotEnabled() + compose.onNodeWithContentDescription("Commit message").performTextInput("Draft only") + compose.onNodeWithContentDescription("Commit staged changes (1)").assertIsNotEnabled() + compose.onNodeWithContentDescription("README.md").performTouchInput { click() } + compose.onNodeWithText("+changed").assertIsDisplayed() + compose.runOnIdle { assertTrue(calls.none { it.first in setOf("git.commit", "git.stage", "git.unstage") }) } + } +} diff --git a/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/HomeSessionsTest.kt b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/HomeSessionsTest.kt new file mode 100644 index 0000000..edf0feb --- /dev/null +++ b/mobile/android/app/src/test/java/io/github/kuddev/pebrel/mobile/ui/HomeSessionsTest.kt @@ -0,0 +1,637 @@ +package io.github.kuddev.pebrel.mobile.ui + +import androidx.compose.material3.MaterialTheme +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.SemanticsActions +import androidx.compose.ui.semantics.SemanticsProperties +import androidx.compose.ui.test.* +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.Density +import io.github.kuddev.pebrel.mobile.R +import io.github.kuddev.pebrel.mobile.session.DisplayPreferences +import io.github.kuddev.pebrel.mobile.connection.* +import io.github.kuddev.pebrel.mobile.session.DesktopWorkspace +import org.junit.Assert.* +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class HomeSessionsTest { + @Test fun terminalHeaderActionsStayCompactAndKeepSeparateTouchTargets() { + val context = androidx.test.core.app.ApplicationProvider.getApplicationContext() + val opened = mutableListOf() + val gitEnabled = mutableStateOf(true) + compose.setContent { MaterialTheme { + CompositionLocalProvider(LocalDensity provides Density(LocalDensity.current.density, fontScale = 2f)) { + Box(Modifier.size(320.dp, 48.dp)) { + TerminalHeader("long-workspace-title-for-compact-header", "Computer", "ready", + onBack = { opened += "back" }, onSessions = { opened += "sessions" }, + onGit = { opened += "git" }, gitEnabled = gitEnabled.value, + onConversation = { opened += "chat" }, onDetails = { opened += "details" }) + } + } + } } + val git = compose.onNodeWithContentDescription(context.getString(R.string.git_title)) + val chat = compose.onNodeWithContentDescription(context.getString(R.string.chat_title)) + val more = compose.onNodeWithContentDescription(context.getString(R.string.more_actions)) + val actions = listOf(git, chat, more) + actions.forEach { it.assertIsDisplayed().assertWidthIsEqualTo(36.dp).assertHeightIsEqualTo(48.dp) } + actions.zipWithNext().forEach { (left, right) -> + val leftBounds = left.getUnclippedBoundsInRoot() + val rightBounds = right.getUnclippedBoundsInRoot() + assertEquals(leftBounds.right, rightBounds.left) + assertEquals(36.dp, rightBounds.left - leftBounds.left) + } + val back = compose.onNodeWithContentDescription(context.getString(R.string.back)) + back.assertWidthIsEqualTo(48.dp).assertHeightIsEqualTo(48.dp) + // 点两侧留白而非仅图形中心,防止紧凑布局下相邻按钮抢走触摸。 + listOf(git, chat).forEach { node -> + node.performTouchInput { + click(Offset(1f, center.y)) + click(Offset(width - 1f, center.y)) + } + } + more.performTouchInput { click(center) } + compose.onNodeWithText(context.getString(R.string.terminal_details)).performClick() + back.performTouchInput { click(center) } + compose.runOnIdle { + assertEquals(listOf("git", "git", "chat", "chat", "details", "back"), opened) + gitEnabled.value = false + } + git.assertIsNotEnabled().performTouchInput { click(center) } + chat.assertIsEnabled().performTouchInput { click(center) } + compose.runOnIdle { assertEquals(listOf("git", "git", "chat", "chat", "details", "back", "chat"), opened) } + } + + @Test fun sshHeaderUsesTheSameCompactGroupWithoutEnablingDisconnectedFiles() { + val context = androidx.test.core.app.ApplicationProvider.getApplicationContext() + val opened = mutableListOf() + val status = mutableStateOf("ready") + compose.setContent { MaterialTheme { + Box(Modifier.size(320.dp, 48.dp)) { + TerminalHeader("SSH", "Computer", status.value, {}, {}, + onFiles = { opened += "files" }, onClose = { opened += "close" }) + } + } } + val files = compose.onNodeWithContentDescription(context.getString(R.string.sftp_title)) + val more = compose.onNodeWithContentDescription(context.getString(R.string.more_actions)) + files.assertWidthIsEqualTo(36.dp).assertHeightIsEqualTo(48.dp).performTouchInput { click(center) } + more.assertWidthIsEqualTo(36.dp).assertHeightIsEqualTo(48.dp).performTouchInput { click(center) } + assertEquals(files.getUnclippedBoundsInRoot().right, more.getUnclippedBoundsInRoot().left) + compose.onNodeWithText(context.getString(R.string.close_session)).performClick() + compose.runOnIdle { status.value = "failed" } + files.assertIsNotEnabled().performTouchInput { click(center) } + compose.runOnIdle { assertEquals(listOf("files", "close"), opened) } + } + + @Test fun firstSnapshotRebindsTheTabPageBeforeItsFirstMutation() { + val context = androidx.test.core.app.ApplicationProvider.getApplicationContext() + val methods = java.util.concurrent.CopyOnWriteArrayList() + val receiver = java.util.concurrent.atomic.AtomicReference<((org.json.JSONObject) -> Unit)?>() + fun snapshot(added: Boolean) = org.json.JSONObject("""{ + "process_id":11,"revision":${if (added) 2 else 1},"mobile_policy":{"allow_input":true}, + "windows":[{"id":7,"tabs":[{"tab_id":"0123456789abcdef0123456789abcdef","index":0, + "label":"work","kind":"shell","active":true,"panes":[{"id":${if (added) 3 else 2},"title":"work","task_state":"idle"}]}]}] + }""") + val transport = object : DesktopTransport { + override suspend fun open(allowInput: Boolean, receive: (org.json.JSONObject) -> Unit, disconnected: (Throwable?) -> Unit) { + receiver.set(receive) + receive(org.json.JSONObject("""{"type":"mobile.ready","protocol":"pebrel.mobile.relay","version":1,"capabilities":{"input":true}}""")) + } + override fun send(frame: org.json.JSONObject) { + val method = frame.getString("method") + methods += method + val result = if (method == "tab.new") org.json.JSONObject() + .put("action", org.json.JSONObject().put("window_id", 7).put("pane_id", 3)) + .put("snapshot", snapshot(true)) else org.json.JSONObject() + checkNotNull(receiver.get()).invoke(org.json.JSONObject().put("id", frame.getString("id")).put("ok", true).put("result", result)) + } + override fun close() = Unit + } + val repository = io.github.kuddev.pebrel.mobile.session.SessionRepository(context) { transport } + val id = repository.connectRelay(RelayProfile("wss://initial.example", "initial-tab", "a".repeat(43), "Computer")) + var opened: DesktopTab? = null + try { + compose.setContent { MaterialTheme { + val computers by repository.desktops.collectAsState() + DesktopTabsPage(computers.find { it.id == id }, repository, {}, { opened = it }, {}, null, {}) + } } + compose.waitUntil(5000) { methods.contains("events.subscribe") } + compose.runOnIdle { assertNull(repository.desktops.value.single().runtimeProcess) } + checkNotNull(receiver.get()).invoke(org.json.JSONObject().put("event", "runtime.snapshot").put("data", snapshot(false))) + compose.waitUntil(5000) { + org.robolectric.Shadows.shadowOf(android.os.Looper.getMainLooper()).idle() + repository.desktops.value.single().status == "ready" && repository.desktops.value.single().allowInput + } + compose.onNodeWithContentDescription(context.getString(R.string.tab_new)).performClick() + compose.onNodeWithText(context.getString(R.string.tab_new_terminal)).performClick() + compose.onNodeWithText(context.getString(R.string.tab_open)).performClick() + compose.waitUntil(5000) { opened != null } + compose.runOnIdle { + assertEquals(1, methods.count { it == "tab.new" }) + assertEquals(3L, opened?.primaryPane?.id) + } + } finally { repository.closeAll() } + } + + @Test fun fileTabsWithoutPanesRemainVisibleAndHaveSeparateCloseTargets() { + val context = androidx.test.core.app.ApplicationProvider.getApplicationContext() + val tabs = parseDesktopTabs(org.json.JSONObject("""{"windows":[{"id":7,"tabs":[ + {"index":0,"tab_id":"0123456789abcdef0123456789abcdef","label":"C:/docs/README.md","kind":"document","active":true,"panes":[],"file":{"path":"C:/docs/README.md","ready":true,"revision":3}}, + {"index":1,"tab_id":"1123456789abcdef0123456789abcdef","label":"preview.png","kind":"image","panes":[],"file":{"path":"C:/docs/preview.png","ready":true}} + ]}]}""")) + val computer = DesktopWorkspace("files", HostProfile("pc", "Computer", "localhost", 22, "user"), + status = "ready", allowInput = true, hasConnected = true, tabs = tabs) + var opened: String? = null + var closed: String? = null + assertEquals(2, sessionCards(emptyList(), listOf(computer)).size) + compose.setContent { MaterialTheme { + Column { DesktopTabRows(computer, { opened = it.id }, {}, { closed = it.id }) } + } } + compose.onNodeWithText("README.md").performClick() + compose.runOnIdle { assertEquals(tabs[0].id, opened); assertNull(closed) } + compose.onNodeWithContentDescription(context.getString(R.string.tab_close_named, "preview.png")) + .assertWidthIsAtLeast(48.dp).assertHeightIsAtLeast(48.dp).performClick() + compose.runOnIdle { assertEquals(tabs[1].id, closed); assertEquals(tabs[0].id, opened) } + } + + @Test fun readerPreservesMarkdownFeaturesButNeverExecutesDocumentHtml() { + val colors = ReaderColors("#ffffff", "#111111", "#555555", "#eeeeee", "#aaaaaa", "#007777") + val source = "# 标题\n\n- [x] 已完成\n\n| A | B |\n| --- | --- |\n| 1 | 2 |\n\n```kotlin\nprintln(\"中文😀\")\n```\n\n\n\n![预览](preview.png)" + val document = prepareReader(source, "README.md", true, colors, "复制", "图片") + assertTrue(document.html.contains("")) + assertTrue(document.html.contains("checkbox")) + assertFalse(document.html.contains("`; +} + +/** Serve the QR page on IPv4 loopback only; no LAN interface can read its secret. */ +export async function startQrPage(invitation, { + host = '127.0.0.1', port = 0, addresses = [], currentAddress = '', onAddressChange, +} = {}) { + if (host !== '127.0.0.1') throw new Error('qr_page_loopback_only'); + const route = `/pair/${randomBytes(18).toString('base64url')}`; + const makeSvg = value => QRCode.toString(JSON.stringify(value), { + type: 'svg', margin: 2, width: 360, errorCorrectionLevel: 'M', + color: { dark: '#171827', light: '#f8f7ff' }, + }); + let svg = await makeSvg(invitation); + let currentStatus = 'connecting'; + let options = normalizeNetworkOptions(addresses); + let changing = false; + const server = http.createServer(async (request, response) => { + const listener = server.address(); + if (!listener) { response.writeHead(503); response.end(); return; } + const origin = `http://127.0.0.1:${listener.port}`; + response.setHeader('cache-control', 'no-store'); + response.setHeader('x-content-type-options', 'nosniff'); + if (!isLoopback(request.socket) || request.headers.host !== new URL(origin).host) { + response.writeHead(403); response.end(); return; + } + let url; + try { url = new URL(request.url ?? '/', origin); } + catch { response.writeHead(400); response.end(); return; } + if (request.method === 'POST' && url.pathname === `${route}/network`) { + if (typeof onAddressChange !== 'function' || request.headers.origin !== origin || + request.headers['content-type']?.split(';')[0].trim() !== 'application/json') { + response.writeHead(403); response.end(); return; + } + if (changing) { response.writeHead(409); response.end(); return; } + changing = true; + try { + const body = JSON.parse(await readBoundedBody(request)); + if (!options.some(item => item.address === body.address)) throw new Error('invalid_network'); + const result = await onAddressChange(body.address); + const nextSvg = await makeSvg(result.invitation); + invitation = result.invitation; + svg = nextSvg; + currentAddress = result.address; + options = normalizeNetworkOptions(result.addresses); + currentStatus = 'network_changed'; + response.writeHead(204); response.end(); + } catch { + currentStatus = 'network_change_failed'; + response.writeHead(400); response.end(); + } finally { changing = false; } + return; + } + if (request.method !== 'GET' || (url.pathname !== route && url.pathname !== `${route}/status`)) { + response.writeHead(404); response.end(); return; + } + if (url.pathname === `${route}/status`) { + response.writeHead(200, { 'content-type': 'application/json; charset=utf-8' }); + response.end(JSON.stringify({ status: currentStatus })); return; + } + response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }); + response.end(pageMarkup(invitation, svg, route, options, currentAddress)); + }); + server.maxConnections = 8; + server.requestTimeout = 5_000; + server.headersTimeout = 5_000; + await new Promise((resolve, reject) => { + const fail = error => { server.off('listening', resolve); reject(error); }; + server.once('error', fail); + server.listen(port, host, () => { server.off('error', fail); resolve(); }); + }); + const actualPort = server.address().port; + return { + server, route, url: `http://127.0.0.1:${actualPort}${route}`, + setStatus(value) { + const next = SAFE_STATUSES.has(value) ? value : 'helper_error'; + if (changing) return; + if (['reconnecting', 'connecting', 'waiting_for_phone'].includes(next) && ERROR_STATUSES.has(currentStatus)) return; + if (currentStatus === 'network_changed' && ['connecting', 'waiting_for_phone'].includes(next)) return; + currentStatus = next; + }, + close: async () => { await new Promise(resolve => server.close(() => resolve())); }, + }; +} + +/** Open the local page without invoking a shell or interpolating user input. */ +export function openBrowser(url) { + let command; + let args; + if (process.platform === 'win32') { command = 'cmd.exe'; args = ['/c', 'start', '', url]; } + else if (process.platform === 'darwin') { command = 'open'; args = [url]; } + else { command = 'xdg-open'; args = [url]; } + const child = spawn(command, args, { detached: true, stdio: 'ignore', windowsHide: true }); + child.on('error', () => {}); + child.unref(); + return child; +} diff --git a/mobile/relay/runtime-link.mjs b/mobile/relay/runtime-link.mjs new file mode 100644 index 0000000..d38fdd5 --- /dev/null +++ b/mobile/relay/runtime-link.mjs @@ -0,0 +1,109 @@ +import net from 'node:net'; +import { readFileSync } from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { policy, validateRequest, failure } from './protocol.mjs'; + +export function discoverEndpoint(runtimeFile) { + let value = process.env.PEBREL_RUNTIME_ENDPOINT; + if (!value) { + let directory = process.env.PEBREL_CONFIG_DIR || process.env.NEBULA_CONFIG_DIR; + if (!directory) directory = process.platform === 'win32' + ? path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Pebrel') + : process.platform === 'darwin' ? path.join(os.homedir(), 'Library', 'Application Support', 'Pebrel') + : path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'pebrel'); + value = readFileSync(runtimeFile || path.join(directory, 'runtime.port'), 'utf8'); + } + const [port, token] = value.trim().split(/\s+/); + if (!/^\d+$/.test(port) || +port <= 0 || +port > 65535 || !/^[a-fA-F0-9]{32}$/.test(token)) throw new Error('invalid_runtime_endpoint'); + return { port: +port, token }; +} + +/** One paired peer owns these loopback requests. The endpoint never changes mid-link. */ +export class RuntimeLink { + constructor(endpoint, allowInput, emit, disconnect) { + this.endpoint = endpoint; + this.allowInput = allowInput; + this.emit = emit; + this.disconnect = disconnect; + this.active = new Map(); + this.seen = new Set(); + this.inputIds = new Set(); + this.subscribed = false; + this.closed = false; + } + request(raw) { + if (this.closed) return; + let request; + try { request = validateRequest(raw, this.allowInput); } + catch (error) { this.emit(failure(typeof raw?.id === 'string' ? raw.id.slice(0, 80) : 'invalid', error.message)); return; } + if (this.seen.has(request.id) || this.inputIds.has(request.id)) { this.emit(failure(request.id, 'duplicate_request')); return; } + if (this.active.size >= policy.maxPending) { this.emit(failure(request.id, 'too_many_requests')); return; } + const subscription = request.method === 'events.subscribe'; + if (subscription && this.subscribed) { this.emit(failure(request.id, 'already_subscribed')); return; } + if (policy.input.includes(request.method)) { + // Polling must never evict an accepted input ID. Bound the lifetime budget + // instead: only an explicit new pairing permits more inputs after it fills. + if (this.inputIds.size >= 4096) { this.emit(failure(request.id, 'input_limit_reconnect')); return; } + this.inputIds.add(request.id); + } + this.seen.add(request.id); + if (this.seen.size > 256) this.seen.delete(this.seen.values().next().value); + if (subscription) this.subscribed = true; + const socket = net.createConnection({ host: '127.0.0.1', port: this.endpoint.port }); + this.active.set(request.id, socket); + socket.setNoDelay(true); + socket.setTimeout(35_000); + let fragments = []; + let length = 0; + let responded = false; + let ended = false; + const lost = () => { + if (ended || this.closed) return; + ended = true; + if (!responded) this.emit(failure(request.id, 'delivery_unknown')); + // Never rediscover a newly started desktop and silently retarget input. + this.disconnect(); + this.close(); + }; + socket.on('connect', () => { + const local = { protocol: 'nebula.runtime', version: 1, ...request, token: this.endpoint.token }; + socket.end(JSON.stringify(local) + '\n'); + }); + socket.on('data', bytes => { + let start = 0; + while (start < bytes.length && !this.closed) { + const newline = bytes.indexOf(10, start); + const end = newline < 0 ? bytes.length : newline + 1; + const fragment = bytes.subarray(start, end); + length += fragment.length; + if (length > policy.maxFrameBytes) { lost(); socket.destroy(); return; } + fragments.push(fragment); + start = end; + if (newline < 0) break; + try { + const frame = JSON.parse(Buffer.concat(fragments, length).toString('utf8')); + fragments = []; length = 0; + if (!responded) { + if (frame.id !== request.id || typeof frame.ok !== 'boolean') throw new Error('invalid_response'); + responded = true; + if (subscription && frame.ok) socket.setTimeout(0); + } else if (!subscription || frame.event !== 'runtime.snapshot') throw new Error('invalid_event'); + this.emit(frame); + } catch { lost(); socket.destroy(); return; } + } + }); + socket.on('timeout', () => { lost(); socket.destroy(); }); + socket.on('error', lost); + socket.on('close', () => { + this.active.delete(request.id); + if (length || !responded || subscription) lost(); + }); + } + close() { + if (this.closed) return; + this.closed = true; + for (const socket of this.active.values()) socket.destroy(); + this.active.clear(); this.seen.clear(); this.inputIds.clear(); + } +} diff --git a/mobile/relay/server.mjs b/mobile/relay/server.mjs new file mode 100644 index 0000000..915c48a --- /dev/null +++ b/mobile/relay/server.mjs @@ -0,0 +1,114 @@ +import http from 'node:http'; +import https from 'node:https'; +import { createHash, timingSafeEqual, randomUUID } from 'node:crypto'; +import { readFileSync } from 'node:fs'; +import { pathToFileURL } from 'node:url'; +import { WebSocketServer, WebSocket } from 'ws'; +import { MAX_ENVELOPE, MAX_BUFFERED, devicePattern, tokenPattern } from './protocol.mjs'; + +const digest = value => createHash('sha256').update(value).digest(); + +/** User-owned rendezvous. No accounts, database, terminal replay or payload logging. */ +export function createRelay(config, options = {}) { + if (!Array.isArray(config.devices) || !config.devices.length || config.devices.length > 64) throw new Error('configure_1_to_64_devices'); + if (options.tls && (!options.tls.key || !options.tls.cert)) throw new Error('invalid_tls_config'); + const rooms = new Map(); + for (const device of config.devices) { + if (!devicePattern.test(device.id) || rooms.has(device.id) || + !tokenPattern.test(device.desktopToken) || !tokenPattern.test(device.mobileToken) || + device.desktopToken === device.mobileToken) throw new Error('invalid_device_credentials'); + rooms.set(device.id, { keys: { desktop: digest(device.desktopToken), mobile: digest(device.mobileToken) }, peers: {}, link: null }); + } + const requestHandler = (request, response) => { + response.writeHead(request.url === '/healthz' ? 200 : 404, { 'content-type': 'text/plain' }); + response.end(request.url === '/healthz' ? 'ok\n' : 'not found\n'); + }; + const server = options.tls + ? https.createServer({ key: options.tls.key, cert: options.tls.cert }, requestHandler) + : http.createServer(requestHandler); + server.maxConnections = 256; + server.requestTimeout = 10_000; + server.headersTimeout = 10_000; + const sockets = new WebSocketServer({ noServer: true, maxPayload: MAX_ENVELOPE, perMessageDeflate: false }); + const send = (socket, value) => { + if (!socket || socket.readyState !== WebSocket.OPEN) return false; + if (socket.bufferedAmount > MAX_BUFFERED) { socket.terminate(); return false; } + socket.send(JSON.stringify(value)); + return true; + }; + server.on('upgrade', (request, socket, head) => { + let accepted = false; + try { + const url = new URL(request.url, 'http://relay.invalid'); + const role = url.searchParams.get('role'); + const room = rooms.get(url.searchParams.get('device')); + const header = request.headers.authorization ?? ''; + const token = header.startsWith('Bearer ') ? header.slice(7) : ''; + if (url.pathname !== '/v1/link' || !room || !['desktop', 'mobile'].includes(role) || + !tokenPattern.test(token) || !timingSafeEqual(digest(token), room.keys[role])) throw new Error('unauthorized'); + if (room.peers[role]) { + socket.end('HTTP/1.1 409 Conflict\r\nConnection: close\r\n\r\n'); + return; + } + accepted = true; + sockets.handleUpgrade(request, socket, head, ws => { + room.peers[role] = ws; + ws.alive = true; + ws.on('pong', () => { ws.alive = true; }); + ws.on('error', () => { /* close handler owns cleanup; never log credentials */ }); + let count = 0; + let windowStart = Date.now(); + ws.on('message', (bytes, binary) => { + if (Date.now() - windowStart > 1000) { count = 0; windowStart = Date.now(); } + if (++count > 100 || binary) { ws.close(1008, 'protocol_limit'); return; } + try { + const frame = JSON.parse(bytes.toString()); + if (!room.link || frame.type !== 'relay.data' || frame.link !== room.link || !frame.body || typeof frame.body !== 'object') throw new Error('invalid_link'); + const peer = room.peers[role === 'desktop' ? 'mobile' : 'desktop']; + // No store-and-forward: a prompt can never cross into the next link. + if (!peer || peer.readyState !== WebSocket.OPEN) throw new Error('peer_offline'); + if (peer.bufferedAmount + bytes.length > MAX_BUFFERED) { peer.terminate(); ws.close(1013, 'congested'); return; } + peer.send(bytes, { binary: false }); + } catch { ws.close(1008, 'invalid_frame'); } + }); + ws.on('close', () => { + if (room.peers[role] !== ws) return; + delete room.peers[role]; + room.link = null; + send(room.peers[role === 'desktop' ? 'mobile' : 'desktop'], { type: 'relay.peer_left' }); + }); + if (room.peers.desktop && room.peers.mobile) { + room.link = randomUUID(); + // Desktop gets the link first; its ready response is delivered after + // both paired control frames have been queued by this event turn. + send(room.peers.desktop, { type: 'relay.paired', link: room.link }); + send(room.peers.mobile, { type: 'relay.paired', link: room.link }); + } else send(ws, { type: 'relay.waiting' }); + }); + } catch { + if (!accepted) socket.end('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n'); + else socket.destroy(); + } + }); + const heartbeat = setInterval(() => { + for (const socket of sockets.clients) { + if (!socket.alive) { socket.terminate(); continue; } + socket.alive = false; + socket.ping(); + } + }, 30_000); + heartbeat.unref(); + return { server, close: async () => { + clearInterval(heartbeat); + for (const socket of sockets.clients) socket.terminate(); + await new Promise(resolve => sockets.close(resolve)); + await new Promise(resolve => server.close(resolve)); + } }; +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + const config = JSON.parse(readFileSync(process.env.PEBREL_RELAY_CONFIG ?? './relay.config.json', 'utf8')); + const relay = createRelay(config); + relay.server.listen(Number(process.env.PORT ?? 8787), process.env.BIND ?? '127.0.0.1', () => console.log('Pebrel relay listening')); + for (const signal of ['SIGINT', 'SIGTERM']) process.on(signal, () => { relay.close().then(() => process.exit(0)); }); +} diff --git a/mobile/relay/test/init.test.mjs b/mobile/relay/test/init.test.mjs new file mode 100644 index 0000000..e2c9029 --- /dev/null +++ b/mobile/relay/test/init.test.mjs @@ -0,0 +1,63 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { spawnSync } from 'node:child_process'; + +const script = fileURLToPath(new URL('../init.mjs', import.meta.url)); + +test('initialization creates matching role credentials and retains existing computers and deployment settings', t => { + const directory = mkdtempSync(path.join(tmpdir(), 'pebrel-relay-init-')); + t.after(() => rmSync(directory, { recursive: true, force: true })); + const initialize = name => { + const result = spawnSync(process.execPath, [script, '--url', 'wss://relay.example.com', '--name', name], { + cwd: directory, encoding: 'utf8', + }); + assert.equal(result.status, 0, result.stderr); + return result.stdout; + }; + const firstOutput = initialize('Windows PC'); + const privateDirectory = path.join(directory, 'private'); + const configuration = () => JSON.parse(readFileSync(path.join(privateDirectory, 'relay.config.json'), 'utf8')); + const first = configuration().devices[0]; + const deployment = readFileSync(path.join(directory, '.env'), 'utf8'); + assert.match(deployment, /^PEBREL_RELAY_DOMAIN=relay\.example\.com\n/); + const secondOutput = initialize('MacBook'); + const devices = configuration().devices; + assert.equal(devices.length, 2); + assert.deepEqual(devices[0], first); + assert.equal(readFileSync(path.join(directory, '.env'), 'utf8'), deployment); + const secrets = new Set(); + for (const device of devices) { + const computer = JSON.parse(readFileSync(path.join(privateDirectory, `computer-${device.id}.json`), 'utf8')); + const phone = JSON.parse(readFileSync(path.join(privateDirectory, `phone-${device.id}.txt`), 'utf8')); + assert.equal(computer.device, device.id); + assert.equal(phone.device, device.id); + assert.equal(computer.token, device.desktopToken); + assert.equal(phone.token, device.mobileToken); + for (const token of [computer.token, phone.token]) { + assert.match(token, /^[a-zA-Z0-9_-]{43}$/); + assert.ok(!secrets.has(token)); + assert.ok(!(firstOutput + secondOutput).includes(token)); + secrets.add(token); + } + } + if (process.platform !== 'win32') { + for (const file of readdirSync(privateDirectory)) { + assert.equal(statSync(path.join(privateDirectory, file)).mode & 0o077, 0); + } + assert.equal(statSync(path.join(directory, '.env')).mode & 0o077, 0); + } +}); + +test('invalid deployment URLs create no credentials or Compose environment', t => { + const directory = mkdtempSync(path.join(tmpdir(), 'pebrel-relay-invalid-')); + t.after(() => rmSync(directory, { recursive: true, force: true })); + for (const url of ['ws://relay.example.com', 'wss://relay.example.com:8443', 'wss://relay.example.com/path', 'wss://user:secret@relay.example.com']) { + const result = spawnSync(process.execPath, [script, '--url', url], { cwd: directory, encoding: 'utf8' }); + assert.notEqual(result.status, 0); + assert.deepEqual(readdirSync(directory), []); + } +}); diff --git a/mobile/relay/test/relay.test.mjs b/mobile/relay/test/relay.test.mjs new file mode 100644 index 0000000..ef77731 --- /dev/null +++ b/mobile/relay/test/relay.test.mjs @@ -0,0 +1,168 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import net from 'node:net'; +import http from 'node:http'; +import { once } from 'node:events'; +import { WebSocket } from 'ws'; +import { createRelay } from '../server.mjs'; +import { connectDesktop } from '../connector.mjs'; +import { validateRequest } from '../protocol.mjs'; + +const device = { id: 'test-device', desktopToken: 'd'.repeat(43), mobileToken: 'm'.repeat(43) }; +const snapshot = { process_id: 42, windows: [{ id: 1, tabs: [{ label: 'Test', panes: [ + { id: 2, title: 'shell', cwd: '/work', running_program: null, task_state: 'idle', state_change_seq: 1 }, +] }] }] }; + +function inbox(socket) { + const queue = []; + const listeners = []; + socket.on('message', bytes => { + const value = JSON.parse(bytes.toString()); + const waiter = listeners.shift(); + if (waiter) waiter(value); else queue.push(value); + }); + return async () => { + if (queue.length) return queue.shift(); + return new Promise(resolve => listeners.push(resolve)); + }; +} +async function relayFixture(t) { + const relay = createRelay({ devices: [device] }); + relay.server.listen(0, '127.0.0.1'); + await once(relay.server, 'listening'); + t.after(() => relay.close()); + const url = `ws://127.0.0.1:${relay.server.address().port}`; + const open = role => { + const ws = new WebSocket(`${url}/v1/link?device=${device.id}&role=${role}`, { + headers: { Authorization: `Bearer ${device[`${role}Token`]}` }, + }); + const next = inbox(ws); + return { ws, next }; + }; + return { url, open }; +} +async function runtimeFixture(t) { + const sockets = new Set(); + const requests = []; + const server = net.createServer({ allowHalfOpen: true }, socket => { + sockets.add(socket); socket.on('close', () => sockets.delete(socket)); socket.on('error', () => {}); + let buffer = ''; + socket.on('data', bytes => { + buffer += bytes; + if (!buffer.endsWith('\n')) return; + const request = JSON.parse(buffer); + requests.push(request); + assert.equal(request.token, 'a'.repeat(32)); + const result = request.method === 'pane.read' ? { text: 'terminal output' } : {}; + socket.write(JSON.stringify({ id: request.id, ok: true, result }) + '\n'); + if (request.method === 'events.subscribe') socket.write(JSON.stringify({ event: 'runtime.snapshot', data: snapshot }) + '\n'); + else socket.end(); + }); + }); + server.listen(0, '127.0.0.1'); await once(server, 'listening'); + t.after(async () => { for (const socket of sockets) socket.destroy(); await new Promise(resolve => server.close(resolve)); }); + return { endpoint: { port: server.address().port, token: 'a'.repeat(32) }, requests }; +} + +test('role credentials cannot be interchanged and duplicate peers cannot replace owners', { timeout: 8000 }, async t => { + const { url, open } = await relayFixture(t); + const rejected = new WebSocket(`${url}/v1/link?device=${device.id}&role=desktop`, { headers: { Authorization: `Bearer ${device.mobileToken}` } }); + rejected.on('error', () => {}); + const response = await new Promise(resolve => rejected.on('unexpected-response', (_request, response) => { resolve(response.statusCode); response.resume(); rejected.terminate(); })); + assert.equal(response, 401); + const desktop = open('desktop'); assert.equal((await desktop.next()).type, 'relay.waiting'); + const duplicate = open('desktop'); duplicate.ws.on('error', () => {}); + const status = await new Promise(resolve => duplicate.ws.on('unexpected-response', (_request, response) => { resolve(response.statusCode); response.resume(); duplicate.ws.terminate(); })); + assert.equal(status, 409); + assert.equal(desktop.ws.readyState, WebSocket.OPEN); +}); + +test('phone reaches the real loopback protocol through relay and reconnect does not replay input', { timeout: 12000 }, async t => { + const { url, open } = await relayFixture(t); + const { endpoint, requests } = await runtimeFixture(t); + let waitForPhone; + const hostOnline = new Promise(resolve => { waitForPhone = resolve; }); + const connector = connectDesktop({ url, device: device.id, token: device.desktopToken }, { + allowLoopback: true, allowInput: true, endpoint, + onStatus(value) { if (value === 'waiting_for_phone') waitForPhone(); }, + }); + t.after(() => connector.close()); + await hostOnline; + const mobile = open('mobile'); + const pair = await mobile.next(); assert.equal(pair.type, 'relay.paired'); + const hello = await mobile.next(); assert.equal(hello.body.protocol, 'pebrel.mobile.relay'); + assert.equal(hello.body.capabilities.input, true); + const send = body => mobile.ws.send(JSON.stringify({ type: 'relay.data', link: pair.link, body })); + send({ id: 'subscribe', method: 'events.subscribe', params: {} }); + assert.equal((await mobile.next()).body.ok, true); + assert.deepEqual((await mobile.next()).body.data, snapshot); + send({ id: 'read', method: 'pane.read', params: { window_id: 1, pane_id: 2, lines: 120 } }); + assert.equal((await mobile.next()).body.result.text, 'terminal output'); + send({ id: 'write', method: 'pane.prompt', params: { window_id: 1, pane_id: 2, text: 'pwd', submit: true } }); + assert.equal((await mobile.next()).body.ok, true); + send({ id: 'write', method: 'pane.prompt', params: { window_id: 1, pane_id: 2, text: 'pwd', submit: true } }); + assert.equal((await mobile.next()).body.error.code, 'duplicate_request'); + for (let index = 0; index < 257; index++) { + // Cross the dedup history size while respecting the real broker's 100/s + // limit; this is a long-lived polling scenario, not a flood exemption. + if (index % 64 === 0) await new Promise(resolve => setTimeout(resolve, 1050)); + send({ id: `poll-${index}`, method: 'runtime.describe', params: {} }); + assert.equal((await mobile.next()).body.ok, true); + } + send({ id: 'write', method: 'pane.prompt', params: { window_id: 1, pane_id: 2, text: 'pwd', submit: true } }); + assert.equal((await mobile.next()).body.error.code, 'duplicate_request'); + const waiting = new Promise(resolve => { waitForPhone = resolve; }); + mobile.ws.close(); await waiting; + const second = open('mobile'); + const newPair = await second.next(); await second.next(); + assert.notEqual(newPair.link, pair.link); + const closed = once(second.ws, 'close'); + second.ws.send(JSON.stringify({ type: 'relay.data', link: pair.link, body: { id: 'old', method: 'pane.prompt', params: { window_id: 1, pane_id: 2, text: 'pwd' } } })); + await closed; + assert.equal(requests.filter(request => request.method === 'pane.prompt').length, 1); +}); + +test('temporary proxy failures reconnect while rejected credentials stop retries', { timeout: 8000 }, async t => { + let attempts = 0; + const server = http.createServer(); + server.on('upgrade', (_request, socket) => { + attempts++; + const response = attempts === 1 ? '503 Service Unavailable' : '401 Unauthorized'; + socket.end(`HTTP/1.1 ${response}\r\nConnection: close\r\nContent-Length: 0\r\n\r\n`); + }); + server.listen(0, '127.0.0.1'); + await once(server, 'listening'); + t.after(() => new Promise(resolve => server.close(resolve))); + const statuses = []; + let rejected; + const rejection = new Promise(resolve => { rejected = resolve; }); + const connector = connectDesktop({ url: `ws://127.0.0.1:${server.address().port}`, device: device.id, token: device.desktopToken }, { + allowLoopback: true, + onStatus(value) { statuses.push(value); if (value === 'authentication_failed') rejected(); }, + }); + t.after(() => connector.close()); + await rejection; + // Drain the close event so an erroneous reconnect scheduling is observable. + await new Promise(resolve => setTimeout(resolve, 1200)); + assert.equal(attempts, 2); + assert.ok(statuses.includes('server_unavailable')); + assert.equal(statuses.filter(value => value === 'reconnecting').length, 1); +}); + +test('read-only channels reject input, caller tokens and incomplete pane identities', () => { + const params = { window_id: 1, pane_id: 2 }; + assert.throws(() => validateRequest({ id: 'x', method: 'pane.prompt', params }, false), /input_not_authorized/); + assert.throws(() => validateRequest({ id: 'x', method: 'runtime.snapshot', token: 'caller' }, false), /invalid_request/); + assert.throws(() => validateRequest({ id: 'x', method: 'pane.read', params: { pane_id: 2 } }, false), /invalid_target/); + assert.throws(() => validateRequest({ id: 'x', method: 'window.close', params }, true), /method_not_found/); + assert.equal(validateRequest({ id: 'x', method: 'pane.read', params }, false).method, 'pane.read'); + for (const method of ['git.status', 'git.diff', 'git.history']) { + assert.equal(validateRequest({ id: 'git', method, params }, false).method, method); + assert.throws(() => validateRequest({ id: 'git', method, params: { pane_id: 2 } }, true), /invalid_target/); + } + for (const method of ['git.stage', 'git.unstage', 'git.commit', 'git.fetch', 'git.pull', 'git.push']) { + assert.throws(() => validateRequest({ id: 'git', method, params }, false), /input_not_authorized/); + assert.equal(validateRequest({ id: 'git', method, params }, true).method, method); + } + assert.throws(() => validateRequest({ id: 'git', method: 'git.reset', params }, true), /method_not_found/); +}); diff --git a/mobile/relay/tls.mjs b/mobile/relay/tls.mjs new file mode 100644 index 0000000..dc31ffb --- /dev/null +++ b/mobile/relay/tls.mjs @@ -0,0 +1,70 @@ +import { createHash, timingSafeEqual, X509Certificate } from 'node:crypto'; +import { checkServerIdentity as defaultCheckServerIdentity } from 'node:tls'; + +const PIN_PREFIX = 'sha256/'; +const PIN_BYTES = 32; +const PIN_TEXT = /^[A-Za-z0-9+/]{43}=$/; + +/** Validate the invitation's canonical SHA-256/SPKI pin representation. */ +export function validateTlsPin(value) { + if (typeof value !== 'string' || !value.startsWith(PIN_PREFIX)) throw new Error('invalid_tls_pin'); + const encoded = value.slice(PIN_PREFIX.length); + if (!PIN_TEXT.test(encoded)) throw new Error('invalid_tls_pin'); + let bytes; + try { bytes = Buffer.from(encoded, 'base64'); } catch { throw new Error('invalid_tls_pin'); } + if (bytes.length !== PIN_BYTES || bytes.toString('base64') !== encoded) throw new Error('invalid_tls_pin'); + return value; +} + +function certificateObject(certificate) { + if (certificate instanceof X509Certificate) return certificate; + if (certificate?.raw) return new X509Certificate(certificate.raw); + if (Buffer.isBuffer(certificate) || typeof certificate === 'string') return new X509Certificate(certificate); + throw new Error('certificate_unavailable'); +} + +export function certificateSpki(certificate) { + return certificateObject(certificate).publicKey.export({ type: 'spki', format: 'der' }); +} + +/** Return the canonical pin for a PEM/DER X.509 certificate. */ +export function spkiPin(certificate) { + return `${PIN_PREFIX}${createHash('sha256').update(certificateSpki(certificate)).digest('base64')}`; +} + +/** Compare a peer certificate's SPKI with an exact invitation pin. */ +export function matchesSpkiPin(pin, certificate) { + validateTlsPin(pin); + const expected = Buffer.from(pin.slice(PIN_PREFIX.length), 'base64'); + const actual = createHash('sha256').update(certificateSpki(certificate)).digest(); + return expected.length === actual.length && timingSafeEqual(expected, actual); +} + +/** + * Keep Node's normal hostname check and add the exact SPKI check used by LAN + * invitations. Returning an Error lets Node abort the TLS handshake normally. + */ +export function pinnedServerIdentity(pin) { + validateTlsPin(pin); + return (hostname, certificate) => { + const hostnameError = defaultCheckServerIdentity(hostname, certificate); + if (hostnameError) return hostnameError; + try { + if (!matchesSpkiPin(pin, certificate)) return new Error('tls_pin_mismatch'); + } catch { return new Error('tls_pin_mismatch'); } + return undefined; + }; +} + +/** Build only the TLS options the connector is allowed to use. */ +export function pinnedTlsOptions({ pin, certificate } = {}) { + const options = {}; + if (certificate && !pin) throw new Error('tls_pin_required_for_custom_certificate'); + if (certificate) options.ca = certificate; + if (pin) { + validateTlsPin(pin); + options.rejectUnauthorized = true; + options.checkServerIdentity = pinnedServerIdentity(pin); + } + return options; +} diff --git a/mobile/ssh/Cargo.lock b/mobile/ssh/Cargo.lock new file mode 100644 index 0000000..5265ddc --- /dev/null +++ b/mobile/ssh/Cargo.lock @@ -0,0 +1,2440 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common 0.1.7", + "generic-array 0.14.7", +] + +[[package]] +name = "aead" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99" +dependencies = [ + "crypto-common 0.2.2", + "inout 0.2.2", +] + +[[package]] +name = "aes" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32" +dependencies = [ + "cipher 0.5.2", + "cpubits", + "cpufeatures 0.3.1", + "zeroize", +] + +[[package]] +name = "aes-gcm" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2b8006a0c83f52b62ba44a97b58bf76fe2f70a329e588f67f89691d93d498f" +dependencies = [ + "aead 0.6.1", + "aes", + "cipher 0.5.2", + "ctr", + "ctutils", + "ghash", + "zeroize", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "argon2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c52ddac6d63c576bef8168db10c83c49c26444ecbc68060fef078925a901c" +dependencies = [ + "base64ct", + "blake2 0.11.0", + "cpufeatures 0.3.1", + "password-hash", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bcrypt-pbkdf" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144e573728da132683b9488acd528274c790e07fc06ff81ee29f9d8f8b1041e0" +dependencies = [ + "blowfish", + "pbkdf2", + "sha2 0.11.0", +] + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" +dependencies = [ + "serde_core", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b5d4d889834ee8ecfc0f8426ad30faf7cdcb10f741a8e6d7224d95325479f6f" +dependencies = [ + "digest 0.11.3", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", + "zeroize", +] + +[[package]] +name = "block-padding" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "blowfish" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ce3946557b35e71d1bbe07ec385073ce9eda05043f95de134eb578fcf1a298" +dependencies = [ + "byteorder", + "cipher 0.5.2", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cbc" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2dc9ee5f88d11e0beb842c88b33c8a5cf0d1329c4b19494af42b07dbfe8896" +dependencies = [ + "cipher 0.5.2", +] + +[[package]] +name = "cc" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3eb0f42d6c360dc3f8a821f6bf2fdea7f72bfd36b3076eb0e6d1e9e0752fff4" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher 0.4.4", + "cpufeatures 0.2.17", +] + +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cipher 0.5.2", + "cpufeatures 0.3.1", + "rand_core", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead 0.5.2", + "chacha20 0.9.1", + "cipher 0.4.4", + "poly1305 0.8.0", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout 0.1.4", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", + "inout 0.2.2", + "zeroize", +] + +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + +[[package]] +name = "combine" +version = "4.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" + +[[package]] +name = "crypto-bigint" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271" +dependencies = [ + "cpubits", + "ctutils", + "getrandom 0.4.3", + "hybrid-array", + "num-traits", + "rand_core", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array 0.14.7", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "getrandom 0.4.3", + "hybrid-array", + "rand_core", +] + +[[package]] +name = "crypto-primes" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3633a51a39c69ebbaa4feaa694bd83d241e4093901c84a0963b19d9bb3f0cf8f" +dependencies = [ + "crypto-bigint", + "rand_core", +] + +[[package]] +name = "ctr" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21" +dependencies = [ + "cipher 0.5.2", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "fiat-crypto 0.2.9", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "5.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c906a87e53a36ff795d72e06e8162a83c5436e3ea89e942a9cb9fc083f0a384f" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "curve25519-dalek-derive", + "digest 0.11.3", + "fiat-crypto 0.3.0", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "dashmap" +version = "6.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "delegate" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "der" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "des" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a94e407b54f9034d71dd748234cd1e516ced6284009906ae246f177eafe5a" +dependencies = [ + "cipher 0.5.2", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", + "ctutils", +] + +[[package]] +name = "ecdsa" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0681a4fc24c767085329728d8dfba959af91228aa4610cca4f8ce317ba46ae0" +dependencies = [ + "der", + "digest 0.11.3", + "elliptic-curve", + "rfc6979", + "signature", + "spki", + "zeroize", +] + +[[package]] +name = "ed25519" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "3.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1685663e23882cd8517dcbcb1c23a6ebff4433c22dfb681d760219b62cd1b849" +dependencies = [ + "curve25519-dalek 5.0.0-rc.1", + "ed25519", + "rand_core", + "serde", + "sha2 0.11.0", + "signature", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65" +dependencies = [ + "base16ct", + "crypto-bigint", + "crypto-common 0.2.2", + "digest 0.11.3", + "ff", + "group", + "hkdf", + "hybrid-array", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "ff" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + +[[package]] +name = "find-msvc-tools" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" + +[[package]] +name = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-executor" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "generic-array" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "337d46834ee672ab3e48caca2cb0c78cc174fb12b3a68d0d88f99a0519a5e36e" +dependencies = [ + "generic-array 0.14.7", + "rustversion", + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5" +dependencies = [ + "polyval", + "zeroize", +] + +[[package]] +name = "gloo-timers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "482ce8a491a501da4cd806bd190275363d674f2845005c6ddbd5d3e1dd54495d" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" + +[[package]] +name = "hkdf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "ctutils", + "subtle", + "typenum", + "zeroize", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "block-padding", + "hybrid-array", +] + +[[package]] +name = "internal-russh-num-bigint" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8e22120c32fb4d19ec55fba35015f57095cd95a2e3b732e44457f5915b2ee8" +dependencies = [ + "num-integer", + "num-traits", + "rand", + "rand_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "js-sys" +version = "0.3.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f198d1db720e4940b5a493201d199d9f24f568f8f746bd13706243a2f71598" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", +] + +[[package]] +name = "kem" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd" +dependencies = [ + "crypto-common 0.2.2", + "rand_core", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "md5" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebb8d8732c6a6df3d8f032a82911cfc747e00efb95cc46e8d0acd5b5b88570c" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "mio" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "ml-kem" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e15f3e5b957493873e396a66914e83e616b6afe335cdef7efe5c6e1216aba66" +dependencies = [ + "hybrid-array", + "kem", + "module-lattice", + "pkcs8", + "rand_core", + "sha3", +] + +[[package]] +name = "module-lattice" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c61b87c9683ab7cb1c6871d261ad5479b6b10ceb52c4352aaca3b5d35a8febe" +dependencies = [ + "ctutils", + "hybrid-array", + "num-traits", +] + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "p256" +version = "0.14.0-rc.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bb40a5099e2c38a09dd29321a7a7f045f165a54317679c7cdfb0cbaf8f6b1e" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "p384" +version = "0.14.0-rc.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "492f329d7eb11d22dadc988626b9ea1f503b4ab043a8b1e4e2cc4ae45dabdd70" +dependencies = [ + "ecdsa", + "elliptic-curve", + "fiat-crypto 0.3.0", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "p521" +version = "0.14.0-rc.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff42e4ace5424e3b6d7cb82514be89866b85af87015f80341e37dcc21a66ce6e" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "pageant" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d8eab09a361a4afe0b1668be978cd97e4f052e927a92b0b608cf902965d49ce" +dependencies = [ + "base16ct", + "byteorder", + "bytes", + "delegate", + "futures", + "log", + "rand", + "sha2 0.11.0", + "thiserror 2.0.20", + "tokio", + "windows", + "windows-strings", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "password-hash" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b" +dependencies = [ + "phc", +] + +[[package]] +name = "pbkdf2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" +dependencies = [ + "digest 0.11.3", + "hmac", +] + +[[package]] +name = "pebrel-mobile-link" +version = "0.1.0" +dependencies = [ + "base64", + "getrandom 0.3.4", + "qrcode", + "serde", + "serde_json", + "sha2 0.10.9", + "snow", + "subtle", + "thiserror 2.0.20", + "zeroize", +] + +[[package]] +name = "pebrel-mobile-ssh" +version = "0.1.0" +dependencies = [ + "base64", + "jni", + "pebrel-mobile-link", + "russh", + "russh-sftp", + "serde", + "serde_json", + "tokio", + "tokio-util", + "zeroize", +] + +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + +[[package]] +name = "phc" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892" +dependencies = [ + "base64ct", + "ctutils", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkcs5" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63d440a804ec8d6fafbb6b84471e013286658d373248927692ab3366686220ca" +dependencies = [ + "aes", + "aes-gcm", + "cbc", + "der", + "pbkdf2", + "rand_core", + "scrypt", + "sha2 0.11.0", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" +dependencies = [ + "der", + "pkcs5", + "rand_core", + "spki", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash 0.5.1", +] + +[[package]] +name = "poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c" +dependencies = [ + "cpufeatures 0.3.1", + "universal-hash 0.6.1", + "zeroize", +] + +[[package]] +name = "polyval" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0fa31d631f2b2cb2a544d0aa321ce847a94764d701ca2becc411138b93d49cd" +dependencies = [ + "cpubits", + "cpufeatures 0.3.1", + "universal-hash 0.6.1", + "zeroize", +] + +[[package]] +name = "primefield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4" +dependencies = [ + "crypto-bigint", + "crypto-common 0.2.2", + "ff", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "primeorder" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06" +dependencies = [ + "elliptic-curve", + "once_cell", + "primefield", + "serdect", + "wnaf", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qrcode" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec" + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20 0.10.2", + "getrandom 0.4.3", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rfc6979" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a459cddafb3fe76b31fd8f1108007566c40301feb64dc7b54656eb7388172b" +dependencies = [ + "crypto-bigint", + "hmac", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.10.0-rc.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b2aa4ba0d89f73d1e332df05be0eeab8840351c36ca5654341dfdb57bb3caf" +dependencies = [ + "const-oid", + "crypto-bigint", + "crypto-primes", + "digest 0.11.3", + "pkcs1", + "pkcs8", + "rand_core", + "sha2 0.11.0", + "signature", + "spki", + "zeroize", +] + +[[package]] +name = "russh" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6c3c1dd0a9ad3a9915a2f6e907d158f9a7e9ac32ddc772b003faafc027d9a8" +dependencies = [ + "aes", + "bitflags", + "block-padding", + "byteorder", + "bytes", + "cbc", + "cipher 0.5.2", + "crypto-bigint", + "ctr", + "curve25519-dalek 5.0.0-rc.1", + "data-encoding", + "delegate", + "der", + "digest 0.11.3", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "enum_dispatch", + "futures", + "generic-array 1.4.5", + "getrandom 0.4.3", + "ghash", + "hex-literal", + "hmac", + "inout 0.2.2", + "internal-russh-num-bigint", + "keccak", + "log", + "md5", + "ml-kem", + "module-lattice", + "num-bigint", + "p256", + "p384", + "p521", + "pageant", + "pbkdf2", + "pkcs1", + "pkcs5", + "pkcs8", + "polyval", + "rand", + "rand_core", + "ring", + "rsa", + "russh-cryptovec", + "russh-util", + "salsa20", + "scrypt", + "sec1", + "sha1", + "sha2 0.11.0", + "sha3", + "signature", + "spki", + "ssh-encoding", + "ssh-key", + "subtle", + "thiserror 2.0.20", + "tokio", + "typenum", + "universal-hash 0.6.1", + "zeroize", +] + +[[package]] +name = "russh-cryptovec" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aec6cb630dbe85d72ffd7bcd95f07e1bd69f9f270ee8adfa1afe443a6331438" +dependencies = [ + "log", + "nix", + "ssh-encoding", + "windows-sys 0.61.2", +] + +[[package]] +name = "russh-sftp" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed8949eca4163c18a8f59ff96d32cf61e9c13b9735e21ef32b3907f4aafa1a9" +dependencies = [ + "bitflags", + "bytes", + "chrono", + "dashmap", + "gloo-timers", + "log", + "serde", + "serde_bytes", + "thiserror 2.0.20", + "tokio", + "tokio-util", + "wasm-bindgen-futures", +] + +[[package]] +name = "russh-util" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668424a5dde0bcb45b55ba7de8476b93831b4aa2fa6947e145f3b053e22c60b6" +dependencies = [ + "chrono", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "salsa20" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f874456e72520ff1375a06c588eaf074b0f01f9e9e1aada45bd9b7954a6e42c" +dependencies = [ + "cfg-if", + "cipher 0.5.2", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scrypt" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87af57419b594aa23fa95f09f0e06d80d84ba01c26148c43844cad6ff4485f0" +dependencies = [ + "cfg-if", + "pbkdf2", + "salsa20", + "sha2 0.11.0", +] + +[[package]] +name = "sec1" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" +dependencies = [ + "base16ct", + "ctutils", + "der", + "hybrid-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" +dependencies = [ + "digest 0.11.3", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "snow" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "599b506ccc4aff8cf7844bc42cf783009a434c1e26c964432560fb6d6ad02d82" +dependencies = [ + "blake2 0.10.6", + "chacha20poly1305", + "curve25519-dalek 4.1.3", + "getrandom 0.3.4", + "ring", + "rustc_version", + "sha2 0.10.9", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spki" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssh-cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d801accda99469cde6d73da741422610fdf6508a72d9a69d1b55cb241c720597" +dependencies = [ + "aead 0.6.1", + "aes", + "aes-gcm", + "chacha20 0.10.2", + "cipher 0.5.2", + "ctutils", + "des", + "poly1305 0.9.1", + "ssh-encoding", + "zeroize", +] + +[[package]] +name = "ssh-encoding" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b54d0ed0498daf3f78d82e00e28c8eec9d75a067c4cfbcc7a0f7d0f4077749e" +dependencies = [ + "base64ct", + "bytes", + "crypto-bigint", + "ctutils", + "digest 0.11.3", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "ssh-key" +version = "0.7.0-rc.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a32fae177b74a22aa9c5b01bf7e68b33545be32d9e381e248058d2adc15ce3" +dependencies = [ + "argon2", + "bcrypt-pbkdf", + "ctutils", + "ed25519-dalek", + "hex", + "hmac", + "p256", + "p384", + "p521", + "rand_core", + "rsa", + "sec1", + "sha1", + "sha2 0.11.0", + "signature", + "ssh-cipher", + "ssh-encoding", + "zeroize", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl 2.0.20", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "tokio" +version = "1.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "universal-hash" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96" +dependencies = [ + "crypto-common 0.2.2", + "ctutils", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.5", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wnaf" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "795ca18b3fdb5e62bf982199278341ddcf7ebf7d32e25e212ad05d496e95f6fa" +dependencies = [ + "ff", + "group", + "hybrid-array", + "primefield", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/mobile/ssh/Cargo.toml b/mobile/ssh/Cargo.toml new file mode 100644 index 0000000..6fcfd41 --- /dev/null +++ b/mobile/ssh/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "pebrel-mobile-ssh" +version = "0.1.0" +edition = "2024" +rust-version = "1.97.1" +license = "GPL-3.0-only" +publish = false + +[workspace] + +[lib] +name = "pebrel_ssh" +crate-type = ["cdylib", "rlib"] + +[dependencies] +russh = { version = "=0.62.2", default-features = false, features = ["ring", "rsa"] } +russh-sftp = "=2.3.0" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +base64 = "0.22" +jni = "=0.21.1" +tokio = { version = "1", features = ["rt-multi-thread", "net", "sync", "time", "macros"] } +tokio-util = { version = "0.7", features = ["rt"] } +zeroize = "1" +pebrel-mobile-link = { path = "../link" } + +[profile.release] +opt-level = 2 +lto = "thin" +codegen-units = 1 +strip = "symbols" diff --git a/mobile/ssh/README.md b/mobile/ssh/README.md new file mode 100644 index 0000000..569493d --- /dev/null +++ b/mobile/ssh/README.md @@ -0,0 +1,35 @@ +# Android russh transport + +This independent Rust crate uses the same pinned russh 0.62.2 and ring/RSA +features as the desktop. It does not depend on desktop UI, Android rendering or +the terminal engine. Kotlin keeps host metadata, UI, explicit trust decisions and application +session ownership. JNI carries control events and bounded byte chunks only. + +A process-wide Tokio runtime has two workers. Each connection owns one SSH +session and one shell or exec channel; there is no implicit connection pooling or +automatic input replay. Numeric JNI handles index an owned registry rather than +exposing raw pointers. Closing removes the handle and cancels pending DNS, +connect, trust, authentication, channel and byte-stream work. In-flight JNI calls +hold an Arc and wake on cancellation; a stale handle cannot access freed memory. + +Queues are bounded: 16 control events, 16 input chunks of at most 8 KiB, and eight +16 KiB chunks for each output stream. Remote output backpressures the transport. +Shell stderr joins terminal output; exec stderr remains separate from RPC stdout. +PTY and shell/exec requests must receive success before the UI can become ready. +No rendering work or network work runs on Android's main thread. + +Connection stages originate from actual operations. Host fingerprints use russh's +SHA256 OpenSSH encoding. A changed stored fingerprint is rejected, and a new +identity waits for the application's explicit confirmation with a 60-second +budget. Authentication first checks server-authorized none authentication, then +tries the supplied password once. Key-file and jump-host transports are not +implemented. +Password bytes are not persisted or logged; temporary bridge-owned copies are +zeroized, without claiming control over every internal library allocation. + +`Cargo.lock` locks transitive dependencies. `mobile/tools/build_russh.py` compiles +both Android ABIs with the pinned NDK and 16 KiB page alignment, and bundles the +resolved dependency notices. Only the pinned open-source dependencies are linked; +no private implementation is included. `SshIntegrationTest` exercises the +optimized APK against a real OpenSSH fixture, including password rejection, +changed identity and declined trust. diff --git a/mobile/ssh/licenses/SOURCE.json b/mobile/ssh/licenses/SOURCE.json new file mode 100644 index 0000000..ae1c7ef --- /dev/null +++ b/mobile/ssh/licenses/SOURCE.json @@ -0,0 +1,5 @@ +{ + "file": "russh-LICENSE.txt", + "url": "https://raw.githubusercontent.com/warp-tech/russh/c4be19f1915c8682f4615c3fd50008512b474491/LICENSE.txt", + "sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30" +} diff --git a/mobile/ssh/licenses/russh-LICENSE.txt b/mobile/ssh/licenses/russh-LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/mobile/ssh/licenses/russh-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/mobile/ssh/src/bridge.rs b/mobile/ssh/src/bridge.rs new file mode 100644 index 0000000..127856a --- /dev/null +++ b/mobile/ssh/src/bridge.rs @@ -0,0 +1,263 @@ +//! JNI contains no protocol state and never exposes a Rust allocation as a pointer. +use std::panic::{AssertUnwindSafe, catch_unwind}; + +use jni::JNIEnv; +use jni::objects::{JByteArray, JObject, JString}; +use jni::sys::{jboolean, jint, jlong, jstring}; +use zeroize::Zeroizing; + +use crate::session::{self, Failure, Geometry, Open, Options, Result, runtime}; + +fn invoke(env: &mut JNIEnv, call: impl FnOnce(&mut JNIEnv) -> Result) -> T { + match catch_unwind(AssertUnwindSafe(|| call(env))) { + Ok(Ok(value)) => value, + failure => { + let code = match failure { + Ok(Err(error)) => error.0, + _ => "INTERNAL", + }; + let _ = env.throw_new("io/github/kuddev/pebrel/ssh/NativeSshException", code); + T::default() + }, + } +} + +fn string(env: &mut JNIEnv, value: &JString) -> Result { + env.get_string(value).map(Into::into).map_err(|_| Failure("INTERNAL")) +} + +fn bounds( + env: &JNIEnv, + bytes: &JByteArray, + offset: jint, + count: jint, + maximum: jint, +) -> Result<()> { + let length = env.get_array_length(bytes).map_err(|_| Failure("INTERNAL"))?; + if offset < 0 || count <= 0 || count > maximum || offset > length - count { + Err(Failure("INVALID_INPUT")) + } else { + Ok(()) + } +} + +fn geometry(columns: jint, rows: jint, width: jint, height: jint) -> Result { + if !(1..=4096).contains(&columns) || !(1..=4096).contains(&rows) || width < 0 || height < 0 { + return Err(Failure("INVALID_INPUT")); + } + Ok(Geometry { + columns: columns as u32, + rows: rows as u32, + width: width as u32, + height: height as u32, + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_create( + mut env: JNIEnv, + _object: JObject, + host: JString, + port: jint, + user: JString, + password: JByteArray, + fingerprint: JString, +) -> jlong { + invoke(&mut env, |env| { + let size = env.get_array_length(&password).map_err(|_| Failure("INTERNAL"))?; + if !(1..=65535).contains(&port) || size > 4096 { + return Err(Failure("INVALID_INPUT")); + } + let password = + Zeroizing::new(env.convert_byte_array(&password).map_err(|_| Failure("INTERNAL"))?); + let options = Options { + host: string(env, &host)?, + port: port as u16, + user: string(env, &user)?, + password, + fingerprint: string(env, &fingerprint)?, + }; + if options.host.len() > 1024 || options.user.len() > 1024 || options.fingerprint.len() > 256 + { + return Err(Failure("INVALID_INPUT")); + } + session::start(options) + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_nextEvent( + mut env: JNIEnv, + _object: JObject, + id: jlong, +) -> jstring { + invoke(&mut env, |env| { + let owner = session::get(id)?; + let event = runtime().block_on(owner.event())?; + env.new_string(event).map(|value| value.into_raw()).map_err(|_| Failure("INTERNAL")) + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_answerHostKey( + mut env: JNIEnv, + _object: JObject, + id: jlong, + accepted: jboolean, +) { + invoke(&mut env, |_| session::get(id)?.answer(accepted != 0)); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_openShell( + mut env: JNIEnv, + _object: JObject, + id: jlong, + columns: jint, + rows: jint, + width: jint, + height: jint, +) { + invoke(&mut env, |_| { + let owner = session::get(id)?; + runtime().block_on(owner.open(Open::Shell(geometry(columns, rows, width, height)?))) + }); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_openExec( + mut env: JNIEnv, + _object: JObject, + id: jlong, + command: JString, +) { + invoke(&mut env, |env| { + let command = string(env, &command)?; + if command.len() > 8192 { + return Err(Failure("INVALID_INPUT")); + } + let owner = session::get(id)?; + runtime().block_on(owner.open(Open::Exec(command))) + }); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_openPtyExec( + mut env: JNIEnv, + _object: JObject, + id: jlong, + command: JString, + columns: jint, + rows: jint, + width: jint, + height: jint, +) { + invoke(&mut env, |env| { + let command = string(env, &command)?; + if command.is_empty() || command.len() > 8192 || command.chars().any(char::is_control) { + return Err(Failure("INVALID_INPUT")); + } + let owner = session::get(id)?; + runtime() + .block_on(owner.open(Open::PtyExec(geometry(columns, rows, width, height)?, command))) + }); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_read( + mut env: JNIEnv, + _object: JObject, + id: jlong, + target: JByteArray, + offset: jint, + count: jint, + stderr: jboolean, +) -> jint { + invoke(&mut env, |env| { + bounds(env, &target, offset, count, 16 * 1024)?; + let owner = session::get(id)?; + let bytes = runtime().block_on(owner.read(stderr != 0, count as usize))?; + if bytes.is_empty() { + return Ok(-1); + } + let signed: Vec = bytes.iter().map(|byte| *byte as i8).collect(); + env.set_byte_array_region(&target, offset, &signed).map_err(|_| Failure("INTERNAL"))?; + Ok(bytes.len() as jint) + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_write( + mut env: JNIEnv, + _object: JObject, + id: jlong, + source: JByteArray, + offset: jint, + count: jint, +) { + invoke(&mut env, |env| { + bounds(env, &source, offset, count, 8192)?; + let mut bytes = vec![0i8; count as usize]; + env.get_byte_array_region(&source, offset, &mut bytes).map_err(|_| Failure("INTERNAL"))?; + let owner = session::get(id)?; + runtime().block_on(owner.write(bytes.into_iter().map(|byte| byte as u8).collect())) + }); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_resize( + mut env: JNIEnv, + _object: JObject, + id: jlong, + columns: jint, + rows: jint, + width: jint, + height: jint, +) { + invoke(&mut env, |_| { + let owner = session::get(id)?; + runtime().block_on(owner.resize(geometry(columns, rows, width, height)?)) + }); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_awaitExit( + mut env: JNIEnv, + _object: JObject, + id: jlong, +) -> jint { + invoke(&mut env, |_| { + let owner = session::get(id)?; + runtime().block_on(owner.exit()) + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_sftp( + mut env: JNIEnv, + _object: JObject, + id: jlong, + request: JString, +) -> jstring { + invoke(&mut env, |env| { + let text = string(env, &request)?; + let request = crate::sftp::parse(&text)?; + let owner = session::get(id)?; + let response = runtime().block_on(owner.sftp(request))?; + env.new_string(response.to_string()) + .map(|value| value.into_raw()) + .map_err(|_| Failure("INTERNAL")) + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeSsh_close( + mut env: JNIEnv, + _object: JObject, + id: jlong, +) { + invoke(&mut env, |_| { + session::close(id); + Ok(()) + }); +} diff --git a/mobile/ssh/src/lib.rs b/mobile/ssh/src/lib.rs new file mode 100644 index 0000000..814f18d --- /dev/null +++ b/mobile/ssh/src/lib.rs @@ -0,0 +1,8 @@ +mod bridge; +mod mobile_link; +mod session; +mod sftp; +mod transport; + +#[cfg(test)] +mod tests; diff --git a/mobile/ssh/src/mobile_link.rs b/mobile/ssh/src/mobile_link.rs new file mode 100644 index 0000000..7a39f9f --- /dev/null +++ b/mobile/ssh/src/mobile_link.rs @@ -0,0 +1,203 @@ +//! Android invokes the exact same crypto/framing implementation as desktop. +//! Handles are bounded IDs, never raw pointers; close revokes and zeroizes them. + +use jni::{ + JNIEnv, + objects::{JByteArray, JObject}, + sys::{jboolean, jbyteArray, jlong, jobjectArray, jstring}, +}; +use pebrel_mobile_link::{ + crypto::{LinkError, MAX_MESSAGE, MAX_PACKET, SecureChannel}, + identity::Secret, +}; +use std::{ + collections::HashMap, + panic::{AssertUnwindSafe, catch_unwind}, + sync::{Arc, Mutex, OnceLock}, +}; +use zeroize::Zeroizing; + +type Channel = Arc>; +#[derive(Default)] +struct Handles { + next: i64, + channels: HashMap, +} + +fn handles() -> &'static Mutex { + static HANDLES: OnceLock> = OnceLock::new(); + HANDLES.get_or_init(|| Mutex::new(Handles::default())) +} + +fn invoke( + env: &mut JNIEnv, + call: impl FnOnce(&mut JNIEnv) -> Result, +) -> T { + match catch_unwind(AssertUnwindSafe(|| call(env))) { + Ok(Ok(value)) => value, + error => { + let code = match error { + Ok(Err(error)) => error.to_string(), + _ => "secure_link_internal_error".into(), + }; + let _ = env.throw_new("java/io/IOException", code); + T::default() + }, + } +} + +fn bytes(env: &JNIEnv, array: &JByteArray, max: usize) -> Result>, LinkError> { + let size = env.get_array_length(array).map_err(|_| LinkError::Frame)?; + if size < 0 || size as usize > max { + return Err(LinkError::Frame); + } + Ok(Zeroizing::new(env.convert_byte_array(array).map_err(|_| LinkError::Frame)?)) +} + +fn channel(id: i64) -> Result { + handles() + .lock() + .map_err(|_| LinkError::State)? + .channels + .get(&id) + .cloned() + .ok_or(LinkError::State) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeLink_create( + mut env: JNIEnv, + _object: JObject, + host: JByteArray, + secret: JByteArray, + context: JByteArray, +) -> jlong { + invoke(&mut env, |env| { + let host = bytes(env, &host, 32)?; + let secret = Secret::from_bytes(&bytes(env, &secret, 32)?)?; + let context = bytes(env, &context, 512)?; + let channel = SecureChannel::initiator( + host.as_slice().try_into().map_err(|_| LinkError::Credential)?, + &secret, + &context, + )?; + let mut handles = handles().lock().map_err(|_| LinkError::State)?; + if handles.channels.len() >= 32 { + return Err(LinkError::State); + } + handles.next = handles.next.checked_add(1).ok_or(LinkError::State)?; + let id = handles.next; + handles.channels.insert(id, Arc::new(Mutex::new(channel))); + Ok(id) + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeLink_hello( + mut env: JNIEnv, + _object: JObject, + id: jlong, +) -> jbyteArray { + invoke(&mut env, |env| { + let owner = channel(id)?; + let output = owner.lock().map_err(|_| LinkError::State)?.write_handshake()?; + env.byte_array_from_slice(&output) + .map(|bytes| bytes.into_raw()) + .map_err(|_| LinkError::State) + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeLink_finish( + mut env: JNIEnv, + _object: JObject, + id: jlong, + response: JByteArray, +) -> jboolean { + invoke(&mut env, |env| { + let owner = channel(id)?; + let mut channel = owner.lock().map_err(|_| LinkError::State)?; + channel.read_handshake(&bytes(env, &response, 128)?)?; + Ok(u8::from(channel.established())) + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeLink_seal( + mut env: JNIEnv, + _object: JObject, + id: jlong, + plaintext: JByteArray, +) -> jobjectArray { + invoke(&mut env, |env| { + let owner = channel(id)?; + let output = owner.lock().map_err(|_| LinkError::State)?.seal(&bytes( + env, + &plaintext, + MAX_MESSAGE, + )?)?; + let arrays = env + .new_object_array(output.len() as i32, "[B", JObject::null()) + .map_err(|_| LinkError::State)?; + for (index, packet) in output.iter().enumerate() { + let bytes = env.byte_array_from_slice(packet).map_err(|_| LinkError::State)?; + env.set_object_array_element(&arrays, index as i32, &bytes) + .map_err(|_| LinkError::State)?; + env.delete_local_ref(bytes).map_err(|_| LinkError::State)?; + } + Ok(arrays.into_raw()) + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeLink_verificationCode( + mut env: JNIEnv, + _object: JObject, + id: jlong, +) -> jstring { + invoke(&mut env, |env| { + let owner = channel(id)?; + let channel = owner.lock().map_err(|_| LinkError::State)?; + let code = channel.verification_code().ok_or(LinkError::State)?; + env.new_string(code).map(|value| value.into_raw()).map_err(|_| LinkError::State) + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeLink_open( + mut env: JNIEnv, + _object: JObject, + id: jlong, + ciphertext: JByteArray, +) -> jbyteArray { + invoke(&mut env, |env| { + let owner = channel(id)?; + let result = owner.lock().map_err(|_| LinkError::State)?.open(&bytes( + env, + &ciphertext, + MAX_PACKET, + )?)?; + match result { + Some(plaintext) => env + .byte_array_from_slice(&plaintext) + .map(|bytes| bytes.into_raw()) + .map_err(|_| LinkError::State), + None => Ok(std::ptr::null_mut()), + } + }) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_io_github_kuddev_pebrel_ssh_NativeLink_close( + mut env: JNIEnv, + _object: JObject, + id: jlong, +) { + invoke(&mut env, |_| { + let owner = handles().lock().map_err(|_| LinkError::State)?.channels.remove(&id); + if let Some(owner) = owner { + owner.lock().map_err(|_| LinkError::State)?.close(); + } + Ok(()) + }); +} diff --git a/mobile/ssh/src/session.rs b/mobile/ssh/src/session.rs new file mode 100644 index 0000000..4c08522 --- /dev/null +++ b/mobile/ssh/src/session.rs @@ -0,0 +1,279 @@ +//! Owned connection handles and blocking boundaries for the Android IO workers. +use std::collections::HashMap; +use std::sync::atomic::{AtomicI64, Ordering}; +use std::sync::{Arc, Mutex, OnceLock}; + +use tokio::runtime::{Builder, Runtime}; +use tokio::sync::{Mutex as AsyncMutex, mpsc, oneshot, watch}; +use tokio_util::sync::CancellationToken; +use zeroize::Zeroizing; + +pub(crate) type Result = std::result::Result; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct Failure(pub &'static str); + +impl From for Failure { + fn from(error: russh::Error) -> Self { + use russh::Error::*; + Self(match error { + ConnectionTimeout | KeepaliveTimeout | InactivityTimeout | Elapsed(_) => "TIMEOUT", + ChannelOpenFailure(_) | RequestDenied | WrongChannel => "CHANNEL", + IO(ref io) if io.kind() == std::io::ErrorKind::ConnectionRefused => "REFUSED", + IO(_) | Disconnect | HUP | SendError | RecvError => "NETWORK", + _ => "NEGOTIATION", + }) + } +} + +pub(crate) fn runtime() -> &'static Runtime { + static RUNTIME: OnceLock = OnceLock::new(); + RUNTIME.get_or_init(|| { + Builder::new_multi_thread() + .worker_threads(2) + .thread_name("pebrel-ssh") + .enable_all() + .build() + .expect("SSH runtime") + }) +} + +pub(crate) struct Options { + pub host: String, + pub port: u16, + pub user: String, + pub password: Zeroizing>, + pub fingerprint: String, +} + +#[derive(Clone, Copy)] +pub(crate) struct Geometry { + pub columns: u32, + pub rows: u32, + pub width: u32, + pub height: u32, +} + +pub(crate) enum Open { + Shell(Geometry), + PtyExec(Geometry, String), + Exec(String), +} + +pub(crate) enum Command { + Open(Open, oneshot::Sender>), + Resize(Geometry), +} + +pub(crate) struct Reader { + rx: mpsc::Receiver>, + pending: Vec, + offset: usize, +} + +impl Reader { + fn new(rx: mpsc::Receiver>) -> Self { + Self { rx, pending: Vec::new(), offset: 0 } + } + + async fn read(&mut self, limit: usize, owner: &Session) -> Result> { + if self.offset == self.pending.len() { + let next = tokio::select! { + biased; + _ = owner.cancel.cancelled() => return Err(Failure("CLOSED")), + next = self.rx.recv() => next, + }; + match next { + Some(bytes) => { + self.pending = bytes; + self.offset = 0; + }, + None => return owner.outcome.borrow().unwrap_or(Ok(-1)).map(|_| Vec::new()), + } + } + let end = (self.offset + limit).min(self.pending.len()); + let bytes = self.pending[self.offset..end].to_vec(); + self.offset = end; + Ok(bytes) + } +} + +pub(crate) struct Session { + pub cancel: CancellationToken, + pub commands: mpsc::Sender, + pub input: mpsc::Sender>, + sftp: mpsc::Sender, + pub trust: Mutex>>, + pub identity_failure: Mutex>, + pub socket: Mutex>, + events: AsyncMutex>, + stdout: AsyncMutex, + stderr: AsyncMutex, + outcome: watch::Receiver>>, +} + +pub(crate) struct Worker { + pub events: mpsc::Sender, + pub commands: mpsc::Receiver, + pub input: mpsc::Receiver>, + pub sftp: mpsc::Receiver, + pub stdout: mpsc::Sender>, + pub stderr: mpsc::Sender>, + pub outcome: watch::Sender>>, +} + +impl Session { + pub fn new() -> (Arc, Worker) { + let (events, event_rx) = mpsc::channel(16); + let (commands, command_rx) = mpsc::channel(16); + let (input, input_rx) = mpsc::channel(16); + let (sftp, sftp_rx) = mpsc::channel(4); + let (stdout, stdout_rx) = mpsc::channel(8); + let (stderr, stderr_rx) = mpsc::channel(8); + let (outcome, outcome_rx) = watch::channel(None); + ( + Arc::new(Self { + cancel: CancellationToken::new(), + commands, + input, + sftp, + trust: Mutex::new(None), + identity_failure: Mutex::new(None), + socket: Mutex::new(None), + events: AsyncMutex::new(event_rx), + stdout: AsyncMutex::new(Reader::new(stdout_rx)), + stderr: AsyncMutex::new(Reader::new(stderr_rx)), + outcome: outcome_rx, + }), + Worker { + events, + commands: command_rx, + input: input_rx, + sftp: sftp_rx, + stdout, + stderr, + outcome, + }, + ) + } + + pub fn shutdown_socket(&self) { + if let Some(socket) = self.socket.lock().unwrap().take() { + let _ = socket.shutdown(std::net::Shutdown::Both); + } + } + + pub fn close(&self) { + self.cancel.cancel(); + self.trust.lock().unwrap().take(); + self.shutdown_socket(); + } + + pub async fn event(&self) -> Result { + tokio::select! { + biased; + _ = self.cancel.cancelled() => Err(Failure("CLOSED")), + event = async { self.events.lock().await.recv().await } => event.ok_or_else(|| self.failure()), + } + } + + pub fn answer(&self, accepted: bool) -> Result<()> { + self.trust + .lock() + .unwrap() + .take() + .ok_or(Failure("CLOSED"))? + .send(accepted) + .map_err(|_| Failure("CLOSED")) + } + + pub async fn open(&self, mode: Open) -> Result<()> { + let (reply, wait) = oneshot::channel(); + tokio::select! { + biased; + _ = self.cancel.cancelled() => Err(Failure("CLOSED")), + result = async { + self.commands.send(Command::Open(mode, reply)).await.map_err(|_| self.failure())?; + wait.await.map_err(|_| self.failure())? + } => result, + } + } + + pub async fn read(&self, stderr: bool, limit: usize) -> Result> { + let stream = if stderr { &self.stderr } else { &self.stdout }; + stream.lock().await.read(limit, self).await + } + + pub async fn write(&self, bytes: Vec) -> Result<()> { + tokio::select! { + biased; + _ = self.cancel.cancelled() => Err(Failure("CLOSED")), + result = self.input.send(bytes) => result.map_err(|_| self.failure()), + } + } + + pub async fn resize(&self, size: Geometry) -> Result<()> { + tokio::select! { + biased; + _ = self.cancel.cancelled() => Err(Failure("CLOSED")), + result = self.commands.send(Command::Resize(size)) => result.map_err(|_| self.failure()), + } + } + + pub async fn sftp(&self, request: crate::sftp::Request) -> Result { + let (reply, response) = oneshot::channel(); + tokio::select! { + biased; + _ = self.cancel.cancelled() => Err(Failure("CLOSED")), + result = async { + self.sftp.send(crate::sftp::Call { request, reply }).await.map_err(|_| self.failure())?; + response.await.map_err(|_| self.failure())? + } => result, + } + } + + pub async fn exit(&self) -> Result { + let mut outcome = self.outcome.clone(); + loop { + if let Some(result) = *outcome.borrow_and_update() { + return result; + } + tokio::select! { + biased; + _ = self.cancel.cancelled() => return Err(Failure("CLOSED")), + changed = outcome.changed() => changed.map_err(|_| Failure("NETWORK"))?, + } + } + } + + fn failure(&self) -> Failure { + self.outcome.borrow().and_then(|r| r.err()).unwrap_or(Failure("CLOSED")) + } +} + +fn registry() -> &'static Mutex>> { + static REGISTRY: OnceLock>>> = OnceLock::new(); + REGISTRY.get_or_init(|| Mutex::new(HashMap::new())) +} + +pub(crate) fn start(options: Options) -> Result { + static NEXT: AtomicI64 = AtomicI64::new(1); + let id = NEXT.fetch_add(1, Ordering::Relaxed); + if id <= 0 { + return Err(Failure("INTERNAL")); + } + let (session, worker) = Session::new(); + registry().lock().unwrap().insert(id, session.clone()); + runtime().spawn(crate::transport::run(session, worker, options)); + Ok(id) +} + +pub(crate) fn get(id: i64) -> Result> { + registry().lock().unwrap().get(&id).cloned().ok_or(Failure("CLOSED")) +} + +pub(crate) fn close(id: i64) { + if let Some(session) = registry().lock().unwrap().remove(&id) { + session.close(); + } +} diff --git a/mobile/ssh/src/sftp.rs b/mobile/ssh/src/sftp.rs new file mode 100644 index 0000000..dcf00e9 --- /dev/null +++ b/mobile/ssh/src/sftp.rs @@ -0,0 +1,516 @@ +//! One lazily opened SFTP subsystem per authenticated SSH connection. +//! +//! russh-sftp owns the protocol. This adapter owns bounded directory/transfer handles, +//! upload staging and cancellation; it never handles terminal bytes. + +use std::{ + collections::{HashMap, VecDeque}, + sync::atomic::{AtomicU64, Ordering}, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; + +use base64::Engine as _; +use russh::{ChannelMsg, client}; +use russh_sftp::{ + client::{RawSftpSession, error::Error as SftpError}, + protocol::{FileAttributes, OpenFlags, StatusCode}, +}; +use serde::Deserialize; +use serde_json::{Value, json}; +use tokio::sync::{mpsc, oneshot}; + +use crate::session::{Failure, Result}; + +pub(crate) const CHUNK: usize = 32 * 1024; +const DIRECTORY_PAGE: usize = 256; +const MAX_HANDLES: usize = 4; + +fn next_handle_id() -> Result { + // 文件通道重开后仍不复用 ID,迟到的取消/读取只会指向已经失效的旧句柄。 + static NEXT: AtomicU64 = AtomicU64::new(1); + NEXT.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| { + id.checked_add(1).filter(|next| *next <= i64::MAX as u64) + }) + .map_err(|_| Failure("SFTP_LIMIT")) +} + +pub(crate) struct Call { + pub request: Request, + pub reply: oneshot::Sender>, +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "op", rename_all = "snake_case", deny_unknown_fields)] +pub(crate) enum Request { + List { path: String, cursor: Option }, + CloseList { cursor: u64 }, + Stat { path: String }, + OpenRead { path: String }, + Read { transfer: u64, offset: u64 }, + BeginUpload { path: String }, + Write { transfer: u64, offset: u64, data: String }, + Commit { transfer: u64 }, + Close { transfer: u64 }, + Mkdir { path: String }, + Rename { path: String, destination: String, revision: String }, + Remove { path: String, revision: String }, +} + +pub(crate) fn parse(text: &str) -> Result { + if text.len() > 64 * 1024 { + return Err(Failure("INVALID_INPUT")); + } + let request: Request = serde_json::from_str(text).map_err(|_| Failure("INVALID_INPUT"))?; + let path = match &request { + Request::List { path, .. } + | Request::Stat { path } + | Request::OpenRead { path } + | Request::BeginUpload { path } + | Request::Mkdir { path } + | Request::Remove { path, .. } => Some(path), + Request::Rename { path, destination, .. } => { + validate_path(destination)?; + Some(path) + }, + Request::Write { data, .. } if data.len() > CHUNK.div_ceil(3) * 4 => { + return Err(Failure("INVALID_INPUT")); + }, + _ => None, + }; + if let Some(path) = path { + validate_path(path)?; + } + Ok(request) +} + +fn validate_path(path: &str) -> Result<()> { + if path.is_empty() || path.len() > 4096 || path.chars().any(char::is_control) { + Err(Failure("INVALID_INPUT")) + } else { + Ok(()) + } +} + +impl From for Failure { + fn from(error: SftpError) -> Self { + Self(match error { + SftpError::Status(status) => match status.status_code { + StatusCode::NoSuchFile => "SFTP_NOT_FOUND", + StatusCode::PermissionDenied => "SFTP_PERMISSION", + StatusCode::OpUnsupported => "SFTP_UNSUPPORTED", + StatusCode::NoConnection | StatusCode::ConnectionLost => "SFTP_CLOSED", + _ => "SFTP_OPERATION", + }, + SftpError::Timeout => "SFTP_TIMEOUT", + SftpError::Limited(_) => "SFTP_LIMIT", + _ => "SFTP_CLOSED", + }) + } +} + +pub(crate) async fn serve( + client: &client::Handle, + calls: &mut mpsc::Receiver, +) -> Result { + let mut files: Option = None; + let mut cleanup = tokio::time::interval(Duration::from_secs(30)); + loop { + tokio::select! { + call = calls.recv() => { + let Call { request, reply } = call.ok_or(Failure("CLOSED"))?; + if reply.is_closed() { continue; } + let result = tokio::time::timeout(Duration::from_secs(20), async { + if files.is_none() { files = Some(Files::open(client).await?); } + files.as_mut().expect("SFTP initialized").execute(request).await + }).await.unwrap_or(Err(Failure("SFTP_TIMEOUT"))); + // 协议超时只回收文件通道,不破坏仍可用的 shell;未确认写入不自动重放。 + if matches!(result, Err(Failure("SFTP_TIMEOUT" | "SFTP_CLOSED"))) { files = None; } + let _ = reply.send(result); + }, + _ = cleanup.tick(), if files.is_some() => { + if let Some(files) = &mut files { files.expire().await; } + }, + } + } +} + +struct Directory { + id: u64, + path: String, + handle: String, + pending: VecDeque, + touched: Instant, +} + +struct Transfer { + handle: Option, + path: String, + temp: Option, + offset: u64, + attrs: FileAttributes, + touched: Instant, +} + +struct Files { + raw: RawSftpSession, + directory: Option, + transfers: HashMap, + nonce: u128, +} + +impl Files { + async fn open(client: &client::Handle) -> Result { + let mut channel = client.channel_open_session().await?; + channel.request_subsystem(true, "sftp").await?; + loop { + match channel.wait().await { + Some(ChannelMsg::Success) => break, + Some(ChannelMsg::WindowAdjusted { .. }) => {}, + _ => return Err(Failure("SFTP_UNSUPPORTED")), + } + } + let raw = RawSftpSession::new_with_config( + channel.into_stream(), + russh_sftp::client::Config { + request_timeout_secs: 10, + max_concurrent_writes: 1, + ..Default::default() + }, + ); + if raw.init().await?.version != 3 { + return Err(Failure("SFTP_UNSUPPORTED")); + } + Ok(Self { + raw, + directory: None, + transfers: HashMap::new(), + nonce: SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_nanos(), + }) + } + + async fn canonical(&self, path: &str) -> Result { + let resolved = self + .raw + .realpath(path) + .await? + .files + .into_iter() + .next() + .map(|file| file.filename) + .ok_or(Failure("SFTP_NOT_FOUND"))?; + validate_path(&resolved)?; + Ok(resolved) + } + + async fn execute(&mut self, request: Request) -> Result { + match request { + Request::List { path, cursor } => self.list(path, cursor).await, + Request::CloseList { cursor } => { + if self.directory.as_ref().is_some_and(|d| d.id == cursor) { + self.close_directory().await; + } + Ok(json!({"ok":true})) + }, + Request::Stat { path } => { + let resolved = self.canonical(&path).await?; + let attrs = self.raw.stat(&resolved).await?.attrs; + Ok(entry(&resolved, resolved.rsplit('/').next().unwrap_or(&resolved), &attrs)) + }, + Request::OpenRead { path } => self.open_read(path).await, + Request::Read { transfer, offset } => self.read(transfer, offset).await, + Request::BeginUpload { path } => self.begin_upload(path).await, + Request::Write { transfer, offset, data } => self.write(transfer, offset, data).await, + Request::Commit { transfer } => self.commit(transfer).await, + Request::Close { transfer } => { + self.close_transfer(transfer).await?; + Ok(json!({"ok":true})) + }, + Request::Mkdir { path } => { + self.raw.mkdir(path, FileAttributes::empty()).await?; + Ok(json!({"ok":true})) + }, + Request::Rename { path, destination, revision: expected } => { + self.check_revision(&path, &expected).await?; + self.ensure_absent(&destination).await?; + self.raw.rename(path, destination).await?; + Ok(json!({"ok":true})) + }, + Request::Remove { path, revision: expected } => { + let attrs = self.check_revision(&path, &expected).await?; + // LSTAT 决定操作类型:只删除链接本身或空目录,绝不递归遍历。 + if attrs.file_type().is_dir() { + self.raw.rmdir(path).await?; + } else { + self.raw.remove(path).await?; + } + Ok(json!({"ok":true})) + }, + } + } + + async fn check_revision(&self, path: &str, expected: &str) -> Result { + let attrs = self.raw.lstat(path).await?.attrs; + if revision(&attrs) != expected { + return Err(Failure("SFTP_CHANGED")); + } + Ok(attrs) + } + + async fn ensure_absent(&self, path: &str) -> Result<()> { + match self.raw.lstat(path).await { + Ok(_) => Err(Failure("SFTP_EXISTS")), + Err(SftpError::Status(status)) if status.status_code == StatusCode::NoSuchFile => { + Ok(()) + }, + Err(error) => Err(error.into()), + } + } + + async fn list(&mut self, path: String, cursor: Option) -> Result { + if cursor.is_none() { + self.close_directory().await; + let path = self.canonical(&path).await?; + let handle = self.raw.opendir(&path).await?.handle; + let id = next_handle_id()?; + self.directory = Some(Directory { + id, + path, + handle, + pending: VecDeque::new(), + touched: Instant::now(), + }); + } + let directory = self.directory.as_mut().ok_or(Failure("SFTP_STALE"))?; + if cursor.is_some_and(|id| id != directory.id || path != directory.path) { + return Err(Failure("SFTP_STALE")); + } + directory.touched = Instant::now(); + let mut rows = Vec::new(); + let mut done = false; + let mut skipped = 0; + while rows.len() < DIRECTORY_PAGE { + if let Some(file) = directory.pending.pop_front() { + if matches!(file.filename.as_str(), "." | "..") { + continue; + } + if validate_name(&file.filename).is_err() { + skipped += 1; + continue; + } + let child = join(&directory.path, &file.filename); + if validate_path(&child).is_err() { + skipped += 1; + continue; + } + rows.push(entry(&child, &file.filename, &file.attrs)); + } else { + match self.raw.readdir(&directory.handle).await { + Ok(page) if !page.files.is_empty() => directory.pending = page.files.into(), + Ok(_) => return Err(Failure("SFTP_OPERATION")), + Err(SftpError::Status(status)) if status.status_code == StatusCode::Eof => { + done = true; + break; + }, + Err(error) => return Err(error.into()), + } + } + } + let result = json!({"path":directory.path,"entries":rows,"cursor":if done { None } else { Some(directory.id) },"skipped":skipped}); + if done { + self.close_directory().await; + } + Ok(result) + } + + async fn open_read(&mut self, path: String) -> Result { + if self.transfers.len() >= MAX_HANDLES { + return Err(Failure("SFTP_LIMIT")); + } + let path = self.canonical(&path).await?; + let attrs = self.raw.stat(&path).await?.attrs; + if !attrs.file_type().is_file() || attrs.size.is_none() { + return Err(Failure("SFTP_FILE_TYPE")); + } + let handle = self.raw.open(&path, OpenFlags::READ, FileAttributes::empty()).await?.handle; + let actual = self.raw.fstat(&handle).await; + if actual.as_ref().is_ok_and(|actual| revision(&actual.attrs) == revision(&attrs)) { + let id = next_handle_id()?; + self.transfers.insert( + id, + Transfer { + handle: Some(handle), + path: path.clone(), + temp: None, + offset: 0, + attrs: attrs.clone(), + touched: Instant::now(), + }, + ); + Ok( + json!({"transfer":id,"file":entry(&path,path.rsplit('/').next().unwrap_or(&path),&attrs)}), + ) + } else { + let _ = self.raw.close(handle).await; + Err(Failure("SFTP_CHANGED")) + } + } + + async fn read(&mut self, id: u64, offset: u64) -> Result { + let transfer = self.transfers.get_mut(&id).ok_or(Failure("SFTP_STALE"))?; + if transfer.temp.is_some() || transfer.offset != offset { + return Err(Failure("INVALID_INPUT")); + } + transfer.touched = Instant::now(); + let handle = transfer.handle.as_ref().ok_or(Failure("SFTP_STALE"))?; + let data = match self.raw.read(handle, offset, CHUNK as u32).await { + Ok(data) => data.data, + Err(SftpError::Status(status)) if status.status_code == StatusCode::Eof => Vec::new(), + Err(error) => return Err(error.into()), + }; + let current = self.raw.fstat(handle).await?.attrs; + let next = offset + data.len() as u64; + if revision(¤t) != revision(&transfer.attrs) + || next > transfer.attrs.len() + || (data.is_empty() && next != transfer.attrs.len()) + { + return Err(Failure("SFTP_CHANGED")); + } + transfer.offset = next; + Ok(json!({"offset":offset,"next_offset":next,"eof":next == transfer.attrs.len(), + "data":base64::engine::general_purpose::STANDARD.encode(data)})) + } + + async fn begin_upload(&mut self, path: String) -> Result { + if self.transfers.len() >= MAX_HANDLES { + return Err(Failure("SFTP_LIMIT")); + } + let (parent, name) = path.rsplit_once('/').ok_or(Failure("INVALID_INPUT"))?; + validate_name(name)?; + let parent = self.canonical(if parent.is_empty() { "/" } else { parent }).await?; + let path = join(&parent, name); + self.ensure_absent(&path).await?; + let id = next_handle_id()?; + let temp = join(&parent, &format!(".pebrel-upload-{:x}-{id}", self.nonce)); + let attrs = FileAttributes { permissions: Some(0o600), ..FileAttributes::empty() }; + let handle = self + .raw + .open(&temp, OpenFlags::CREATE | OpenFlags::EXCLUDE | OpenFlags::WRITE, attrs.clone()) + .await? + .handle; + self.transfers.insert( + id, + Transfer { + handle: Some(handle), + path, + temp: Some(temp), + offset: 0, + attrs, + touched: Instant::now(), + }, + ); + Ok(json!({"transfer":id})) + } + + async fn write(&mut self, id: u64, offset: u64, data: String) -> Result { + let bytes = base64::engine::general_purpose::STANDARD + .decode(data) + .map_err(|_| Failure("INVALID_INPUT"))?; + if bytes.is_empty() || bytes.len() > CHUNK { + return Err(Failure("INVALID_INPUT")); + } + let transfer = self.transfers.get_mut(&id).ok_or(Failure("SFTP_STALE"))?; + if transfer.temp.is_none() || transfer.offset != offset { + return Err(Failure("INVALID_INPUT")); + } + transfer.touched = Instant::now(); + let handle = transfer.handle.as_ref().ok_or(Failure("SFTP_STALE"))?; + let next = offset.checked_add(bytes.len() as u64).ok_or(Failure("INVALID_INPUT"))?; + self.raw.write(handle, offset, bytes).await?; + transfer.offset = next; + Ok(json!({"next_offset":next})) + } + + async fn commit(&mut self, id: u64) -> Result { + let transfer = self.transfers.get_mut(&id).ok_or(Failure("SFTP_STALE"))?; + let temp = transfer.temp.as_ref().ok_or(Failure("INVALID_INPUT"))?.clone(); + let path = transfer.path.clone(); + if let Some(handle) = transfer.handle.as_ref() { + let size = self.raw.fstat(handle).await?.attrs.size; + self.raw.close(handle).await?; + transfer.handle = None; + if size != Some(transfer.offset) { + return Err(Failure("SFTP_CHANGED")); + } + } + self.ensure_absent(&path).await?; + // SFTP v3 RENAME 要求目标不存在;不使用会覆盖目标的 posix-rename 扩展。 + self.raw.rename(temp, &path).await?; + self.transfers.remove(&id); + Ok(json!({"ok":true,"path":path})) + } + + async fn close_directory(&mut self) { + if let Some(directory) = self.directory.take() { + let _ = self.raw.close(directory.handle).await; + } + } + + async fn close_transfer(&mut self, id: u64) -> Result<()> { + let mut result = Ok(()); + if let Some(transfer) = self.transfers.remove(&id) { + if let Some(handle) = transfer.handle { + result = self.raw.close(handle).await.map(|_| ()).map_err(Failure::from); + } + if let Some(temp) = transfer.temp { + let cleanup = self.raw.remove(temp).await.map(|_| ()).map_err(Failure::from); + result = result.and(cleanup); + } + } + result + } + + async fn expire(&mut self) { + if self.directory.as_ref().is_some_and(|d| d.touched.elapsed().as_secs() > 60) { + self.close_directory().await; + } + let expired = self + .transfers + .iter() + .filter_map(|(id, transfer)| (transfer.touched.elapsed().as_secs() > 60).then_some(*id)) + .collect::>(); + for id in expired { + let _ = self.close_transfer(id).await; + } + } +} + +fn validate_name(name: &str) -> Result<()> { + validate_path(name)?; + if matches!(name, "." | "..") || name.contains('/') { + Err(Failure("INVALID_INPUT")) + } else { + Ok(()) + } +} + +fn join(parent: &str, name: &str) -> String { + format!("{}/{name}", parent.trim_end_matches('/')) +} + +fn revision(attrs: &FileAttributes) -> String { + format!( + "{:?}:{:?}:{:?}:{:?}:{:?}", + attrs.size, attrs.mtime, attrs.permissions, attrs.uid, attrs.gid + ) +} + +fn entry(path: &str, name: &str, attrs: &FileAttributes) -> Value { + let kind = match attrs.file_type() { + russh_sftp::protocol::FileType::Dir => "directory", + russh_sftp::protocol::FileType::File => "file", + russh_sftp::protocol::FileType::Symlink => "symlink", + _ => "other", + }; + json!({"path":path,"name":name,"kind":kind,"size":attrs.size,"modified":attrs.mtime, + "permissions":attrs.permissions,"revision":revision(attrs)}) +} diff --git a/mobile/ssh/src/tests.rs b/mobile/ssh/src/tests.rs new file mode 100644 index 0000000..b810756 --- /dev/null +++ b/mobile/ssh/src/tests.rs @@ -0,0 +1,119 @@ +use std::time::Duration; + +use crate::session::{self, Failure, Open, Session, runtime}; + +#[test] +fn cancellation_wakes_trust_events_reads_and_queued_open() { + runtime().block_on(async { + let (owner, _worker) = Session::new(); + let read_owner = owner.clone(); + let read = tokio::spawn(async move { read_owner.read(false, 1024).await }); + let open_owner = owner.clone(); + let open = tokio::spawn(async move { open_owner.open(Open::Exec("true".into())).await }); + let event_owner = owner.clone(); + let event = tokio::spawn(async move { event_owner.event().await }); + let files_owner = owner.clone(); + let files = tokio::spawn(async move { + files_owner.sftp(crate::sftp::Request::List { path: ".".into(), cursor: None }).await + }); + owner.close(); + tokio::time::timeout(Duration::from_secs(2), async { + assert_eq!(read.await.unwrap().unwrap_err(), Failure("CLOSED")); + assert_eq!(open.await.unwrap().unwrap_err(), Failure("CLOSED")); + assert_eq!(event.await.unwrap().unwrap_err(), Failure("CLOSED")); + assert_eq!(files.await.unwrap().unwrap_err(), Failure("CLOSED")); + }) + .await + .unwrap(); + }); +} + +#[test] +fn sftp_requests_have_explicit_operations_and_bounded_utf8_paths_and_chunks() { + use crate::sftp::parse; + assert!(parse(r#"{"op":"list","path":"/项目/图片"}"#).is_ok()); + assert!( + parse(r#"{"op":"rename","path":"/one","destination":"/two","revision":"123"}"#).is_ok() + ); + assert!(parse(r#"{"op":"remove","path":"/one","revision":"123","recursive":true}"#).is_err()); + assert!(parse(r#"{"op":"list","path":"/one\u0000other"}"#).is_err()); + assert!(parse(&format!(r#"{{"op":"list","path":"{}"}}"#, "x".repeat(4097))).is_err()); + assert!( + parse(&format!( + r#"{{"op":"write","transfer":1,"offset":0,"data":"{}"}}"#, + "x".repeat(50000) + )) + .is_err() + ); +} + +#[test] +fn partial_reads_preserve_bytes_and_drain_before_exit() { + runtime().block_on(async { + let (owner, worker) = Session::new(); + worker.stdout.send(b"abcdef".to_vec()).await.unwrap(); + worker.stderr.send(b"error".to_vec()).await.unwrap(); + worker.outcome.send_replace(Some(Ok(7))); + drop(worker); + assert_eq!(owner.read(false, 2).await.unwrap(), b"ab"); + assert_eq!(owner.read(false, 8).await.unwrap(), b"cdef"); + assert_eq!(owner.read(true, 8).await.unwrap(), b"error"); + assert!(owner.read(false, 8).await.unwrap().is_empty()); + assert_eq!(owner.exit().await.unwrap(), 7); + }); +} + +#[test] +fn closed_handle_is_rejected_without_accessing_memory() { + session::close(-1); + assert!(matches!(session::get(-1), Err(Failure("CLOSED")))); +} + +#[test] +fn transport_failure_does_not_turn_into_successful_eof() { + runtime().block_on(async { + let (owner, worker) = Session::new(); + worker.stdout.send(b"last output".to_vec()).await.unwrap(); + worker.outcome.send_replace(Some(Err(Failure("NETWORK")))); + drop(worker); + assert_eq!(owner.read(false, 32).await.unwrap(), b"last output"); + assert_eq!(owner.read(false, 32).await.unwrap_err(), Failure("NETWORK")); + }); +} + +#[test] +fn cancelling_key_exchange_closes_the_actual_socket() { + use tokio::io::AsyncReadExt; + use zeroize::Zeroizing; + + runtime().block_on(async { + tokio::time::timeout(Duration::from_secs(5), async { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let id = session::start(session::Options { + host: "127.0.0.1".into(), + port: listener.local_addr().unwrap().port(), + user: "fixture".into(), + password: Zeroizing::new(Vec::new()), + fingerprint: String::new(), + }) + .unwrap(); + let (mut peer, _) = listener.accept().await.unwrap(); + let mut bytes = [0u8; 256]; + // Receive the client's banner but never send a server banner. This + // leaves connect_stream pending, before a russh Handle is returned. + assert!(peer.read(&mut bytes).await.unwrap() > 0); + session::close(id); + assert!(matches!(session::get(id), Err(Failure("CLOSED")))); + loop { + match peer.read(&mut bytes).await { + Ok(0) => break, + Ok(_) => continue, + Err(error) if error.kind() == std::io::ErrorKind::ConnectionReset => break, + Err(error) => panic!("Unexpected loopback read failure: {error}"), + } + } + }) + .await + .expect("Cancellation left the KEX socket open"); + }); +} diff --git a/mobile/ssh/src/transport.rs b/mobile/ssh/src/transport.rs new file mode 100644 index 0000000..37c9dad --- /dev/null +++ b/mobile/ssh/src/transport.rs @@ -0,0 +1,297 @@ +//! russh protocol ownership, explicit request acknowledgements and byte streams. +use std::sync::Arc; +use std::time::Duration; + +use russh::{Channel, ChannelMsg, client}; +use tokio::net::{TcpStream, lookup_host}; +use tokio::sync::{mpsc, oneshot}; +use tokio::time::timeout; +use zeroize::{Zeroize, Zeroizing}; + +use crate::session::{Command, Failure, Open, Options, Result, Session, Worker}; + +struct HostVerifier { + owner: Arc, + known: String, + events: mpsc::Sender, +} + +impl client::Handler for HostVerifier { + type Error = russh::Error; + + async fn check_server_key( + &mut self, + key: &russh::keys::ssh_key::PublicKey, + ) -> std::result::Result { + let fingerprint = key.fingerprint(russh::keys::ssh_key::HashAlg::Sha256).to_string(); + if !self.known.is_empty() { + if self.known == fingerprint { + return Ok(true); + } + *self.owner.identity_failure.lock().unwrap() = Some(Failure("HOST_KEY_CHANGED")); + return Ok(false); + } + let (reply, wait) = oneshot::channel(); + *self.owner.trust.lock().unwrap() = Some(reply); + let answer = tokio::select! { + biased; + _ = self.owner.cancel.cancelled() => Err(Failure("CLOSED")), + result = timeout(Duration::from_secs(60), async { + self.events.send(format!("verify:{fingerprint}")).await.map_err(|_| Failure("CLOSED"))?; + wait.await.map_err(|_| Failure("CLOSED")) + }) => result.map_err(|_| Failure("TIMEOUT")).and_then(|r| r), + }; + let failure = match answer { + Ok(true) => return Ok(true), + Ok(false) => Failure("TRUST_REJECTED"), + Err(error) => error, + }; + *self.owner.identity_failure.lock().unwrap() = Some(failure); + Ok(false) + } +} + +pub(crate) async fn run(owner: Arc, mut worker: Worker, options: Options) { + let result = tokio::select! { + biased; + _ = owner.cancel.cancelled() => Err(Failure("CLOSED")), + result = operate(&owner, &mut worker, options) => result, + }; + // russh spawns its protocol reader during KEX. Dropping its Handle alone does + // not stop it; shutdown also covers cancellation before connect_stream returns. + owner.shutdown_socket(); + worker.outcome.send_replace(Some(result)); + if let Err(error) = result { + let _ = worker.events.try_send(format!("error:{}", error.0)); + } + // Dropping Worker closes output queues; readers can drain already received data. +} + +async fn operate(owner: &Arc, worker: &mut Worker, mut options: Options) -> Result { + worker.events.send("stage:NETWORK".into()).await.map_err(|_| Failure("CLOSED"))?; + let mut client = connect(owner, &worker.events, &options).await?; + worker.events.send("stage:AUTHENTICATING".into()).await.map_err(|_| Failure("CLOSED"))?; + let password = + Zeroizing::new(String::from_utf8(options.password.to_vec()).map_err(|_| Failure("AUTH"))?); + options.password.zeroize(); + let auth = timeout(Duration::from_secs(15), async { + if client.authenticate_none(&options.user).await?.success() { + return Ok(()); + } + if !password.is_empty() + && client.authenticate_password(&options.user, password.to_string()).await?.success() + { + Ok(()) + } else { + Err(Failure("AUTH")) + } + }) + .await + .map_err(|_| Failure("TIMEOUT"))?; + drop(password); + auth?; + worker.events.send("connected".into()).await.map_err(|_| Failure("CLOSED"))?; + let (mode, reply) = loop { + match worker.commands.recv().await.ok_or(Failure("CLOSED"))? { + Command::Open(mode, reply) => break (mode, reply), + Command::Resize(_) => {}, + } + }; + let shell = matches!(mode, Open::Shell(_) | Open::PtyExec(..)); + let opened = timeout(Duration::from_secs(15), open_channel(&client, mode)) + .await + .map_err(|_| Failure("TIMEOUT")) + .and_then(|r| r); + let (channel, early) = match opened { + Ok(opened) => { + let _ = reply.send(Ok(())); + opened + }, + Err(error) => { + let _ = reply.send(Err(error)); + return Err(error); + }, + }; + let (mut reader, writer) = channel.split(); + let Worker { stdout, stderr, commands, input, sftp, .. } = worker; + let receive = async { + let mut status = -1; + for message in early { + if forward(message, shell, stdout, stderr, &mut status).await? { + return Ok(status); + } + } + loop { + let message = reader.wait().await.ok_or(Failure("NETWORK"))?; + if forward(message, shell, stdout, stderr, &mut status).await? { + return Ok(status); + } + } + }; + let transmit = async { + loop { + tokio::select! { + bytes = input.recv() => writer.data_bytes(bytes.ok_or(Failure("CLOSED"))?).await?, + command = commands.recv() => match command.ok_or(Failure("CLOSED"))? { + Command::Resize(size) if shell => { + writer.window_change(size.columns, size.rows, size.width, size.height).await?; + } + Command::Resize(_) => {} + Command::Open(_, reply) => { let _ = reply.send(Err(Failure("CHANNEL"))); } + } + } + } + #[allow(unreachable_code)] + Ok::(-1) + }; + // 文件请求与终端收发是独立 future;同一已认证连接另开子通道,不等待传输才处理按键。 + let files = crate::sftp::serve(&client, sftp); + let result = tokio::select! { result = receive => result, result = transmit => result, result = files => result }; + let _ = timeout( + Duration::from_secs(1), + client.disconnect(russh::Disconnect::ByApplication, "", ""), + ) + .await; + result +} + +async fn connect( + owner: &Arc, + events: &mpsc::Sender, + options: &Options, +) -> Result> { + let addresses = + timeout(Duration::from_secs(15), lookup_host((options.host.as_str(), options.port))) + .await + .map_err(|_| Failure("TIMEOUT"))? + .map_err(|_| Failure("UNKNOWN_HOST"))? + .collect::>(); + if addresses.is_empty() { + return Err(Failure("UNKNOWN_HOST")); + } + let socket = timeout(Duration::from_secs(15), TcpStream::connect(addresses.as_slice())) + .await + .map_err(|_| Failure("TIMEOUT"))? + .map_err(|e| { + Failure(if e.kind() == std::io::ErrorKind::ConnectionRefused { + "REFUSED" + } else { + "NETWORK" + }) + })?; + socket.set_nodelay(true).map_err(|_| Failure("NETWORK"))?; + let socket = socket.into_std().map_err(|_| Failure("NETWORK"))?; + *owner.socket.lock().unwrap() = Some(socket.try_clone().map_err(|_| Failure("NETWORK"))?); + if owner.cancel.is_cancelled() { + owner.shutdown_socket(); + return Err(Failure("CLOSED")); + } + let socket = TcpStream::from_std(socket).map_err(|_| Failure("NETWORK"))?; + let config = Arc::new(client::Config { + window_size: 256 * 1024, + maximum_packet_size: 32 * 1024, + channel_buffer_size: 16, + keepalive_interval: Some(Duration::from_secs(30)), + keepalive_max: 3, + nodelay: true, + ..Default::default() + }); + let handler = HostVerifier { + owner: owner.clone(), + known: options.fingerprint.clone(), + events: events.clone(), + }; + timeout(Duration::from_secs(90), client::connect_stream(config, socket, handler)) + .await + .map_err(|_| Failure("TIMEOUT"))? + .map_err(|error| owner.identity_failure.lock().unwrap().unwrap_or_else(|| error.into())) +} + +async fn open_channel( + client: &client::Handle, + mode: Open, +) -> Result<(Channel, Vec)> { + let mut channel = client.channel_open_session().await?; + let mut early = Vec::new(); + let (size, command) = match mode { + Open::Shell(size) => (Some(size), None), + Open::PtyExec(size, command) => (Some(size), Some(command)), + Open::Exec(command) => (None, Some(command)), + }; + if let Some(size) = size { + channel + .request_pty( + true, + "xterm-256color", + size.columns, + size.rows, + size.width, + size.height, + &[], + ) + .await?; + acknowledged(&mut channel, &mut early).await?; + } + match command { + Some(command) => channel.exec(true, command.into_bytes()).await?, + None => channel.request_shell(true).await?, + } + acknowledged(&mut channel, &mut early).await?; + Ok((channel, early)) +} + +async fn acknowledged( + channel: &mut Channel, + early: &mut Vec, +) -> Result<()> { + let mut bytes = early.iter().map(message_size).sum::(); + loop { + match channel.wait().await { + Some(ChannelMsg::Success) => return Ok(()), + Some(ChannelMsg::Failure | ChannelMsg::Close) | None => return Err(Failure("CHANNEL")), + Some( + message @ (ChannelMsg::Data { .. } + | ChannelMsg::ExtendedData { .. } + | ChannelMsg::ExitStatus { .. } + | ChannelMsg::Eof), + ) => { + bytes += message_size(&message); + if bytes > 128 * 1024 || early.len() >= 64 { + return Err(Failure("CHANNEL")); + } + early.push(message); + }, + _ => {}, + } + } +} + +fn message_size(message: &ChannelMsg) -> usize { + match message { + ChannelMsg::Data { data } | ChannelMsg::ExtendedData { data, .. } => data.len(), + _ => 0, + } +} + +async fn forward( + message: ChannelMsg, + shell: bool, + stdout: &mpsc::Sender>, + stderr: &mpsc::Sender>, + status: &mut i32, +) -> Result { + let (bytes, destination) = match message { + ChannelMsg::Data { data } => (data, stdout), + ChannelMsg::ExtendedData { data, .. } => (data, if shell { stdout } else { stderr }), + ChannelMsg::ExitStatus { exit_status } => { + *status = exit_status as i32; + return Ok(false); + }, + ChannelMsg::Close => return Ok(true), + _ => return Ok(false), + }; + for chunk in bytes.chunks(16 * 1024) { + destination.send(chunk.to_vec()).await.map_err(|_| Failure("CLOSED"))?; + } + Ok(false) +} diff --git a/mobile/tools/build_ghostty.py b/mobile/tools/build_ghostty.py new file mode 100644 index 0000000..eddf7b4 --- /dev/null +++ b/mobile/tools/build_ghostty.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +"""Build the pinned public VT core, with no desktop UI or private binary input. + +Linux x86_64 (CI) and Windows x64 produce both Android ABIs using pinned Zig +distributions. Upstream dependency content hashes remain authoritative for transitive +sources. All downloads, extraction, caches and products stay under --output. +""" +from __future__ import annotations + +import argparse +import hashlib +import json +import os +from pathlib import Path +import platform +import shutil +import subprocess +import tarfile +import urllib.request +import zipfile + + +ROOT = Path(__file__).resolve().parents[2] +PINS = ROOT / "mobile/android/ghostty/UPSTREAM.json" + + +def digest(path: Path) -> str: + with path.open("rb") as stream: + return hashlib.file_digest(stream, "sha256").hexdigest() + + +def source_archive(url: str, expected: str, archive: Path, extracted: Path) -> Path: + archive.parent.mkdir(parents=True, exist_ok=True) + if not archive.exists() or digest(archive) != expected: + temporary = archive.with_suffix(archive.suffix + ".partial") + with urllib.request.urlopen(url, timeout=120) as response, temporary.open("wb") as output: + shutil.copyfileobj(response, output) + if digest(temporary) != expected: + temporary.unlink() + raise RuntimeError(f"Source checksum mismatch: {url}") + temporary.replace(archive) + # A hash-specific extraction prevents stale products from another revision. + destination = extracted / expected + marker = destination / ".source-complete" + if not marker.exists(): + destination.mkdir(parents=True, exist_ok=True) + if archive.suffix == ".zip": + with zipfile.ZipFile(archive) as package: + for member in package.infolist(): + if not (destination / member.filename).resolve().is_relative_to(destination.resolve()): + raise RuntimeError("Tool archive contains a path outside the output directory") + package.extractall(destination) + else: + with tarfile.open(archive) as tar: + tar.extractall(destination, filter="data") + marker.write_text(expected + "\n", encoding="utf-8") + directories = [entry for entry in destination.iterdir() if entry.is_dir()] + if len(directories) != 1: + raise RuntimeError(f"Expected one source root in {archive}") + return directories[0] + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--output", type=Path, default=ROOT / "mobile/android/ghostty/build/upstream") + parser.add_argument("--abi", choices=("arm64-v8a", "x86_64", "all"), default="all") + args = parser.parse_args() + windows = platform.system() == "Windows" + if platform.system() not in ("Linux", "Windows") or platform.machine().lower() not in ("x86_64", "amd64"): + raise SystemExit("The pinned builder runs on Linux x86_64 or Windows x64.") + pins = json.loads(PINS.read_text(encoding="utf-8")) + output = args.output.resolve() + ndk_value = os.environ.get("ANDROID_NDK_HOME") + if not ndk_value: + sdk = os.environ.get("ANDROID_HOME") or os.environ.get("ANDROID_SDK_ROOT") + if not sdk: + raise SystemExit("Set ANDROID_NDK_HOME or ANDROID_HOME for the pinned NDK.") + ndk_value = str(Path(sdk) / "ndk" / pins["ndk_version"]) + ndk = Path(ndk_value).resolve() + properties = (ndk / "source.properties").read_text(encoding="utf-8") + if f"Pkg.Revision = {pins['ndk_version']}" not in properties: + raise SystemExit(f"Expected Android NDK {pins['ndk_version']} at {ndk}") + sources = source_archive(pins["source_url"], pins["source_sha256"], output / "downloads/terminal-core.tar.gz", output / "sources") + zig_key = "zig_windows" if windows else "zig" + zig_archive = "zig-windows.zip" if windows else "zig.tar.xz" + zig_root = source_archive(pins[f"{zig_key}_url"], pins[f"{zig_key}_sha256"], output / "downloads" / zig_archive, output / "tools") + zig = zig_root / ("zig.exe" if windows else "zig") + if subprocess.check_output([zig, "version"], text=True, encoding="utf-8").strip() != pins["zig_version"]: + raise SystemExit("Unexpected Zig version") + environment = os.environ.copy() + environment.update(ANDROID_NDK_HOME=str(ndk), ZIG_GLOBAL_CACHE_DIR=str(output / "cache/global")) + targets = {"arm64-v8a": "aarch64", "x86_64": "x86_64"} + for abi, architecture in targets.items(): + if args.abi not in (abi, "all"): + continue + prefix = output / abi + command = [str(zig), "build", "-Demit-lib-vt", "-Doptimize=ReleaseFast", + f"-Dtarget={architecture}-linux-android.{pins['android_api']}", "-Dcpu=baseline", + "--prefix", str(prefix), "--cache-dir", str(output / "cache" / abi)] + print(f"Building terminal core {pins['revision']} for {abi}", flush=True) + subprocess.run(command, cwd=sources, env=environment, check=True) + library = prefix / "lib/libghostty-vt.a" + if not library.is_file() or library.stat().st_size == 0: + raise RuntimeError(f"Missing static library: {library}") + (prefix / "SOURCE.json").write_text(json.dumps({**pins, "abi": abi, + "library_sha256": digest(library)}, indent=2) + "\n", encoding="utf-8") + notices = prefix / "licenses" + notices.mkdir(exist_ok=True) + shutil.copy2(sources / "LICENSE", notices / "Ghostty-MIT.txt") + shutil.copy2(sources / "build.zig.zon", notices / "upstream-dependencies.zig.zon") + shutil.copy2(zig_root / "LICENSE", notices / "Zig-MIT.txt") + packages = output / "cache/global/p" + if packages.is_dir(): + for package in sorted(packages.iterdir()): + if not package.is_dir(): + continue + for name in ("LICENSE", "LICENSE.md", "LICENSE.txt", "LICENSE-MIT", "LICENSE-APACHE", "COPYING", "NOTICE"): + notice = package / name + if notice.is_file(): + target = notices / package.name / name + target.parent.mkdir(exist_ok=True) + shutil.copy2(notice, target) + print(f"Verified {abi}: {digest(library)}", flush=True) + + +if __name__ == "__main__": + main() diff --git a/mobile/tools/build_russh.py b/mobile/tools/build_russh.py new file mode 100644 index 0000000..4f64ab8 --- /dev/null +++ b/mobile/tools/build_russh.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +"""Build the public russh transport for Android and retain resolved license texts.""" +from pathlib import Path +import hashlib +import json +import os +import platform +import shutil +import subprocess + + +ROOT = Path(__file__).resolve().parents[2] +CRATE = ROOT / "mobile/ssh" +OUTPUT = ROOT / "mobile/android/ssh/build" +TARGETS = {"arm64-v8a": "aarch64-linux-android", "x86_64": "x86_64-linux-android"} + + +def notices() -> None: + license_source = json.loads((CRATE / "licenses/SOURCE.json").read_text(encoding="utf-8")) + apache = CRATE / "licenses" / license_source["file"] + if hashlib.sha256(apache.read_bytes()).hexdigest() != license_source["sha256"]: + raise RuntimeError("Pinned Apache license text changed") + metadata = json.loads(subprocess.check_output([ + "cargo", "metadata", "--locked", "--format-version", "1", "--manifest-path", str(CRATE / "Cargo.toml") + ], text=True, encoding="utf-8")) + destination = OUTPUT / "assets/licenses/Russh" + destination.mkdir(parents=True, exist_ok=True) + packages = [] + resolved = {node["id"] for node in metadata["resolve"]["nodes"]} + for package in metadata["packages"]: + if package["id"] not in resolved or package["source"] is None: + continue + source = Path(package["manifest_path"]).parent + target = destination / f"{package['name']}-{package['version']}" + texts = [] + for notice in sorted(source.rglob("*")): + if not notice.is_file() or not notice.name.lower().startswith(("license", "licence", "copying", "notice")): + continue + relative = notice.relative_to(source) + output = target / relative + output.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(notice, output) + texts.append(str(relative)) + if package.get("license_file"): + declared = source / package["license_file"] + if declared.is_file() and str(declared.relative_to(source)) not in texts: + target.mkdir(parents=True, exist_ok=True) + shutil.copy2(declared, target / declared.name) + texts.append(declared.name) + selected = None + if not texts: + # These published crates omit the workspace's standard license file. + # Choose the declared Apache option, retaining authors and origin. + if "Apache-2.0" not in (package["license"] or ""): + raise RuntimeError(f"Missing license text for {package['name']}") + target.mkdir(parents=True, exist_ok=True) + shutil.copy2(apache, target / "Apache-2.0.txt") + shutil.copy2(source / "Cargo.toml", target / "upstream-Cargo.toml") + texts = ["Apache-2.0.txt", "upstream-Cargo.toml"] + selected = "Apache-2.0" + packages.append({"name": package["name"], "version": package["version"], + "license": package["license"], "selected_license": selected, + "authors": package["authors"], "repository": package["repository"], "texts": texts}) + if not any(p["name"] == "russh" and p["texts"] for p in packages): + raise RuntimeError("Missing russh license") + shutil.copy2(CRATE / "Cargo.lock", destination / "Cargo.lock") + shutil.copy2(CRATE / "licenses/SOURCE.json", destination / "LICENSE-SOURCE.json") + (destination / "DEPENDENCIES.json").write_text(json.dumps(packages, indent=2) + "\n", encoding="utf-8") + + +def main() -> None: + ndk = Path(os.environ["ANDROID_NDK_HOME"]) + host = {"Linux": "linux-x86_64", "Windows": "windows-x86_64"}.get(platform.system()) + if host is None: + raise RuntimeError("Use the pinned Linux or Windows Android NDK builder") + compiler = ndk / "toolchains/llvm/prebuilt" / host / "bin" + suffix = ".exe" if platform.system() == "Windows" else "" + archive_tool = compiler / f"llvm-ar{suffix}" + if not archive_tool.is_file() or "Pkg.Revision = 27.2.12479018" not in (ndk / "source.properties").read_text(encoding="utf-8"): + raise RuntimeError("Expected Android NDK 27.2.12479018") + records = [] + for abi, target in TARGETS.items(): + environment = os.environ.copy() + # 直接调用 clang,避免 Windows 的 .cmd 包装器在带空格的 SDK 路径中再次解释参数。 + clang = compiler / f"clang{suffix}" + environment.update({ + f"CARGO_TARGET_{target.upper().replace('-', '_')}_LINKER": str(clang), + f"CC_{target.replace('-', '_')}": str(clang), + f"CFLAGS_{target.replace('-', '_')}": f"--target={target}26", + f"AR_{target.replace('-', '_')}": str(archive_tool), + "RUSTFLAGS": f"-C link-arg=--target={target}26 -C link-arg=-Wl,-z,max-page-size=16384", + }) + subprocess.run(["cargo", "build", "--locked", "--release", "--target", target, + "--manifest-path", str(CRATE / "Cargo.toml"), "--target-dir", str(CRATE / "target")], + env=environment, check=True) + library = CRATE / "target" / target / "release/libpebrel_ssh.so" + destination = OUTPUT / "jniLibs" / abi + destination.mkdir(parents=True, exist_ok=True) + shutil.copy2(library, destination / library.name) + records.append({"abi": abi, "sha256": hashlib.sha256(library.read_bytes()).hexdigest()}) + notices() + provenance = {"russh": "0.62.2", "rust": subprocess.check_output(["rustc", "--version"], text=True, encoding="utf-8").strip(), + "ndk": "27.2.12479018", "api": 26, "libraries": records, + "lock_sha256": hashlib.sha256((CRATE / "Cargo.lock").read_bytes()).hexdigest()} + (OUTPUT / "assets/licenses/Russh/BUILD.json").write_text(json.dumps(provenance, indent=2) + "\n", encoding="utf-8") + + +if __name__ == "__main__": + main() diff --git a/mobile/tools/generate_assets.py b/mobile/tools/generate_assets.py new file mode 100644 index 0000000..17875ce --- /dev/null +++ b/mobile/tools/generate_assets.py @@ -0,0 +1,73 @@ +"""Build Android palette assets from Pebrel's single desktop theme authority.""" +from pathlib import Path +import argparse +import json +import re +import shutil +from package_relay import package_relay + + +def generate(output: Path, resources: Path | None = None) -> None: + root = Path(__file__).resolve().parents[2] + source = (root / "nebula_settings/src/themes.rs").read_text(encoding="utf-8") + names_block = source.split("pub const BUILTIN:", 1)[1].split("];", 1)[0] + names = re.findall(r"Self::(\w+)", names_block) + palettes = {} + for name, body in re.findall(r"Self::(\w+)\s*=>\s*ReviewedPalette\s*\{(.*?)\n\s*\}", source, re.S): + colors = {} + for key, values in re.findall(r"(\w+):\s*\[([^\]]+)\]", body): + colors[key] = [int(value.strip(), 0) for value in values.split(",") if value.strip()] + if name in names: + for key in ["shell", "background", "foreground", "accent"]: + if len(colors.get(key, [])) != 3: + raise ValueError(f"Missing authoritative {name}.{key}") + palettes[name] = colors + if set(palettes) != set(names): + raise ValueError(f"Unparsed built-in palettes: {set(names) - set(palettes)}") + output.mkdir(parents=True, exist_ok=True) + (output / "themes.json").write_text(json.dumps(palettes, separators=(",", ":")), encoding="utf-8") + # Keep icon identity, glyph metrics and both labels in the desktop authority. + icon_source = (root / "nebula_app/src/display/ui/os_icons.rs").read_text(encoding="utf-8") + catalog = icon_source.split("pub(crate) const CATALOG:", 1)[1].split("];", 1)[0] + icons = [] + for body in re.findall(r"OsIcon \{ (.*?) \}", catalog): + def field(name: str) -> str: + return re.search(rf'{name}: "([^"]+)"', body).group(1) + codepoint = re.search(r"glyph: '\\u\{([0-9a-f]+)\}'", body).group(1) + icons.append({"id": field("id"), "glyph": chr(int(codepoint, 16)), + "zh": field("zh"), "en": field("en")}) + if len(icons) != catalog.count("OsIcon {") or not any(icon["id"] == "term" for icon in icons): + raise ValueError("Unparsed desktop host icon catalog") + (output / "host-icons.json").write_text(json.dumps(icons, ensure_ascii=False), encoding="utf-8") + shutil.copyfile(root / "assets/fonts/MapleMono-NF-CN-Regular.ttf", output / "terminal.ttf") + fonts = output / "fonts" + fonts.mkdir(exist_ok=True) + shutil.copyfile(root / "assets/fonts/JetBrainsMono-Regular.ttf", fonts / "JetBrainsMono-Regular.ttf") + if resources is not None: + drawable = resources / "drawable-nodpi" + drawable.mkdir(parents=True, exist_ok=True) + shutil.copyfile(root / "extra/logo/nebula-titanium.png", drawable / "ic_pebrel.png") + notices = output / "licenses" + notices.mkdir(exist_ok=True) + for license_file in (root / "mobile/android/third_party/licenses").glob("*.txt"): + shutil.copyfile(license_file, notices / license_file.name) + # AAPT transparently expands .gz assets and removes the suffix. Keep gzip bytes + # under a neutral extension so Android and the remote tar reader agree. + (output / "relay-kit.tar.gz").unlink(missing_ok=True) + (output / "relay-kit.tar").unlink(missing_ok=True) + package_relay(root, output / "relay-kit.bin") + # Bundle the licenses corresponding to the pinned native dependency. + native_notices = root / "mobile/android/ghostty/build/upstream/arm64-v8a/licenses" + if not (native_notices / "Ghostty-MIT.txt").is_file(): + raise FileNotFoundError("Build the pinned terminal core and licenses before Android packaging") + shutil.copytree(native_notices, notices / "Ghostty", dirs_exist_ok=True) + shutil.copyfile(root / "mobile/android/ghostty/UPSTREAM.json", notices / "Ghostty-UPSTREAM.json") + shutil.copyfile(root / "mobile/android/third_party/THIRD-PARTY-NOTICES.md", notices / "THIRD-PARTY-NOTICES.md") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--output", required=True, type=Path) + parser.add_argument("--resources", type=Path) + args = parser.parse_args() + generate(args.output, args.resources) diff --git a/mobile/tools/package_manual_relay.py b/mobile/tools/package_manual_relay.py new file mode 100644 index 0000000..5d85781 --- /dev/null +++ b/mobile/tools/package_manual_relay.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +"""Create an offline manual kit from a verified APK, without fetching executables.""" +import argparse +import hashlib +import io +import json +from pathlib import Path +import tarfile +import zipfile + +from verify_native_relay_apk import verify + + +def package(apk: Path, output: Path, commit: str) -> None: + verify(apk, commit) + if output.exists(): + raise ValueError("Refusing to overwrite an existing manual kit") + root = Path(__file__).resolve().parents[1] + members = {} + with zipfile.ZipFile(apk) as archive: + for arch in ("x86_64", "aarch64"): + prefix = f"assets/native-relay/{arch}" + binary = archive.read(f"{prefix}/pebrel-relay") + manifest = archive.read(f"{prefix}/manifest.json") + record = json.loads(manifest) + assert record["commit"] == commit + assert hashlib.sha256(binary).hexdigest() == record["sha256"] + members[f"{arch}/pebrel-relay"] = binary + members[f"{arch}/manifest.json"] = manifest + members[f"{arch}/SHA256SUMS"] = (record["sha256"] + " pebrel-relay\n").encode() + members["install.sh"] = (root / "relay-native/install.sh").read_bytes() + members["INSTALL.md"] = (root / "relay-native/INSTALL.md").read_bytes() + members["SOURCE_COMMIT"] = (commit + "\n").encode() + output.parent.mkdir(parents=True, exist_ok=True) + with tarfile.open(output, "w:gz") as archive: + for name, data in members.items(): + info = tarfile.TarInfo("pebrel-relay-manual/" + name) + info.size = len(data) + info.mode = 0o700 if name.endswith("/pebrel-relay") or name == "install.sh" else 0o600 + archive.addfile(info, io.BytesIO(data)) + digest = hashlib.sha256(output.read_bytes()).hexdigest() + output.with_name(output.name + ".sha256").write_text(digest + " " + output.name + "\n", encoding="utf-8") + print(json.dumps({"file": str(output), "sha256": digest, "bytes": output.stat().st_size, "binary_source": commit})) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--apk", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--commit", required=True) + args = parser.parse_args() + package(args.apk, args.output, args.commit) diff --git a/mobile/tools/package_native_relay.py b/mobile/tools/package_native_relay.py new file mode 100644 index 0000000..d649ca7 --- /dev/null +++ b/mobile/tools/package_native_relay.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Package a same-build native relay; Android never downloads an unpinned binary.""" +import argparse +import hashlib +import json +from pathlib import Path +import shutil +import struct + + +def package(binary: Path, output: Path, arch: str, commit: str) -> None: + data = binary.read_bytes() + machine = {"x86_64": 62, "aarch64": 183}[arch] + if (len(data) < 64 or len(data) > 64 * 1024 * 1024 or data[:6] != b"\x7fELF\x02\x01" + or struct.unpack_from(" None: + # A bounded source-only kit: no private configuration, dependencies or user files. + relay_files = ( + "Caddyfile", "Dockerfile", "compose.yaml", "connector.mjs", "init.mjs", + "package-lock.json", "package.json", "protocol.mjs", "runtime-link.mjs", "server.mjs", + "invite.mjs", "tls.mjs", "lan.mjs", "loopback-proxy.mjs", "pairing.mjs", "qr-page.mjs", + "README.md", "THIRD-PARTY-NOTICES.md", + ) + payload = [(root / "mobile/relay" / name, "relay/" + name) for name in relay_files] + payload.append((root / "mobile/protocol/bridge-policy.json", "protocol/bridge-policy.json")) + payload.append((root / "LICENSE", "LICENSE")) + if sum(path.stat().st_size for path, _ in payload) > 2 * 1024 * 1024: + raise ValueError("Relay source kit exceeds its 2 MiB input budget") + with destination.open("wb") as target: + with gzip.GzipFile(filename="", fileobj=target, mode="wb", mtime=0) as compressed: + with tarfile.open(fileobj=compressed, mode="w") as archive: + for path, name in payload: + if path.is_symlink() or not path.is_file(): + raise ValueError(f"Relay kit input must be a regular source file: {path}") + entry = archive.gettarinfo(str(path), arcname=name) + entry.uid = entry.gid = 0 + entry.uname = entry.gname = "" + entry.mtime = 0 + entry.mode = 0o644 + with path.open("rb") as source_file: + archive.addfile(entry, source_file) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--output", required=True, type=Path) + args = parser.parse_args() + args.output.parent.mkdir(parents=True, exist_ok=True) + package_relay(Path(__file__).resolve().parents[2], args.output) diff --git a/mobile/tools/package_relay_installer.py b/mobile/tools/package_relay_installer.py new file mode 100644 index 0000000..8e348c0 --- /dev/null +++ b/mobile/tools/package_relay_installer.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Publish a fixed-version bootstrap and raw native assets, never a latest alias.""" +import argparse +import hashlib +import json +from pathlib import Path +import re +import shutil + + +def package(source: Path, output: Path, commit: str, tag: str) -> None: + if not re.fullmatch(r"[a-f0-9]{40}", commit): + raise ValueError("An exact source commit is required") + if not re.fullmatch(r"relay-[A-Za-z0-9._-]+", tag): + raise ValueError("A separate, immutable relay release tag is required") + template = Path(__file__).resolve().parents[1] / "relay-native/install-online.sh" + script = template.read_text(encoding="utf-8").replace("@RELEASE_TAG@", tag) + output.mkdir(parents=True, exist_ok=True) + checksums = {} + for arch in ("x86_64", "aarch64"): + binary = source / arch / "pebrel-relay" + manifest = json.loads((source / arch / "manifest.json").read_text(encoding="utf-8")) + data = binary.read_bytes() + sha = hashlib.sha256(data).hexdigest() + if (manifest != {"protocol": 2, "arch": arch, "commit": commit, + "size": len(data), "sha256": sha} + or not data.startswith(b"\x7fELF\x02\x01")): + raise ValueError(f"Unverified {arch} artifact") + name = f"pebrel-relay-linux-{arch}" + shutil.copyfile(binary, output / name) + checksums[name] = sha + script = script.replace(f"@{arch.upper()}_SHA256@", sha) + if re.search(r"@[A-Z0-9_]+@", script): + raise ValueError("Unresolved installer placeholder") + (output / "pebrel-relay.sh").write_text(script, encoding="utf-8", newline="\n") + checksums["pebrel-relay.sh"] = hashlib.sha256((output / "pebrel-relay.sh").read_bytes()).hexdigest() + (output / "build.json").write_text(json.dumps({ + "source": commit, "tag": tag, "protocol": 2, "sha256": checksums, + }, indent=2) + "\n", encoding="utf-8") + checksums["build.json"] = hashlib.sha256((output / "build.json").read_bytes()).hexdigest() + (output / "SHA256SUMS").write_text("".join( + f"{sha} {name}\n" for name, sha in sorted(checksums.items()) + ), encoding="utf-8") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--commit", required=True) + parser.add_argument("--tag", required=True) + args = parser.parse_args() + package(args.source, args.output, args.commit, args.tag) diff --git a/mobile/tools/package_windows_preview.py b/mobile/tools/package_windows_preview.py new file mode 100644 index 0000000..7bbe30b --- /dev/null +++ b/mobile/tools/package_windows_preview.py @@ -0,0 +1,75 @@ +"""Bundle a freshly built GPUI desktop with native mobile pairing instructions.""" +import argparse +import hashlib +import json +from pathlib import Path, PurePosixPath +import re +import subprocess +import zipfile + + +def safe_name(name: str) -> str: + name = name.replace("\\", "/") + path = PurePosixPath(name) + if path.is_absolute() or ".." in path.parts or ":" in name: + raise ValueError(f"Unsafe package path: {name}") + return name + + +def package(desktop: Path, output: Path, commit: str) -> Path: + root = Path(__file__).resolve().parents[2] + if not re.fullmatch(r"[a-f0-9]{40}", commit): + raise ValueError("An exact source commit is required") + actual = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=root, text=True).strip() + if actual != commit: + raise ValueError("Requested commit differs from the checked-out source") + output.mkdir(parents=True, exist_ok=True) + destination = output / f"Pebrel-Windows-x64-mobile-preview-{commit[:7]}.zip" + with desktop.open("rb") as source_file: + desktop_digest = hashlib.file_digest(source_file, "sha256").hexdigest() + record = { + "source_commit": commit, + "desktop_package_sha256": desktop_digest, + "mode": "package_only", + "tests": "See the matching CI run for targeted check results; real-device and visual acceptance remain manual", + "files": {}, + } + with zipfile.ZipFile(destination, "x", compression=zipfile.ZIP_DEFLATED) as target: + def add(name: str, data: bytes) -> None: + name = safe_name(name) + if name in record["files"]: + raise ValueError(f"Duplicate package entry: {name}") + target.writestr(name, data) + record["files"][name] = hashlib.sha256(data).hexdigest() + + with zipfile.ZipFile(desktop) as source: + for entry in source.infolist(): + if not entry.is_dir(): + add(entry.filename, source.read(entry)) + for required in ("pebrel.exe", "runtime/pebrel-hook.exe", "runtime/conpty.dll", "runtime/OpenConsole.exe"): + if required not in record["files"]: + raise ValueError(f"Missing desktop runtime: {required}") + # Native Settings replaces the external Node/browser pairing launcher. + for name in ("Start-Pebrel-Preview.cmd", "START-NATIVE-LAN.md", "SETTINGS-PREVIEW-0.4.9.md", "RELAY-PREVIEW-0.4.10.md"): + data = (root / "mobile/desktop" / name).read_bytes() + if name.endswith(".cmd"): + data = data.replace(b"\r\n", b"\n").replace(b"\n", b"\r\n") + add(name, data) + add("SOURCE_COMMIT", (commit + "\n").encode()) + metadata = json.dumps(record, ensure_ascii=False, indent=2) + "\n" + target.writestr("BUILD.json", metadata) + digest = hashlib.sha256(destination.read_bytes()).hexdigest() + (output / "SHA256SUMS").write_text(f"{digest} {destination.name}\n", encoding="utf-8") + (output / "SOURCE_COMMIT").write_text(commit + "\n", encoding="utf-8") + (output / "BUILD.json").write_text(metadata, encoding="utf-8") + print(json.dumps({"package": str(destination), "sha256": digest, "source_commit": commit})) + return destination + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--desktop", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--commit", required=True) + arguments = parser.parse_args() + package(arguments.desktop, arguments.output, arguments.commit) diff --git a/mobile/tools/test_apk_audit.py b/mobile/tools/test_apk_audit.py new file mode 100644 index 0000000..856efb5 --- /dev/null +++ b/mobile/tools/test_apk_audit.py @@ -0,0 +1,116 @@ +"""Positive and negative fixtures for native APK composition gates (not real APKs).""" +from pathlib import Path +import json +import io +import tarfile +import struct +import tempfile +import unittest +import zipfile + +from verify_ghostty_apk import verify + + +def deployment_kit() -> bytes: + output = io.BytesIO() + with tarfile.open(fileobj=output, mode="w:gz") as archive: + for name in ("relay/server.mjs", "relay/init.mjs", "relay/invite.mjs", "relay/tls.mjs", + "relay/compose.yaml", "relay/Dockerfile", "relay/package-lock.json", + "protocol/bridge-policy.json"): + entry = tarfile.TarInfo(name) + entry.size = len(b"fixture") + archive.addfile(entry, io.BytesIO(b"fixture")) + return output.getvalue() + + +def elf(machine: int, alignment: int = 16384) -> bytes: + data = bytearray(128) + data[:6] = b"\x7fELF\x02\x01" + struct.pack_into(" "$manual_dir/$arch/SHA256SUMS" +cp /fixture/install.sh "$manual_dir/install.sh" +install_relay() { + sh "$manual_dir/install.sh" 127.0.0.1 18443 +} +cleanup() { + if [ -f /opt/pebrel-relay/installation.json ]; then + "$binary" service-uninstall --purge + fi +} +trap cleanup EXIT +install_relay +"$binary" service-status | grep '"ready":true' +# The listener must have permanently dropped root, not just its supervising PID. +pid=$(pidof pebrel-relay) +test -n "$pid" +awk '/^Uid:/ { if ($2 == 0 || $3 == 0 || $4 == 0 || $5 == 0) exit 1; found=1 } END { if (!found) exit 1 }' "/proc/$pid/status" +before=$(sha256sum /etc/pebrel-relay/access.json) +"$binary" service-stop +"$binary" service-status | grep '"running":false' +"$binary" service-start +"$binary" service-status | grep '"ready":true' +"$binary" service-uninstall +"$binary" service-status | grep '"configuration_retained":true' +test ! -e /etc/init.d/pebrel-relay +install_relay +after=$(sha256sum /etc/pebrel-relay/access.json) +test "$before" = "$after" +"$binary" service-uninstall --purge +"$binary" service-status | grep '"configuration_retained":false' +printf 'OpenRC: install, unprivileged listener, readiness, stop/start, retained reinstall and purge passed\n' diff --git a/mobile/tools/test_native_relay_service.py b/mobile/tools/test_native_relay_service.py new file mode 100644 index 0000000..9d73195 --- /dev/null +++ b/mobile/tools/test_native_relay_service.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Focused real-systemd acceptance on a disposable GitHub runner only.""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import subprocess + + +def verify(binary: Path) -> None: + if os.environ.get("GITHUB_ACTIONS") != "true" or os.environ.get("RUNNER_ENVIRONMENT") != "github-hosted": + raise RuntimeError("Only disposable GitHub-hosted runners are allowed") + # Never adopt, stop or remove any installation that predates this test. + for path in ("/opt/pebrel-relay", "/etc/pebrel-relay", "/etc/systemd/system/pebrel-relay.service"): + if Path(path).exists() or Path(path).is_symlink(): + raise RuntimeError("Existing relay installation is outside the fixture") + binary = binary.resolve(strict=True) + sha = hashlib.sha256(binary.read_bytes()).hexdigest() + + def run(*args: str) -> list[dict]: + completed = subprocess.run(["sudo", str(binary), *args], capture_output=True, timeout=90) + if completed.returncode: + raise RuntimeError(f"Native relay {args[0]} failed (server details redacted)") + return [json.loads(line) for line in completed.stdout.decode().splitlines() if line.startswith("{")] + + install = ("service-install", "--source", str(binary), "--sha256", sha, + "--address", "127.0.0.1", "--port", "18443") + try: + # Match the private umask used by Android, not the permissive CI default. + previous_umask = os.umask(0o077) + try: + run(*install) + finally: + os.umask(previous_umask) + assert run("service-status")[-1]["ready"] + run("service-stop") + assert not run("service-status")[-1]["running"] + run("service-start") + assert run("service-status")[-1]["ready"] + run("service-uninstall") + state = run("service-status")[-1] + assert not state["installed"] and state["configuration_retained"] + run(*install) + assert run("service-status")[-1]["ready"] + run("service-uninstall", "--purge") + state = run("service-status")[-1] + assert not state["installed"] and not state["configuration_retained"] + print("Native service: install, readiness, stop/start, uninstall, retained-config reinstall and purge passed") + finally: + # The executable itself verifies the exact ownership manifest. No broad + # directory removal or fallback deletion on an ownership mismatch. + if Path("/opt/pebrel-relay/installation.json").exists(): + run("service-uninstall", "--purge") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("binary", type=Path) + verify(parser.parse_args().binary) diff --git a/mobile/tools/test_online_relay.sh b/mobile/tools/test_online_relay.sh new file mode 100644 index 0000000..275692f --- /dev/null +++ b/mobile/tools/test_online_relay.sh @@ -0,0 +1,95 @@ +#!/bin/sh +# Real lifecycle acceptance only on a disposable CI host/container. +set -eu +[ "${PEBREL_DISPOSABLE_INSTALL_TEST:-}" = 1 ] || exit 1 +[ "$(id -u)" = 0 ] || exit 1 +[ ! -e /opt/pebrel-relay ] && [ ! -e /etc/pebrel-relay ] || exit 1 +installer=$1 +binary=$2 +if [ "${3:-}" = 239 ]; then + [ "$(systemctl --version | awk 'NR == 1 { print $2 }')" = 239 ] + [ -L /etc/init.d ] +fi +fixture=$(mktemp -d /tmp/pebrel-online-test.XXXXXXXX) +decoy_pid= +cleanup() { + if [ -f /opt/pebrel-relay/installation.json ]; then "$binary" service-uninstall --purge; fi + if [ -n "$decoy_pid" ]; then kill "$decoy_pid" 2>/dev/null || :; wait "$decoy_pid" 2>/dev/null || :; fi + rm -f "$fixture/relay.json" "$fixture/access.json" "$fixture/certificate.pem" \ + "$fixture/private-key.pem" "$fixture/corrupt" "$fixture/executed" "$fixture/download.log" "$fixture/corrupt.log" + rmdir "$fixture" +} +trap cleanup EXIT +export PEBREL_FIXTURE_BINARY="$binary" +export PEBREL_FIXTURE_DIR="$fixture" + +# An unrelated 443 listener must stay alive while Pebrel selects 8443. +"$binary" init --directory "$fixture" --address 127.0.0.1 --listen 0.0.0.0:443 +"$binary" serve-unprivileged --config "$fixture/relay.json" & +decoy_pid=$! +attempt=0 +until "$binary" probe --config "$fixture/relay.json"; do + attempt=$((attempt + 1)); [ "$attempt" -lt 20 ]; sleep 0.2 +done + +# Full script execution with only the HTTPS boundary substituted. Truncated or +# poisoned bytes must never execute, even if the downloader reports success. +printf '#!/bin/sh\ntouch "%s/executed"\n' "$fixture" > "$fixture/corrupt" +if sh -c ' + installer=$1; shift + curl() { for destination; do :; done; cp "$PEBREL_FIXTURE_DIR/corrupt" "$destination"; } + . "$installer" +' fixture "$installer" --address 127.0.0.1 > "$fixture/corrupt.log" 2>&1; then + printf 'Corrupt download was accepted\n' >&2; exit 1 +fi +grep -q 'SHA256 mismatch' "$fixture/corrupt.log" +[ ! -e "$fixture/executed" ] && [ ! -e /opt/pebrel-relay ] + +# Force curl failure and verify that wget's bytes are checked and installed. +sh -c ' + installer=$1; shift + curl() { printf "curl\n" >> "$PEBREL_FIXTURE_DIR/download.log"; return 22; } + wget() { + printf "wget\n" >> "$PEBREL_FIXTURE_DIR/download.log" + for destination; do :; done + cp "$PEBREL_FIXTURE_BINARY" "$destination" + } + . "$installer" +' fixture "$installer" --address 127.0.0.1 +[ "$(wc -l < "$fixture/download.log")" -eq 2 ] +grep -q '0.0.0.0:8443' /etc/pebrel-relay/relay.json +"$binary" probe --config "$fixture/relay.json" +pid=$(systemctl show -p MainPID --value pebrel-relay.service) +awk '/^Uid:/ { if ($2 == 0 || $3 == 0 || $4 == 0 || $5 == 0) exit 1; found=1 } + END { if (!found) exit 1 }' "/proc/$pid/status" +if [ "${3:-}" = 239 ]; then + grep -q 'serve-unprivileged' /etc/systemd/system/pebrel-relay.service + grep -q '^CapEff:[[:space:]]*0000000000000000$' "/proc/$pid/status" +fi +before=$(sha256sum /etc/pebrel-relay/access.json) +sh "$installer" status # Verified installed binary works without network. +sh "$installer" stop --binary "$binary" +sh "$installer" start --binary "$binary" +sh "$installer" uninstall --binary "$binary" +sh "$installer" install --binary "$binary" +[ "$before" = "$(sha256sum /etc/pebrel-relay/access.json)" ] +grep -q '0.0.0.0:8443' /etc/pebrel-relay/relay.json +sh "$installer" purge --binary "$binary" +[ ! -e /etc/pebrel-relay/access.json ] +# Simulate unreachable GitHub. A proxy is only a transport for already pinned +# bytes; it never supplies a new expected checksum or an executable script. +sh -c ' + installer=$1; shift + curl() { + for arg; do case "$arg" in https://*) url=$arg;; esac; destination=$arg; done + case "$url" in + https://gh-proxy.com/*) cp "$PEBREL_FIXTURE_BINARY" "$destination";; + *) return 22;; + esac + } + wget() { return 4; } + . "$installer" +' fixture "$installer" --address 127.0.0.1 +sh "$installer" purge --binary "$binary" +"$binary" probe --config "$fixture/relay.json" +printf 'Online script: corrupt download rejection, curl/wget and proxy fallback, 443 preservation, 8443 selection, non-root listener, retained reinstall and purge passed\n' diff --git a/mobile/tools/verify_ghostty_apk.py b/mobile/tools/verify_ghostty_apk.py new file mode 100644 index 0000000..a04a020 --- /dev/null +++ b/mobile/tools/verify_ghostty_apk.py @@ -0,0 +1,86 @@ +"""Reject legacy engines and require both native terminal/SSH Android ABIs.""" +from pathlib import Path +import hashlib +import io +import tarfile +import json +import re +import struct +import sys +import zipfile + + +def verify(path: Path) -> dict: + libraries = [] + with zipfile.ZipFile(path) as apk: + names = apk.namelist() + deployment = apk.read("assets/relay-kit.bin") + if len(deployment) > 2 * 1024 * 1024 or deployment[:2] != b"\x1f\x8b": + raise ValueError("Deployment resource must retain its gzip bytes") + with tarfile.open(fileobj=io.BytesIO(deployment), mode="r:gz") as kit: + members = kit.getmembers() + required = {"relay/server.mjs", "relay/init.mjs", "relay/invite.mjs", "relay/tls.mjs", + "relay/compose.yaml", "relay/Dockerfile", "relay/package-lock.json", + "protocol/bridge-policy.json"} + if not required.issubset({entry.name for entry in members}): + raise ValueError("Deployment source kit is incomplete") + if sum(entry.size for entry in members) > 2 * 1024 * 1024 or any( + not entry.isfile() or entry.name.startswith("/") or ".." in Path(entry.name).parts + for entry in members): + raise ValueError("Deployment source kit contains invalid entries") + dex = [apk.read(name) for name in names if name.endswith(".dex")] + if not dex: + raise ValueError("Missing dex in application") + external_terminal = re.compile(rb"Lcom/[^/]+/(?:terminal/|view/Terminal(?:View|Renderer))") + if any(external_terminal.search(payload) for payload in dex): + raise ValueError("External legacy terminal classes remain in application") + if any(b"Lnet/schmizz/sshj/" in payload for payload in dex): + raise ValueError("SSHJ classes remain in application") + if not any(b"Lio/github/kuddev/pebrel/ssh/NativeSsh;" in payload for payload in dex): + raise ValueError("russh JNI class missing from application") + if not any(b"Lio/github/kuddev/pebrel/terminal/NativeBridge;" in payload for payload in dex): + raise ValueError("Terminal JNI class missing from application") + for descriptor in (b"Lio/github/kuddev/pebrel/ssh/NativeLink;", b"Lio/github/kuddev/pebrel/voice/NativeWhisper;"): + if not any(descriptor in payload for payload in dex): + raise ValueError("Mobile link or voice JNI class missing from application") + allowed_libraries = {"libpebrel_ghostty.so", "libpebrel_ssh.so", "libpebrel_voice.so", "libandroidx.graphics.path.so"} + for name in names: + if name.endswith(".so") and (name.split("/")[-1] not in allowed_libraries or + name.split("/")[:2] not in [["lib", "arm64-v8a"], ["lib", "x86_64"]]): + raise ValueError(f"Unexpected native library in application: {name}") + for abi, machine in (("arm64-v8a", 183), ("x86_64", 62)): + for library in ("libpebrel_ghostty.so", "libpebrel_ssh.so", "libpebrel_voice.so"): + name = f"lib/{abi}/{library}" + payload = apk.read(name) + if payload[:6] != b"\x7fELF\x02\x01" or struct.unpack_from(" None: + with zipfile.ZipFile(apk) as archive: + for arch, machine in (("x86_64", 62), ("aarch64", 183)): + prefix = f"assets/native-relay/{arch}" + metadata = json.loads(archive.read(f"{prefix}/manifest.json")) + data = archive.read(f"{prefix}/pebrel-relay") + if (len(data) < 64 or len(data) > 64 * 1024 * 1024 or + data[:6] != b"\x7fELF\x02\x01" or + struct.unpack_from(" diff --git a/nebula_app/assets/icons/nebula-backup-cloud.svg b/nebula_app/assets/icons/nebula-backup-cloud.svg new file mode 100644 index 0000000..deb4503 --- /dev/null +++ b/nebula_app/assets/icons/nebula-backup-cloud.svg @@ -0,0 +1 @@ + diff --git a/nebula_app/assets/icons/nebula-backup-database.svg b/nebula_app/assets/icons/nebula-backup-database.svg new file mode 100644 index 0000000..d484760 --- /dev/null +++ b/nebula_app/assets/icons/nebula-backup-database.svg @@ -0,0 +1 @@ + diff --git a/nebula_app/assets/icons/nebula-backup-drive.svg b/nebula_app/assets/icons/nebula-backup-drive.svg new file mode 100644 index 0000000..47740f8 --- /dev/null +++ b/nebula_app/assets/icons/nebula-backup-drive.svg @@ -0,0 +1 @@ + diff --git a/nebula_app/assets/icons/nebula-backup-folder.svg b/nebula_app/assets/icons/nebula-backup-folder.svg new file mode 100644 index 0000000..0c973cf --- /dev/null +++ b/nebula_app/assets/icons/nebula-backup-folder.svg @@ -0,0 +1 @@ + diff --git a/nebula_app/assets/icons/nebula-backup-globe.svg b/nebula_app/assets/icons/nebula-backup-globe.svg new file mode 100644 index 0000000..b1b219f --- /dev/null +++ b/nebula_app/assets/icons/nebula-backup-globe.svg @@ -0,0 +1 @@ + diff --git a/nebula_app/assets/icons/nebula-backup-lock.svg b/nebula_app/assets/icons/nebula-backup-lock.svg new file mode 100644 index 0000000..88e935a --- /dev/null +++ b/nebula_app/assets/icons/nebula-backup-lock.svg @@ -0,0 +1 @@ + diff --git a/nebula_app/assets/icons/nebula-backup-server.svg b/nebula_app/assets/icons/nebula-backup-server.svg new file mode 100644 index 0000000..03c21f4 --- /dev/null +++ b/nebula_app/assets/icons/nebula-backup-server.svg @@ -0,0 +1 @@ + diff --git a/nebula_app/assets/icons/nebula-phone.svg b/nebula_app/assets/icons/nebula-phone.svg new file mode 100644 index 0000000..cfd41a2 --- /dev/null +++ b/nebula_app/assets/icons/nebula-phone.svg @@ -0,0 +1,4 @@ + + + + diff --git a/nebula_app/assets/icons/nebula-sliders.svg b/nebula_app/assets/icons/nebula-sliders.svg index 542e624..9927c54 100644 --- a/nebula_app/assets/icons/nebula-sliders.svg +++ b/nebula_app/assets/icons/nebula-sliders.svg @@ -1 +1 @@ - + diff --git a/nebula_app/examples/plugins/lua-overview/init.lua b/nebula_app/examples/plugins/lua-overview/init.lua new file mode 100644 index 0000000..01a06f4 --- /dev/null +++ b/nebula_app/examples/plugins/lua-overview/init.lua @@ -0,0 +1,10 @@ +return { + summary = function(ctx, args) + local runtime = ctx.runtime.call('runtime.describe', {}) + return { + label = args.label or 'Pebrel', + app_version = runtime.app_version, + capability_count = #runtime.capabilities, + } + end, +} diff --git a/nebula_app/examples/plugins/lua-overview/plugin.toml b/nebula_app/examples/plugins/lua-overview/plugin.toml new file mode 100644 index 0000000..61bd053 --- /dev/null +++ b/nebula_app/examples/plugins/lua-overview/plugin.toml @@ -0,0 +1,12 @@ +manifest_version = 1 +api_version = 1 +id = "example.lua-overview" +name = "Lua Runtime Overview" +version = "0.1.0" +entry = "init.lua" +permissions = ["runtime.describe"] + +[[commands]] +id = "summary" +title = "Summarize runtime capabilities" +handler = "summary" diff --git a/nebula_app/examples/plugins/native-overview/plugin.toml b/nebula_app/examples/plugins/native-overview/plugin.toml new file mode 100644 index 0000000..f00d53a --- /dev/null +++ b/nebula_app/examples/plugins/native-overview/plugin.toml @@ -0,0 +1,11 @@ +manifest_version = 1 +api_version = 1 +id = "example.native-overview" +name = "Native Runtime Overview" +version = "0.1.0" +permissions = ["runtime.describe"] + +[[commands]] +id = "describe" +title = "Describe the running Pebrel instance" +method = "runtime.describe" diff --git a/nebula_app/i18n/en-US.json b/nebula_app/i18n/en-US.json index b2442b6..1e962ea 100644 --- a/nebula_app/i18n/en-US.json +++ b/nebula_app/i18n/en-US.json @@ -589,6 +589,7 @@ "drag_hint": "Drop files here to upload; drag an item to File Explorer to download." }, "hosts": { + "copied": "Created host copy: {name}", "search": "Search name, address, group, tags or notes", "group": "Group", "tags": "Tags", @@ -621,7 +622,20 @@ "auth_auto": "Automatic", "auth_password": "Password", "auth_key": "Private key", - "auth_interactive": "Interactive sign-in" + "auth_interactive": "Interactive sign-in", + "list_subtitle": "Save frequently used hosts and open remote terminals. Passwords stay in the system credential store. Aliases from ~/.ssh/config are read without modifying the file.", + "scope_all": "All", + "summary": "{total} hosts, including {config} from ~/.ssh/config", + "hidden_count": "{count} hidden", + "collapse_hidden": "Collapse hidden", + "more": "Host actions", + "pin": "Pin", + "unpin": "Unpin", + "confirm_delete": "Confirm delete", + "via": "via {host}", + "connection_time_unknown": "Connection time has not been recorded for this host.", + "key_file": "Private key {name}", + "search_placeholder": "Name, address, tags" }, "vcs": { "changes": "Changes", @@ -812,6 +826,10 @@ "too_large": "The background image exceeds the memory limit. Choose a smaller image." }, "update": { + "managed_label": "Managed by installation source", + "managed_scoop": "Update this installation in a terminal with: scoop update pebrel", + "managed_msix": "This Windows package is updated by its installation source. For Microsoft Store installations, open Microsoft Store and check for updates.", + "managed_unknown": "The installation source could not be identified. Update through the source you originally used; the standalone installer is disabled for this copy.", "downloading_hint": "Downloading and verifying the update package for this device.", "no_verified_package": "This release has no verified update package for this device. Open the Releases page to download it manually.", "check_fallback_failed": "GitHub API access is restricted (HTTP {status}), and the official Releases page could not be reached: {error}. Retry later or open the Releases page.", @@ -1003,5 +1021,203 @@ "empty": "This directory is empty.", "error_retry": "{error} (click Reload in the top-right to retry)", "busy_other": "Another host is transferring: {destination}" + }, + "backup_flow": { + "password_notice": "Pebrel keeps this password in memory only for the current run and never writes it to disk. You will be asked again for the first backup after restarting. If you forget it, existing backups cannot be unlocked.", + "connect_provider": "Connect {provider}", + "address_label": "Address", + "folder_label": "Directory", + "password_label": "Password", + "app_password_label": "App password", + "access_key_label": "Access Key ID", + "secret_key_label": "Secret Access Key", + "ssh_host_label": "SSH host", + "webdav_placeholder": "https://cloud.example.com/remote.php/dav/files/me/pebrel", + "username_placeholder": "Username", + "nutstore_account_placeholder": "Nutstore login email", + "password_placeholder": "Password or app-specific password", + "app_password_placeholder": "Paste app password", + "s3_placeholder": "https://s3.amazonaws.com", + "bucket_placeholder": "pebrel-backup", + "access_key_placeholder": "AKIA…", + "secret_key_placeholder": "Enter it here once", + "folder_placeholder": "D:\\OneDrive\\Pebrel", + "remote_path_placeholder": "~/pebrel-backup", + "new_password_placeholder": "At least 8 characters", + "webdav_address_hint": "Start with https://. HTTP for a self-hosted NAS must be explicitly allowed in the backup configuration file.", + "app_password_hint": "Generate it on the Nutstore website under Account information → Security options.", + "s3_address_hint": "For R2, use https://.r2.cloudflarestorage.com.", + "ssh_host_hint": "Choose a host saved on the SSH page.", + "export_instead": "Not ready to configure storage?", + "title": "Backup and restore", + "intro": "Settings, SSH hosts and history are encrypted on this computer before being saved. Your storage service only receives ciphertext.", + "storage": "Storage location", + "connection": "Connection", + "choose_storage": "Where should backups go?", + "choose_storage_hint": "Use a drive or server you already have. You can change it later.", + "nutstore_hint": "Generate a third-party app password in Nutstore, rather than using your login password.", + "webdav_hint": "Enter an existing HTTPS WebDAV folder and its login details.", + "s3_hint": "AWS S3, Cloudflare R2, MinIO or another S3-compatible bucket.", + "sftp_hint": "Choose a saved SSH host and an existing remote directory. Its existing authentication is reused.", + "folder_hint": "Choose an existing folder on a USB drive, NAS or a synced cloud drive.", + "set_password": "Set a backup password", + "set_password_hint": "Backups are encrypted with this password. You need the same password to restore on another computer.", + "confirm_password": "Enter it again", + "password_hint": "At least 8 characters. Kept only in memory for this run, never written to disk. Forgotten passwords cannot be recovered.", + "mismatch": "The two passwords do not match.", + "choose_contents": "What should be backed up?", + "choose_contents_hint": "Common items are selected. You can change these each time you back up.", + "excluded": "SSH private keys and passwords or API keys in the system credential store are never included.", + "next": "Next", + "back": "Back", + "check_next": "Check connection and continue", + "finish_now": "Finish and back up", + "finish_later": "Finish, back up later", + "browse": "Browse…", + "credential_hint": "Saved to the system credential store on completion. Leave blank to use the stored credential.", + "appearance_hint": "Theme, font size, background and opacity", + "terminal_hint": "Shell, startup directory and terminal profiles", + "assistant_hint": "Assistant switches and preferences", + "sync_hint": "Sync addresses and options, without stored passwords", + "fonts_hint": "Imported fonts can make backups larger", + "hosts_hint": "Saved host addresses and connection profiles", + "session_hint": "Windows, tabs and split layouts", + "dirs_hint": "Recently used directories, including project paths", + "commands_hint": "May include tokens pasted into commands", + "settings": "Settings", + "data": "Data and history", + "measuring": "Calculating contents…", + "select_one": "Select at least one item", + "total": "{count} items · {size} before encryption", + "last_backup": "Last backup", + "latest": "Latest", + "snapshot_meta": "{count} items · {size} · {device}", + "empty": "No backups yet", + "empty_hint": "Storage is configured. Choose Back up now to select contents and start.", + "change": "Change", + "connected": "Connection checked", + "connection_failed": "Connection failed", + "retention": "The latest 10 backups are kept. Older backups are removed after upload.", + "encryption": "Encryption", + "password_ready": "Entered for this run", + "password_missing": "Not entered this run", + "clear": "Clear", + "enter": "Enter…", + "files": "Files", + "export_hint": "Encrypt the selected contents into a .pebrel-backup file to copy to another computer.", + "import_hint": "Choose a .pebrel-backup file and unlock it with its backup password.", + "choose_file": "Choose file…", + "storage_hint": "The connection is checked before saving.", + "upload_hint": "Encrypt and upload to your storage, using the last selection by default.", + "restore_hint": "Unlock the archive to see its contents and choose what to restore.", + "start": "Start backup", + "switch_hint": "Changing storage only changes the displayed history. Backups in the previous location are not deleted.", + "read_only_check": "Lists the selected directory without writing any files.", + "defaults": "Reset selection", + "unselected_untouched": "Only checked items will be restored. Unchecked items are left unchanged.", + "restore_warning": "Restoring replaces the selected local files. An encrypted recovery copy is saved first, so you can undo this restore.", + "unlock": "Unlock", + "processing": "Processing…", + "restored": "Backup restored. Your previous files were saved separately.", + "undo": "Undo", + "undone": "Restore undone. The original files have been restored.", + "selection_save_failed": "Backup completed, but the selection could not be saved: {error}" + }, + "mobile": { + "title": "Phone Remote", + "subtitle": "Follow terminal tasks on your phone, get notified when they finish or need attention, and send a command when needed. Your computer does the work; your phone only sends requests.", + "off_title": "Phone remote is off", + "off_description": "Turn it on to prepare a pairing QR code for this computer. A phone can connect only after you scan the code and approve it here.", + "enable": "Enable and pair a phone", + "pair_title": "Scan to pair your phone", + "pair_description": "This QR code can be used once and expires after 10 minutes.", + "lan": "LAN / Tailscale", + "relay": "Self-hosted relay", + "step_open": "Open Pebrel on your phone", + "step_scan": "Tap Connect computer and scan the QR code", + "step_approve": "Compare the verification codes, then approve this phone here", + "step_relay": "Use the same relay server on your phone and computer", + "same_network": "Connect to the same Wi-Fi as your computer", + "relay_outbound": "Both devices connect out to the relay; no incoming port is needed on your computer", + "qr_unavailable": "Waiting for a pairing code", + "expires": "Expires in {time}", + "expired": "Pairing code expired", + "refresh_qr": "Refresh", + "manual_pair": "Copy complete invitation", + "short_code_prompt": "Trouble scanning? Enter", + "short_code_used": "Code used or locked. Refresh to get a new one.", + "discovery_unavailable": "Network discovery is unavailable. Scan or paste the complete invitation.", + "copied": "Copied", + "request_title": "{name} wants to pair", + "request_description": "Scanned just now · {route} {peer} · Compare the code on your phone: {code}", + "deny": "Reject", + "approve": "Approve", + "approving": "Pairing…", + "devices": "Paired phones", + "add_phone": "Add phone", + "no_devices": "No paired phones yet. Scan and approve a phone to see it here.", + "read_only": "View only", + "allow_input": "Allow input", + "online": "Online", + "offline": "Offline", + "remove": "Remove", + "remove_title": "Remove {name}?", + "remove_description": "This phone will be disconnected and its authorization revoked. It must scan and be approved again to reconnect.", + "connections": "Connections", + "connections_hint": "Both can stay on; local connections take priority", + "lan_direct": "LAN / Tailscale direct", + "lan_hint": "Connect over the same LAN or Tailscale network without a relay server. For Tailscale, connect both devices to the same tailnet.", + "interface": "Network interface", + "interface_hint": "Choose a LAN or Tailscale address your phone can reach. Refresh after changing networks.", + "refresh_addresses": "Refresh network interfaces", + "port": "Listening port", + "port_hint": "Leave blank to choose automatically. A fixed port is easier to allow through your firewall.", + "automatic": "Automatic", + "relay_hint": "Connect across networks through your own relay server.", + "configure_relay": "Configure relay…", + "manage_relay": "Manage…", + "permissions": "Permissions and notifications", + "default_permission": "Default permission for new phones", + "default_permission_hint": "View-only phones do not send terminal input. Change each paired phone’s permission above.", + "notifications": "Notify when tasks finish", + "notifications_hint": "Notify your phone when an agent finishes, a command exits, or a task waits for your confirmation.", + "pause_title": "Pause phone remote", + "pause_hint": "Disconnect all phones and stop listening. Paired phones are remembered and can reconnect when enabled again.", + "pause": "Pause", + "starting": "Starting…", + "waiting": "Waiting for a phone", + "connected": "Connected", + "reconnecting": "Reconnecting…", + "stopped": "Paused", + "invalid": "Check the address, port and relay configuration, then try again.", + "address_failed": "This network address is unavailable. Refresh the interfaces and select a connected network.", + "port_failed": "This port is in use. Choose another port or leave it blank for automatic selection.", + "credentials_failed": "The system credential store did not complete the operation. Unlock it and try again.", + "connection_failed": "The connection was not established. Check your network or relay settings and try again.", + "cancelled": "Operation cancelled", + "relay_drawer_subtitle": "Both devices connect out to this server; your computer needs no incoming port", + "close": "Close", + "read_server": "Read from server", + "read_server_hint": "Choose an SSH host with pebrel-relay installed. Read its address and tokens over SSH without changing the server. To install it, use the Servers card in the phone app.", + "read": "Read", + "reading": "Reading…", + "connection_info": "Connection details", + "import_clipboard": "Import from clipboard", + "import_file": "Import from file", + "relay_url": "Server address", + "relay_room": "Room", + "relay_room_hint": "This computer’s room on the relay. Use one room per computer.", + "desktop_token": "Computer token", + "phone_token": "Phone token", + "relay_tokens_hint": "Tokens are saved in the system credential store. The phone token is included in the pairing QR code.", + "tls_pin": "Certificate fingerprint", + "relay_pin_hint": "Use the SHA-256 fingerprint printed during server setup to verify its certificate.", + "encrypted_hint": "Your phone and computer use end-to-end encryption. The relay forwards ciphertext and cannot read terminal content.", + "disable_relay": "Disable relay", + "check_save": "Check and save", + "checking": "Checking…", + "relay_imported": "Connection details imported. Check and save to apply them.", + "relay_invalid": "Invalid configuration. Import connection details exported by pebrel-relay, or complete the address, room, tokens and fingerprint.", + "relay_read_failed": "Reading did not complete. Check the SSH connection and that pebrel-relay is installed." } } diff --git a/nebula_app/i18n/zh-CN.json b/nebula_app/i18n/zh-CN.json index d50a96f..16c9681 100644 --- a/nebula_app/i18n/zh-CN.json +++ b/nebula_app/i18n/zh-CN.json @@ -589,6 +589,7 @@ "drag_hint": "拖入文件即可上传;拖到资源管理器即可下载。" }, "hosts": { + "copied": "已创建主机副本:{name}", "search": "搜索名称、地址、分组、标签或备注", "group": "分组", "tags": "标签", @@ -621,7 +622,20 @@ "auth_auto": "自动认证", "auth_password": "密码", "auth_key": "私钥", - "auth_interactive": "交互式认证" + "auth_interactive": "交互式认证", + "list_subtitle": "保存常用主机,一键打开远程终端。密码存在系统凭据管理器里;~/.ssh/config 里的别名也会列出来,Pebrel 只读不改。", + "scope_all": "全部", + "summary": "{total} 台主机,其中 {config} 台来自 ~/.ssh/config", + "hidden_count": "已隐藏 {count} 台", + "collapse_hidden": "收起已隐藏", + "more": "主机操作", + "pin": "置顶", + "unpin": "取消置顶", + "confirm_delete": "确认删除", + "via": "经 {host}", + "connection_time_unknown": "这台主机尚无已记录的连接时间。", + "key_file": "私钥 {name}", + "search_placeholder": "名称、地址、标签" }, "vcs": { "changes": "变更", @@ -812,6 +826,10 @@ "too_large": "背景图超出内存限制,请选择尺寸更小的图片。" }, "update": { + "managed_label": "由安装渠道管理更新", + "managed_scoop": "请在终端执行以下命令更新此安装:scoop update pebrel", + "managed_msix": "此 Windows 包由其安装渠道更新。通过微软商店安装的版本,请打开 Microsoft Store 检查更新。", + "managed_unknown": "尚未识别此安装的来源。请通过原安装渠道更新;此副本已停用普通安装器更新。", "downloading_hint": "正在后台下载并校验适用于此设备的更新安装包。", "no_verified_package": "此版本没有适用于此设备的可验证安装包,请打开发布页手动下载。", "check_fallback_failed": "GitHub API 访问受限(HTTP {status}),备用发布页查询也失败了:{error}。请稍后重试或打开发布页。", @@ -1003,5 +1021,203 @@ "empty": "此目录为空。", "error_retry": "{error}(点右上角重新读取)", "busy_other": "另一主机正在传输:{destination}" + }, + "backup_flow": { + "password_notice": "Pebrel 不把这个密码写到磁盘,只在本次运行期间留在内存里,下次启动后第一次备份时再问。忘了它,已有的备份就解不开了。", + "connect_provider": "连接{provider}", + "address_label": "地址", + "folder_label": "目录", + "password_label": "密码", + "app_password_label": "应用密码", + "access_key_label": "Access Key ID", + "secret_key_label": "Secret Access Key", + "ssh_host_label": "SSH 主机", + "webdav_placeholder": "https://cloud.example.com/remote.php/dav/files/me/pebrel", + "username_placeholder": "用户名", + "nutstore_account_placeholder": "坚果云登录邮箱", + "password_placeholder": "密码或应用专用密码", + "app_password_placeholder": "粘贴应用密码", + "s3_placeholder": "https://s3.amazonaws.com", + "bucket_placeholder": "pebrel-backup", + "access_key_placeholder": "AKIA…", + "secret_key_placeholder": "只在这里输入一次", + "folder_placeholder": "D:\\OneDrive\\Pebrel", + "remote_path_placeholder": "~/pebrel-backup", + "new_password_placeholder": "至少 8 位", + "webdav_address_hint": "以 https:// 开头。自建 NAS 的 http 地址需在备份配置文件中单独允许。", + "app_password_hint": "在坚果云网页端「账户信息 → 安全选项」里生成。", + "s3_address_hint": "R2 形如 https://<账号>.r2.cloudflarestorage.com。", + "ssh_host_hint": "从「SSH」页里已保存的主机中选。", + "export_instead": "暂时不想配置存储,也可以", + "title": "备份与恢复", + "intro": "设置、SSH 主机和历史会先在这台电脑上加密,再存到你选的位置。存储服务只能看到密文。", + "storage": "存储位置", + "connection": "连接", + "choose_storage": "备份存到哪里", + "choose_storage_hint": "用你已经在用的网盘或服务器就行,以后随时可以换。", + "nutstore_hint": "需要坚果云账号和一个「第三方应用密码」,它不是你的登录密码。", + "webdav_hint": "填已有 WebDAV 目录的完整 HTTPS 地址和登录信息。", + "s3_hint": "AWS S3、Cloudflare R2、MinIO 等 S3 兼容存储。", + "sftp_hint": "选择已保存的 SSH 主机和已有远程目录,沿用主机现有的登录方式。", + "folder_hint": "选择已有文件夹,可以是 U 盘、NAS 挂载盘,或网盘的同步目录。", + "set_password": "设置备份密码", + "set_password_hint": "备份上传前用它加密。换电脑恢复时,要输入同一个密码。", + "confirm_password": "再输入一次", + "password_hint": "至少 8 位。密码只在本次运行期间留在内存里,不写到磁盘。下次启动后再问,忘了它就解不开备份。", + "mismatch": "两次输入的密码不一致。", + "choose_contents": "备份哪些内容", + "choose_contents_hint": "默认选了常用的几项。以后每次点「立即备份」时还能再改。", + "excluded": "任何时候都不包含 SSH 私钥,以及存在系统凭据管理器里的密码与 API Key。", + "next": "下一步", + "back": "上一步", + "check_next": "检查连接并继续", + "finish_now": "完成并开始备份", + "finish_later": "完成,稍后备份", + "browse": "浏览…", + "credential_hint": "完成后写入系统凭据管理器;留空则使用已保存的凭据。", + "appearance_hint": "主题、字号、背景图与透明度", + "terminal_hint": "Shell、启动目录与终端配置", + "assistant_hint": "错误修复助手的开关与偏好", + "sync_hint": "云同步的地址与选项,不含存储的密码", + "fonts_hint": "会让每份备份变大", + "hosts_hint": "已保存的主机地址与连接配置", + "session_hint": "窗口、标签页与分屏布局", + "dirs_hint": "最近进入过的目录,会带上项目路径", + "commands_hint": "可能包含粘贴进命令行的令牌", + "settings": "设置", + "data": "数据与历史", + "measuring": "正在计算内容体积…", + "select_one": "至少选一项", + "total": "{count} 项 · 加密前 {size}", + "last_backup": "上次备份", + "latest": "最新", + "snapshot_meta": "{count} 项内容 · {size} · {device}", + "empty": "还没有备份", + "empty_hint": "存储已经配置。点「立即备份」,选好内容就开始。", + "change": "更改", + "connected": "连接正常", + "connection_failed": "连接失败", + "retention": "保留最近 10 份,超出后删除最早的一份", + "encryption": "加密", + "password_ready": "本次已输入", + "password_missing": "本次未输入", + "clear": "清除", + "enter": "输入…", + "files": "文件", + "export_hint": "把选中的内容加密成一个 .pebrel-backup 文件,拷到 U 盘或另一台电脑。", + "import_hint": "选一个 .pebrel-backup 文件,输入它的备份密码后恢复。", + "choose_file": "选择文件…", + "storage_hint": "保存前会先检查一次连接。", + "upload_hint": "加密后上传到所选位置,默认沿用上一次的选择。", + "restore_hint": "解开后会列出这份备份里有什么,可以只恢复其中几项。", + "start": "开始备份", + "switch_hint": "换位置后,这里只列出新位置里的备份。旧位置里的备份不会被删除。", + "read_only_check": "会列出这个位置里的备份,不写入任何文件。", + "defaults": "恢复默认选择", + "unselected_untouched": "只恢复勾选的内容,没勾选的不会动。", + "restore_warning": "恢复会覆盖本机对应的设置。开始前会把当前文件加密另存一份,恢复后可以撤回。", + "unlock": "解开", + "processing": "处理中…", + "restored": "已从备份恢复。恢复前的文件已另存一份。", + "undo": "撤回", + "undone": "已撤回恢复,原文件已还原。", + "selection_save_failed": "备份已完成,但保存内容选择失败:{error}" + }, + "mobile": { + "title": "手机远程", + "subtitle": "在手机上看终端里正在跑的任务,结束或卡住时收到提醒,需要时回一句指令。电脑是唯一的执行者,手机只发请求。", + "off_title": "手机远程已关闭", + "off_description": "开启后,Pebrel 会为这台电脑准备一个配对二维码。只有你扫码并在这里批准的手机才能连上。", + "enable": "开启并配对手机", + "pair_title": "用手机扫码配对", + "pair_description": "二维码只能用一次,10 分钟后失效。", + "lan": "局域网 / Tailscale", + "relay": "自建中转", + "step_open": "在手机上打开 Pebrel", + "step_scan": "点「连接电脑」,扫右边的二维码", + "step_approve": "核对两端的校验码,回到这里批准这台手机", + "step_relay": "手机和电脑使用同一台中转服务器", + "same_network": "和电脑连同一个 Wi-Fi", + "relay_outbound": "两端都主动连接中转,电脑不用开端口", + "qr_unavailable": "等待生成配对码", + "expires": "{time} 后失效", + "expired": "配对码已失效", + "refresh_qr": "换一个", + "manual_pair": "复制完整配对信息", + "short_code_prompt": "扫不了?输入", + "short_code_used": "短码已使用或锁定,换一个即可重新生成。", + "discovery_unavailable": "当前网络发现未就绪,请扫码或粘贴完整配对信息。", + "copied": "已复制", + "request_title": "{name} 请求配对", + "request_description": "刚刚扫码 · {route} {peer} · 请核对手机上的校验码 {code}", + "deny": "拒绝", + "approve": "批准", + "approving": "正在配对…", + "devices": "已配对的手机", + "add_phone": "添加手机", + "no_devices": "还没有配对的手机。扫码并批准后,会显示在这里。", + "read_only": "只看", + "allow_input": "允许输入", + "online": "在线", + "offline": "离线", + "remove": "移除", + "remove_title": "移除 {name}?", + "remove_description": "这台手机会立即断开,原来的授权同时失效。下次连接需要重新扫码并批准。", + "connections": "连接方式", + "connections_hint": "两种可以同时开,手机优先走局域网", + "lan_direct": "局域网 / Tailscale 直连", + "lan_hint": "通过同一局域网或 Tailscale 网络直连,无需中转服务器。使用 Tailscale 时,两台设备需加入同一个 Tailnet。", + "interface": "网卡", + "interface_hint": "选择手机可达的局域网或 Tailscale 地址。换网后点刷新。", + "refresh_addresses": "刷新网卡", + "port": "监听端口", + "port_hint": "留空自动选。电脑开了防火墙时,固定一个端口更好放行。", + "automatic": "自动", + "relay_hint": "跨网络时,通过你自己的中转服务器连接。", + "configure_relay": "配置中转…", + "manage_relay": "管理…", + "permissions": "权限与提醒", + "default_permission": "新手机的默认权限", + "default_permission_hint": "「只看」的手机不向终端输入任何内容。每台手机的权限在上面单独改。", + "notifications": "任务结束时提醒", + "notifications_hint": "Agent 跑完、命令退出或停下来等你确认时,手机收到一条通知。", + "pause_title": "暂停手机远程", + "pause_hint": "断开所有手机并停止监听,配对记录保留,重新开启后不用再扫码。", + "pause": "暂停", + "starting": "正在准备…", + "waiting": "等待手机连接", + "connected": "已连接", + "reconnecting": "正在重新连接…", + "stopped": "已暂停", + "invalid": "连接信息有误。请检查地址、端口和中转配置后重试。", + "address_failed": "当前网卡地址不可用。请刷新网卡并选择已连接的网络。", + "port_failed": "这个端口已被占用。换一个端口或留空自动选择。", + "credentials_failed": "系统凭据存储未完成,设置没有保存。请解锁系统凭据管理器后重试。", + "connection_failed": "连接未建立。请检查网络或中转配置后重试。", + "cancelled": "操作已取消", + "relay_drawer_subtitle": "手机和电脑都主动连这台服务器,电脑不用开端口", + "close": "关闭", + "read_server": "从服务器读取", + "read_server_hint": "选一台装过 pebrel-relay 的 SSH 主机,用 SSH 读出地址和令牌,只读不改。还没装的话,在手机 App 的「服务器」卡片里安装。", + "read": "读取", + "reading": "正在读取…", + "connection_info": "连接信息", + "import_clipboard": "从剪贴板导入", + "import_file": "从文件导入", + "relay_url": "服务器地址", + "relay_room": "房间号", + "relay_room_hint": "这台电脑在中转上的编号,每台电脑一个。", + "desktop_token": "电脑令牌", + "phone_token": "手机令牌", + "relay_tokens_hint": "令牌保存到系统凭据管理器。手机令牌会写进配对二维码。", + "tls_pin": "证书指纹", + "relay_pin_hint": "填入安装时服务器打印的 SHA-256 指纹,连接时核对证书。", + "encrypted_hint": "手机和电脑之间先做端到端加密,中转只转发密文,看不到终端内容。", + "disable_relay": "停用中转", + "check_save": "检查并保存", + "checking": "正在检查…", + "relay_imported": "已导入连接信息。检查并保存后生效。", + "relay_invalid": "配置格式有误。请导入 pebrel-relay 导出的连接信息,或补全地址、房间号、令牌和指纹。", + "relay_read_failed": "读取未完成。请检查 SSH 主机连接,以及服务器是否已安装 pebrel-relay。" } } diff --git a/nebula_app/src/ai_hook/lifecycle.rs b/nebula_app/src/ai_hook/lifecycle.rs index 9f415eb..648647f 100644 --- a/nebula_app/src/ai_hook/lifecycle.rs +++ b/nebula_app/src/ai_hook/lifecycle.rs @@ -121,6 +121,16 @@ impl AgentActivity { self.input_sent(); } + pub fn submitted_text(&mut self, program: &str, text: &str) { + // Codex /plan 只切换交互模式,不触发任务 hook;若先制造 Running, + // 对话输入会一直等一个不存在的完成事件。普通任务仍保留提交屏障。 + if program == "codex" && text.trim() == "/plan" { + self.input_sent(); + } else { + self.submitted(); + } + } + /// Physical Enter alone can also dismiss a menu. It only permits observing a /// new turn; it is not itself a successful submission or a completion. pub fn input_sent(&mut self) { diff --git a/nebula_app/src/ai_hook/lifecycle/tests.rs b/nebula_app/src/ai_hook/lifecycle/tests.rs index 97fb17c..8846569 100644 --- a/nebula_app/src/ai_hook/lifecycle/tests.rs +++ b/nebula_app/src/ai_hook/lifecycle/tests.rs @@ -150,6 +150,42 @@ fn runtime_submission_does_not_finish_on_the_previous_idle_frame() { assert_eq!(activity.status(), AgentStatus::Done); } +#[test] +fn local_mode_command_keeps_activity_and_real_prompts_keep_the_submission_barrier() { + for native_hook in [false, true] { + let mut activity = AgentActivity::default(); + if native_hook { + let wire = b"nebula-hook/1 source=codex codex_hooks=full\n{\"hook_event_name\":\"SessionStart\",\"session_id\":\"s\"}"; + let event = parse_remote_envelope(wire, Some(1)).unwrap(); + activity.apply_hook(&event); + } else { + activity.begin_command(true); + for _ in 0..2 { + activity.observe_screen(screen(AgentStatus::Idle)); + } + } + let source = activity.source(); + activity.submitted_text("codex", "/plan"); + assert_eq!(activity.status(), AgentStatus::Idle); + assert_eq!(activity.source(), source); + assert!(!activity.pending_submit); + activity.submitted_text("codex", "请问我一个选择题"); + for _ in 0..8 { + activity.observe_screen(screen(AgentStatus::Idle)); + } + assert_eq!(activity.status(), AgentStatus::Working); + assert!(activity.pending_submit); + activity.submitted_text("codex", "/plan"); + assert_eq!(activity.status(), AgentStatus::Working, "a mode switch cannot finish work"); + } + for (program, text) in [("claude", "/plan"), ("codex", "/review"), ("codex", "/plan\nmy task")] + { + let mut activity = AgentActivity::default(); + activity.submitted_text(program, text); + assert!(activity.pending_submit, "{program}: {text}"); + } +} + #[test] fn nested_local_or_remote_sessions_cannot_replace_primary_ownership() { for pid in [None, Some(10)] { diff --git a/nebula_app/src/assistant_answer.rs b/nebula_app/src/assistant_answer.rs index c5f9ab6..6acb6ad 100644 --- a/nebula_app/src/assistant_answer.rs +++ b/nebula_app/src/assistant_answer.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use serde_json::Value; +pub(crate) mod conversation; pub mod document; pub const MAX_ANSWER_BYTES: usize = 128 * 1024; diff --git a/nebula_app/src/assistant_answer/conversation.rs b/nebula_app/src/assistant_answer/conversation.rs new file mode 100644 index 0000000..4bdc394 --- /dev/null +++ b/nebula_app/src/assistant_answer/conversation.rs @@ -0,0 +1,645 @@ +//! Bounded native transcript projection. No history scan or screen-to-message guessing. + +use std::{ + collections::HashMap, + fs::File, + io::{Read, Seek, SeekFrom}, + path::Path, +}; + +use base64::Engine as _; +use serde::Serialize; +use serde_json::{Value, json}; +use sha2::{Digest, Sha256}; + +use crate::{ + runtime_api::{ApiError, conversation::Identity}, + runtime_exec::PaneExecContext, +}; + +const HEAD_BYTES: usize = 256 * 1024; +const PAGE_BYTES: usize = 1024 * 1024; +const MESSAGE_BYTES: usize = 48 * 1024; +const OUTPUT_BYTES: usize = 128 * 1024; +const MESSAGE_COUNT: usize = 160; + +mod remote; + +pub(crate) enum Location { + Local(PaneExecContext), + Ssh(crate::ssh_session::TranscriptReader), +} + +pub(crate) struct Source { + pub identity: Identity, + pub path: String, + pub location: Location, + pub cwd: String, +} + +#[derive(Clone, Debug, Serialize)] +pub(crate) struct Message { + pub id: String, + pub role: &'static str, + pub text: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub detail: Option, + pub complete: bool, + pub truncated: bool, + #[serde(skip)] + paired: bool, + #[serde(skip)] + event: bool, +} + +struct Capture { + head: Vec, + bytes: Vec, + start: u64, + end: u64, + stamp: String, +} + +pub(crate) fn read( + source: Source, + before: Option, + previous: Option<&str>, +) -> Result { + let capture = match &source.location { + Location::Ssh(reader) => remote::capture(reader, &source.path, before)?, + Location::Local(context) if context.wsl_distribution().is_some() => { + capture_wsl(&source, context, before)? + }, + Location::Local(_) => capture_file(Path::new(&source.path), before)?, + }; + validate_source(&source.identity, &source.path, &capture.head)?; + let mut hash = Sha256::new(); + hash.update(capture.stamp.as_bytes()); + hash.update(&capture.bytes); + hash.update(capture.start.to_le_bytes()); + let revision = hash.finalize().iter().map(|byte| format!("{byte:02x}")).collect::(); + if previous == Some(revision.as_str()) { + return Ok(json!({"revision": revision, "unchanged": true})); + } + let (messages, cursor, truncated) = project(&source.identity, &capture)?; + Ok(json!({ + "revision": revision, "unchanged": false, "messages": messages, + "before": cursor, "truncated": truncated, "cwd": source.cwd, + })) +} + +fn io_failure(_: impl std::fmt::Display) -> ApiError { + // 不把本地会话路径、原文或系统错误中的私人信息转发给手机。 + ApiError::new("conversation_unavailable", "the native conversation file is not readable") +} + +fn capture_file(path: &Path, before: Option) -> Result { + let mut file = File::open(path).map_err(io_failure)?; + let meta = file.metadata().map_err(io_failure)?; + if !meta.is_file() { + return Err(io_failure("not a file")); + } + let end = before.unwrap_or(meta.len()); + if end > meta.len() { + return Err(changed()); + } + let start = end.saturating_sub(PAGE_BYTES as u64); + let mut head = Vec::new(); + (&mut file).take(HEAD_BYTES as u64).read_to_end(&mut head).map_err(io_failure)?; + file.seek(SeekFrom::Start(start.saturating_sub(1))).map_err(io_failure)?; + let mut bytes = Vec::new(); + (&mut file).take(end - start.saturating_sub(1)).read_to_end(&mut bytes).map_err(io_failure)?; + let after = file.metadata().map_err(io_failure)?; + if meta.len() != after.len() || meta.modified().ok() != after.modified().ok() { + return Err(changed()); + } + Ok(Capture { + head, + bytes, + start, + end, + stamp: format!("{}:{:?}", meta.len(), meta.modified().ok()), + }) +} + +fn capture_wsl( + source: &Source, + context: &PaneExecContext, + before: Option, +) -> Result { + // 路径只作为 argv 传入,脚本固定;在所属发行版中读取,避免 9P/UNC 与用户映射差异。 + const SCRIPT: &str = r#"set -eu +p=$1 +[ -f "$p" ] +size=$(stat -c %s -- "$p") +stamp=$(stat -c '%i:%s:%y:%z' -- "$p") +end=${2:-$size} +[ "$end" -le "$size" ] +start=$((end > 1048576 ? end - 1048576 : 0)) +readstart=$((start > 0 ? start - 1 : 0)) +printf '%s\n%s\n' "$end" "$stamp" +head -c 262144 -- "$p" | base64 -w 0 +printf '\n' +tail -c +$((readstart + 1)) -- "$p" | head -c $((end - readstart)) | base64 -w 0 +printf '\n%s\n' "$(stat -c '%i:%s:%y:%z' -- "$p")" +"#; + let result = crate::runtime_exec::execute( + context.clone(), + source.cwd.clone(), + vec![ + "sh".into(), + "-c".into(), + SCRIPT.into(), + "pebrel-conversation".into(), + source.path.clone(), + before.map(|value| value.to_string()).unwrap_or_default(), + ], + 5000, + 2 * 1024 * 1024, + )?; + if result["success"] != true || result["capture"]["stdout"]["truncated"] != false { + return Err(io_failure("guest read failed")); + } + let mut lines = result["stdout"].as_str().unwrap_or_default().lines(); + let end = + lines.next().and_then(|s| s.parse::().ok()).ok_or_else(|| io_failure("metadata"))?; + let stamp = lines.next().ok_or_else(|| io_failure("metadata"))?.to_owned(); + let base64 = base64::engine::general_purpose::STANDARD; + let head = base64.decode(lines.next().unwrap_or_default()).map_err(io_failure)?; + let bytes = base64.decode(lines.next().unwrap_or_default()).map_err(io_failure)?; + if lines.next() != Some(stamp.as_str()) { + return Err(changed()); + } + Ok(Capture { head, bytes, start: end.saturating_sub(PAGE_BYTES as u64), end, stamp }) +} + +fn changed() -> ApiError { + ApiError::new( + "conversation_changed", + "the transcript changed while reading; refresh its current page", + ) +} + +fn validate_source(identity: &Identity, path: &str, head: &[u8]) -> Result<(), ApiError> { + let valid = match identity.kind.as_str() { + "codex" => head + .split(|b| *b == b'\n') + .next() + .and_then(|line| serde_json::from_slice::(line).ok()) + .is_some_and(|record| { + record["type"] == "session_meta" && record["payload"]["id"] == identity.session_id + }), + "claude" => { + path.rsplit(['/', '\\']).next().and_then(|name| name.strip_suffix(".jsonl")) + == Some(identity.session_id.as_str()) + }, + _ => false, + }; + if valid { + Ok(()) + } else { + Err(ApiError::new( + "conversation_identity_changed", + "the native file belongs to a different conversation", + )) + } +} + +fn project( + identity: &Identity, + capture: &Capture, +) -> Result<(Vec, Option, bool), ApiError> { + let mut bytes = capture.bytes.as_slice(); + let mut offset = capture.start; + if capture.start > 0 { + if bytes.first() == Some(&b'\n') { + bytes = &bytes[1..]; + } else if let Some(end) = bytes.iter().position(|b| *b == b'\n') { + bytes = &bytes[end + 1..]; + offset = capture.start - 1 + end as u64 + 1; + } else { + return Ok((Vec::new(), Some(capture.start), true)); + } + } + let cursor = (offset > 0).then_some(offset); + let mut messages = Vec::new(); + let mut calls = HashMap::new(); + let mut truncated = false; + for line in bytes.split_inclusive(|b| *b == b'\n') { + let id = offset.to_string(); + offset += line.len() as u64; + // CLI 正在追加的最后半行等待下次读取,不把不完整 JSON 当作对话内容。 + if !line.ends_with(b"\n") { + break; + } + let Ok(record) = serde_json::from_slice::(line) else { + truncated = true; + continue; + }; + if identity.kind == "codex" { + codex(&record, &id, &mut messages, &mut calls); + } else { + claude(&record, identity, &id, &mut messages, &mut calls)?; + } + } + let mut kept = 0; + let mut first = messages.len(); + let length = messages.len(); + for (index, message) in messages.iter_mut().enumerate().rev() { + message.truncated |= trim(&mut message.text, MESSAGE_BYTES); + if let Some(detail) = &mut message.detail { + message.truncated |= trim(detail, MESSAGE_BYTES); + } + let size = message.text.len() + message.detail.as_ref().map_or(0, String::len); + if kept + size > OUTPUT_BYTES || length - first >= MESSAGE_COUNT { + break; + } + kept += size; + first = index; + } + let next = if first > 0 { + truncated = true; + messages.get(first).and_then(|m| m.id.split(':').next()?.parse().ok()).or(cursor) + } else { + cursor + }; + messages.drain(..first); + Ok((messages, next.filter(|before| *before < capture.end), truncated)) +} + +fn trim(text: &mut String, maximum: usize) -> bool { + if text.len() <= maximum { + return false; + } + let mut end = maximum; + while !text.is_char_boundary(end) { + end -= 1; + } + text.truncate(end); + true +} + +fn text_content(value: &Value) -> String { + if let Some(text) = value.as_str() { + return text.to_owned(); + } + value + .as_array() + .map(|parts| { + parts + .iter() + .filter_map(|part| { + matches!( + part["type"].as_str(), + Some("text" | "Text" | "input_text" | "output_text") + ) + .then(|| part["text"].as_str()) + .flatten() + }) + .collect::>() + .join("\n") + }) + .unwrap_or_default() +} + +fn push_message( + messages: &mut Vec, + id: String, + role: &'static str, + text: String, + event: bool, +) { + if text.trim().is_empty() { + return; + } + // Codex 同时落 response_item 与 event_msg;只合并互为副本的两种来源,保留用户重复发送。 + if let Some(previous) = messages + .iter_mut() + .rev() + .find(|m| m.role == role && m.text == text && m.event != event && !m.paired) + { + previous.paired = true; + return; + } + messages.push(Message { + id, + role, + text, + name: None, + detail: None, + complete: true, + truncated: false, + paired: false, + event, + }); +} + +fn tool( + messages: &mut Vec, + calls: &mut HashMap, + id: String, + call: &str, + name: &str, + input: String, +) { + calls.insert(call.to_owned(), messages.len()); + messages.push(Message { + id, + role: "tool", + text: input, + name: Some(name.to_owned()), + detail: None, + complete: false, + truncated: false, + paired: false, + event: false, + }); +} + +fn result( + messages: &mut Vec, + calls: &HashMap, + id: String, + call: &str, + output: String, +) { + if let Some(message) = calls.get(call).and_then(|index| messages.get_mut(*index)) { + message.detail = Some(output); + message.complete = true; + } else { + messages.push(Message { + id, + role: "tool", + text: String::new(), + name: None, + detail: Some(output), + complete: true, + truncated: false, + paired: false, + event: false, + }); + } +} + +fn codex( + record: &Value, + id: &str, + messages: &mut Vec, + calls: &mut HashMap, +) { + let p = &record["payload"]; + match (record["type"].as_str(), p["type"].as_str()) { + (Some("response_item"), Some("message")) + if p["role"] == "assistant" && p["channel"] != "analysis" => + { + // 原始 user 记录还包含环境和 AGENTS 上下文;真实输入以用户消息事件为准, + // 不按正文前缀过滤,避免误删用户主动发送的 XML 或 Markdown。 + push_message(messages, id.into(), "assistant", text_content(&p["content"]), false); + }, + (Some("event_msg"), Some("item_completed")) => { + let item = &p["item"]; + let role = match item["type"].as_str() { + Some("UserMessage") => "user", + Some("AgentMessage") if item["phase"] != "analysis" => "assistant", + _ => return, + }; + push_message(messages, id.into(), role, text_content(&item["content"]), true); + }, + (Some("event_msg"), Some("user_message")) => push_message( + messages, + id.into(), + "user", + p["message"].as_str().unwrap_or_default().into(), + true, + ), + (Some("event_msg"), Some("agent_message")) => push_message( + messages, + id.into(), + "assistant", + p["message"].as_str().unwrap_or_default().into(), + true, + ), + (Some("response_item"), Some("function_call" | "custom_tool_call")) => { + tool( + messages, + calls, + id.into(), + p["call_id"].as_str().unwrap_or(id), + p["name"].as_str().unwrap_or("tool"), + p["arguments"].as_str().or_else(|| p["input"].as_str()).unwrap_or_default().into(), + ); + }, + (Some("response_item"), Some("function_call_output" | "custom_tool_call_output")) => { + result( + messages, + calls, + id.into(), + p["call_id"].as_str().unwrap_or_default(), + text_content(&p["output"]), + ); + }, + _ => {}, + } +} + +fn claude( + record: &Value, + identity: &Identity, + id: &str, + messages: &mut Vec, + calls: &mut HashMap, +) -> Result<(), ApiError> { + let role = match record["type"].as_str() { + Some("user") => "user", + Some("assistant") => "assistant", + _ => return Ok(()), + }; + if record["isSidechain"] == true { + return Ok(()); + } + if record["sessionId"] != identity.session_id { + return Err(ApiError::new( + "conversation_identity_changed", + "a transcript record belongs to another session", + )); + } + let content = &record["message"]["content"]; + if let Some(text) = content.as_str() { + push_message(messages, id.into(), role, text.into(), false); + } + if let Some(parts) = content.as_array() { + for (index, part) in parts.iter().enumerate() { + let block_id = format!("{id}:{index}"); + match part["type"].as_str() { + Some("text") => push_message( + messages, + block_id, + role, + part["text"].as_str().unwrap_or_default().into(), + false, + ), + Some("tool_use") => tool( + messages, + calls, + block_id, + part["id"].as_str().unwrap_or(id), + part["name"].as_str().unwrap_or("tool"), + serde_json::to_string_pretty(&part["input"]).unwrap_or_default(), + ), + Some("tool_result") => result( + messages, + calls, + block_id, + part["tool_use_id"].as_str().unwrap_or_default(), + text_content(&part["content"]), + ), + _ => {}, + } + } + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Write; + + fn identity(kind: &str) -> Identity { + Identity { + kind: kind.into(), + session_id: "0199a213-c2a4-7cf5-8f6b-d746fbb6e86c".into(), + epoch: Some(7), + } + } + + fn fixture(records: &[Value]) -> (tempfile::TempDir, std::path::PathBuf) { + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join(format!("{}.jsonl", identity("claude").session_id)); + let mut file = File::create(&path).unwrap(); + for record in records { + writeln!(file, "{record}").unwrap(); + } + (directory, path) + } + + #[test] + fn native_codex_messages_deduplicate_events_and_keep_tools_without_reasoning() { + let identity = identity("codex"); + let (_dir, path) = fixture(&[ + json!({"type":"session_meta","payload":{"id":identity.session_id}}), + json!({"type":"event_msg","payload":{"type":"user_message","message":"中文测试🙂"}}), + json!({"type":"response_item","payload":{"type":"message","role":"user","content":[{"type":"input_text","text":"中文测试🙂"}]}}), + json!({"type":"response_item","payload":{"type":"reasoning","summary":[{"text":"private reasoning"}]}}), + json!({"type":"response_item","payload":{"type":"function_call","name":"exec","call_id":"tool-1","arguments":"{\"cmd\":\"pwd\"}"}}), + json!({"type":"response_item","payload":{"type":"function_call_output","call_id":"tool-1","output":"/project"}}), + json!({"type":"response_item","payload":{"type":"message","role":"assistant","content":[{"type":"output_text","text":"## 回答\n`done`"}]}}), + json!({"type":"event_msg","payload":{"type":"agent_message","message":"## 回答\n`done`"}}), + json!({"type":"event_msg","payload":{"type":"user_message","message":"中文测试🙂"}}), + ]); + let capture = capture_file(&path, None).unwrap(); + validate_source(&identity, path.to_str().unwrap(), &capture.head).unwrap(); + let (messages, before, _) = project(&identity, &capture).unwrap(); + assert_eq!(messages.len(), 4); + assert_eq!(messages[0].text, "中文测试🙂"); + assert_eq!(messages[1].detail.as_deref(), Some("/project")); + assert!(messages[1].complete); + assert_eq!(messages[2].role, "assistant"); + assert!(before.is_none()); + assert!(!serde_json::to_string(&messages).unwrap().contains("private reasoning")); + let mut other = identity.clone(); + other.session_id = "another-thread".into(); + assert!(validate_source(&other, path.to_str().unwrap(), &capture.head).is_err()); + } + + #[test] + fn codex_completed_items_hide_injected_context_but_preserve_real_markup_and_repeats() { + let identity = identity("codex"); + let input = "# AGENTS.md instructions\n用户正文"; + let (_dir, path) = fixture(&[ + json!({"type":"session_meta","payload":{"id":identity.session_id}}), + json!({"type":"response_item","payload":{"type":"message","role":"user","content":[{"type":"input_text","text":"internal fixture context"}]}}), + json!({"type":"response_item","payload":{"type":"message","role":"user","content":[{"type":"input_text","text":input}]}}), + json!({"type":"event_msg","payload":{"type":"item_completed","item":{"type":"UserMessage","content":[{"type":"text","text":input}]}}}), + json!({"type":"event_msg","payload":{"type":"item_completed","item":{"type":"Reasoning","raw_content":["private reasoning"]}}}), + json!({"type":"event_msg","payload":{"type":"item_completed","item":{"type":"AgentMessage","phase":"final_answer","content":[{"type":"Text","text":"## 回答"}]}}}), + json!({"type":"response_item","payload":{"type":"message","role":"assistant","content":[{"type":"output_text","text":"## 回答"}]}}), + json!({"type":"response_item","payload":{"type":"message","role":"user","content":[{"type":"input_text","text":input}]}}), + json!({"type":"event_msg","payload":{"type":"item_completed","item":{"type":"UserMessage","content":[{"type":"text","text":input}]}}}), + ]); + let capture = capture_file(&path, None).unwrap(); + let (messages, _, truncated) = project(&identity, &capture).unwrap(); + assert!(!truncated); + assert_eq!(messages.len(), 3); + assert_eq!(messages[0].role, "user"); + assert_eq!(messages[0].text, input); + assert_eq!(messages[1].role, "assistant"); + assert_eq!(messages[1].text, "## 回答"); + assert_eq!(messages[2].text, input); + assert_ne!(messages[0].id, messages[2].id); + } + + #[test] + fn codex_legacy_user_events_do_not_promote_raw_context_or_analysis() { + let identity = identity("codex"); + let (_dir, path) = fixture(&[ + json!({"type":"session_meta","payload":{"id":identity.session_id}}), + json!({"type":"response_item","payload":{"type":"message","role":"user","content":[{"type":"input_text","text":"internal context"}]}}), + json!({"type":"event_msg","payload":{"type":"user_message","message":"real question"}}), + json!({"type":"response_item","payload":{"type":"message","role":"assistant","channel":"analysis","content":[{"type":"output_text","text":"private reasoning"}]}}), + json!({"type":"event_msg","payload":{"type":"item_completed","item":{"type":"AgentMessage","phase":"analysis","content":[{"type":"Text","text":"private reasoning"}]}}}), + json!({"type":"event_msg","payload":{"type":"agent_message","message":"answer"}}), + ]); + let capture = capture_file(&path, None).unwrap(); + let (messages, _, _) = project(&identity, &capture).unwrap(); + assert_eq!( + messages.iter().map(|m| m.text.as_str()).collect::>(), + ["real question", "answer"] + ); + } + + #[test] + fn claude_records_require_the_owner_and_partial_writes_wait_for_a_newline() { + let identity = identity("claude"); + let (_dir, path) = fixture(&[ + json!({"type":"user","sessionId":identity.session_id,"message":{"content":"question"}}), + json!({"type":"assistant","sessionId":identity.session_id,"message":{"content":[{"type":"text","text":"answer"},{"type":"tool_use","id":"x","name":"Read","input":{"path":"src/main.rs"}}]}}), + json!({"type":"user","sessionId":identity.session_id,"message":{"content":[{"type":"tool_result","tool_use_id":"x","content":[{"type":"text","text":"file text"}]}]}}), + ]); + File::options() + .append(true) + .open(&path) + .unwrap() + .write_all(b"{\"type\":\"assistant\"") + .unwrap(); + let capture = capture_file(&path, None).unwrap(); + let (messages, _, _) = project(&identity, &capture).unwrap(); + assert_eq!(messages.len(), 3); + assert_eq!(messages[2].detail.as_deref(), Some("file text")); + let mut other = identity.clone(); + other.session_id = "other".into(); + assert!(project(&other, &capture).is_err()); + assert!(validate_source(&other, path.to_str().unwrap(), &capture.head).is_err()); + } + + #[test] + fn native_pages_have_utf8_and_history_budgets_and_epoch_checks_are_exact() { + let identity = identity("codex"); + let text = "中🙂".repeat(100_000); + let (_dir, path) = fixture(&[ + json!({"type":"session_meta","payload":{"id":identity.session_id}}), + json!({"type":"response_item","payload":{"type":"message","role":"assistant","content":[{"type":"output_text","text":text}]}}), + ]); + let capture = capture_file(&path, None).unwrap(); + let (messages, _, _) = project(&identity, &capture).unwrap(); + assert!(messages[0].truncated); + assert!(messages[0].text.len() <= MESSAGE_BYTES); + assert!(text.starts_with(&messages[0].text)); + assert!(identity.matches(&identity)); + assert!(!identity.matches(&Identity { epoch: Some(8), ..identity.clone() })); + assert!(Identity { epoch: None, ..identity.clone() }.matches(&identity)); + assert!(capture_file(&path, Some(capture.end + 1)).is_err()); + } +} diff --git a/nebula_app/src/assistant_answer/conversation/remote.rs b/nebula_app/src/assistant_answer/conversation/remote.rs new file mode 100644 index 0000000..b6a7b59 --- /dev/null +++ b/nebula_app/src/assistant_answer/conversation/remote.rs @@ -0,0 +1,93 @@ +use base64::Engine as _; +use serde_json::{Value, json}; + +use super::{Capture, HEAD_BYTES, PAGE_BYTES, changed, io_failure}; +use crate::runtime_api::ApiError; +use crate::ssh_session::TranscriptReader; + +const SCRIPT: &str = include_str!("remote_read.py"); + +pub(super) fn capture( + reader: &TranscriptReader, + path: &str, + before: Option, +) -> Result { + let base64 = base64::engine::general_purpose::STANDARD; + let argument = base64.encode(json!({"path": path, "before": before}).to_string()); + let script = format!("{SCRIPT}\nmain(json.loads(base64.b64decode('{argument}')))\n"); + let raw = crate::ssh_session::runtime() + .map_err(io_failure)? + .block_on(reader.capture(script.as_bytes())) + .map_err(io_failure)?; + decode(&raw, before) +} + +fn decode(raw: &str, before: Option) -> Result { + if raw.len() > 2 * 1024 * 1024 { + return Err(io_failure("response budget")); + } + let line = raw + .lines() + .rev() + .find_map(|line| line.strip_prefix("PEBREL_TRANSCRIPT=")) + .ok_or_else(|| io_failure("response"))?; + let value: Value = serde_json::from_str(line).map_err(io_failure)?; + if value["version"] != 1 { + return Err(io_failure("version")); + } + if value["error"] == "conversation_changed" { + return Err(changed()); + } + if value.get("error").is_some() { + return Err(io_failure("remote file")); + } + let end = value["end"].as_u64().ok_or_else(|| io_failure("end"))?; + let start = value["start"].as_u64().ok_or_else(|| io_failure("start"))?; + if before.is_some_and(|before| end != before) || start != end.saturating_sub(PAGE_BYTES as u64) + { + return Err(changed()); + } + let base64 = base64::engine::general_purpose::STANDARD; + let head = base64 + .decode(value["head"].as_str().ok_or_else(|| io_failure("head"))?) + .map_err(io_failure)?; + let bytes = base64 + .decode(value["bytes"].as_str().ok_or_else(|| io_failure("bytes"))?) + .map_err(io_failure)?; + let stamp = value["stamp"] + .as_str() + .filter(|stamp| stamp.len() <= 256) + .ok_or_else(|| io_failure("stamp"))? + .to_owned(); + if head.len() > HEAD_BYTES + || bytes.len() > PAGE_BYTES + 1 + || bytes.len() as u64 != end - start.saturating_sub(1) + { + return Err(io_failure("byte range")); + } + Ok(Capture { head, bytes, start, end, stamp }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ssh_capture_checks_revision_shape_and_does_not_accept_unbounded_bytes() { + let response = |value: Value| format!("banner\nPEBREL_TRANSCRIPT={value}\n"); + let valid = + json!({"version":1,"start":0,"end":3,"head":"YWJj","bytes":"YWJj","stamp":"owner"}); + assert_eq!(decode(&response(valid.clone()), None).unwrap().bytes, b"abc"); + assert!(decode(&response(valid.clone()), Some(4)).is_err()); + let mut invalid = valid; + invalid["end"] = json!(5); + assert!(decode(&response(invalid), None).is_err()); + assert_eq!( + decode(&response(json!({"version":1,"error":"conversation_changed"})), None) + .err() + .unwrap() + .code, + "conversation_changed" + ); + } +} diff --git a/nebula_app/src/assistant_answer/conversation/remote_read.py b/nebula_app/src/assistant_answer/conversation/remote_read.py new file mode 100644 index 0000000..bc8e53a --- /dev/null +++ b/nebula_app/src/assistant_answer/conversation/remote_read.py @@ -0,0 +1,44 @@ +"""Bounded native transcript bytes over an already authenticated SSH channel.""" +import base64 +import json +import os +import stat + + +def stamp(info): + return (info.st_dev, info.st_ino, info.st_size, info.st_mtime_ns, info.st_ctime_ns) + + +def capture(request): + path = request["path"] + if not isinstance(path, str) or not path.startswith("/") or len(path.encode("utf-8")) > 4096: + raise ValueError("invalid_path") + with open(path, "rb") as file: + before = os.fstat(file.fileno()) + if not stat.S_ISREG(before.st_mode): + raise ValueError("invalid_file") + end = request.get("before") + if end is None: + end = before.st_size + if not isinstance(end, int) or end < 0 or end > before.st_size: + return {"error": "conversation_changed"} + start = max(0, end - 1024 * 1024) + read_start = max(0, start - 1) + head = file.read(256 * 1024) + file.seek(read_start) + data = file.read(end - read_start) + # 同时核对打开的句柄和路径,替换/截短中的记录不伪装成稳定的一页。 + if stamp(before) != stamp(os.fstat(file.fileno())) or stamp(before) != stamp(os.stat(path)): + return {"error": "conversation_changed"} + return {"head": base64.b64encode(head).decode("ascii"), + "bytes": base64.b64encode(data).decode("ascii"), "start": start, "end": end, + "stamp": ":".join(map(str, stamp(before)))} + + +def main(request): + try: + result = capture(request) + except (OSError, ValueError, KeyError, TypeError): + result = {"error": "conversation_unavailable"} + result["version"] = 1 + print("PEBREL_TRANSCRIPT=" + json.dumps(result, separators=(",", ":"))) diff --git a/nebula_app/src/backup_remote.rs b/nebula_app/src/backup_remote.rs index 7542632..7ce3709 100644 --- a/nebula_app/src/backup_remote.rs +++ b/nebula_app/src/backup_remote.rs @@ -1,4 +1,4 @@ -//! 多协议远程备份:把 [`crate::encrypted_backup`] 的加密归档(NEBUBAK1, +//! 多协议远程备份:把 [`crate::encrypted_backup`] 的加密归档(PEBRBAK1, //! Argon2id + AES-256-GCM,服务器只见密文)推到远端,或取回最新一份恢复。 //! //! 协议后端: @@ -17,7 +17,9 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use log::warn; +mod listing; pub(crate) mod preferences; +pub(crate) use listing::Snapshot; /// 远程备份配置文件(位于 `nebula_data_dir()`),独立于 `nebula_sync.txt`: /// 备份管道自身的配置不该被备份恢复覆盖到不可用。 @@ -26,7 +28,6 @@ const CONFIG_FILE: &str = "pebrel_backup.txt"; /// 归档文件名:`pebrel-backup-YYYYMMDD-HHMMSS.nbk`(UTC)。前后缀是列表 /// 过滤的安全边界——清理绝不会碰远端目录里不带这两段的文件。 const ARCHIVE_PREFIX: &str = "pebrel-backup-"; -const LEGACY_ARCHIVE_PREFIX: &str = "nebula-backup-"; const ARCHIVE_SUFFIX: &str = ".nbk"; /// 每个远端保留的归档份数。 @@ -34,9 +35,9 @@ pub(crate) const KEEP_ARCHIVES: usize = 10; /// Windows 凭据管理器条目(与 `sync.rs` 的通用 DPAPI 存取同一后端)。 #[cfg(windows)] -const WEBDAV_PASSWORD_TARGET: &str = "Nebula Backup WebDAV Password"; +const WEBDAV_PASSWORD_TARGET: &str = "Pebrel Backup WebDAV Password"; #[cfg(windows)] -const S3_SECRET_TARGET: &str = "Nebula Backup S3 Secret Key"; +const S3_SECRET_TARGET: &str = "Pebrel Backup S3 Secret Key"; #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum BackupProtocol { @@ -277,7 +278,7 @@ pub fn store_s3_secret(_access_key: &str, _secret: &str) -> Result<(), String> { } fn webdav_password() -> Option { - for name in ["PEBREL_BACKUP_WEBDAV_PASSWORD", "NEBULA_BACKUP_WEBDAV_PASSWORD"] { + for name in ["PEBREL_BACKUP_WEBDAV_PASSWORD"] { if let Ok(password) = std::env::var(name) { if !password.trim().is_empty() { return Some(password.trim().to_owned()); @@ -294,7 +295,7 @@ fn webdav_password() -> Option { } fn s3_secret() -> Option { - for name in ["PEBREL_BACKUP_S3_SECRET", "NEBULA_BACKUP_S3_SECRET"] { + for name in ["PEBREL_BACKUP_S3_SECRET"] { if let Ok(secret) = std::env::var(name) { if !secret.trim().is_empty() { return Some(secret.trim().to_owned()); @@ -361,10 +362,7 @@ fn is_archive_name(name: &str) -> bool { } fn archive_timestamp(name: &str) -> Option<&str> { - let timestamp = name - .strip_prefix(ARCHIVE_PREFIX) - .or_else(|| name.strip_prefix(LEGACY_ARCHIVE_PREFIX))? - .strip_suffix(ARCHIVE_SUFFIX)?; + let timestamp = name.strip_prefix(ARCHIVE_PREFIX)?.strip_suffix(ARCHIVE_SUFFIX)?; (timestamp.len() == 15 && timestamp .bytes() @@ -386,7 +384,10 @@ fn sort_archives(names: &mut [String]) { trait Backend { fn put(&self, name: &str, bytes: &[u8]) -> Result<(), String>; fn get(&self, name: &str) -> Result, String>; - fn list(&self) -> Result, String>; + fn list_details(&self) -> Result, String>; + fn list(&self) -> Result, String> { + self.list_details().map(|entries| entries.into_iter().map(|entry| entry.name).collect()) + } fn delete(&self, name: &str) -> Result<(), String>; /// 状态行里的目的地描述(不含凭据)。 fn describe(&self) -> String; @@ -394,6 +395,13 @@ trait Backend { /// 校验配置完整性并组装后端。缺什么直接说清楚缺什么,绝不半配置上路。 fn backend(cfg: &BackupRemoteConfig) -> Result, String> { + backend_with_secret(cfg, None) +} + +fn backend_with_secret( + cfg: &BackupRemoteConfig, + secret: Option<&str>, +) -> Result, String> { match cfg.protocol { BackupProtocol::Off => Err("未启用远程备份:先在设置 → 备份里选择协议".to_owned()), BackupProtocol::Folder => { @@ -418,7 +426,9 @@ fn backend(cfg: &BackupRemoteConfig) -> Result, String> { if username.is_empty() { return Err("未配置 WebDAV 用户名".to_owned()); } - let password = webdav_password() + let password = secret + .map(str::to_owned) + .or_else(webdav_password) .ok_or("缺少 WebDAV 密码:在设置里输入或设 PEBREL_BACKUP_WEBDAV_PASSWORD")?; Ok(Box::new(WebDavBackend { url, username, password })) }, @@ -449,7 +459,9 @@ fn backend(cfg: &BackupRemoteConfig) -> Result, String> { if access_key.is_empty() { return Err("未配置 S3 Access Key".to_owned()); } - let secret_key = s3_secret() + let secret_key = secret + .map(str::to_owned) + .or_else(s3_secret) .ok_or("缺少 S3 Secret Key:在设置里输入或设 PEBREL_BACKUP_S3_SECRET")?; Ok(Box::new(S3Backend { endpoint, region, bucket, prefix, access_key, secret_key })) }, @@ -484,6 +496,13 @@ pub fn push(packet: &[u8]) -> Result { } pub(crate) fn push_to(cfg: &BackupRemoteConfig, packet: &[u8]) -> Result { + push_snapshot(cfg, packet).map(|(_, message)| message) +} + +pub(crate) fn push_snapshot( + cfg: &BackupRemoteConfig, + packet: &[u8], +) -> Result<(Snapshot, String), String> { let backend = backend(cfg)?; let name = archive_name(now_unix()); backend.put(&name, packet)?; @@ -498,7 +517,20 @@ pub(crate) fn push_to(cfg: &BackupRemoteConfig, packet: &[u8]) -> Result, +) -> Result, String> { + let mut entries = backend_with_secret(cfg, secret)?.list_details()?; + entries.retain(|entry| is_archive_name(&entry.name)); + entries + .sort_by(|left, right| archive_timestamp(&right.name).cmp(&archive_timestamp(&left.name))); + entries.dedup_by(|left, right| left.name == right.name); + Ok(entries) } /// 取回远端最新一份归档:`(归档名, 密文字节)`。阻塞,跑在后台线程。 @@ -561,20 +593,20 @@ impl Backend for FolderBackend { std::fs::read(self.root.join(name)).map_err(|err| format!("读取备份失败:{err}")) } - fn list(&self) -> Result, String> { - let entries = match std::fs::read_dir(&self.root) { - Ok(entries) => entries, - Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()), - Err(err) => return Err(format!("读取备份目录失败:{err}")), - }; - let mut names = Vec::new(); + fn list_details(&self) -> Result, String> { + let entries = + std::fs::read_dir(&self.root).map_err(|err| format!("读取备份目录失败:{err}"))?; + let mut snapshots = Vec::new(); for entry in entries { let entry = entry.map_err(|err| format!("读取备份目录失败:{err}"))?; - if entry.file_type().map(|kind| kind.is_file()).unwrap_or(false) { - names.push(entry.file_name().to_string_lossy().into_owned()); + if entry.file_type().map_err(|err| err.to_string())?.is_file() { + snapshots.push(Snapshot { + name: entry.file_name().to_string_lossy().into_owned(), + bytes: Some(entry.metadata().map_err(|err| err.to_string())?.len()), + }); } } - Ok(names) + Ok(snapshots) } fn delete(&self, name: &str) -> Result<(), String> { @@ -674,27 +706,25 @@ impl Backend for WebDavBackend { } } - fn list(&self) -> Result, String> { + fn list_details(&self) -> Result, String> { let request = ureq::http::Request::builder() .method("PROPFIND") .uri(format!("{}/", self.url)) .header("Authorization", self.auth()) .header("Depth", "1") .header("Content-Type", "application/xml") - .body(r#""#.to_owned()) + .body(r#""#.to_owned()) .map_err(|err| format!("构造列表请求失败:{err}"))?; let mut response = http_agent().run(request).map_err(|err| format!("列出远端失败:{err}"))?; match response.status().as_u16() { 207 | 200 => {}, - // 目录还不存在 = 还没有任何备份。 - 404 => return Ok(Vec::new()), 401 | 403 => return Err("认证失败:检查 WebDAV 用户名/密码".to_owned()), status => return Err(format!("列出远端失败:HTTP {status}")), } let body = response.body_mut().read_to_vec().map_err(|err| format!("读取列表失败:{err}"))?; - Ok(propfind_archive_names(&String::from_utf8_lossy(&body))) + Ok(listing::webdav(&String::from_utf8_lossy(&body))) } fn delete(&self, name: &str) -> Result<(), String> { @@ -716,26 +746,7 @@ impl Backend for WebDavBackend { } } -/// 从 PROPFIND 的多状态 XML 里挑出我们的归档名。不引 XML 解析器:只认 -/// `…href>… Vec { - let mut names = Vec::new(); - let mut rest = xml; - while let Some(open) = rest.find("href>") { - rest = &rest[open + "href>".len()..]; - let Some(close) = rest.find('<') else { break }; - let href = rest[..close].trim().trim_end_matches('/'); - let name = href.rsplit('/').next().unwrap_or_default(); - if is_archive_name(name) && !names.iter().any(|seen| seen == name) { - names.push(name.to_owned()); - } - rest = &rest[close..]; - } - names -} - -// ---- 后端:S3 兼容(SigV4) ---- +// ---- 后端:S3 ---- struct S3Backend { /// `https://host[:port]`,已去尾部斜杠。 @@ -842,32 +853,16 @@ impl Backend for S3Backend { } } - fn list(&self) -> Result, String> { + fn list_details(&self) -> Result, String> { let path = format!("/{}/", self.bucket); - let mut names = Vec::new(); - for prefix in [ARCHIVE_PREFIX, LEGACY_ARCHIVE_PREFIX] { - let prefix = self.key(prefix); - let (status, body) = - self.request("GET", &path, &[("list-type", "2"), ("prefix", &prefix)], &[])?; - match status { - 200 => {}, - 403 => return Err("认证失败:检查 S3 Access Key / Secret Key / 区域".to_owned()), - _ => return Err(format!("列出远端失败:{}", Self::explain(status, &body))), - } - let text = String::from_utf8_lossy(&body); - let mut rest: &str = &text; - while let Some(open) = rest.find("") { - rest = &rest[open + "".len()..]; - let Some(close) = rest.find("") else { break }; - let key = &rest[..close]; - let name = key.rsplit('/').next().unwrap_or_default(); - if is_archive_name(name) && !names.iter().any(|seen| seen == name) { - names.push(name.to_owned()); - } - rest = &rest[close..]; - } + let prefix = self.key(ARCHIVE_PREFIX); + let (status, body) = + self.request("GET", &path, &[("list-type", "2"), ("prefix", &prefix)], &[])?; + match status { + 200 => Ok(listing::s3(&String::from_utf8_lossy(&body))), + 403 => Err("认证失败:检查 S3 Access Key / Secret Key / 区域".into()), + _ => Err(format!("列出远端失败:{}", Self::explain(status, &body))), } - Ok(names) } fn delete(&self, name: &str) -> Result<(), String> { @@ -1039,15 +1034,16 @@ impl Backend for SftpBackend { }) } - fn list(&self) -> Result, String> { + fn list_details(&self) -> Result, String> { let path = self.path.clone(); self.run(|sftp| async move { - let entries = match sftp.read_dir(path).await { - Ok(entries) => entries, - // 目录还不存在 = 还没有任何备份。 - Err(_) => return Ok(Vec::new()), - }; - Ok(entries.into_iter().map(|entry| entry.file_name()).collect()) + // 检查连接必须读到真实目录,权限和路径错误都需要就地反馈。 + let entries = + sftp.read_dir(path).await.map_err(|err| format!("读取备份目录失败:{err}"))?; + Ok(entries + .into_iter() + .map(|entry| Snapshot { name: entry.file_name(), bytes: entry.metadata().size }) + .collect()) }) } @@ -1087,25 +1083,25 @@ mod tests { #[test] fn archive_filter_rejects_foreign_files() { - assert!(is_archive_name("nebula-backup-20260813-051900.nbk")); + assert!(!is_archive_name("nebula-backup-20260813-051900.nbk")); assert!(is_archive_name("pebrel-backup-20260813-051900.nbk")); - assert!(!is_archive_name("nebula-backup-.nbk")); + assert!(!is_archive_name("pebrel-backup-.nbk")); assert!(!is_archive_name("photo.png")); - assert!(!is_archive_name("nebula-backup-20260813-051900.nbk.tmp")); + assert!(!is_archive_name("pebrel-backup-20260813-051900.nbk.tmp")); assert!(!is_archive_name("other-backup-20260813.nbk")); assert!(!is_archive_name("pebrel-backup-../keep.nbk")); } #[test] - fn mixed_brand_archives_are_ordered_by_timestamp_for_restore_and_retention() { + fn archives_are_ordered_by_timestamp_for_restore_and_retention() { let mut names = vec![ "pebrel-backup-20260810-010000.nbk".to_owned(), - "nebula-backup-20260813-010000.nbk".to_owned(), + "pebrel-backup-20260813-010000.nbk".to_owned(), "pebrel-backup-20260812-010000.nbk".to_owned(), ]; sort_archives(&mut names); assert_eq!(names[0], "pebrel-backup-20260810-010000.nbk"); - assert_eq!(names[2], "nebula-backup-20260813-010000.nbk"); + assert_eq!(names[2], "pebrel-backup-20260813-010000.nbk"); } #[test] @@ -1172,8 +1168,8 @@ mod tests { assert_eq!(sigv4_uri_encode("a b/c~d", true), "a%20b%2Fc~d"); // 查询串键排序 + 空值保留等号。 assert_eq!( - sigv4_canonical_query(&[("prefix", "nebula-backup-"), ("list-type", "2")]), - "list-type=2&prefix=nebula-backup-" + sigv4_canonical_query(&[("prefix", "pebrel-backup-"), ("list-type", "2")]), + "list-type=2&prefix=pebrel-backup-" ); assert_eq!(sigv4_canonical_query(&[("lifecycle", "")]), "lifecycle="); assert_eq!(sigv4_timestamp(1_369_353_600), "20130524T000000Z"); @@ -1184,16 +1180,22 @@ mod tests { let xml = r#" /dav/nebula/ - /dav/nebula/nebula-backup-20260813-051900.nbk - /dav/nebula/nebula-backup-20260812-231000.nbk + /dav/nebula/pebrel-backup-20260813-051900.nbk + /dav/nebula/pebrel-backup-20260812-231000.nbk /dav/nebula/notes.txt - /dav/nebula/nebula-backup-20260812-231000.nbk + /dav/nebula/pebrel-backup-20260812-231000.nbk "#; assert_eq!( - propfind_archive_names(xml), + listing::webdav(xml) + .into_iter() + .map(|entry| entry.name) + .collect::>() + .into_iter() + .rev() + .collect::>(), vec![ - "nebula-backup-20260813-051900.nbk".to_owned(), - "nebula-backup-20260812-231000.nbk".to_owned(), + "pebrel-backup-20260813-051900.nbk".to_owned(), + "pebrel-backup-20260812-231000.nbk".to_owned(), ] ); } @@ -1224,8 +1226,13 @@ mod tests { fn get(&self, _name: &str) -> Result, String> { Ok(Vec::new()) } - fn list(&self) -> Result, String> { - Ok(self.0.borrow().clone()) + fn list_details(&self) -> Result, String> { + Ok(self + .0 + .borrow() + .iter() + .map(|name| Snapshot { name: name.clone(), bytes: Some(0) }) + .collect()) } fn delete(&self, name: &str) -> Result<(), String> { self.0.borrow_mut().retain(|kept| kept != name); @@ -1239,7 +1246,7 @@ mod tests { #[test] fn prune_keeps_newest_archives_and_ignores_foreign_files() { let names: Vec = (0..13) - .map(|hour| format!("nebula-backup-202608{:02}-000000.nbk", hour + 1)) + .map(|hour| format!("pebrel-backup-202608{:02}-000000.nbk", hour + 1)) .chain(["keep-me.txt".to_owned()]) .collect(); let backend = MemoryBackend(std::cell::RefCell::new(names)); diff --git a/nebula_app/src/backup_remote/listing.rs b/nebula_app/src/backup_remote/listing.rs new file mode 100644 index 0000000..024b3de --- /dev/null +++ b/nebula_app/src/backup_remote/listing.rs @@ -0,0 +1,80 @@ +//! 保留协议列表已经返回的大小,避免为画时间线额外下载归档或逐个发 HEAD。 +use super::*; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) struct Snapshot { + pub name: String, + pub bytes: Option, +} + +/// 列表只提取固定元素的文本;归档名称仍经过父模块的 ASCII 白名单校验。 +fn elements<'a>(xml: &'a str, wanted: &str) -> Vec<&'a str> { + let mut values = Vec::new(); + let mut rest = xml; + while let Some(start) = rest.find('<') { + rest = &rest[start + 1..]; + let Some(end) = rest.find('>') else { break }; + let tag = rest[..end].split_whitespace().next().unwrap_or_default(); + if !tag.starts_with('/') && tag.rsplit(':').next() == Some(wanted) { + let close = format!(""); + let body = &rest[end + 1..]; + if let Some(end) = body.find(&close) { + values.push(body[..end].trim()); + rest = &body[end + close.len()..]; + continue; + } + } + rest = &rest[end + 1..]; + } + values +} + +pub(super) fn webdav(xml: &str) -> Vec { + elements(xml, "response") + .into_iter() + .filter_map(|record| { + let href = *elements(record, "href").first()?; + let name = href.trim_end_matches('/').rsplit('/').next()?; + is_archive_name(name).then(|| Snapshot { + name: name.to_owned(), + bytes: elements(record, "getcontentlength") + .first() + .and_then(|value| value.parse().ok()), + }) + }) + .collect() +} + +pub(super) fn s3(xml: &str) -> Vec { + elements(xml, "Contents") + .into_iter() + .filter_map(|record| { + let key = *elements(record, "Key").first()?; + let name = key.rsplit('/').next()?; + is_archive_name(name).then(|| Snapshot { + name: name.to_owned(), + bytes: elements(record, "Size").first().and_then(|value| value.parse().ok()), + }) + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sizes_stay_with_their_record_and_unknown_sizes_are_not_zero() { + let entries = webdav( + r#"/a/pebrel-backup-20260927-010203.nbk42/a/pebrel-backup-20260926-010203.nbk"#, + ); + assert_eq!(entries.len(), 2); + assert_eq!(entries[0].bytes, Some(42)); + assert_eq!(entries[1].bytes, None); + let entries = s3( + "prefix/pebrel-backup-20260927-010203.nbk96keep.txt20", + ); + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].bytes, Some(96)); + } +} diff --git a/nebula_app/src/backup_remote/preferences.rs b/nebula_app/src/backup_remote/preferences.rs index b372865..7ab38bd 100644 --- a/nebula_app/src/backup_remote/preferences.rs +++ b/nebula_app/src/backup_remote/preferences.rs @@ -134,7 +134,7 @@ mod tests { ..Default::default() }; for name in - ["pebrel-backup-20260918-010101.nbk", "nebula-backup-20260917-010101.nbk", "other.txt"] + ["pebrel-backup-20260918-010101.nbk", "pebrel-backup-20260917-010101.nbk", "other.txt"] { std::fs::write(directory.path().join(name), b"encrypted fixture").unwrap(); } diff --git a/nebula_app/src/cli.rs b/nebula_app/src/cli.rs index abbe941..9b71dd2 100644 --- a/nebula_app/src/cli.rs +++ b/nebula_app/src/cli.rs @@ -305,6 +305,8 @@ pub enum Subcommands { Migrate(MigrateOptions), /// Validate or create the Pebrel configuration. Config(ConfigOptions), + /// Check or invoke a local plugin package without starting a GUI or plugin daemon. + Plugin(crate::plugins::cli::Options), /// Test system notification (toast) delivery. #[cfg(windows)] NotifyTest, @@ -1548,6 +1550,55 @@ mod tests { )); } + #[test] + fn parses_plugin_commands_without_selecting_the_gui() { + use crate::plugins::cli::Command; + let check = + Options::try_parse_from(["pebrel", "plugin", "check", "sample", "--pretty"]).unwrap(); + assert!(matches!( + check.subcommands, + Some(Subcommands::Plugin(crate::plugins::cli::Options { + command: Command::Check { .. }, + pretty: true + })) + )); + let run = Options::try_parse_from([ + "pebrel", + "plugin", + "run", + "sample", + "summary", + "--args", + "{\"label\":\"中文\"}", + "--timeout-ms", + "1000", + ]) + .unwrap(); + #[cfg(feature = "gpui-shell")] + assert!(!crate::wants_gpui_shell(&run)); + assert!(matches!( + run.subcommands, + Some(Subcommands::Plugin(crate::plugins::cli::Options { + command: Command::Run { .. }, + .. + })) + )); + for timeout in ["0", "30001"] { + assert!( + Options::try_parse_from([ + "pebrel", + "plugin", + "run", + "sample", + "summary", + "--timeout-ms", + timeout + ]) + .is_err() + ); + } + } + #[test] fn send_options_after_text_are_not_swallowed_by_the_positional() { let agent = Options::try_parse_from([ diff --git a/nebula_app/src/display/side_panel/vcs.rs b/nebula_app/src/display/side_panel/vcs.rs index 9730991..0672bd2 100644 --- a/nebula_app/src/display/side_panel/vcs.rs +++ b/nebula_app/src/display/side_panel/vcs.rs @@ -815,7 +815,7 @@ pub(crate) fn read_git(root: &Path) -> Option { /// 宿主 git 要读 WSL 仓库,只能经 `\\wsl.localhost\…` UNC,而那条路依赖 9P /// 文件重定向——实测在 WSL 2.7.8 + Windows 22631 上完全不可达(见 /// [`crate::shell_detect::wsl_unc_cwd`])。来宾里的 git 反而什么都不缺: -/// 输出格式与宿主 `git status --porcelain -b` 逐字相同,所以解析与宿主 +/// 输出格式与宿主 `git status --porcelain=v2 -z` 相同,所以解析与宿主 /// 路径完全共用 [`collect_git_info`],没有第二套解析要维护。 /// /// 也不需要 `safe.directory`:来宾里的仓库属于来宾用户自己,不会触发 @@ -864,43 +864,20 @@ pub(crate) fn run_git( /// 宿主与 WSL 来宾两条运行路径共用的 porcelain 解析。`run` 收一组 git 参数、 /// 回 stdout(失败即 `None`)。 pub(crate) fn collect_git_info(run: impl Fn(&[&str]) -> Option) -> Option { - // `-b --porcelain` yields `## branch...upstream [ahead N]` + one `XY path` - // per change, X = index (staged) status, Y = worktree status. - let status = run(&["status", "--porcelain", "-b"])?; - let mut info = GitInfo::default(); - for line in status.lines() { - if let Some(head) = line.strip_prefix("## ") { - // `main...origin/main [ahead 1]` → `main`; detached prints as-is. - info.branch = head.split("...").next().unwrap_or(head).to_string(); - if let Some(idx) = head.find("ahead ") { - info.ahead = head[idx + 6..] - .chars() - .take_while(char::is_ascii_digit) - .collect::() - .parse() - .unwrap_or(0); - } - } else if line.len() > 3 { - let x = line.as_bytes()[0] as char; - let y = line.as_bytes()[1] as char; - let path = line[3..].trim().to_string(); - if x == '?' || y == '?' { - info.unstaged.push(('?', path)); - continue; - } - // Collect merge conflicts in their own group. The path - // also stays in staged/unstaged below so the legacy view keeps - // rendering it untouched. - if x == 'U' || y == 'U' || (x == 'A' && y == 'A') || (x == 'D' && y == 'D') { - info.conflicts.push(('U', path.clone())); - } - // One file can be in BOTH lists (partially staged). - if x != ' ' { - info.staged.push((x, path.clone())); - } - if y != ' ' { - info.unstaged.push((y, path)); - } + use crate::git_worktree::status::{STATUS_ARGS, parse_status}; + let status = parse_status(&run(STATUS_ARGS)?).ok()?; + let mut info = + GitInfo { branch: status.branch, ahead: status.ahead as _, ..GitInfo::default() }; + for entry in status.entries { + if entry.conflict { + info.conflicts.push(('U', entry.path.clone())); + } + // 部分暂存的同一文件仍在两组中出现;路径始终使用 Git 的原始身份。 + if entry.staged() { + info.staged.push((entry.index, entry.path.clone())); + } + if entry.unstaged() { + info.unstaged.push((entry.worktree, entry.path)); } } diff --git a/nebula_app/src/encrypted_backup.rs b/nebula_app/src/encrypted_backup.rs index 23d9dfa..cc43ecc 100644 --- a/nebula_app/src/encrypted_backup.rs +++ b/nebula_app/src/encrypted_backup.rs @@ -13,7 +13,9 @@ use aes_gcm::aead::{Aead, Payload}; use aes_gcm::{Aes256Gcm, KeyInit, Nonce}; use serde::{Deserialize, Serialize}; -const MAGIC: &[u8; 8] = b"NEBUBAK1"; +pub(crate) mod recovery; + +const MAGIC: &[u8; 8] = b"PEBRBAK1"; const ARCHIVE_VERSION: u32 = 1; const SALT_LEN: usize = 16; const NONCE_LEN: usize = 12; @@ -69,7 +71,29 @@ impl BackupSelection { self.categories().next().is_none() } - fn categories(self) -> impl Iterator { + pub(crate) fn set(&mut self, category: BackupCategory, enabled: bool) { + *match category { + BackupCategory::Appearance => &mut self.appearance, + BackupCategory::Config => &mut self.config, + BackupCategory::Ssh => &mut self.ssh, + BackupCategory::Sync => &mut self.sync, + BackupCategory::Assistant => &mut self.assistant, + BackupCategory::Session => &mut self.session, + BackupCategory::DirectoryHistory => &mut self.directory_history, + BackupCategory::CommandHistory => &mut self.command_history, + BackupCategory::Fonts => &mut self.fonts, + } = enabled; + } + + pub(crate) fn from_categories(categories: impl IntoIterator) -> Self { + let mut selection = Self { appearance: false, ..Self::default() }; + for category in categories { + selection.set(category, true); + } + selection + } + + pub(crate) fn categories(self) -> impl Iterator { [ (self.appearance, BackupCategory::Appearance), (self.config, BackupCategory::Config), @@ -97,6 +121,7 @@ pub(crate) struct BackupEntry { pub(crate) struct BackupManifest { pub version: u32, pub categories: Vec, + pub device: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -105,6 +130,29 @@ pub(crate) struct BackupArchive { pub entries: Vec, } +#[derive(Debug, Clone)] +pub(crate) struct CategorySummary { + pub category: BackupCategory, + pub files: usize, + pub bytes: u64, +} + +impl BackupArchive { + pub(crate) fn summary(&self) -> Vec { + self.manifest + .categories + .iter() + .map(|category| { + let entries = self.entries.iter().filter(|entry| entry.category == *category); + let (files, bytes) = entries.fold((0, 0), |(files, bytes), entry| { + (files + 1, bytes + entry.bytes.len() as u64) + }); + CategorySummary { category: *category, files, bytes } + }) + .collect() + } +} + fn validate_passphrase(passphrase: &str) -> Result<(), String> { (passphrase.chars().count() >= 8) .then_some(()) @@ -122,7 +170,7 @@ fn derive_key(passphrase: &str, salt: &[u8]) -> Result<[u8; KEY_LEN], String> { Ok(key) } -/// Serialize and encrypt an archive as `NEBUBAK1 | salt | nonce | ciphertext`. +/// Serialize and encrypt an archive as `PEBRBAK1 | salt | nonce | ciphertext`. pub(crate) fn seal(archive: &BackupArchive, passphrase: &str) -> Result, String> { validate_passphrase(passphrase)?; if archive.manifest.version != ARCHIVE_VERSION { @@ -131,15 +179,20 @@ pub(crate) fn seal(archive: &BackupArchive, passphrase: &str) -> Result, validate_archive(archive)?; let plaintext = serde_json::to_vec(archive).map_err(|error| format!("serialize backup: {error}"))?; + encrypt_bytes(&plaintext, passphrase) +} + +fn encrypt_bytes(plaintext: &[u8], passphrase: &str) -> Result, String> { + validate_passphrase(passphrase)?; let mut salt = [0; SALT_LEN]; let mut nonce = [0; NONCE_LEN]; getrandom::fill(&mut salt).map_err(|error| format!("random salt: {error}"))?; getrandom::fill(&mut nonce).map_err(|error| format!("random nonce: {error}"))?; - let key = derive_key(passphrase, &salt)?; - let ciphertext = Aes256Gcm::new((&key).into()) + let key = zeroize::Zeroizing::new(derive_key(passphrase, &salt)?); + let ciphertext = Aes256Gcm::new((&*key).into()) .encrypt( &Nonce::try_from(&nonce[..]).map_err(|_| "invalid backup nonce".to_owned())?, - Payload { msg: &plaintext, aad: MAGIC }, + Payload { msg: plaintext, aad: MAGIC }, ) .map_err(|_| "backup encryption failed".to_owned())?; let mut output = Vec::with_capacity(MAGIC.len() + SALT_LEN + NONCE_LEN + ciphertext.len()); @@ -152,6 +205,14 @@ pub(crate) fn seal(archive: &BackupArchive, passphrase: &str) -> Result, /// Authenticate, decrypt, deserialize, and validate a complete archive. pub(crate) fn open(packet: &[u8], passphrase: &str) -> Result { + let plaintext = decrypt_bytes(packet, passphrase)?; + let archive: BackupArchive = serde_json::from_slice(&plaintext) + .map_err(|error| format!("invalid backup archive: {error}"))?; + validate_archive(&archive)?; + Ok(archive) +} + +fn decrypt_bytes(packet: &[u8], passphrase: &str) -> Result>, String> { validate_passphrase(passphrase)?; let header_len = MAGIC.len() + SALT_LEN + NONCE_LEN; if packet.len() <= header_len || packet.get(..MAGIC.len()) != Some(MAGIC) { @@ -159,17 +220,14 @@ pub(crate) fn open(packet: &[u8], passphrase: &str) -> Result Result { @@ -219,6 +277,9 @@ fn collect_from(root: &Path, selection: BackupSelection) -> Result Result<(), String> { let paths = archive .entries .iter() - .map(|entry| restore_path(root, nebula_settings::canonical_data_file_name(&entry.name))) + .map(|entry| restore_path(root, &entry.name)) .collect::, _>>()?; for (entry, path) in archive.entries.iter().zip(paths) { crate::atomic_file::write(&path, &entry.bytes) @@ -395,7 +456,7 @@ fn validate_archive(archive: &BackupArchive) -> Result<(), String> { } let mut names = HashSet::new(); for entry in &archive.entries { - let canonical = nebula_settings::canonical_data_file_name(&entry.name); + let canonical = entry.name.as_str(); if !categories.contains(&entry.category) || !names.insert(canonical) { return Err("invalid or duplicate backup entry".to_owned()); } @@ -455,10 +516,14 @@ mod tests { fn archive() -> BackupArchive { BackupArchive { - manifest: BackupManifest { version: 1, categories: vec![BackupCategory::Appearance] }, + manifest: BackupManifest { + version: 1, + categories: vec![BackupCategory::Appearance], + device: String::new(), + }, entries: vec![BackupEntry { category: BackupCategory::Appearance, - name: "nebula_settings.txt".into(), + name: "pebrel_settings.txt".into(), bytes: b"theme=dark".to_vec(), }], } @@ -477,13 +542,13 @@ mod tests { #[test] fn roundtrip_and_wrong_password_fail() { let packet = seal(&archive(), "correct horse").unwrap(); - assert_eq!(&packet[..8], b"NEBUBAK1"); + assert_eq!(&packet[..8], b"PEBRBAK1"); assert_eq!(open(&packet, "correct horse").unwrap(), archive()); assert!(open(&packet, "wrong horse").is_err()); } #[test] - fn legacy_archive_restores_with_new_names_and_keeps_its_encryption_format() { + fn archive_restores_current_file_names() { let original = archive(); let packet = seal(&original, "correct horse").unwrap(); let restored = open(&packet, "correct horse").unwrap(); @@ -495,7 +560,7 @@ mod tests { } #[test] - fn conflicting_legacy_and_new_archive_names_are_rejected_before_writes() { + fn duplicate_archive_names_are_rejected_before_writes() { let mut archive = archive(); let mut duplicate = archive.entries[0].clone(); duplicate.name = "pebrel_settings.txt".into(); @@ -602,7 +667,11 @@ mod tests { fn invalid_terminal_profiles_json_is_rejected_before_restore() { let directory = tempdir().unwrap(); let archive = BackupArchive { - manifest: BackupManifest { version: 1, categories: vec![BackupCategory::Config] }, + manifest: BackupManifest { + version: 1, + categories: vec![BackupCategory::Config], + device: String::new(), + }, entries: vec![ BackupEntry { category: BackupCategory::Config, @@ -625,7 +694,11 @@ mod tests { fn path_traversal_is_rejected_before_writes() { let directory = tempdir().unwrap(); let archive = BackupArchive { - manifest: BackupManifest { version: 1, categories: vec![BackupCategory::Appearance] }, + manifest: BackupManifest { + version: 1, + categories: vec![BackupCategory::Appearance], + device: String::new(), + }, entries: vec![BackupEntry { category: BackupCategory::Appearance, name: "../outside".into(), diff --git a/nebula_app/src/encrypted_backup/recovery.rs b/nebula_app/src/encrypted_backup/recovery.rs new file mode 100644 index 0000000..73cf3de --- /dev/null +++ b/nebula_app/src/encrypted_backup/recovery.rs @@ -0,0 +1,166 @@ +//! 选择性恢复及其加密恢复点;UI 只持有句柄,不负责文件写入规则。 +use super::*; + +#[derive(Clone, Debug)] +pub(crate) struct RestorePoint { + pub path: PathBuf, + names: Vec, +} + +#[derive(Serialize, Deserialize)] +struct OriginalFile { + name: String, + bytes: Option>, +} + +fn read_original(root: &Path, name: &str) -> Result { + let path = restore_path(root, name)?; + if fs::symlink_metadata(&path).is_ok_and(|meta| meta.file_type().is_symlink()) { + return Err(format!("backup destination is a symbolic link: {name}")); + } + let bytes = match fs::read(path) { + Ok(bytes) => Some(bytes), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => None, + Err(error) => return Err(format!("read recovery source {name}: {error}")), + }; + Ok(OriginalFile { name: name.to_owned(), bytes }) +} + +fn write_originals(root: &Path, originals: &[OriginalFile]) -> Result<(), String> { + let paths = originals + .iter() + .map(|file| restore_path(root, &file.name)) + .collect::, _>>()?; + for (original, path) in originals.iter().zip(paths) { + match &original.bytes { + Some(bytes) => crate::atomic_file::write(&path, bytes) + .map_err(|error| format!("undo {}: {error}", original.name))?, + None => match fs::remove_file(path) { + Ok(()) => {}, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}, + Err(error) => return Err(format!("undo {}: {error}", original.name)), + }, + } + } + Ok(()) +} + +pub(crate) fn restore_selected( + archive: &BackupArchive, + selection: BackupSelection, + passphrase: &str, +) -> Result { + restore_selected_at(crate::platform::dirs::data_dir(), archive, selection, passphrase) +} + +fn restore_selected_at( + root: &Path, + archive: &BackupArchive, + selection: BackupSelection, + passphrase: &str, +) -> Result { + validate_archive(archive)?; + let categories: HashSet<_> = selection.categories().collect(); + if categories.is_empty() { + return Err("Select at least one category to restore".into()); + } + let mut selected = archive.clone(); + selected.entries.retain(|entry| categories.contains(&entry.category)); + selected.manifest.categories.retain(|category| categories.contains(category)); + let originals = selected + .entries + .iter() + .map(|entry| read_original(root, &entry.name)) + .collect::, _>>()?; + // 精确保存原字节和“不存在”状态:撤回不能丢掉本机 SSH 偏好或留下恢复新建的文件。 + // 恢复点只在本机加密落盘,不经过会过滤凭据字段的可分享备份收集器。 + let plaintext = zeroize::Zeroizing::new( + serde_json::to_vec(&originals) + .map_err(|error| format!("serialize recovery point: {error}"))?, + ); + let packet = encrypt_bytes(&plaintext, passphrase)?; + let name = format!( + "backup-recovery/{}.pebrel-recovery", + chrono::Utc::now().format("%Y%m%d-%H%M%S-%f") + ); + let path = restore_path(root, &name)?; + fs::create_dir_all(path.parent().ok_or("invalid recovery path")?) + .map_err(|error| format!("create recovery directory: {error}"))?; + crate::atomic_file::write(&path, &packet) + .map_err(|error| format!("save recovery point: {error}"))?; + if let Err(error) = restore_to(root, &selected) { + return match write_originals(root, &originals) { + Ok(()) => Err(error), + Err(rollback) => Err(format!("{error}; {rollback}; recovery: {}", path.display())), + }; + } + Ok(RestorePoint { path, names: originals.into_iter().map(|file| file.name).collect() }) +} + +pub(crate) fn undo(point: &RestorePoint, passphrase: &str) -> Result<(), String> { + undo_at(crate::platform::dirs::data_dir(), point, passphrase) +} + +fn undo_at(root: &Path, point: &RestorePoint, passphrase: &str) -> Result<(), String> { + let packet = fs::read(&point.path).map_err(|error| format!("read recovery point: {error}"))?; + let plaintext = decrypt_bytes(&packet, passphrase)?; + let originals: Vec = serde_json::from_slice(&plaintext) + .map_err(|error| format!("invalid recovery point: {error}"))?; + // 只接受该次已验证恢复操作拥有的目标集合,外部导入文件不能借撤回扩展白名单。 + if originals.iter().map(|file| &file.name).ne(point.names.iter()) { + return Err("recovery target mismatch".into()); + } + write_originals(root, &originals) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn selective_restore_preserves_other_categories_and_undo_restores_exact_bytes() { + let root = tempfile::tempdir().unwrap(); + fs::write(root.path().join("pebrel_settings.txt"), b"theme=old\npinned_hosts=local") + .unwrap(); + let archive = BackupArchive { + manifest: BackupManifest { + version: 1, + device: String::new(), + categories: vec![BackupCategory::Appearance, BackupCategory::Session], + }, + entries: vec![ + BackupEntry { + category: BackupCategory::Appearance, + name: "pebrel_settings.txt".into(), + bytes: b"theme=new".to_vec(), + }, + BackupEntry { + category: BackupCategory::Session, + name: "session.json".into(), + bytes: b"{}".to_vec(), + }, + ], + }; + let point = + restore_selected_at(root.path(), &archive, BackupSelection::default(), "correct horse") + .unwrap(); + assert_eq!(fs::read(root.path().join("pebrel_settings.txt")).unwrap(), b"theme=new"); + assert!(!root.path().join("session.json").exists()); + assert!(undo_at(root.path(), &point, "wrong horse").is_err()); + undo_at(root.path(), &point, "correct horse").unwrap(); + assert_eq!( + fs::read(root.path().join("pebrel_settings.txt")).unwrap(), + b"theme=old\npinned_hosts=local" + ); + let point = restore_selected_at( + root.path(), + &archive, + BackupSelection::from_categories([BackupCategory::Session]), + "correct horse", + ) + .unwrap(); + assert!(root.path().join("session.json").exists()); + undo_at(root.path(), &point, "correct horse").unwrap(); + assert!(!root.path().join("session.json").exists()); + } +} diff --git a/nebula_app/src/event/runtime_control.rs b/nebula_app/src/event/runtime_control.rs index 294a086..4849ea3 100644 --- a/nebula_app/src/event/runtime_control.rs +++ b/nebula_app/src/event/runtime_control.rs @@ -101,6 +101,10 @@ impl Processor { command: &RuntimeCommand, ) -> Result { match command { + RuntimeCommand::Tab { .. } | RuntimeCommand::Conversation { .. } => Err(ApiError::new( + "method_not_found", + "shared files and conversations require the GPUI runtime", + )), RuntimeCommand::Snapshot => serde_json::to_value(self.publish_runtime_snapshot()) .map_err(|error| ApiError::new("serialization_failed", error.to_string())), // 旧壳(winit/GL)没有"按 id 选 shell"的能力。显式拒绝而不是忽略: @@ -289,13 +293,13 @@ impl Processor { "input": "paste" })) }, - RuntimeCommand::ReadPane { window_id, pane_id, lines } => { + RuntimeCommand::ReadPane { window_id, pane_id, lines, screen } => { let id = self.runtime_target_window(*window_id, Some(*pane_id))?; let read = self .windows .get(&id) .expect("resolved runtime window exists") - .runtime_read(*pane_id, *lines)?; + .runtime_read(*pane_id, *lines, *screen)?; serde_json::to_value(read) .map_err(|error| ApiError::new("serialization_failed", error.to_string())) }, @@ -337,7 +341,7 @@ impl Processor { "run_id": run_id })) }, - RuntimeCommand::Exec { .. } => Err(ApiError::new( + RuntimeCommand::Exec { .. } | RuntimeCommand::Git { .. } => Err(ApiError::new( "invalid_runtime_command", "pane.exec must be prepared before entering the synchronous UI dispatcher", )), @@ -377,7 +381,7 @@ impl Processor { format!("agent {:?} no longer has a live window", managed.name), )); }; - let read = window.runtime_read(managed.pane_id, *lines)?; + let read = window.runtime_read(managed.pane_id, *lines, false)?; Ok(serde_json::json!({ "agent": managed, "read": read })) }, } @@ -388,24 +392,15 @@ impl Processor { event_loop: &ActiveEventLoop, dispatch: &std::sync::Arc, ) { - if let RuntimeCommand::Exec { window_id, pane_id, argv, timeout_ms, max_output_bytes } = - &dispatch.command - { - let prepared = self.runtime_target_window(*window_id, Some(*pane_id)).and_then(|id| { + if let Some((window_id, pane_id)) = dispatch.command.execution_target() { + let prepared = self.runtime_target_window(window_id, Some(pane_id)).and_then(|id| { self.windows .get(&id) .expect("resolved runtime window exists") - .runtime_exec_context(*pane_id) + .runtime_exec_context(pane_id) }); match prepared { - Ok((context, cwd)) => crate::runtime_exec::spawn( - dispatch.clone(), - context, - cwd, - argv.clone(), - *timeout_ms, - *max_output_bytes, - ), + Ok((context, cwd)) => crate::runtime_exec::spawn(dispatch.clone(), context, cwd), Err(error) => dispatch.respond(Err(error)), } return; diff --git a/nebula_app/src/git_worktree.rs b/nebula_app/src/git_worktree.rs index 2222b0d..1b990a8 100644 --- a/nebula_app/src/git_worktree.rs +++ b/nebula_app/src/git_worktree.rs @@ -13,6 +13,8 @@ use serde_json::json; use crate::runtime_api::ApiError; +pub(crate) mod status; + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct WorktreeProvenance { pub repo_root: PathBuf, @@ -288,7 +290,7 @@ impl WorktreeTransaction { /// /// Pebrel 是 `windows_subsystem = "windows"` 的 GUI 进程(见 `main.rs`),本身没有 /// 控制台可给子进程继承;不加 `CREATE_NO_WINDOW` 时 Windows 会给每条 git 命令分配 -/// 一个新控制台——在默认终端应用是 Windows Terminal 的机器上,那就是**每跑一条 +/// 一个新控制台——在默认终端应用会托管新控制台的机器上,那就是**每跑一条 /// git 弹一扇窗口**。worktree 操作会连着跑好几条,用户看到的就是一串窗口。 /// /// 名字里的 `hidden` 不是装饰:`gpui_shell::code_tab` 里已有一个签名完全不同的 diff --git a/nebula_app/src/git_worktree/status.rs b/nebula_app/src/git_worktree/status.rs new file mode 100644 index 0000000..edf4844 --- /dev/null +++ b/nebula_app/src/git_worktree/status.rs @@ -0,0 +1,136 @@ +//! 桌面与手机共用 Git 的 NUL 分隔状态,文件名不是展示文本,不能按行或空格拆分。 + +use serde::Serialize; + +pub(crate) const STATUS_ARGS: &[&str] = &["status", "--porcelain=v2", "--branch", "-z"]; + +#[derive(Clone, Debug, Default, Serialize)] +pub(crate) struct GitStatus { + pub branch: String, + pub head: String, + pub upstream: Option, + pub ahead: u32, + pub behind: u32, + pub entries: Vec, +} + +#[derive(Clone, Debug, Serialize)] +pub(crate) struct GitEntry { + pub path: String, + pub original: Option, + pub index: char, + pub worktree: char, + pub conflict: bool, +} + +impl GitEntry { + pub(crate) fn staged(&self) -> bool { + !matches!(self.index, '.' | '?') + } + + pub(crate) fn unstaged(&self) -> bool { + self.worktree != '.' + } +} + +pub(crate) fn parse_status(output: &str) -> Result { + if !output.ends_with('\0') { + return Err("git_invalid_status"); + } + let mut result = GitStatus::default(); + let mut records = output.split_terminator('\0'); + while let Some(record) = records.next() { + if let Some(header) = record.strip_prefix("# ") { + if let Some(value) = header.strip_prefix("branch.head ") { + result.branch = value.into(); + } else if let Some(value) = header.strip_prefix("branch.oid ") { + result.head = value.into(); + } else if let Some(value) = header.strip_prefix("branch.upstream ") { + result.upstream = Some(value.into()); + } else if let Some(value) = header.strip_prefix("branch.ab ") { + let (ahead, behind) = value.split_once(' ').ok_or("git_invalid_status")?; + result.ahead = ahead + .strip_prefix('+') + .ok_or("git_invalid_status")? + .parse() + .map_err(|_| "git_invalid_status")?; + result.behind = behind + .strip_prefix('-') + .ok_or("git_invalid_status")? + .parse() + .map_err(|_| "git_invalid_status")?; + } + continue; + } + if record.starts_with("! ") { + continue; + } + let kind = record.as_bytes().first().copied().ok_or("git_invalid_status")?; + let entry = if kind == b'?' { + GitEntry { + path: record.strip_prefix("? ").ok_or("git_invalid_status")?.into(), + original: None, + index: '?', + worktree: '?', + conflict: false, + } + } else { + let count = match kind { + b'1' => 9, + b'2' => 10, + b'u' => 11, + _ => return Err("git_invalid_status"), + }; + let fields: Vec<_> = record.splitn(count, ' ').collect(); + if fields.len() != count || fields[1].len() != 2 || !fields[1].is_ascii() { + return Err("git_invalid_status"); + } + let xy = fields[1].as_bytes(); + GitEntry { + path: fields[count - 1].into(), + original: if kind == b'2' { + Some(records.next().ok_or("git_invalid_status")?.into()) + } else { + None + }, + index: xy[0] as char, + worktree: xy[1] as char, + conflict: kind == b'u', + } + }; + if entry.path.is_empty() || entry.original.as_ref().is_some_and(|p| p.is_empty()) { + return Err("git_invalid_status"); + } + result.entries.push(entry); + } + if result.branch.is_empty() || result.head.is_empty() { + return Err("git_invalid_status"); + } + Ok(result) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn preserves_rename_identity_and_partial_staging() { + let status = parse_status( + "# branch.oid abc\0# branch.head main\0# branch.ab +2 -1\0\ + 2 RM N... 100644 100644 100644 abc def R100 新 name\n.txt\0old name.txt\0? -new.txt\0", + ) + .unwrap(); + assert_eq!((status.ahead, status.behind), (2, 1)); + assert_eq!(status.entries[0].path, "新 name\n.txt"); + assert_eq!(status.entries[0].original.as_deref(), Some("old name.txt")); + assert!(status.entries[0].staged() && status.entries[0].unstaged()); + assert!(!status.entries[1].staged()); + } + + #[test] + fn rejects_partial_records_and_keeps_conflicts() { + assert!(parse_status("# branch.head main").is_err()); + let result = parse_status("# branch.oid abc\0# branch.head main\0u UU N... 100644 100644 100644 100644 a b c conflict.txt\0").unwrap(); + assert!(result.entries[0].conflict); + } +} diff --git a/nebula_app/src/gpui_shell/assets.rs b/nebula_app/src/gpui_shell/assets.rs index d17e017..e17167d 100644 --- a/nebula_app/src/gpui_shell/assets.rs +++ b/nebula_app/src/gpui_shell/assets.rs @@ -1,6 +1,6 @@ //! 本仓库自带的图标资源,叠在组件库资源之上。 //! -//! 只补 lucide 缺的那几个,不另起一套。 +//! 补充项目专用图标,并固定已确认原型所需的 Lucide 路径与描边。 //! //! 试过"统一圆角外壳 + 内部挖空骨架"的自绘方案,实机否掉了:外壳吃掉大半视觉 //! 重量后内部只剩 12×10px,20px 显示下无论画什么都糊成"方框里几个点",反而 @@ -30,10 +30,19 @@ const NEBULA_ICONS: &[(&str, &[u8])] = icons![ "layout-grid", "mouse-pointer", "sliders", + "backup-drive", + "backup-cloud", + "backup-globe", + "backup-database", + "backup-server", + "backup-folder", + "backup-lock", + "backup-check", "pin", "pencil", "trash-2", "refresh", + "phone", "vcs-changes", "vcs-history", "vcs-conflict", @@ -101,6 +110,7 @@ pub mod nav { /// Lucide trash-2;删除保存命令不能借用表示 Backspace 的 `IconName::Delete`。 pub const TRASH: &str = "icons/nebula-trash-2.svg"; pub const REFRESH: &str = "icons/nebula-refresh.svg"; + pub const PHONE: &str = "icons/nebula-phone.svg"; /// IDEA Commit 工具窗口同语义的“基线 + 提交节点”:工作区变更入口。 pub const VCS_CHANGES: &str = "icons/nebula-vcs-changes.svg"; /// 带分叉节点的提交线路:版本历史入口。 @@ -108,3 +118,15 @@ pub mod nav { /// 两侧分支汇入结果并在交点标出冲突:三栏冲突解决入口。 pub const VCS_CONFLICT: &str = "icons/nebula-vcs-conflict.svg"; } + +/// Backup icons share the approved settings prototype geometry. +pub mod backup { + pub const DRIVE: &str = "icons/nebula-backup-drive.svg"; + pub const CLOUD: &str = "icons/nebula-backup-cloud.svg"; + pub const GLOBE: &str = "icons/nebula-backup-globe.svg"; + pub const DATABASE: &str = "icons/nebula-backup-database.svg"; + pub const SERVER: &str = "icons/nebula-backup-server.svg"; + pub const FOLDER: &str = "icons/nebula-backup-folder.svg"; + pub const LOCK: &str = "icons/nebula-backup-lock.svg"; + pub const CHECK: &str = "icons/nebula-backup-check.svg"; +} diff --git a/nebula_app/src/gpui_shell/file_editor/mod.rs b/nebula_app/src/gpui_shell/file_editor/mod.rs index c3e28af..0ac97fa 100644 --- a/nebula_app/src/gpui_shell/file_editor/mod.rs +++ b/nebula_app/src/gpui_shell/file_editor/mod.rs @@ -145,6 +145,7 @@ pub struct TextFileView { selected_heading: Option, all_selected: bool, revision: u64, + content_revision: u64, preview_task: Option>, _input_subscription: Subscription, } @@ -207,6 +208,7 @@ impl TextFileView { }); let subscription = cx.subscribe_in(&input, window, |this, _, event, _, cx| { if matches!(event, InputEvent::Change) { + this.content_revision = this.content_revision.wrapping_add(1); if this.markdown { let input = this.input.read(cx); this.history.record_rope(input.text()); @@ -273,6 +275,7 @@ impl TextFileView { selected_heading: None, all_selected: false, revision: 0, + content_revision: 0, preview_task: None, _input_subscription: subscription, }; @@ -295,6 +298,19 @@ impl TextFileView { self.saving } + pub(super) fn reader_revision(&self) -> Option { + (!self.loading && self.document.is_some()).then_some(self.content_revision) + } + + pub(super) fn reader_snapshot(&self, cx: &App) -> Option<(gpui_component::Rope, u64)> { + // Rope 克隆共享不可变节点;编码和分块放到后台,不在 UI 线程复制整篇文档。 + self.reader_revision().map(|revision| (self.input.read(cx).text().clone(), revision)) + } + + pub(super) fn reader_is_remote(&self) -> bool { + self.source.is_remote() + } + pub(super) fn reader_focus(&self) -> bool { self.reader_focus } @@ -377,6 +393,7 @@ impl TextFileView { fn load(&mut self, window: &mut Window, cx: &mut Context) { self.loading = true; + self.content_revision = self.content_revision.wrapping_add(1); self.revision += 1; self.preview_task = None; let path = self.path.clone(); diff --git a/nebula_app/src/gpui_shell/mod.rs b/nebula_app/src/gpui_shell/mod.rs index 8dd0f02..f471af0 100644 --- a/nebula_app/src/gpui_shell/mod.rs +++ b/nebula_app/src/gpui_shell/mod.rs @@ -165,6 +165,9 @@ pub fn run_shell( std::thread::sleep(std::time::Duration::from_millis(25)); } } + if runtime_server.is_some() { + crate::mobile_connection::resume_saved(); + } let _runtime_server = runtime_server; let _acrylic = crate::platform::acrylic::RunGuard::default(); // GPUI 默认只有 macOS 在最后一扇窗关掉后仍驻留(Dock 里留个没有窗口的 diff --git a/nebula_app/src/gpui_shell/settings_pane.rs b/nebula_app/src/gpui_shell/settings_pane.rs index b4bd589..63a8a27 100644 --- a/nebula_app/src/gpui_shell/settings_pane.rs +++ b/nebula_app/src/gpui_shell/settings_pane.rs @@ -58,6 +58,7 @@ mod initialization; mod keymap; mod launcher_actions; mod localization; +mod mobile; mod navigation; mod notifications; mod shell_picker; @@ -105,6 +106,7 @@ pub struct SettingsPane { /// 当前分区(`SECTIONS` 下标);默认落在应用主页。 active_section: usize, agents: agents::AgentSettingsState, + mobile: mobile::MobileState, appearance_picker: Option, appearance_picker_seq: u64, pub(super) theme_editor: Option, @@ -327,10 +329,7 @@ impl SettingsPane { } self.settings_search_input.update(cx, |state, cx| { state.set_placeholder( - language.pick( - "搜索全部设置,例如「字号」「透明度」「更新」", - "Search all settings, e.g. font, opacity, update", - ), + language.text(crate::i18n::Message::CommonSearchSettings), window, cx, ) @@ -1304,13 +1303,14 @@ impl SettingsPane { 1 => self.section_appearance(window, cx), 2 => self.section_profiles(window, cx), 3 => self.section_providers(cx), - 4 => self.section_ssh(cx), + 4 => self.section_ssh(window, cx), 5 => self.section_network(cx), 6 => self.section_interaction(cx), 7 => self.section_keymap(cx), 8 => self.section_advanced(cx), 10 => self.section_agents(cx), - _ => self.section_backup(cx), + MOBILE_SECTION => self.section_mobile(window, cx), + _ => self.section_backup(window, cx), } .into_any_element() } @@ -1336,7 +1336,7 @@ impl SettingsPane { .px_2() .pt(px(12.0)) .pb(px(8.0)) - .gap(px(4.0)) + .gap(px(2.0)) .text_sm() .line_height(px(20.0)) .border_r_1() @@ -1360,7 +1360,14 @@ impl SettingsPane { .child(language.pick("返回工作区", "Back to workspace")), ) .child(self.render_nav_search(window, cx)); + let searching = !self.settings_search_input.read(cx).value().trim().is_empty(); + let mut previous_group = None; for ix in self.matching_settings_sections(cx) { + let group = NAV_GROUPS.iter().position(|(_, sections)| sections.contains(&ix)); + if !searching && previous_group.is_some() && previous_group != group { + nav = nav.child(div().h(px(10.0)).flex_shrink_0()); + } + previous_group = group; let active = ix == self.active_section; nav = nav.child( div() @@ -1371,7 +1378,7 @@ impl SettingsPane { .h(px(SETTINGS_NAV_ROW_HEIGHT)) .flex() .items_center() - .gap(px(8.0)) + .gap(px(10.0)) .rounded_md() .cursor_pointer() // 选中态同时改变底色、墨色和字重,余光扫过也能确认当前位置。 @@ -1441,7 +1448,10 @@ impl Render for SettingsPane { let appearance_picker_modal = self.appearance_picker_modal(window, cx); let theme_editor_modal = self.theme_editor_modal(window, cx); let theme_transfer_modal = self.theme_transfer_modal(window, cx); + let backup_drawer = self.backup_drawer(window, cx); + let mobile_relay_modal = self.mobile_relay_modal(cx); let application_page = self.active_section == 0; + let mobile_page = self.active_section == MOBILE_SECTION; div() .size_full() @@ -1508,6 +1518,7 @@ impl Render for SettingsPane { .pt(px(20.0)) .pb(px(22.0)) .when(!application_page, |content| content.pt(px(28.0)).pb(px(30.0))) + .when(mobile_page, |content| content.p_0()) // 注意这层包装 `v_flex` 的 `w_full` 不能删(2026-08-23 // 又栽了一次):`overflow_y_scrollbar` 把内容层清成 // `Display::Block`,而 flex 容器在 block 父里 @@ -1545,7 +1556,7 @@ impl Render for SettingsPane { .child( v_flex() .w_full() - .when(matches!(self.active_section, 9 | 10), |content| { + .when(matches!(self.active_section, 4 | 9 | 10 | MOBILE_SECTION), |content| { content.items_center() }) .when(application_page, |content| content.max_w(px(960.0))) @@ -1554,10 +1565,12 @@ impl Render for SettingsPane { ), ), ) + .when_some(backup_drawer, |root, drawer| root.child(drawer)) .when_some(ssh_editor_modal, |root, modal| root.child(modal)) .when_some(appearance_picker_modal, |root, modal| root.child(modal)) .when_some(theme_editor_modal, |root, modal| root.child(modal)) .when_some(theme_transfer_modal, |root, modal| root.child(modal)) + .when_some(mobile_relay_modal, |root, modal| root.child(modal)) .when(font_picker_open, |root| { root // 搜索框是当前焦点时 Escape 仍沿元素树冒泡到设置根; diff --git a/nebula_app/src/gpui_shell/settings_pane/about.rs b/nebula_app/src/gpui_shell/settings_pane/about.rs index 1826d76..bdcdef2 100644 --- a/nebula_app/src/gpui_shell/settings_pane/about.rs +++ b/nebula_app/src/gpui_shell/settings_pane/about.rs @@ -69,6 +69,8 @@ impl SettingsPane { pub(super) fn section_home(&mut self, window: &Window, cx: &mut Context) -> gpui::Div { let language = crate::gpui_shell::config::ui_language(cx); + let distribution = crate::platform::distribution::current(); + let managed = distribution.externally_managed(); let theme = cx.theme(); let muted = theme.muted_foreground; let ink = theme.foreground; @@ -77,11 +79,12 @@ impl SettingsPane { let danger = theme.danger; let base_px = self.font_size_px(cx); let cached_update = crate::update_download::cached_asset().filter(|asset| { - crate::update_check::is_newer(&asset.version, env!("CARGO_PKG_VERSION")) - || matches!( - crate::update_download::status(asset), - crate::update_download::DownloadStatus::InstallFailed(_) - ) + !managed + && (crate::update_check::is_newer(&asset.version, env!("CARGO_PKG_VERSION")) + || matches!( + crate::update_download::status(asset), + crate::update_download::DownloadStatus::InstallFailed(_) + )) }); let checking = matches!(self.about_update, AboutUpdateState::Checking); let (status, status_color): (SharedString, Hsla) = match &self.about_update { @@ -104,14 +107,21 @@ impl SettingsPane { danger, ), }; + let (status, status_color) = if managed { + (language.text(crate::i18n::Message::UpdateManagedLabel).into(), muted) + } else { + (status, status_color) + }; let update_button = NebulaButton::new("about-check-updates") - .label(if checking { + .label(if managed { + language.text(crate::i18n::Message::UpdateManagedLabel) + } else if checking { language.pick("正在检查…", "Checking...") } else { language.pick("检查更新", "Check for updates") }) .primary() - .disabled(checking) + .disabled(checking || managed) .on_click(cx.listener(|this, _, window, cx| this.check_for_updates(window, cx))); let status_badge = h_flex() @@ -231,26 +241,34 @@ impl SettingsPane { .flex_1() .min_w(px(280.0)) .child(section_title(language.pick("版本与更新", "Version and updates"))) - .child(Self::about_value_row( - language.pick("自动检查更新", "Automatically check for updates"), - auto_update, - cx, - )) - .child(Self::about_value_row( - language.text(crate::i18n::Message::UpdateAutoDownload), - predownload, - cx, - )) + .when(!managed, |column| { + column + .child(Self::about_value_row( + language.pick("自动检查更新", "Automatically check for updates"), + auto_update, + cx, + )) + .child(Self::about_value_row( + language.text(crate::i18n::Message::UpdateAutoDownload), + predownload, + cx, + )) + }) .child(Self::about_value_row( language.pick("更新通道", "Update channel"), - div().text_color(muted).child("Stable"), + div().text_color(muted).child(distribution.label()), cx, )) - .child(Self::about_value_row( - language.pick("上次检查", "Last checked"), - div().text_color(muted).child(last_checked), - cx, - )); + .when(!managed, |column| { + column.child(Self::about_value_row( + language.pick("上次检查", "Last checked"), + div().text_color(muted).child(last_checked), + cx, + )) + }) + .when_some(distribution.message(), |column, message| { + column.child(div().text_color(muted).child(language.text(message))) + }); let actions = v_flex() .flex_1() .min_w(px(280.0)) diff --git a/nebula_app/src/gpui_shell/settings_pane/backup.rs b/nebula_app/src/gpui_shell/settings_pane/backup.rs index b101acf..5914b54 100644 --- a/nebula_app/src/gpui_shell/settings_pane/backup.rs +++ b/nebula_app/src/gpui_shell/settings_pane/backup.rs @@ -1,115 +1,146 @@ -//! Backup operations and view-owned state; storage and encryption have one authority. +//! 备份设置的草稿与操作生命周期;文件、协议和加密规则归业务层所有。 use super::*; -use crate::backup_remote::{self as remote, BackupProtocol, preferences::ConfigWriter}; +use crate::backup_remote::{self as remote, BackupProtocol, BackupRemoteConfig, Snapshot}; +use crate::encrypted_backup::{ + self as archive, BackupArchive, BackupCategory, BackupSelection, CategorySummary, +}; use crate::i18n::Message; +use zeroize::Zeroizing; +mod drawer; +mod form; +mod operations; +mod setup; #[cfg(all(test, feature = "gpui-test-support"))] mod tests; mod view; #[derive(Default)] -pub(super) struct BackupUiState { - initialized: bool, - configuration: bool, - scope_open: bool, - writer: ConfigWriter, - save_revision: u64, - save_result: Option>, - save_watch: Option>, - list_revision: u64, - listing: bool, - snapshots: Vec, - checked: bool, - list_error: Option, - secret_ready: Option, - secret_busy: bool, - secret_revision: u64, +struct BackupPassword(Option>); +impl gpui::Global for BackupPassword {} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum BackupSheet { + Storage, + Backup, + Export, + Restore, + Password, } +#[derive(Clone)] enum RestoreSource { File(std::path::PathBuf), - Remote(Option), + Remote(String), +} + +#[derive(Default)] +pub(super) struct BackupUiState { + initialized: bool, + step: usize, + draft: BackupRemoteConfig, + tested: bool, + draft_snapshots: Vec, + snapshots: Vec, + listing: bool, + list_error: Option, + summary: Vec, + summary_loading: bool, + summary_error: Option, + confirm: Option>, + sheet: Option, + sheet_focus: Option, + previous_focus: Option, + source: Option, + opened: Option, + restore_pass: Option>, + undo: Option<(archive::recovery::RestorePoint, Zeroizing)>, + known: std::collections::HashMap, + task: Option>, +} + +fn recommended() -> BackupSelection { + BackupSelection::from_categories([ + BackupCategory::Appearance, + BackupCategory::Config, + BackupCategory::Assistant, + BackupCategory::Ssh, + BackupCategory::Session, + ]) } impl SettingsPane { - fn initialize_backup(&mut self, cx: &mut Context) { + fn initialize_backup(&mut self, window: &mut Window, cx: &mut Context) { if self.backup_ui.initialized { return; } self.backup_ui.initialized = true; - for input in self.backup_remote_inputs.clone() { + self.backup_ui.step = 1; + self.backup_ui.confirm = Some(cx.new(|cx| InputState::new(window, cx).masked(true))); + self.backup_ui.sheet_focus = Some(cx.focus_handle()); + self.backup_ui.draft = self.backup_remote.clone(); + if self.backup_remote.protocol == BackupProtocol::Off { + self.backup_selection = recommended(); + self.backup_ui.draft.protocol = BackupProtocol::WebDav; + self.backup_ui.draft.webdav_url = "https://dav.jianguoyun.com/dav/pebrel_backup".into(); + } + self.fill_backup_fields(window, cx); + for input in self + .backup_remote_inputs + .iter() + .chain([&self.backup_secret_input]) + .cloned() + .collect::>() + { self._subscriptions.push(cx.subscribe(&input, |this, _, event, cx| { if matches!(event, InputEvent::Change) { - let old = this.backup_remote.clone(); - this.read_backup_fields(cx); - if this.backup_remote != old { - this.queue_backup_save(cx); - } + this.backup_ui.tested = false; + cx.notify(); } })); } - self.read_backup_secret_status(cx); - self.backup_ui.configuration = self.backup_remote.protocol == BackupProtocol::Off; + if self.backup_remote.protocol != BackupProtocol::Off { + self.refresh_backup_snapshots(cx); + } } fn read_backup_fields(&mut self, cx: &App) { for (index, input) in self.backup_remote_inputs.iter().enumerate() { - self.backup_remote.set_slot(index, input.read(cx).value().trim().to_owned()); + self.backup_ui.draft.set_slot(index, input.read(cx).value().trim().to_owned()); } - self.backup_remote.selection = self.backup_selection; } - fn queue_backup_save(&mut self, cx: &mut Context) { - self.backup_remote.selection = self.backup_selection; - self.backup_ui.checked = false; - self.backup_ui.snapshots.clear(); - self.backup_ui.list_error = None; - self.backup_ui.list_revision = self.backup_ui.list_revision.wrapping_add(1); - self.backup_ui.listing = false; - self.backup_ui.save_result = None; - let writer = self.backup_ui.writer.clone(); - let revision = writer.submit(self.backup_remote.clone()); - self.backup_ui.save_revision = revision; - // The observer can stop with the view; the writer still persists the last edit. - self.backup_ui.save_watch = Some(cx.spawn(async move |this, cx| { - loop { - cx.background_executor().timer(Duration::from_millis(100)).await; - if let Some(result) = writer.result(revision) { - let _ = this.update(cx, |this, cx| { - if this.backup_ui.save_revision == revision { - this.backup_ui.save_result = Some(result); - cx.notify(); - } - }); - break; - } - if this.upgrade().is_none() { - break; - } - } - })); - cx.notify(); - } - - fn read_backup_secret_status(&mut self, cx: &mut Context) { - let protocol = self.backup_remote.protocol; - self.backup_ui.secret_revision = self.backup_ui.secret_revision.wrapping_add(1); - let revision = self.backup_ui.secret_revision; - self.backup_ui.secret_ready = None; - let task = - cx.background_executor().spawn(async move { remote::protocol_secret_set(protocol) }); - cx.spawn(async move |this, cx| { - let ready = task.await; - let _ = this.update(cx, |this, cx| { - if this.backup_ui.secret_revision == revision - && this.backup_remote.protocol == protocol - { - this.backup_ui.secret_ready = Some(ready); - } - cx.notify(); + fn fill_backup_fields(&self, window: &mut Window, cx: &mut Context) { + let language = crate::gpui_shell::config::ui_language(cx); + let fields = form::storage_fields(&self.backup_ui.draft); + for (index, input) in self.backup_remote_inputs.iter().enumerate() { + let value = self.backup_ui.draft.slot(index).unwrap_or_default().to_owned(); + let placeholder = fields + .get(index) + .filter(|field| !field.secret) + .and_then(|field| field.placeholder) + .map(|id| language.text(id)) + .unwrap_or(""); + input.update(cx, |input, cx| { + input.set_placeholder(placeholder, window, cx); + input.set_value(value, window, cx); }); - }) - .detach(); + } + let secret_placeholder = fields + .iter() + .find(|field| field.secret) + .and_then(|field| field.placeholder) + .map(|id| language.text(id)) + .unwrap_or(""); + self.backup_secret_input + .update(cx, |input, cx| input.set_placeholder(secret_placeholder, window, cx)); + self.backup_pass_input.update(cx, |input, cx| { + input.set_placeholder( + language.text(Message::BackupFlowNewPasswordPlaceholder), + window, + cx, + ) + }); } fn select_backup_protocol( @@ -119,62 +150,55 @@ impl SettingsPane { window: &mut Window, cx: &mut Context, ) { - if self.backup_busy || self.backup_ui.secret_busy { + if self.backup_busy { return; } self.read_backup_fields(cx); - self.backup_remote.protocol = protocol; + self.backup_ui.draft.protocol = protocol; if nutstore { - self.backup_remote.webdav_url = "https://dav.jianguoyun.com/dav/pebrel_backup".into(); + self.backup_ui.draft.webdav_url = "https://dav.jianguoyun.com/dav/pebrel_backup".into(); + } else if protocol == BackupProtocol::WebDav + && self.backup_ui.draft.webdav_url.starts_with("https://dav.jianguoyun.com/") + { + self.backup_ui.draft.webdav_url.clear(); } - for (index, input) in self.backup_remote_inputs.iter().enumerate() { - let value = self.backup_remote.slot(index).unwrap_or_default().to_owned(); - input.update(cx, |input, cx| input.set_value(value, window, cx)); + if protocol == BackupProtocol::S3 && self.backup_ui.draft.s3_region.is_empty() { + self.backup_ui.draft.s3_region = "auto".into(); } + self.fill_backup_fields(window, cx); self.backup_secret_input.update(cx, |input, cx| input.set_value("", window, cx)); - self.queue_backup_save(cx); - self.read_backup_secret_status(cx); + self.backup_ui.tested = false; + self.backup_status = None; + cx.notify(); } - fn store_remote_secret(&mut self, window: &mut Window, cx: &mut Context) { - if self.backup_ui.secret_busy || self.backup_busy { - return; - } - let secret = zeroize::Zeroizing::new(self.backup_secret_input.read(cx).value().to_string()); - if secret.is_empty() { - self.backup_status = Some(BackupStatus::CredentialEmpty); - cx.notify(); - return; - } - let config = self.backup_remote.clone(); - self.backup_ui.secret_busy = true; - self.backup_ui.secret_revision = self.backup_ui.secret_revision.wrapping_add(1); - let task = cx.background_executor().spawn(async move { - match config.protocol { - BackupProtocol::WebDav => { - remote::store_webdav_password(&config.webdav_username, &secret) - }, - BackupProtocol::S3 => remote::store_s3_secret(&config.s3_access_key, &secret), - _ => Err("No separate credential is required".into()), - } - }); - cx.spawn_in(window, async move |this, cx| { - let result = task.await; + fn backup_task( + &mut self, + window: &mut Window, + cx: &mut Context, + work: impl std::future::Future> + Send + 'static, + done: impl FnOnce(&mut Self, T, &mut Window, &mut Context) + 'static, + ) { + self.backup_seq = self.backup_seq.wrapping_add(1); + let seq = self.backup_seq; + self.backup_busy = true; + self.backup_status = Some(BackupStatus::Processing); + let work = cx.background_executor().spawn(work); + self.backup_ui.task = Some(cx.spawn_in(window, async move |this, cx| { + let result = work.await; let _ = this.update_in(cx, |this, window, cx| { - this.backup_ui.secret_busy = false; + if seq != this.backup_seq { + return; + } + this.backup_busy = false; + this.backup_status = None; match result { - Ok(()) => { - this.backup_secret_input - .update(cx, |input, cx| input.set_value("", window, cx)); - this.backup_ui.secret_ready = Some(true); - this.backup_status = Some(BackupStatus::CredentialSaved); - }, + Ok(value) => done(this, value, window, cx), Err(error) => this.backup_status = Some(BackupStatus::Error(error)), } cx.notify(); }); - }) - .detach(); + })); cx.notify(); } @@ -182,213 +206,195 @@ impl SettingsPane { if self.backup_ui.listing || self.backup_remote.protocol == BackupProtocol::Off { return; } - self.backup_ui.list_revision = self.backup_ui.list_revision.wrapping_add(1); - let revision = self.backup_ui.list_revision; let config = self.backup_remote.clone(); + let original = config.clone(); self.backup_ui.listing = true; self.backup_ui.list_error = None; - let task = cx.background_executor().spawn(async move { remote::snapshots(&config) }); + let task = + cx.background_executor().spawn(async move { remote::snapshot_details(&config, None) }); cx.spawn(async move |this, cx| { let result = task.await; let _ = this.update(cx, |this, cx| { - if this.backup_ui.list_revision != revision { + this.backup_ui.listing = false; + if this.backup_remote != original { + this.refresh_backup_snapshots(cx); return; } - this.backup_ui.listing = false; - this.backup_ui.checked = result.is_ok(); match result { - Ok(names) => this.backup_ui.snapshots = names, - Err(error) => { - this.backup_ui.snapshots.clear(); - this.backup_ui.list_error = Some(error); - }, + Ok(entries) => this.backup_ui.snapshots = entries, + Err(error) => this.backup_ui.list_error = Some(error), } cx.notify(); }); }) .detach(); - cx.notify(); } - fn backup_passphrase(&mut self, cx: &mut Context) -> Option> { - let pass = self.backup_pass_input.read(cx).value().to_string(); - if pass.chars().count() < 8 { - self.backup_status = Some(BackupStatus::PassphraseTooShort); - self.backup_ui.configuration = true; - cx.notify(); - return None; + fn load_backup_summary(&mut self, cx: &mut Context) { + if self.backup_ui.summary_loading { + return; } - Some(zeroize::Zeroizing::new(pass)) - } - - fn backup_run_async( - &mut self, - task: impl std::future::Future> + Send + 'static, - cx: &mut Context, - ) { - self.backup_seq = self.backup_seq.wrapping_add(1); - let seq = self.backup_seq; - self.backup_busy = true; - self.backup_status = Some(BackupStatus::Processing); - let task = cx.background_executor().spawn(task); + self.backup_ui.summary_loading = true; + self.backup_ui.summary_error = None; + let all = BackupSelection { + appearance: true, + config: true, + ssh: true, + sync: true, + assistant: true, + session: true, + directory_history: true, + command_history: true, + fonts: true, + }; + let task = cx + .background_executor() + .spawn(async move { archive::collect(all).map(|a| a.summary()) }); cx.spawn(async move |this, cx| { let result = task.await; - let _ = this.update(cx, |pane, cx| { - if seq != pane.backup_seq { - return; - } - pane.backup_busy = false; - if matches!(result, Ok(BackupCompletion::Restored | BackupCompletion::Pulled(_))) { - pane.reload_after_restore(cx); - } - let refresh = - matches!(result, Ok(BackupCompletion::Pushed(_) | BackupCompletion::Pulled(_))); - pane.backup_status = Some(match result { - Ok(completion) => BackupStatus::Completed(completion), - Err(error) => BackupStatus::Error(error), - }); - if refresh { - pane.refresh_backup_snapshots(cx); + let _ = this.update(cx, |this, cx| { + this.backup_ui.summary_loading = false; + match result { + Ok(summary) => this.backup_ui.summary = summary, + Err(error) => this.backup_ui.summary_error = Some(error), } cx.notify(); }); }) .detach(); - cx.notify(); } - fn push_remote(&mut self, cx: &mut Context) { + fn check_backup_connection(&mut self, save: bool, window: &mut Window, cx: &mut Context) { if self.backup_busy { return; } - let Some(pass) = self.backup_passphrase(cx) else { return }; - if self.backup_selection.is_empty() { - self.backup_status = Some(BackupStatus::SelectionRequired); - cx.notify(); - return; - } - let selection = self.backup_selection; - let config = self.backup_remote.clone(); - self.backup_run_async( - async move { - let archive = crate::encrypted_backup::collect(selection)?; - let packet = crate::encrypted_backup::seal(&archive, &pass)?; - remote::push_to(&config, &packet).map(BackupCompletion::Pushed) - }, + self.read_backup_fields(cx); + let config = self.backup_ui.draft.clone(); + let secret = Zeroizing::new(self.backup_secret_input.read(cx).value().to_string()); + self.backup_ui.tested = false; + self.backup_task( + window, cx, + async move { + remote::snapshot_details(&config, (!secret.is_empty()).then_some(secret.as_str())) + }, + move |this, entries, window, cx| { + this.backup_ui.tested = true; + this.backup_ui.draft_snapshots = entries; + if save { + this.save_backup_storage(false, window, cx); + } else if this.backup_ui.sheet.is_none() { + this.backup_ui.step = 3; + this.backup_pass_input.update(cx, |input, cx| input.focus(window, cx)); + } + }, ); } - fn confirm_backup_restore( + fn next_backup_step(&mut self, window: &mut Window, cx: &mut Context) { + if self.backup_busy { + return; + } + self.backup_status = None; + match self.backup_ui.step { + 1 => { + self.backup_ui.step = 2; + self.backup_remote_inputs[0].update(cx, |input, cx| input.focus(window, cx)); + }, + 2 => self.check_backup_connection(false, window, cx), + 3 => { + let pass = self.backup_pass_input.read(cx).value(); + let confirm = self.backup_ui.confirm.as_ref().unwrap().read(cx).value(); + if pass.chars().count() < 8 { + self.backup_status = Some(BackupStatus::PassphraseTooShort); + } else if pass != confirm { + self.backup_status = Some(BackupStatus::Error( + crate::gpui_shell::config::ui_language(cx) + .text(Message::BackupFlowMismatch) + .into(), + )); + } else { + cx.set_global(BackupPassword(Some(Zeroizing::new(pass.to_string())))); + self.backup_ui.step = 4; + self.load_backup_summary(cx); + } + }, + _ => self.save_backup_storage(true, window, cx), + } + cx.notify(); + } + + fn save_backup_storage( &mut self, - source: RestoreSource, + backup_now: bool, window: &mut Window, cx: &mut Context, ) { - if self.backup_busy { + if self.backup_busy || self.backup_selection.is_empty() { return; } - let Some(pass) = self.backup_passphrase(cx) else { return }; - let language = crate::gpui_shell::config::ui_language(cx); - let prompt = window.prompt( - gpui::PromptLevel::Warning, - language.text(Message::CloudRestore), - Some(language.text(Message::CloudRestoreWarning)), - &[language.text(Message::CommonCancel), language.text(Message::CloudRestore)], + if !self.backup_ui.tested { + self.check_backup_connection(true, window, cx); + return; + } + self.read_backup_fields(cx); + let mut config = self.backup_ui.draft.clone(); + config.selection = self.backup_selection; + let secret = Zeroizing::new(self.backup_secret_input.read(cx).value().to_string()); + self.backup_task( + window, cx, - ); - let config = self.backup_remote.clone(); - self.backup_busy = true; - cx.spawn(async move |this, cx| { - let accepted = matches!(prompt.await, Ok(1)); - let _ = this.update(cx, |pane, cx| { - pane.backup_busy = false; - if accepted { - pane.backup_run_async( - async move { - let (name, packet) = match source { - RestoreSource::File(path) => { - (None, std::fs::read(path).map_err(|error| error.to_string())?) - }, - RestoreSource::Remote(name) => { - let (name, packet) = - remote::pull_from(&config, name.as_deref())?; - (Some(name), packet) - }, - }; - crate::encrypted_backup::restore(&packet, &pass)?; - Ok(name - .map(BackupCompletion::Pulled) - .unwrap_or(BackupCompletion::Restored)) + async move { + if !secret.is_empty() { + match config.protocol { + BackupProtocol::WebDav => { + remote::store_webdav_password(&config.webdav_username, &secret)? }, - cx, - ); - } - cx.notify(); - }); - }) - .detach(); - } - - fn export_backup(&mut self, cx: &mut Context) { - if self.backup_busy { - return; - } - let Some(pass) = self.backup_passphrase(cx) else { return }; - let selection = self.backup_selection; - if selection.is_empty() { - self.backup_status = Some(BackupStatus::SelectionRequired); - cx.notify(); - return; - } - let picked = cx - .prompt_for_new_path(&crate::display::nebula_data_dir(), Some("pebrel.pebrel-backup")); - self.backup_busy = true; - cx.spawn(async move |this, cx| { - let path = picked.await; - let _ = this.update(cx, |pane, cx| { - pane.backup_busy = false; - if let Ok(Ok(Some(path))) = path { - pane.backup_run_async( - async move { - let archive = crate::encrypted_backup::collect(selection)?; - let packet = crate::encrypted_backup::seal(&archive, &pass)?; - std::fs::write(&path, packet).map_err(|error| error.to_string())?; - Ok(BackupCompletion::Exported(path)) + BackupProtocol::S3 => { + remote::store_s3_secret(&config.s3_access_key, &secret)? }, - cx, - ); - } - cx.notify(); - }); - }) - .detach(); - } - - fn restore_backup(&mut self, window: &mut Window, cx: &mut Context) { - if self.backup_busy { - return; - } - let picked = cx.prompt_for_paths(gpui::PathPromptOptions { - files: true, - directories: false, - multiple: false, - prompt: None, - }); - self.backup_busy = true; - cx.spawn_in(window, async move |this, cx| { - let paths = picked.await; - let _ = this.update_in(cx, |pane, window, cx| { - pane.backup_busy = false; - if let Ok(Ok(Some(paths))) = paths { - if let Some(path) = paths.into_iter().next() { - pane.confirm_backup_restore(RestoreSource::File(path), window, cx); + _ => {}, } } - cx.notify(); - }); - }) - .detach(); + config.save()?; + Ok(config) + }, + move |this, config, window, cx| { + this.backup_remote = config; + this.backup_ui.snapshots = std::mem::take(&mut this.backup_ui.draft_snapshots); + this.backup_ui.list_error = None; + this.close_backup_sheet(window, cx); + this.clear_backup_inputs(window, cx); + this.backup_status = Some(BackupStatus::RemoteConfigSaved); + if backup_now { + this.perform_backup(false, window, cx); + } + }, + ); + } + + fn backup_passphrase(&mut self, cx: &mut Context) -> Option> { + let input = self.backup_pass_input.read(cx).value(); + let pass = if input.is_empty() { + cx.try_global::().and_then(|p| p.0.clone()) + } else { + Some(Zeroizing::new(input.to_string())) + }; + if pass.as_ref().is_none_or(|p| p.chars().count() < 8) { + self.backup_status = Some(BackupStatus::PassphraseTooShort); + cx.notify(); + return None; + } + pass + } + + fn clear_backup_inputs(&self, window: &mut Window, cx: &mut Context) { + for input in [&self.backup_pass_input, &self.backup_secret_input] + .into_iter() + .chain(self.backup_ui.confirm.iter()) + { + input.update(cx, |input, cx| input.set_value("", window, cx)); + } } pub(super) fn reload_after_restore(&mut self, cx: &mut Context) { diff --git a/nebula_app/src/gpui_shell/settings_pane/backup/drawer.rs b/nebula_app/src/gpui_shell/settings_pane/backup/drawer.rs new file mode 100644 index 0000000..1174083 --- /dev/null +++ b/nebula_app/src/gpui_shell/settings_pane/backup/drawer.rs @@ -0,0 +1,271 @@ +//! 设置页拥有抽屉和焦点;离开设置时不会在窗口全局留下悬空弹层。 +use super::view::caption; +use super::*; +use gpui_component::FocusTrapElement as _; + +impl SettingsPane { + pub(in crate::gpui_shell::settings_pane) fn backup_drawer( + &self, + window: &Window, + cx: &mut Context, + ) -> Option { + let sheet = self.backup_ui.sheet?; + let l = crate::gpui_shell::config::ui_language(cx); + let focus = self.backup_ui.sheet_focus.as_ref()?; + let (title, lead, action) = match sheet { + BackupSheet::Storage => { + (Message::BackupFlowStorage, Message::BackupFlowStorageHint, Message::CommonSave) + }, + BackupSheet::Backup => { + (Message::CloudBackupNow, Message::BackupFlowUploadHint, Message::BackupFlowStart) + }, + BackupSheet::Export => { + (Message::CloudExport, Message::BackupFlowExportHint, Message::CloudExport) + }, + BackupSheet::Restore => { + (Message::CloudRestore, Message::BackupFlowRestoreHint, Message::CloudRestore) + }, + BackupSheet::Password => { + (Message::CloudPassphrase, Message::BackupFlowPasswordHint, Message::CommonSave) + }, + }; + let cached = cx.try_global::().is_some_and(|p| p.0.is_some()); + let mut body = v_flex().w_full().gap_4(); + body = match sheet { + BackupSheet::Storage => body + .child(self.backup_provider_menu(cx)) + .child(caption(l.text(Message::BackupFlowSwitchHint), cx)) + .child(self.backup_storage_fields(true, cx)) + .child( + Button::new("backup-check") + .label(l.text(Message::CloudTest)) + .disabled(self.backup_busy) + .on_click(cx.listener(|this, _, window, cx| { + this.check_backup_connection(false, window, cx) + })), + ) + .child(caption( + l.text(if self.backup_ui.tested { + Message::BackupFlowConnected + } else { + Message::BackupFlowReadOnlyCheck + }), + cx, + )), + BackupSheet::Backup | BackupSheet::Export => body + .child( + h_flex() + .gap_2() + .child(div().flex_1().child(l.text(Message::BackupFlowChooseContents))) + .child( + Button::new("backup-defaults") + .label(l.text(Message::BackupFlowDefaults)) + .small() + .ghost() + .disabled(self.backup_busy) + .on_click(cx.listener(|this, _, _, cx| { + this.backup_selection = recommended(); + cx.notify(); + })), + ), + ) + .child(self.backup_content_picker(false, false, cx)) + .child(caption(l.text(Message::BackupFlowExcluded), cx)) + .when(!cached, |d| d.child(self.backup_password_field(cx))), + BackupSheet::Restore => { + let source = match &self.backup_ui.source { + Some(RestoreSource::File(path)) => { + path.file_name().unwrap_or_default().to_string_lossy().into_owned() + }, + Some(RestoreSource::Remote(name)) => name.clone(), + None => String::new(), + }; + body = body.child(caption(source, cx)); + if self.backup_ui.opened.is_some() { + body.child(caption(l.text(Message::BackupFlowUnselectedUntouched), cx)) + .child(self.backup_content_picker(false, true, cx)) + .child(caption(l.text(Message::BackupFlowRestoreWarning), cx)) + } else { + body.child(self.backup_password_field(cx)).child( + Button::new("backup-unlock") + .debug_selector(|| "backup-unlock".into()) + .label(l.text(Message::BackupFlowUnlock)) + .disabled(self.backup_busy) + .on_click( + cx.listener(|this, _, window, cx| this.unlock_backup(window, cx)), + ), + ) + } + }, + BackupSheet::Password => body.child(self.backup_password_field(cx)), + }; + body = body.children(self.backup_status_view(cx)); + let disabled = self.backup_busy + || match sheet { + BackupSheet::Backup | BackupSheet::Export => self.backup_selection.is_empty(), + BackupSheet::Restore => { + self.backup_ui.opened.is_none() || self.backup_selection.is_empty() + }, + _ => false, + }; + let viewport = window.viewport_size(); + let drawer = v_flex() + .id("backup-drawer") + .debug_selector(|| "backup-drawer".into()) + .role(gpui::accesskit::Role::Dialog) + .aria_label(l.text(title)) + .w(px(440.0).min(viewport.width)) + .h_full() + .min_w_0() + .bg(crate::gpui_shell::theme::settings_panel_bg(cx)) + .text_color(cx.theme().foreground) + .border_l_1() + .border_color(cx.theme().border) + .shadow_xl() + .track_focus(focus) + .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()) + .on_key_down(cx.listener(|this, event: &KeyDownEvent, window, cx| { + if event.keystroke.key.eq_ignore_ascii_case("escape") { + cx.stop_propagation(); + this.cancel_backup_sheet(window, cx); + } + })) + .child( + h_flex() + .px_6() + .py_5() + .gap_3() + .border_b_1() + .border_color(cx.theme().border) + .child( + v_flex() + .gap_2() + .flex_1() + .min_w_0() + .child(div().text_size(px(18.0)).font_semibold().child(l.text(title))) + .child(caption(l.text(lead), cx)), + ) + .child( + Button::new("backup-close-drawer") + .icon(IconName::Close) + .small() + .ghost() + .tooltip(l.text(Message::CommonClose)) + .disabled(self.backup_busy) + .on_click(cx.listener(|this, _, window, cx| { + this.cancel_backup_sheet(window, cx) + })), + ), + ) + .child(div().flex_1().min_h_0().overflow_y_scrollbar().child(body.p_6())) + .child( + v_flex() + .px_6() + .py_4() + .gap_3() + .border_t_1() + .border_color(cx.theme().border) + .when( + matches!( + sheet, + BackupSheet::Backup | BackupSheet::Export | BackupSheet::Restore + ), + |d| { + d.child( + self.backup_selection_summary(sheet == BackupSheet::Restore, cx), + ) + }, + ) + .child( + h_flex() + .justify_end() + .gap_2() + .child( + Button::new("backup-cancel") + .debug_selector(|| "backup-cancel".into()) + .label(l.text(Message::CommonCancel)) + .ghost() + .small() + .disabled(self.backup_busy) + .on_click(cx.listener(|this, _, window, cx| { + this.cancel_backup_sheet(window, cx) + })), + ) + .child( + Button::new("backup-confirm") + .debug_selector(|| "backup-confirm".into()) + .label(l.text(if self.backup_busy { + Message::BackupFlowProcessing + } else { + action + })) + .primary() + .small() + .disabled(disabled) + .on_click(cx.listener( + move |this, _, window, cx| match sheet { + BackupSheet::Storage => { + this.check_backup_connection(true, window, cx) + }, + BackupSheet::Backup => { + this.perform_backup(false, window, cx) + }, + BackupSheet::Export => { + this.perform_backup(true, window, cx) + }, + BackupSheet::Restore => { + this.restore_selected_backup(window, cx) + }, + BackupSheet::Password => { + if let Some(pass) = this.backup_passphrase(cx) { + cx.set_global(BackupPassword(Some(pass))); + this.close_backup_sheet(window, cx); + this.clear_backup_inputs(window, cx); + } + }, + }, + )), + ), + ), + ) + .focus_trap("backup-drawer-trap", focus); + Some( + deferred( + anchored() + .anchor(gpui::Anchor::TopLeft) + .position(gpui::point(px(0.0), px(0.0))) + .child( + div() + .id("backup-overlay") + .w(viewport.width) + .h(viewport.height) + .flex() + .justify_end() + .occlude() + .bg(gpui::hsla(0.0, 0.0, 0.0, 0.30)) + .on_mouse_down( + MouseButton::Left, + cx.listener(|this, _, window, cx| { + cx.stop_propagation(); + this.cancel_backup_sheet(window, cx); + }), + ) + .on_scroll_wheel(|_, _, cx| cx.stop_propagation()) + .child(drawer), + ), + ) + .with_priority(8) + .into_any_element(), + ) + } + + fn backup_password_field(&self, cx: &App) -> gpui::Div { + let l = crate::gpui_shell::config::ui_language(cx); + v_flex() + .w_full() + .gap_2() + .child(l.text(Message::CloudPassphrase)) + .child(Input::new(&self.backup_pass_input).h(px(34.0)).disabled(self.backup_busy)) + .child(caption(l.text(Message::BackupFlowPasswordHint), cx)) + } +} diff --git a/nebula_app/src/gpui_shell/settings_pane/backup/form.rs b/nebula_app/src/gpui_shell/settings_pane/backup/form.rs new file mode 100644 index 0000000..0831432 --- /dev/null +++ b/nebula_app/src/gpui_shell/settings_pane/backup/form.rs @@ -0,0 +1,208 @@ +//! Storage field presentation shared by the setup wizard and storage drawer. +use super::view::{caption, provider}; +use super::*; +use gpui_component::menu::PopupMenuItem; + +pub(super) struct StorageField { + pub label: Message, + pub placeholder: Option, + hint: Option, + mono: bool, + pub secret: bool, +} + +impl StorageField { + const fn new(label: Message, placeholder: Option) -> Self { + Self { label, placeholder, hint: None, mono: false, secret: false } + } + const fn mono(mut self) -> Self { + self.mono = true; + self + } + const fn secret(mut self) -> Self { + self.secret = true; + self + } + const fn hint(mut self, hint: Message) -> Self { + self.hint = Some(hint); + self + } +} + +pub(super) fn storage_fields(config: &BackupRemoteConfig) -> &'static [StorageField] { + use Message::*; + match config.protocol { + BackupProtocol::WebDav if provider(config) == CloudNutstore => { + const { + &[ + StorageField::new(BackupFlowAddressLabel, None).mono(), + StorageField::new(CloudUsername, Some(BackupFlowNutstoreAccountPlaceholder)), + StorageField::new( + BackupFlowAppPasswordLabel, + Some(BackupFlowAppPasswordPlaceholder), + ) + .secret() + .hint(BackupFlowAppPasswordHint), + ] + } + }, + BackupProtocol::WebDav => { + const { + &[ + StorageField::new(BackupFlowAddressLabel, Some(BackupFlowWebdavPlaceholder)) + .mono() + .hint(BackupFlowWebdavAddressHint), + StorageField::new(CloudUsername, Some(BackupFlowUsernamePlaceholder)), + StorageField::new(BackupFlowPasswordLabel, Some(BackupFlowPasswordPlaceholder)) + .secret(), + ] + } + }, + BackupProtocol::S3 => { + const { + &[ + StorageField::new(CloudS3Address, Some(BackupFlowS3Placeholder)) + .mono() + .hint(BackupFlowS3AddressHint), + StorageField::new(CloudRegion, None).mono(), + StorageField::new(CloudBucket, Some(BackupFlowBucketPlaceholder)).mono(), + StorageField::new( + BackupFlowAccessKeyLabel, + Some(BackupFlowAccessKeyPlaceholder), + ) + .mono(), + StorageField::new( + BackupFlowSecretKeyLabel, + Some(BackupFlowSecretKeyPlaceholder), + ) + .secret(), + ] + } + }, + BackupProtocol::Sftp => { + const { + &[ + StorageField::new(BackupFlowSshHostLabel, None).hint(BackupFlowSshHostHint), + StorageField::new(CloudRemotePath, Some(BackupFlowRemotePathPlaceholder)) + .mono(), + ] + } + }, + BackupProtocol::Folder => { + const { + &[StorageField::new(BackupFlowFolderLabel, Some(BackupFlowFolderPlaceholder)) + .mono()] + } + }, + BackupProtocol::Off => &[], + } +} + +impl SettingsPane { + pub(super) fn backup_storage_fields(&self, stacked: bool, cx: &mut Context) -> gpui::Div { + let l = crate::gpui_shell::config::ui_language(cx); + let protocol = self.backup_ui.draft.protocol; + let mut fields = v_flex().w_full().gap_0(); + for (index, field) in storage_fields(&self.backup_ui.draft).iter().enumerate() { + let mut control = h_flex() + .gap_2() + .min_w_0() + .debug_selector(move || format!("backup-field-control-{index}")) + .when(stacked, |d| d.w_full()) + .when(!stacked, |d| d.w(px(300.0)).flex_shrink_0()); + if protocol == BackupProtocol::Sftp && index == 0 { + let hosts = self.ssh_hosts.merged_with_labels(); + let owner = cx.entity().downgrade(); + let selected = self.backup_remote_inputs[0].read(cx).value(); + control = control.child( + Button::new("backup-ssh-host") + .flex_1() + .min_w_0() + .dropdown_caret(true) + .label(if selected.is_empty() { + l.text(Message::CloudSshHost).into() + } else { + selected + }) + .disabled(self.backup_busy || hosts.is_empty()) + .dropdown_menu(move |mut menu, _, _| { + for (destination, label) in &hosts { + let owner = owner.clone(); + let destination = destination.clone(); + let label = if label.is_empty() { + destination.clone() + } else { + format!("{label} · {destination}") + }; + menu = menu.item(PopupMenuItem::new(label).on_click( + move |_, window, cx| { + let _ = owner.update(cx, |this, cx| { + this.backup_remote_inputs[0].update(cx, |input, cx| { + input.set_value(destination.clone(), window, cx) + }); + this.backup_ui.tested = false; + cx.notify(); + }); + }, + )); + } + menu + }), + ); + } else { + control = control.child( + div() + .flex_1() + .min_w_0() + .when(field.mono, |d| d.font_family(cx.theme().mono_font_family.clone())) + .child( + Input::new(if field.secret { + &self.backup_secret_input + } else { + &self.backup_remote_inputs[index] + }) + .aria_label(l.text(field.label)) + .text_size(px(if field.mono { 12.5 } else { 13.0 })) + .h(px(30.0)) + .disabled(self.backup_busy), + ), + ); + } + if protocol == BackupProtocol::Folder { + control = control.child( + Button::new("backup-browse") + .label(l.text(Message::BackupFlowBrowse)) + .small() + .disabled(self.backup_busy) + .on_click( + cx.listener(|this, _, window, cx| this.pick_backup_folder(window, cx)), + ), + ); + } + let description = v_flex() + .gap(px(3.0)) + .min_w_0() + .debug_selector(move || format!("backup-field-label-{index}")) + .when(!stacked, |d| d.flex_1()) + .child(div().text_size(px(14.0)).font_medium().child(l.text(field.label))) + .when(!stacked, |d| d.children(field.hint.map(|hint| caption(l.text(hint), cx)))); + fields = fields.child( + div() + .flex() + .w_full() + .py(px(12.0)) + .when(stacked, |d| d.flex_col().gap_2()) + .when(!stacked, |d| d.flex_row().items_center().gap(px(24.0))) + .child(description) + .child(control) + .when(stacked, |d| { + d.children(field.hint.map(|hint| caption(l.text(hint), cx))) + }), + ); + } + if stacked && matches!(protocol, BackupProtocol::WebDav | BackupProtocol::S3) { + fields = fields.child(caption(l.text(Message::BackupFlowCredentialHint), cx)); + } + fields + } +} diff --git a/nebula_app/src/gpui_shell/settings_pane/backup/operations.rs b/nebula_app/src/gpui_shell/settings_pane/backup/operations.rs new file mode 100644 index 0000000..ea40590 --- /dev/null +++ b/nebula_app/src/gpui_shell/settings_pane/backup/operations.rs @@ -0,0 +1,287 @@ +//! 上传、导出与分项恢复:后台执行,成功后再关闭抽屉和刷新页面。 +use super::*; + +impl SettingsPane { + pub(super) fn open_backup_sheet( + &mut self, + sheet: BackupSheet, + window: &mut Window, + cx: &mut Context, + ) { + if self.backup_busy { + return; + } + self.backup_status = None; + self.backup_ui.previous_focus = window.focused(cx); + self.backup_ui.sheet = Some(sheet); + self.backup_selection = self.backup_remote.selection; + self.backup_ui.opened = None; + self.backup_ui.restore_pass = None; + self.clear_backup_inputs(window, cx); + if sheet == BackupSheet::Storage { + self.backup_ui.draft = self.backup_remote.clone(); + self.backup_ui.tested = false; + self.fill_backup_fields(window, cx); + } + if matches!(sheet, BackupSheet::Backup | BackupSheet::Export) { + if self.backup_remote.protocol == BackupProtocol::Off { + self.backup_selection = recommended(); + } + self.load_backup_summary(cx); + } + self.backup_ui.sheet_focus.as_ref().unwrap().focus(window, cx); + cx.notify(); + } + + pub(super) fn close_backup_sheet(&mut self, window: &mut Window, cx: &mut Context) { + if self.backup_busy { + return; + } + self.backup_ui.sheet = None; + self.backup_ui.opened = None; + self.backup_ui.restore_pass = None; + self.backup_ui.source = None; + if let Some(focus) = self.backup_ui.previous_focus.take() { + focus.focus(window, cx); + } + cx.notify(); + } + + pub(super) fn cancel_backup_sheet(&mut self, window: &mut Window, cx: &mut Context) { + if self.backup_busy { + return; + } + self.close_backup_sheet(window, cx); + self.clear_backup_inputs(window, cx); + self.backup_selection = self.backup_remote.selection; + self.backup_status = None; + } + + pub(super) fn perform_backup( + &mut self, + export: bool, + window: &mut Window, + cx: &mut Context, + ) { + if self.backup_busy || self.backup_selection.is_empty() { + return; + } + let Some(pass) = self.backup_passphrase(cx) else { + return; + }; + let selection = self.backup_selection; + let existing = + (!export).then(|| self.backup_ui.snapshots.first().map(|s| s.name.clone())).flatten(); + let mut config = self.backup_remote.clone(); + config.selection = selection; + // 文件选择取消时不写配置、不加密,也不改变上一次选择。 + let path = export.then(|| { + cx.prompt_for_new_path(&crate::display::nebula_data_dir(), Some("pebrel.pebrel-backup")) + }); + self.backup_task( + window, + cx, + async move { + let output = match path { + Some(picker) => { + match picker.await.map_err(|e| e.to_string())?.map_err(|e| e.to_string())? { + Some(path) => Some(path), + None => return Ok(None), + } + }, + None => None, + }; + if let Some(name) = existing { + let (_, previous) = remote::pull_from(&config, Some(&name))?; + archive::open(&previous, &pass)?; + } + let archive = archive::collect(selection)?; + let packet = archive::seal(&archive, &pass)?; + let (snapshot, completion) = if let Some(path) = output { + crate::atomic_file::write(&path, &packet).map_err(|error| error.to_string())?; + (None, BackupCompletion::Exported(path)) + } else { + let (snapshot, message) = remote::push_snapshot(&config, &packet)?; + (Some(snapshot), BackupCompletion::Pushed(message)) + }; + // 选择在一次真实备份成功后保存;取消抽屉不会改变后续默认范围。 + let saved = config.save(); + Ok(Some((config, pass, archive.manifest, snapshot, completion, saved))) + }, + |this, result, window, cx| { + let Some((config, pass, manifest, snapshot, completion, saved)) = result else { + return; + }; + this.backup_remote = config; + cx.set_global(BackupPassword(Some(pass))); + if let Some(snapshot) = snapshot { + this.backup_ui.known.insert( + snapshot.name.clone(), + (manifest.categories.len(), manifest.device), + ); + this.backup_ui.snapshots.insert(0, snapshot); + this.refresh_backup_snapshots(cx); + } + this.close_backup_sheet(window, cx); + this.clear_backup_inputs(window, cx); + this.backup_status = Some(match saved { + Ok(()) => BackupStatus::Completed(completion), + Err(error) => BackupStatus::Error( + crate::gpui_shell::config::ui_language(cx) + .format(Message::BackupFlowSelectionSaveFailed, &[("error", &error)]), + ), + }); + }, + ); + } + + pub(super) fn pick_backup_folder(&mut self, window: &mut Window, cx: &mut Context) { + if self.backup_busy { + return; + } + let picker = cx.prompt_for_paths(gpui::PathPromptOptions { + files: false, + directories: true, + multiple: false, + prompt: None, + }); + self.backup_task( + window, + cx, + async move { picker.await.map_err(|e| e.to_string())?.map_err(|e| e.to_string()) }, + |this, paths, window, cx| { + if let Some(path) = paths.and_then(|paths| paths.into_iter().next()) { + this.backup_remote_inputs[0].update(cx, |input, cx| { + input.set_value(path.to_string_lossy().into_owned(), window, cx) + }); + this.read_backup_fields(cx); + this.backup_ui.tested = false; + } + }, + ); + } + + pub(super) fn restore_backup(&mut self, window: &mut Window, cx: &mut Context) { + if self.backup_busy { + return; + } + let picker = cx.prompt_for_paths(gpui::PathPromptOptions { + files: true, + directories: false, + multiple: false, + prompt: None, + }); + self.backup_task( + window, + cx, + async move { picker.await.map_err(|e| e.to_string())?.map_err(|e| e.to_string()) }, + |this, paths, window, cx| { + if let Some(path) = paths.and_then(|paths| paths.into_iter().next()) { + this.open_backup_restore(RestoreSource::File(path), window, cx); + } + }, + ); + } + + pub(super) fn open_backup_restore( + &mut self, + source: RestoreSource, + window: &mut Window, + cx: &mut Context, + ) { + if self.backup_busy { + return; + } + self.open_backup_sheet(BackupSheet::Restore, window, cx); + self.backup_ui.source = Some(source); + if cx.try_global::().is_some_and(|p| p.0.is_some()) { + self.unlock_backup(window, cx); + } else { + self.backup_pass_input.update(cx, |input, cx| input.focus(window, cx)); + } + } + + pub(super) fn unlock_backup(&mut self, window: &mut Window, cx: &mut Context) { + if self.backup_busy { + return; + } + let Some(source) = self.backup_ui.source.clone() else { + return; + }; + let Some(pass) = self.backup_passphrase(cx) else { + return; + }; + let config = self.backup_remote.clone(); + self.backup_task( + window, + cx, + async move { + let packet = match source { + RestoreSource::File(path) => std::fs::read(path).map_err(|e| e.to_string())?, + RestoreSource::Remote(name) => remote::pull_from(&config, Some(&name))?.1, + }; + archive::open(&packet, &pass).map(|archive| (archive, pass)) + }, + |this, (archive, pass), window, cx| { + this.backup_selection = + BackupSelection::from_categories(archive.manifest.categories.iter().copied()); + if let Some(RestoreSource::Remote(name)) = &this.backup_ui.source { + this.backup_ui.known.insert( + name.clone(), + (archive.manifest.categories.len(), archive.manifest.device.clone()), + ); + } + cx.set_global(BackupPassword(Some(pass.clone()))); + this.backup_ui.opened = Some(archive); + this.backup_ui.restore_pass = Some(pass); + this.clear_backup_inputs(window, cx); + }, + ); + } + + pub(super) fn restore_selected_backup(&mut self, window: &mut Window, cx: &mut Context) { + if self.backup_busy || self.backup_selection.is_empty() { + return; + } + let Some(archive) = self.backup_ui.opened.clone() else { + return; + }; + let Some(pass) = self.backup_ui.restore_pass.clone() else { + return; + }; + let selection = self.backup_selection; + self.backup_task( + window, + cx, + async move { + archive::recovery::restore_selected(&archive, selection, &pass) + .map(|point| (point, pass)) + }, + |this, point, window, cx| { + this.backup_ui.undo = Some(point); + this.reload_after_restore(cx); + this.close_backup_sheet(window, cx); + this.backup_status = Some(BackupStatus::Completed(BackupCompletion::Restored)); + }, + ); + } + + pub(super) fn undo_backup_restore(&mut self, window: &mut Window, cx: &mut Context) { + if self.backup_busy { + return; + } + let Some((point, pass)) = self.backup_ui.undo.clone() else { + return; + }; + self.backup_task( + window, + cx, + async move { archive::recovery::undo(&point, &pass) }, + |this, _, _, cx| { + this.backup_ui.undo = None; + this.reload_after_restore(cx); + this.backup_status = Some(BackupStatus::Notice(Message::BackupFlowUndone)); + }, + ); + } +} diff --git a/nebula_app/src/gpui_shell/settings_pane/backup/setup.rs b/nebula_app/src/gpui_shell/settings_pane/backup/setup.rs new file mode 100644 index 0000000..dfa8415 --- /dev/null +++ b/nebula_app/src/gpui_shell/settings_pane/backup/setup.rs @@ -0,0 +1,474 @@ +//! 首次四步引导与抽屉共用的存储表单、内容选择。 +use super::view::{caption, panel, provider, size_text}; +use super::*; +use crate::gpui_shell::assets::backup as icons; +use gpui_component::menu::PopupMenuItem; + +const PROVIDERS: [(BackupProtocol, bool, Message, Message, &'static str); 5] = [ + ( + BackupProtocol::WebDav, + true, + Message::CloudNutstore, + Message::BackupFlowNutstoreHint, + icons::CLOUD, + ), + ( + BackupProtocol::WebDav, + false, + Message::CloudWebdav, + Message::BackupFlowWebdavHint, + icons::GLOBE, + ), + (BackupProtocol::S3, false, Message::CloudS3, Message::BackupFlowS3Hint, icons::DATABASE), + (BackupProtocol::Sftp, false, Message::CloudSftp, Message::BackupFlowSftpHint, icons::SERVER), + ( + BackupProtocol::Folder, + false, + Message::CloudFolder, + Message::BackupFlowFolderHint, + icons::FOLDER, + ), +]; + +const CONTENTS: [(BackupCategory, Message, Message); 9] = [ + (BackupCategory::Appearance, Message::CloudAppearance, Message::BackupFlowAppearanceHint), + (BackupCategory::Config, Message::CloudTerminal, Message::BackupFlowTerminalHint), + (BackupCategory::Assistant, Message::CloudAssistant, Message::BackupFlowAssistantHint), + (BackupCategory::Sync, Message::CloudSync, Message::BackupFlowSyncHint), + (BackupCategory::Fonts, Message::CloudFonts, Message::BackupFlowFontsHint), + (BackupCategory::Ssh, Message::CloudHosts, Message::BackupFlowHostsHint), + (BackupCategory::Session, Message::CloudSessions, Message::BackupFlowSessionHint), + (BackupCategory::DirectoryHistory, Message::CloudDirectories, Message::BackupFlowDirsHint), + (BackupCategory::CommandHistory, Message::CloudCommands, Message::BackupFlowCommandsHint), +]; + +pub(super) fn provider_icon(config: &BackupRemoteConfig) -> &'static str { + PROVIDERS + .iter() + .find(|entry| entry.2 == provider(config)) + .map(|entry| entry.4) + .unwrap_or(icons::DRIVE) +} + +impl SettingsPane { + pub(super) fn backup_wizard(&self, window: &Window, cx: &mut Context) -> gpui::Div { + let l = crate::gpui_shell::config::ui_language(cx); + let step = self.backup_ui.step; + let steps = [ + Message::BackupFlowStorage, + Message::BackupFlowConnection, + Message::CloudPassphrase, + Message::CloudScope, + ]; + let progress = h_flex() + .flex_shrink_0() + .w_full() + .px_6() + .py_4() + .gap_3() + .border_b_1() + .border_color(cx.theme().border) + .children(steps.into_iter().enumerate().map(|(i, label)| { + h_flex() + .flex_1() + .min_w_0() + .gap_2() + .text_size(px(12.0)) + .text_color(if i + 1 == step { + cx.theme().foreground + } else { + cx.theme().muted_foreground + }) + .child( + div() + .size(px(22.0)) + .flex_shrink_0() + .rounded_full() + .flex() + .items_center() + .justify_center() + .border_1() + .border_color(if i < step { + cx.theme().primary + } else { + cx.theme().border + }) + .when(i + 1 == step, |d| { + d.bg(cx.theme().primary).text_color(cx.theme().primary_foreground) + }) + .child(if i + 1 < step { + Icon::default() + .path(icons::CHECK) + .size(px(14.0)) + .text_color(cx.theme().primary) + .into_any_element() + } else { + div().child((i + 1).to_string()).into_any_element() + }), + ) + .child(l.text(label)) + .when(i < 3, |row| row.child(div().flex_1().h(px(1.0)).bg(cx.theme().border))) + })); + let (title, lead) = match step { + 1 => (Message::BackupFlowChooseStorage, Message::BackupFlowChooseStorageHint), + 2 => ( + Message::BackupFlowConnection, + PROVIDERS + .iter() + .find(|(_, _, name, _, _)| *name == provider(&self.backup_ui.draft)) + .unwrap() + .3, + ), + 3 => (Message::BackupFlowSetPassword, Message::BackupFlowSetPasswordHint), + _ => (Message::BackupFlowChooseContents, Message::BackupFlowChooseContentsHint), + }; + let title = if step == 2 { + l.format( + Message::BackupFlowConnectProvider, + &[("provider", l.text(provider(&self.backup_ui.draft)))], + ) + } else { + l.text(title).to_owned() + }; + let mut body = v_flex() + .flex_shrink_0() + .w_full() + .min_h(px(380.0)) + .p_6() + .gap(px(14.0)) + .when(step == 3, |d| d.items_center().gap_5()) + .child( + v_flex() + .w_full() + .gap_1() + .debug_selector(|| "backup-wizard-heading".into()) + .when(step == 3, |d| d.items_center().text_center()) + .child( + div() + .text_size(px(18.0)) + .line_height(px(26.0)) + .font_semibold() + .child(title), + ) + .child(caption(l.text(lead), cx).max_w(px(520.0))), + ); + body = match step { + 1 => body.child(v_flex().w_full().flex_shrink_0().gap_2().children( + PROVIDERS.into_iter().enumerate().map( + |(i, (protocol, nutstore, title, hint, icon))| { + Button::new(("backup-provider", i)) + .debug_selector(move || format!("backup-provider-{i}")) + .w_full() + .h_auto() + .py_3() + .px_4() + .justify_start() + .ghost() + .selected(provider(&self.backup_ui.draft) == title) + .disabled(self.backup_busy) + .child(Icon::default().path(icon).size(px(18.0)).flex_shrink_0()) + .child( + v_flex() + .flex_1() + .min_w_0() + .gap_1() + .items_start() + .child( + div() + .text_size(px(14.0)) + .font_medium() + .child(l.text(title)), + ) + .child(caption(l.text(hint), cx)), + ) + .when(provider(&self.backup_ui.draft) == title, |b| { + b.child(Icon::default().path(icons::CHECK).size(px(16.0))) + }) + .on_click(cx.listener(move |this, _, window, cx| { + this.select_backup_protocol(protocol, nutstore, window, cx) + })) + }, + ), + )), + 2 => { + body.child(self.backup_storage_fields(window.viewport_size().width < px(960.0), cx)) + }, + 3 => body + .child( + v_flex() + .debug_selector(|| "backup-password-form".into()) + .text_size(px(13.0)) + .max_w(px(360.0)) + .w_full() + .gap_2() + .child(l.text(Message::CloudPassphrase)) + .child( + Input::new(&self.backup_pass_input) + .aria_label(l.text(Message::CloudPassphrase)) + .text_size(px(13.0)) + .h(px(30.0)) + .disabled(self.backup_busy), + ) + .child(div().mt_3().child(l.text(Message::BackupFlowConfirmPassword))) + .child( + Input::new(self.backup_ui.confirm.as_ref().unwrap()) + .aria_label(l.text(Message::BackupFlowConfirmPassword)) + .text_size(px(13.0)) + .h(px(30.0)) + .disabled(self.backup_busy), + ), + ) + .child( + h_flex() + .w_full() + .max_w(px(520.0)) + .items_start() + .gap(px(10.0)) + .debug_selector(|| "backup-password-note".into()) + .p(px(14.0)) + .rounded(px(8.0)) + .border_1() + .border_color(cx.theme().border) + .bg(crate::gpui_shell::theme::settings_panel_bg(cx)) + .child( + Icon::default() + .path(icons::LOCK) + .size(px(15.0)) + .mt(px(2.0)) + .flex_shrink_0() + .text_color(cx.theme().muted_foreground), + ) + .child( + caption(l.text(Message::BackupFlowPasswordNotice), cx) + .flex_1() + .min_w_0(), + ), + ), + _ => body + .child(self.backup_content_picker(true, false, cx)) + .child(caption(l.text(Message::BackupFlowExcluded), cx)), + }; + let next = match step { + 2 => Message::BackupFlowCheckNext, + 4 => Message::BackupFlowFinishNow, + _ => Message::BackupFlowNext, + }; + panel(cx) + .flex_shrink_0() + .debug_selector(|| "backup-wizard".into()) + .child(progress) + .child(body.children(self.backup_status_view(cx))) + .child( + h_flex() + .w_full() + .flex_wrap() + .items_center() + .gap_2() + .px_6() + .py_4() + .border_t_1() + .border_color(cx.theme().border) + .when(step > 1, |foot| { + foot.child( + Button::new("backup-back") + .debug_selector(|| "backup-back".into()) + .label(l.text(Message::BackupFlowBack)) + .small() + .ghost() + .disabled(self.backup_busy) + .on_click(cx.listener(|this, _, _, cx| { + this.backup_ui.step -= 1; + this.backup_status = None; + cx.notify(); + })), + ) + }) + .child( + div() + .flex_1() + .min_w_0() + .when(step == 1, |d| { + d.child( + h_flex() + .gap_1() + .flex_wrap() + .child(caption( + l.text(Message::BackupFlowExportInstead), + cx, + )) + .child( + Button::new("backup-export-setup") + .label(l.text(Message::CloudExport)) + .small() + .ghost() + .disabled(self.backup_busy) + .on_click(cx.listener(|this, _, window, cx| { + this.open_backup_sheet( + BackupSheet::Export, + window, + cx, + ) + })), + ), + ) + }) + .when(step == 4, |d| d.child(self.backup_selection_summary(false, cx))), + ) + .when(step == 4, |foot| { + foot.child( + Button::new("backup-later") + .debug_selector(|| "backup-later".into()) + .label(l.text(Message::BackupFlowFinishLater)) + .small() + .ghost() + .disabled(self.backup_busy || self.backup_selection.is_empty()) + .on_click(cx.listener(|this, _, window, cx| { + this.save_backup_storage(false, window, cx) + })), + ) + }) + .child( + Button::new("backup-next") + .debug_selector(|| "backup-next".into()) + .label(l.text(if self.backup_busy { + Message::CloudChecking + } else { + next + })) + .primary() + .small() + .h(px(34.0)) + .disabled( + self.backup_busy || (step == 4 && self.backup_selection.is_empty()), + ) + .on_click( + cx.listener(|this, _, window, cx| { + this.next_backup_step(window, cx) + }), + ), + ), + ) + } + + pub(super) fn backup_provider_menu(&self, cx: &mut Context) -> impl IntoElement + use<> { + let l = crate::gpui_shell::config::ui_language(cx); + let owner = cx.entity().downgrade(); + Button::new("backup-provider-menu") + .w_full() + .dropdown_caret(true) + .justify_between() + .label(l.text(provider(&self.backup_ui.draft))) + .disabled(self.backup_busy) + .dropdown_menu(move |mut menu, _, _| { + for (protocol, nutstore, name, _, _) in PROVIDERS { + let owner = owner.clone(); + menu = menu.item(PopupMenuItem::new(l.text(name)).on_click( + move |_, window, cx| { + let _ = owner.update(cx, |this, cx| { + this.select_backup_protocol(protocol, nutstore, window, cx) + }); + }, + )); + } + menu + }) + } + + pub(super) fn backup_content_picker( + &self, + columns: bool, + restore: bool, + cx: &mut Context, + ) -> gpui::Div { + let l = crate::gpui_shell::config::ui_language(cx); + let summary = if restore { + self.backup_ui.opened.as_ref().map(|a| a.summary()).unwrap_or_default() + } else { + self.backup_ui.summary.clone() + }; + let mut groups = div() + .flex() + .w_full() + .gap_5() + .when(columns, |d| d.flex_row().flex_wrap()) + .when(!columns, |d| d.flex_col()); + for (start, end, title) in + [(0, 5, Message::BackupFlowSettings), (5, 9, Message::BackupFlowData)] + { + let mut group = + v_flex().gap_1().min_w(px(220.0)).flex_1().child(caption(l.text(title), cx)); + for (index, &(category, name, hint)) in + CONTENTS.iter().enumerate().take(end).skip(start) + { + let stats = summary.iter().find(|s| s.category == category); + if restore && stats.is_none() { + continue; + } + let checked = self.backup_selection.categories().any(|c| c == category); + group = group.child( + Checkbox::new(("backup-content", index)) + .debug_selector(move || format!("backup-content-{index}")) + .w_full() + .py_2() + .px_2() + .rounded_md() + .hover(|d| d.bg(cx.theme().muted)) + .label(l.text(name)) + .checked(checked) + .disabled(self.backup_busy) + .child(caption(l.text(hint), cx)) + .when_some(stats, |c, s| { + c.child(caption(format!("{} · {}", s.files, size_text(s.bytes)), cx)) + }) + .on_click(cx.listener(move |this, checked: &bool, _, cx| { + this.backup_selection.set(category, *checked); + cx.notify(); + })), + ); + } + groups = groups.child(group); + } + v_flex() + .w_full() + .gap_3() + .child(groups) + .when(!restore && self.backup_ui.summary_loading, |d| { + d.child(caption(l.text(Message::BackupFlowMeasuring), cx)) + }) + .when(!restore, |d| { + d.children( + self.backup_ui + .summary_error + .as_ref() + .map(|e| div().text_sm().text_color(cx.theme().danger).child(e.clone())), + ) + }) + } + + pub(super) fn backup_selection_summary(&self, restore: bool, cx: &App) -> gpui::Div { + let l = crate::gpui_shell::config::ui_language(cx); + let summary = if restore { + self.backup_ui.opened.as_ref().map(|a| a.summary()).unwrap_or_default() + } else { + self.backup_ui.summary.clone() + }; + let categories: Vec<_> = self.backup_selection.categories().collect(); + let bytes = + summary.iter().filter(|s| categories.contains(&s.category)).map(|s| s.bytes).sum(); + caption( + if categories.is_empty() { + l.text(Message::BackupFlowSelectOne).to_owned() + } else if !restore + && (self.backup_ui.summary_loading || self.backup_ui.summary_error.is_some()) + { + l.text(Message::BackupFlowMeasuring).to_owned() + } else { + l.format( + Message::BackupFlowTotal, + &[("count", &categories.len().to_string()), ("size", &size_text(bytes))], + ) + }, + cx, + ) + } +} diff --git a/nebula_app/src/gpui_shell/settings_pane/backup/tests.rs b/nebula_app/src/gpui_shell/settings_pane/backup/tests.rs index fab6856..2c1044f 100644 --- a/nebula_app/src/gpui_shell/settings_pane/backup/tests.rs +++ b/nebula_app/src/gpui_shell/settings_pane/backup/tests.rs @@ -1,21 +1,34 @@ use super::*; +fn draw(cx: &mut gpui::VisualTestContext) { + cx.run_until_parked(); + cx.update(|window, cx| { + window.refresh(); + window.draw(cx).clear(cx); + }); +} + +fn click(cx: &mut gpui::VisualTestContext, selector: &'static str) { + let bounds = cx.debug_bounds(selector).unwrap_or_else(|| panic!("missing {selector}")); + // 留白同样属于真实点击区域,不能只测文本中点。 + let point = gpui::point(bounds.left() + px(6.0), bounds.center().y); + cx.simulate_mouse_down(point, MouseButton::Left, gpui::Modifiers::default()); + cx.simulate_mouse_up(point, MouseButton::Left, gpui::Modifiers::default()); + draw(cx); +} + #[gpui::test] -fn pairing_design_backup_groups_scope_and_save_feedback_with_storage( - cx: &mut gpui::TestAppContext, -) { +fn backup_wizard_requires_connection_and_matching_passwords(cx: &mut gpui::TestAppContext) { cx.update(|cx| { gpui_component::init(cx); + cx.set_reduce_motion(true); cx.set_global(crate::gpui_shell::config::Settings::load(ThemeName::Nord)); }); let mut pane = None; let (_, cx) = cx.add_window_view(|window, cx| { let view = cx.new(|cx| SettingsPane::new(window, cx)); view.update(cx, |pane, _| { - // Exercise real layout and hitboxes without touching credentials or storage. - pane.backup_ui.initialized = true; - pane.backup_ui.configuration = true; - pane.backup_remote.protocol = BackupProtocol::Off; + pane.backup_remote = BackupRemoteConfig::default(); pane.active_section = 9; }); pane = Some(view.clone()); @@ -24,43 +37,218 @@ fn pairing_design_backup_groups_scope_and_save_feedback_with_storage( let pane = pane.unwrap(); for width in [1280.0, 800.0] { cx.simulate_resize(gpui::size(px(width), px(1400.0))); - cx.run_until_parked(); - cx.update(|window, cx| { - window.refresh(); - window.draw(cx).clear(cx); - }); - let card = cx.debug_bounds("cloud-storage-card").expect("storage configuration"); - assert!(card.right() <= px(width)); - assert!(card.size.width <= px(720.0)); + draw(cx); + let wizard = cx.debug_bounds("backup-wizard").unwrap(); + assert!(wizard.right() <= px(width)); + assert!(wizard.size.width <= px(700.0)); let center = px((width + SETTINGS_NAV_WIDTH) / 2.0); - assert!((f32::from(card.center().x - center)).abs() <= 2.0); - let provider = cx.debug_bounds("cloud-provider").expect("bounded provider dropdown"); - assert!(provider.size.width <= px(220.0)); - assert!(provider.left() >= card.left() && provider.right() <= card.right()); - for selector in ["cloud-scope-toggle", "cloud-save-status"] { - let control = cx.debug_bounds(selector).expect("in-card control"); - assert!(control.origin.x >= card.origin.x && control.right() <= card.right()); - assert!(control.origin.y >= card.origin.y && control.bottom() <= card.bottom()); - } - assert!(cx.debug_bounds("cloud-scope-options").is_none()); + assert!(f32::from(wizard.center().x - center).abs() <= 2.0); + assert!(cx.debug_bounds("backup-content-0").is_none()); } - let toggle = cx.debug_bounds("cloud-scope-toggle").unwrap(); - let point = gpui::point(toggle.origin.x + px(4.0), toggle.center().y); - cx.simulate_mouse_down(point, MouseButton::Left, gpui::Modifiers::default()); - cx.simulate_mouse_up(point, MouseButton::Left, gpui::Modifiers::default()); - cx.run_until_parked(); + click(cx, "backup-provider-4"); + click(cx, "backup-next"); + assert_eq!(pane.read_with(cx, |p, _| p.backup_ui.step), 2); + click(cx, "backup-next"); + assert_eq!(pane.read_with(cx, |p, _| p.backup_ui.step), 2); + assert!( + pane.read_with(cx, |p, _| p.backup_status.as_ref().is_some_and(BackupStatus::is_error)) + ); + let folder = tempfile::tempdir().unwrap(); cx.update(|window, cx| { - window.refresh(); - window.draw(cx).clear(cx); + pane.update(cx, |p, cx| { + p.backup_remote_inputs[0].update(cx, |input, cx| { + input.set_value(folder.path().to_string_lossy().into_owned(), window, cx) + }); + }) }); - assert!(pane.read_with(cx, |pane, _| pane.backup_ui.scope_open)); - let card = cx.debug_bounds("cloud-storage-card").unwrap(); - let scope = cx.debug_bounds("cloud-scope-options").unwrap(); - assert!(scope.origin.y >= card.origin.y && scope.bottom() <= card.bottom()); - let history = cx.debug_bounds("cloud-tab-snapshots").unwrap(); - cx.simulate_mouse_down(history.center(), MouseButton::Left, gpui::Modifiers::default()); - cx.simulate_mouse_up(history.center(), MouseButton::Left, gpui::Modifiers::default()); - cx.run_until_parked(); - assert!(!pane.read_with(cx, |pane, _| pane.backup_ui.configuration)); - assert!(pane.read_with(cx, |pane, _| pane.backup_ui.snapshots.is_empty())); + click(cx, "backup-next"); + assert_eq!(pane.read_with(cx, |p, _| p.backup_ui.step), 3); + assert_eq!(pane.read_with(cx, |p, _| p.backup_remote.protocol), BackupProtocol::Off); + click(cx, "backup-back"); + assert_eq!( + pane.read_with(cx, |p, cx| p.backup_remote_inputs[0].read(cx).value().to_string()), + folder.path().to_string_lossy() + ); + click(cx, "backup-next"); + click(cx, "backup-next"); + assert_eq!(pane.read_with(cx, |p, _| p.backup_ui.step), 3); + cx.update(|window, cx| { + pane.update(cx, |p, cx| { + p.backup_pass_input + .update(cx, |input, cx| input.set_value("correct horse", window, cx)); + p.backup_ui + .confirm + .as_ref() + .unwrap() + .update(cx, |input, cx| input.set_value("different horse", window, cx)); + }) + }); + click(cx, "backup-next"); + assert_eq!(pane.read_with(cx, |p, _| p.backup_ui.step), 3); + cx.update(|window, cx| { + pane.update(cx, |p, cx| { + p.backup_ui + .confirm + .as_ref() + .unwrap() + .update(cx, |input, cx| input.set_value("correct horse", window, cx)); + }) + }); + click(cx, "backup-next"); + assert_eq!(pane.read_with(cx, |p, _| p.backup_ui.step), 4); + assert_eq!(pane.read_with(cx, |p, _| p.backup_selection), recommended()); + assert!(cx.debug_bounds("backup-later").is_some()); + click(cx, "backup-content-0"); + assert!(!pane.read_with(cx, |p, _| p.backup_selection.appearance)); + assert_eq!(pane.read_with(cx, |p, _| p.backup_remote.protocol), BackupProtocol::Off); +} + +#[gpui::test] +fn backup_drawer_keeps_scope_off_dashboard_and_cancel_discards_draft( + cx: &mut gpui::TestAppContext, +) { + cx.update(|cx| { + gpui_component::init(cx); + cx.set_reduce_motion(true); + cx.set_global(crate::gpui_shell::config::Settings::load(ThemeName::Nord)); + }); + let mut pane = None; + let (_, cx) = cx.add_window_view(|window, cx| { + let view = cx.new(|cx| SettingsPane::new(window, cx)); + view.update(cx, |p, cx| { + p.backup_remote = BackupRemoteConfig::default(); + p.initialize_backup(window, cx); + p.backup_remote.protocol = BackupProtocol::Folder; + p.backup_remote.selection = recommended(); + p.active_section = 9; + }); + pane = Some(view.clone()); + gpui_component::Root::new(view, window, cx) + }); + let pane = pane.unwrap(); + cx.simulate_resize(gpui::size(px(1100.0), px(1000.0))); + draw(cx); + assert!(cx.debug_bounds("backup-content-0").is_none()); + click(cx, "backup-now"); + let drawer = cx.debug_bounds("backup-drawer").unwrap(); + assert_eq!(drawer.size.width, px(440.0)); + assert!(drawer.right() <= px(1100.0)); + click(cx, "backup-content-0"); + assert!(!pane.read_with(cx, |p, _| p.backup_selection.appearance)); + click(cx, "backup-cancel"); + assert!(cx.debug_bounds("backup-drawer").is_none()); + assert_eq!(pane.read_with(cx, |p, _| p.backup_selection), recommended()); + assert_eq!(pane.read_with(cx, |p, _| p.backup_remote.selection), recommended()); +} + +#[gpui::test] +fn backup_form_columns_and_password_group_follow_prototype(cx: &mut gpui::TestAppContext) { + check_backup_form_layout(cx, crate::display::UiLanguage::EnUs); +} + +#[gpui::test] +fn backup_form_columns_and_password_group_in_chinese(cx: &mut gpui::TestAppContext) { + check_backup_form_layout(cx, crate::display::UiLanguage::ZhCn); +} + +fn check_backup_form_layout(cx: &mut gpui::TestAppContext, language: crate::display::UiLanguage) { + cx.update(|cx| { + gpui_component::init(cx); + cx.set_reduce_motion(true); + cx.set_global(crate::gpui_shell::config::Settings::load(ThemeName::Nord)); + }); + cx.update(|cx| cx.global_mut::().ui_language = language); + let mut pane = None; + let (_, cx) = cx.add_window_view(|window, cx| { + let view = cx.new(|cx| SettingsPane::new(window, cx)); + view.update(cx, |p, _| { + p.backup_remote = BackupRemoteConfig::default(); + p.active_section = 9; + }); + pane = Some(view.clone()); + gpui_component::Root::new(view, window, cx) + }); + let pane = pane.unwrap(); + for width in [800.0, 1280.0] { + cx.update(|_, cx| { + pane.update(cx, |p, cx| { + p.backup_ui.step = 1; + cx.notify(); + }) + }); + cx.simulate_resize(gpui::size(px(width), px(1400.0))); + draw(cx); + let folder = cx.debug_bounds("backup-provider-4").unwrap(); + let next = cx.debug_bounds("backup-next").unwrap(); + let wizard = cx.debug_bounds("backup-wizard").unwrap(); + assert!( + next.bottom() <= wizard.bottom(), + "footer is clipped at {width}: {next:?}, {wizard:?}" + ); + assert!( + folder.bottom() <= next.top(), + "storage choices overlap the footer at {width}: {folder:?}, {next:?}" + ); + click(cx, "backup-provider-4"); + assert_eq!(pane.read_with(cx, |p, _| p.backup_ui.draft.protocol), BackupProtocol::Folder); + click(cx, "backup-provider-1"); + assert_eq!( + pane.read_with(cx, |p, _| view::provider(&p.backup_ui.draft)), + Message::CloudWebdav + ); + click(cx, "backup-next"); + assert_eq!(pane.read_with(cx, |p, _| p.backup_ui.step), 2); + assert_eq!(pane.read_with(cx, |p, _| p.backup_ui.draft.protocol), BackupProtocol::WebDav); + for selector in + ["backup-field-control-0", "backup-field-control-1", "backup-field-control-2"] + { + assert!(cx.debug_bounds(selector).is_some(), "missing {selector} at {width}"); + } + } + for width in [1280.0, 800.0] { + cx.simulate_resize(gpui::size(px(width), px(1400.0))); + draw(cx); + let first = cx.debug_bounds("backup-field-control-0").unwrap(); + for (control, label) in [ + ("backup-field-control-0", "backup-field-label-0"), + ("backup-field-control-1", "backup-field-label-1"), + ("backup-field-control-2", "backup-field-label-2"), + ] { + let control = cx.debug_bounds(control).unwrap_or_else(|| { + let state = + pane.read_with(cx, |p, _| (p.backup_ui.step, p.backup_ui.draft.protocol)); + panic!("missing {control} at width {width}, wizard state {state:?}") + }); + let label = cx.debug_bounds(label).unwrap(); + assert!((control.left() - first.left()).abs() <= px(1.0)); + assert!((control.right() - first.right()).abs() <= px(1.0)); + if width >= 960.0 { + assert!(label.right() < control.left()); + assert_eq!(control.size.width, px(300.0)); + } else { + assert!(label.bottom() <= control.top()); + } + } + cx.update(|_, cx| { + pane.update(cx, |p, cx| { + p.backup_ui.step = 3; + cx.notify(); + }) + }); + draw(cx); + let wizard = cx.debug_bounds("backup-wizard").unwrap(); + for selector in ["backup-wizard-heading", "backup-password-form", "backup-password-note"] { + let bounds = cx.debug_bounds(selector).unwrap(); + assert!((bounds.center().x - wizard.center().x).abs() <= px(1.0)); + assert!(bounds.left() >= wizard.left() && bounds.right() <= wizard.right()); + } + click(cx, "backup-next"); + assert_eq!(pane.read_with(cx, |p, _| p.backup_ui.step), 3); + cx.update(|_, cx| { + pane.update(cx, |p, cx| { + p.backup_ui.step = 2; + cx.notify(); + }) + }); + } } diff --git a/nebula_app/src/gpui_shell/settings_pane/backup/view.rs b/nebula_app/src/gpui_shell/settings_pane/backup/view.rs index 5d14c0f..86d1c52 100644 --- a/nebula_app/src/gpui_shell/settings_pane/backup/view.rs +++ b/nebula_app/src/gpui_shell/settings_pane/backup/view.rs @@ -1,20 +1,25 @@ -//! Compact backup dashboard and configuration card based on the approved layout. +//! 原型的阅读顺序:状态 → 存储 → 时间线;低频操作放在面板下面。 use super::*; -use crate::gpui_shell::widgets::NebulaSwitch; -use gpui_component::menu::PopupMenuItem; -fn card(cx: &App) -> gpui::Div { +pub(super) fn panel(cx: &App) -> gpui::Div { v_flex() .w_full() .min_w_0() - .gap_4() - .p_4() - .rounded_lg() + .rounded(px(10.0)) .border_1() .border_color(crate::gpui_shell::theme::settings_hairline(cx)) + .overflow_hidden() } -fn provider(config: &remote::BackupRemoteConfig) -> Message { +pub(super) fn caption(text: impl Into, cx: &App) -> gpui::Div { + div() + .text_size(px(12.5)) + .line_height(gpui::relative(1.6)) + .text_color(cx.theme().muted_foreground) + .child(text.into()) +} + +pub(super) fn provider(config: &BackupRemoteConfig) -> Message { match config.protocol { BackupProtocol::Off => Message::CloudOff, BackupProtocol::Folder => Message::CloudFolder, @@ -27,576 +32,426 @@ fn provider(config: &remote::BackupRemoteConfig) -> Message { } } +pub(super) fn size_text(bytes: u64) -> String { + if bytes < 1024 { + format!("{bytes} B") + } else if bytes < 1024 * 1024 { + format!("{:.1} KB", bytes as f64 / 1024.0) + } else { + format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0)) + } +} + +fn backup_time(name: &str) -> String { + name.strip_prefix("pebrel-backup-") + .and_then(|s| s.strip_suffix(".nbk")) + .and_then(|s| chrono::NaiveDateTime::parse_from_str(s, "%Y%m%d-%H%M%S").ok()) + .map(|time| time.and_utc().with_timezone(&chrono::Local).format("%m-%d %H:%M").to_string()) + .unwrap_or_else(|| name.to_owned()) +} + impl SettingsPane { pub(in crate::gpui_shell::settings_pane) fn section_backup( &mut self, + window: &mut Window, cx: &mut Context, ) -> gpui::Div { - self.initialize_backup(cx); - let language = crate::gpui_shell::config::ui_language(cx); - let configuration = self.backup_ui.configuration; - let tabs = - gpui_component::button::ButtonGroup::new("cloud-tabs").small().outline().children( - [(false, Message::CloudSnapshots), (true, Message::CloudSettings)].into_iter().map( - |(selected, title)| { - Button::new(if selected { - "cloud-tab-settings" - } else { - "cloud-tab-snapshots" - }) - .debug_selector(move || { - if selected { "cloud-tab-settings" } else { "cloud-tab-snapshots" } - .into() - }) - .label(language.text(title)) - .small() - .h(px(28.0)) - .rounded(px(14.0)) - .selected(configuration == selected) - .on_click(cx.listener(move |this, _, _, cx| { - this.backup_ui.configuration = selected; - if !selected && !this.backup_ui.checked { - this.refresh_backup_snapshots(cx); - } - cx.notify(); - })) - }, - ), - ); - let body = - if configuration { self.backup_configuration(cx) } else { self.backup_dashboard(cx) }; + self.initialize_backup(window, cx); + let l = crate::gpui_shell::config::ui_language(cx); + let off = self.backup_remote.protocol == BackupProtocol::Off; v_flex() .w_full() - .max_w(px(720.0)) - .gap_4() + .max_w(px(700.0)) + .gap_6() + .text_size(px(14.0)) .child( - h_flex() - .gap_3() - .items_center() - .flex_wrap() + v_flex() + .gap_2() + .mb_1() .child( div() - .flex_1() - .text_size(px(20.0)) - .font_weight(gpui::FontWeight::SEMIBOLD) - .child(language.text(Message::CloudTitle)), + .text_size(px(22.0)) + .font_semibold() + .child(l.text(Message::BackupFlowTitle)), ) - .child(tabs), + .child(caption(l.text(Message::BackupFlowIntro), cx)), ) - .child(body) + .when(self.backup_ui.undo.is_some(), |d| { + d.child( + h_flex() + .gap_3() + .p_3() + .rounded_md() + .bg(cx.theme().muted) + .flex_wrap() + .child(div().flex_1().child(l.text(Message::BackupFlowRestored))) + .child( + Button::new("backup-undo") + .label(l.text(Message::BackupFlowUndo)) + .ghost() + .small() + .disabled(self.backup_busy) + .on_click(cx.listener(|this, _, window, cx| { + this.undo_backup_restore(window, cx) + })), + ) + .child( + Button::new("backup-dismiss-undo") + .icon(IconName::Close) + .ghost() + .small() + .disabled(self.backup_busy) + .tooltip(l.text(Message::CommonClose)) + .on_click(cx.listener(|this, _, _, cx| { + this.backup_ui.undo = None; + cx.notify(); + })), + ), + ) + }) + .child(if off { self.backup_wizard(window, cx) } else { self.backup_dashboard(cx) }) + .when(!off, |d| d.children(self.backup_status_view(cx))) + .when(!off, |d| d.child(self.backup_encryption_row(cx))) + .when(!off, |d| d.child(self.backup_file_rows(cx))) } - fn backup_status_view(&self, cx: &App) -> Option { - let language = crate::gpui_shell::config::ui_language(cx); + pub(super) fn backup_status_view(&self, cx: &App) -> Option { + let l = crate::gpui_shell::config::ui_language(cx); self.backup_status.as_ref().map(|status| { div() - .text_sm() + .text_size(px(13.0)) .text_color(if status.is_error() { cx.theme().danger } else { cx.theme().muted_foreground }) - .child(status.text(language)) + .child(status.text(l)) }) } - fn backup_connection_state(&self) -> Message { - if self.backup_ui.listing { - Message::CloudChecking - } else if self.backup_ui.list_error.is_some() { - Message::CloudUnreachable - } else if self.backup_ui.checked { - Message::CloudReachable - } else { - Message::CloudNotChecked + fn snapshot_meta(&self, entry: &Snapshot, cx: &App) -> String { + let l = crate::gpui_shell::config::ui_language(cx); + let size = entry.bytes.map(size_text).unwrap_or_else(|| "—".into()); + match self.backup_ui.known.get(&entry.name) { + Some((count, device)) => l.format( + Message::BackupFlowSnapshotMeta, + &[("count", &count.to_string()), ("size", &size), ("device", device)], + ), + None => format!("{size} · {}", l.text(Message::CloudEncryptedArchive)), } } fn backup_dashboard(&self, cx: &mut Context) -> gpui::Div { - let language = crate::gpui_shell::config::ui_language(cx); - let off = self.backup_remote.protocol == BackupProtocol::Off; - let busy = self.backup_busy || self.backup_ui.secret_busy; - let mut history = card(cx).child( - h_flex() - .gap_3() - .items_center() - .child( - v_flex() - .flex_1() - .gap_1() - .child( - div() - .font_weight(gpui::FontWeight::SEMIBOLD) - .child(language.text(Message::CloudSnapshots)), - ) - .child(div().text_xs().text_color(cx.theme().muted_foreground).child( - language.format( - Message::CloudRetention, - &[("count", &remote::KEEP_ARCHIVES.to_string())], - ), - )), - ) - .child( - Button::new("cloud-refresh") - .ghost() - .size(px(28.0)) - .icon(Icon::default().path(crate::gpui_shell::assets::nav::REFRESH)) - .tooltip(language.text(Message::CloudRefresh)) - .disabled(off || self.backup_ui.listing || busy) - .on_click(cx.listener(|this, _, _, cx| this.refresh_backup_snapshots(cx))), - ), - ); - if self.backup_ui.snapshots.is_empty() { - history = history.child( - v_flex() - .w_full() - .py_6() - .gap_2() - .items_center() - .text_center() - .text_sm() - .text_color(cx.theme().muted_foreground) - .child(Icon::new(IconName::FolderOpen).size(px(24.0))) - .child(language.text(if self.backup_ui.checked { - Message::CloudEmpty + let l = crate::gpui_shell::config::ui_language(cx); + let latest = self.backup_ui.snapshots.first(); + let mut head = v_flex().flex_1().min_w_0().gap_2(); + if let Some(entry) = latest { + head = head + .child(caption(l.text(Message::BackupFlowLastBackup), cx)) + .child(div().text_size(px(28.0)).font_semibold().child(backup_time(&entry.name))) + .child(caption(self.snapshot_meta(entry, cx), cx)); + } else { + head = head + .child(div().text_size(px(18.0)).font_semibold().child(l.text( + if self.backup_ui.listing { + Message::CloudChecking } else { - Message::CloudLoadHistory - })), - ); + Message::BackupFlowEmpty + }, + ))) + .child(caption(l.text(Message::BackupFlowEmptyHint), cx)); } - for (index, name) in self.backup_ui.snapshots.iter().enumerate() { - let target = name.clone(); - history = history.child( - h_flex() - .w_full() - .min_w_0() - .py_3() - .gap_3() - .items_center() - .border_t_1() - .border_color(cx.theme().border) - .child( - div() - .size(px(8.0)) - .flex_shrink_0() - .rounded_full() - .bg(cx.theme().muted_foreground), - ) - .child( - v_flex() - .flex_1() - .min_w_0() - .gap_1() - .child(div().truncate().text_sm().child(name.clone())) - .child(div().text_xs().text_color(cx.theme().muted_foreground).child( - language.text(if index == 0 { - Message::CloudLatest - } else { - Message::CloudEncryptedArchive - }), - )), - ) - .child( - Button::new(("cloud-restore-version", index)) - .label(language.text(Message::CloudRestore)) - .small() - .disabled(busy) - .on_click(cx.listener(move |this, _, window, cx| { - this.confirm_backup_restore( - RestoreSource::Remote(Some(target.clone())), - window, - cx, - ); - })), - ), - ); - } - let connection = self.backup_connection_state(); - v_flex() - .w_full() - .gap_5() - .child( - card(cx) - .child( - h_flex() - .gap_4() - .items_center() - .flex_wrap() - .child(Icon::new(IconName::Globe).size(px(20.0))) - .child( - v_flex() - .flex_1() - .min_w(px(160.0)) - .gap_1() - .child( - h_flex() - .gap_2() - .flex_wrap() - .child( - div() - .font_weight(gpui::FontWeight::SEMIBOLD) - .child( - language - .text(provider(&self.backup_remote)), - ), - ) - .child( - div() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(language.text(connection)), - ), - ) - .child( - div() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(language.text(Message::CloudEncryptedArchive)), - ), + let actions = h_flex() + .gap_2() + .flex_wrap() + .when_some(latest, |row, entry| { + let name = entry.name.clone(); + row.child( + Button::new("backup-restore-latest") + .label(l.text(Message::CloudRestore)) + .small() + .disabled(self.backup_busy) + .on_click(cx.listener(move |this, _, window, cx| { + this.open_backup_restore( + RestoreSource::Remote(name.clone()), + window, + cx, ) - .child( - h_flex() - .gap_2() - .flex_wrap() - .child( - Button::new("cloud-pull") - .label(language.text(Message::CloudRestoreLatest)) - .small() - .disabled(off || busy) - .on_click(cx.listener(|this, _, window, cx| { - this.confirm_backup_restore( - RestoreSource::Remote(None), - window, - cx, - ) - })), - ) - .child( - Button::new("cloud-push") - .label(language.text(Message::CloudBackupNow)) - .small() - .primary() - .disabled(off || busy) - .on_click( - cx.listener(|this, _, _, cx| this.push_remote(cx)), - ), - ), - ), + })), + ) + }) + .child( + Button::new("backup-now") + .debug_selector(|| "backup-now".into()) + .label(l.text(Message::CloudBackupNow)) + .primary() + .small() + .h(px(34.0)) + .disabled( + self.backup_busy + || self.backup_ui.listing + || self.backup_ui.list_error.is_some(), ) - .children(self.backup_status_view(cx)) - .children(self.backup_ui.list_error.as_ref().map(|error| { - div().text_sm().text_color(cx.theme().danger).child(error.clone()) + .on_click(cx.listener(|this, _, window, cx| { + this.open_backup_sheet(BackupSheet::Backup, window, cx) })), - ) - .child(history) - .child( - h_flex() - .gap_2() - .flex_wrap() - .child( - Button::new("cloud-export-file") - .label(language.text(Message::CloudExport)) - .small() - .ghost() - .disabled(busy) - .on_click(cx.listener(|this, _, _, cx| this.export_backup(cx))), - ) - .child( - Button::new("cloud-import-file") - .label(language.text(Message::CloudImport)) - .small() - .ghost() - .disabled(busy) - .on_click( - cx.listener(|this, _, window, cx| this.restore_backup(window, cx)), - ), - ), - ) - } - - fn backup_configuration(&self, cx: &mut Context) -> gpui::Div { - let language = crate::gpui_shell::config::ui_language(cx); - let busy = self.backup_busy || self.backup_ui.secret_busy; - let owner = cx.entity().downgrade(); - let provider_menu = Button::new("cloud-provider") - .debug_selector(|| "cloud-provider".into()) - .w(px(SETTINGS_SELECT_WIDTH)) - .max_w_full() - .small() - .h(px(32.0)) - .dropdown_caret(true) - .label(language.text(provider(&self.backup_remote))) - .disabled(busy) - .dropdown_menu(move |mut menu, _, _| { - for (protocol, nutstore, label) in [ - (BackupProtocol::WebDav, true, Message::CloudNutstore), - (BackupProtocol::WebDav, false, Message::CloudWebdav), - (BackupProtocol::S3, false, Message::CloudS3), - (BackupProtocol::Sftp, false, Message::CloudSftp), - (BackupProtocol::Folder, false, Message::CloudFolder), - (BackupProtocol::Off, false, Message::CloudOff), - ] { - let owner = owner.clone(); - menu = menu.item(PopupMenuItem::new(language.text(label)).on_click( - move |_, window, cx| { - let _ = owner.update(cx, |pane, cx| { - pane.select_backup_protocol(protocol, nutstore, window, cx) - }); - }, - )); - } - menu - }); - let mut storage = card(cx) - .child( - h_flex() - .gap_3() - .items_center() - .flex_wrap() - .child(Icon::new(IconName::Globe).size(px(18.0))) - .child( - div() - .flex_1() - .font_weight(gpui::FontWeight::SEMIBOLD) - .child(language.text(Message::CloudConfigure)), - ) - .child( - Button::new("cloud-scope-toggle") - .debug_selector(|| "cloud-scope-toggle".into()) - .label(language.text(Message::CloudScope)) - .small() - .ghost() - .icon(if self.backup_ui.scope_open { - IconName::ChevronUp - } else { - IconName::ChevronDown - }) - .selected(self.backup_ui.scope_open) - .on_click(cx.listener(|this, _, _, cx| { - this.backup_ui.scope_open = !this.backup_ui.scope_open; - cx.notify(); - })), - ), - ) - .child( - v_flex() - .items_start() - .gap_2() - .child( - div() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(language.text(Message::CloudProvider)), - ) - .child(provider_menu), ); - let fields: &[Message] = match self.backup_remote.protocol { - BackupProtocol::Off => &[], - BackupProtocol::Folder => &[Message::CloudFolderPath], - BackupProtocol::WebDav => &[Message::CloudAddress, Message::CloudUsername], - BackupProtocol::S3 => &[ - Message::CloudS3Address, - Message::CloudRegion, - Message::CloudBucket, - Message::CloudAccessKey, - ], - BackupProtocol::Sftp => &[Message::CloudSshHost, Message::CloudRemotePath], + let path = match self.backup_remote.protocol { + BackupProtocol::Folder => self.backup_remote.folder_path.clone(), + BackupProtocol::WebDav => self.backup_remote.webdav_url.clone(), + BackupProtocol::S3 => { + format!("{} / {}", self.backup_remote.s3_endpoint, self.backup_remote.s3_bucket) + }, + BackupProtocol::Sftp => { + format!("{}:{}", self.backup_remote.sftp_destination, self.backup_remote.sftp_path) + }, + BackupProtocol::Off => String::new(), }; - for (index, label) in fields.iter().enumerate() { - storage = storage.child( - v_flex() - .w_full() - .max_w(px(420.0)) - .gap_2() - .child( - div() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(language.text(*label)), - ) - .child( - Input::new(&self.backup_remote_inputs[index]).h(px(32.0)).disabled(busy), - ), - ); - } - if matches!(self.backup_remote.protocol, BackupProtocol::WebDav | BackupProtocol::S3) { - storage = storage.child( - v_flex() - .w_full() - .max_w(px(420.0)) - .gap_2() - .child( - h_flex() - .gap_2() - .child(div().text_xs().child(language.text(Message::CloudCredential))) - .child(div().text_xs().text_color(cx.theme().muted_foreground).child( - language.text(if self.backup_ui.secret_ready == Some(true) { - Message::CloudCredentialSet - } else { - Message::CloudCredentialNeeded - }), - )), - ) - .child( - h_flex() - .gap_2() - .items_center() - .flex_wrap() - .child(div().flex_1().min_w(px(180.0)).child( - Input::new(&self.backup_secret_input).h(px(32.0)).disabled(busy), - )) - .child( - Button::new("cloud-store-secret") - .label(language.text(Message::CloudStoreCredential)) - .small() - .disabled(busy) - .on_click(cx.listener(|this, _, window, cx| { - this.store_remote_secret(window, cx) - })), - ), - ), - ); - } - if self.backup_ui.scope_open { - storage = storage.child(self.backup_scope(cx)); - } - let save_label = match &self.backup_ui.save_result { - Some(Ok(())) => Message::CloudSaved, - Some(Err(_)) => Message::CloudSaveFailed, - None if self.backup_ui.save_revision > 0 => Message::CloudSaving, - None => Message::CloudAutoSave, - }; - storage = - storage + let store = + v_flex() + .px_6() + .py_3() + .gap_2() + .border_t_1() + .border_color(cx.theme().border) .child( h_flex() .gap_3() .items_center() .flex_wrap() - .pt_3() - .border_t_1() - .border_color(cx.theme().border) .child( - Button::new("cloud-test") - .label(language.text(Message::CloudTest)) + Icon::default() + .path(super::setup::provider_icon(&self.backup_remote)) + .size(px(16.0)), + ) + .child(div().font_medium().child(l.text(provider(&self.backup_remote)))) + .child(caption(path, cx).flex_1().min_w_0().truncate()) + .child( + Button::new("backup-edit-storage") + .label(l.text(Message::BackupFlowChange)) + .ghost() .small() - .disabled( - busy || self.backup_ui.listing - || self.backup_remote.protocol == BackupProtocol::Off, - ) + .disabled(self.backup_busy) + .on_click(cx.listener(|this, _, window, cx| { + this.open_backup_sheet(BackupSheet::Storage, window, cx) + })), + ), + ) + .child( + h_flex() + .gap_2() + .child(caption( + l.text(if self.backup_ui.listing { + Message::CloudChecking + } else if self.backup_ui.list_error.is_some() { + Message::BackupFlowConnectionFailed + } else { + Message::BackupFlowConnected + }), + cx, + )) + .child( + Button::new("backup-refresh") + .icon(IconName::Redo2) + .ghost() + .small() + .tooltip(l.text(Message::CloudTest)) + .disabled(self.backup_busy || self.backup_ui.listing) .on_click( cx.listener(|this, _, _, cx| this.refresh_backup_snapshots(cx)), ), - ) - .child( - div() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(language.text(self.backup_connection_state())), - ) - .child(div().flex_1()) - .child( - div() - .id("cloud-save-status") - .debug_selector(|| "cloud-save-status".into()) - .text_xs() - .text_color(if matches!(self.backup_ui.save_result, Some(Err(_))) { - cx.theme().danger - } else { - cx.theme().muted_foreground - }) - .child(language.text(save_label)), ), ) - .when(matches!(self.backup_ui.save_result, Some(Err(_))), |card| { - card.child( - Button::new("cloud-retry-save") - .label(language.text(Message::CloudRetrySave)) - .small() - .on_click(cx.listener(|this, _, _, cx| this.queue_backup_save(cx))), - ) - }) - .children(self.backup_status_view(cx)) .children(self.backup_ui.list_error.as_ref().map(|error| { div().text_sm().text_color(cx.theme().danger).child(error.clone()) })); + let mut history = + v_flex().w_full().px_6().py_3().border_t_1().border_color(cx.theme().border); + for (index, entry) in self.backup_ui.snapshots.iter().enumerate() { + let name = entry.name.clone(); + let device = self + .backup_ui + .known + .get(&name) + .map(|(_, d)| d.clone()) + .filter(|s| !s.is_empty()) + .unwrap_or_else(|| l.text(Message::CloudEncryptedArchive).into()); + history = history.child( + h_flex() + .w_full() + .min_h(px(42.0)) + .gap_3() + .child( + div() + .relative() + .w(px(14.0)) + .h(px(42.0)) + .flex_shrink_0() + .flex() + .items_center() + .justify_center() + .child( + div() + .absolute() + .top_0() + .bottom_0() + .left(px(6.0)) + .w(px(1.0)) + .bg(cx.theme().border), + ) + .child( + div() + .relative() + .size(px(8.0)) + .rounded_full() + .border_1() + .border_color(if index == 0 { + cx.theme().primary + } else { + cx.theme().muted_foreground + }) + .bg(if index == 0 { + cx.theme().primary + } else { + crate::gpui_shell::theme::settings_panel_bg(cx) + }), + ), + ) + .child( + h_flex() + .gap_2() + .flex_1() + .min_w_0() + .child(div().text_size(px(13.0)).child(backup_time(&name))) + .when(index == 0, |r| { + r.child(caption(l.text(Message::BackupFlowLatest), cx)) + }), + ) + .child(caption(device, cx).flex_1().min_w_0().truncate()) + .child(caption(entry.bytes.map(size_text).unwrap_or_else(|| "—".into()), cx)) + .child( + Button::new(("backup-restore", index)) + .label(l.text(Message::CloudRestore)) + .small() + .ghost() + .disabled(self.backup_busy) + .on_click(cx.listener(move |this, _, window, cx| { + this.open_backup_restore( + RestoreSource::Remote(name.clone()), + window, + cx, + ) + })), + ), + ); + } + history = + history.child(caption(l.text(Message::BackupFlowRetention), cx).pl(px(26.0)).py_3()); + panel(cx) + .debug_selector(|| "backup-dashboard".into()) + .child(h_flex().w_full().p_6().gap_4().flex_wrap().child(head).child(actions)) + .child(store) + .when(latest.is_some(), |d| d.child(history)) + } + + fn backup_encryption_row(&self, cx: &mut Context) -> gpui::Div { + let l = crate::gpui_shell::config::ui_language(cx); + let ready = cx.try_global::().is_some_and(|p| p.0.is_some()); v_flex() - .w_full() - .gap_5() + .gap_3() + .child(div().font_semibold().child(l.text(Message::BackupFlowEncryption))) .child( - div() - .id("cloud-storage-card") - .debug_selector(|| "cloud-storage-card".into()) - .child(storage), - ) - .child( - card(cx) + h_flex() + .gap_4() + .items_center() + .flex_wrap() + .py_3() + .border_t_1() + .border_color(cx.theme().border) .child( - div() - .font_weight(gpui::FontWeight::SEMIBOLD) - .child(language.text(Message::CloudPassphrase)), + v_flex() + .flex_1() + .min_w(px(180.0)) + .gap_1() + .child(l.text(Message::CloudPassphrase)) + .child(caption(l.text(Message::BackupFlowPasswordHint), cx)), ) + .child(caption( + l.text(if ready { + Message::BackupFlowPasswordReady + } else { + Message::BackupFlowPasswordMissing + }), + cx, + )) .child( - div() - .w_full() - .max_w(px(420.0)) - .child(Input::new(&self.backup_pass_input).h(px(32.0)).disabled(busy)), - ) - .child( - div() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(language.text(Message::CloudPassphraseHint)), + Button::new("backup-password") + .label(l.text(if ready { + Message::BackupFlowClear + } else { + Message::BackupFlowEnter + })) + .small() + .ghost() + .disabled(self.backup_busy) + .on_click(cx.listener(move |this, _, window, cx| { + if ready { + cx.set_global(BackupPassword::default()); + this.clear_backup_inputs(window, cx); + cx.notify(); + } else { + this.open_backup_sheet(BackupSheet::Password, window, cx); + } + })), ), ) } - fn backup_scope(&self, cx: &mut Context) -> gpui::Div { - let language = crate::gpui_shell::config::ui_language(cx); - let selection = self.backup_selection; - let categories: [(Message, bool, fn(&mut crate::encrypted_backup::BackupSelection, bool)); - 9] = [ - (Message::CloudAppearance, selection.appearance, |s, v| s.appearance = v), - (Message::CloudTerminal, selection.config, |s, v| s.config = v), - (Message::CloudHosts, selection.ssh, |s, v| s.ssh = v), - (Message::CloudSync, selection.sync, |s, v| s.sync = v), - (Message::CloudAssistant, selection.assistant, |s, v| s.assistant = v), - (Message::CloudSessions, selection.session, |s, v| s.session = v), - (Message::CloudDirectories, selection.directory_history, |s, v| { - s.directory_history = v - }), - (Message::CloudCommands, selection.command_history, |s, v| s.command_history = v), - (Message::CloudFonts, selection.fonts, |s, v| s.fonts = v), - ]; + fn backup_file_rows(&self, cx: &mut Context) -> gpui::Div { + let l = crate::gpui_shell::config::ui_language(cx); v_flex() - .w_full() - .gap_2() - .pt_3() - .border_t_1() - .border_color(cx.theme().border) - .debug_selector(|| "cloud-scope-options".into()) - .child( - div() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(language.text(Message::CloudScopeHint)), + .gap_3() + .child(div().font_semibold().child(l.text(Message::BackupFlowFiles))) + .children( + [ + (true, Message::CloudExport, Message::BackupFlowExportHint), + (false, Message::CloudImport, Message::BackupFlowImportHint), + ] + .into_iter() + .map(|(export, title, hint)| { + h_flex() + .gap_4() + .items_center() + .flex_wrap() + .py_3() + .border_t_1() + .border_color(cx.theme().border) + .child( + v_flex() + .flex_1() + .min_w(px(180.0)) + .gap_1() + .child(l.text(title)) + .child(caption(l.text(hint), cx)), + ) + .child( + Button::new(if export { "backup-export" } else { "backup-import" }) + .label(l.text(if export { + Message::CloudExport + } else { + Message::BackupFlowChooseFile + })) + .small() + .disabled(self.backup_busy) + .on_click(cx.listener(move |this, _, window, cx| { + if export { + this.open_backup_sheet(BackupSheet::Export, window, cx); + } else { + this.restore_backup(window, cx); + } + })), + ) + }), ) - .children(categories.into_iter().enumerate().map(|(index, (label, checked, apply))| { - h_flex() - .w_full() - .gap_3() - .items_center() - .py_1() - .child(div().flex_1().text_sm().child(language.text(label))) - .child( - NebulaSwitch::new(format!("cloud-scope-{index}")) - .checked(checked) - .disabled(self.backup_busy) - .on_click(cx.listener(move |this, enabled: &bool, _, cx| { - apply(&mut this.backup_selection, *enabled); - this.queue_backup_save(cx); - })), - ) - })) } } diff --git a/nebula_app/src/gpui_shell/settings_pane/initialization.rs b/nebula_app/src/gpui_shell/settings_pane/initialization.rs index 1ff1abe..3e913af 100644 --- a/nebula_app/src/gpui_shell/settings_pane/initialization.rs +++ b/nebula_app/src/gpui_shell/settings_pane/initialization.rs @@ -545,10 +545,8 @@ impl SettingsPane { subscriptions.push(cx.intercept_keystrokes(appearance_interceptor)); let settings_search_input = cx.new(|cx| { - InputState::new(window, cx).placeholder(language.pick( - "搜索全部设置,例如「字号」「透明度」「更新」", - "Search all settings, e.g. font, opacity, update", - )) + InputState::new(window, cx) + .placeholder(language.text(crate::i18n::Message::CommonSearchSettings)) }); subscriptions.push(cx.subscribe_in( &settings_search_input, @@ -573,6 +571,7 @@ impl SettingsPane { launch_at_login: crate::platform::startup::launch_at_login(), active_section: 1, agents: agents::AgentSettingsState::new(cx), + mobile: mobile::MobileState::new(window, cx), appearance_picker: None, appearance_picker_seq: 0, theme_editor: None, diff --git a/nebula_app/src/gpui_shell/settings_pane/mobile.rs b/nebula_app/src/gpui_shell/settings_pane/mobile.rs new file mode 100644 index 0000000..bc68d8b --- /dev/null +++ b/nebula_app/src/gpui_shell/settings_pane/mobile.rs @@ -0,0 +1,505 @@ +//! HTML 三态的 UI 所有者;配对、权限和监听生命周期只调用 mobile_connection。 + +use super::*; +use crate::{ + gpui_shell::copy_feedback::CopyFeedback, + i18n::Message, + mobile_connection::{self as connection, Failure, Mode, Preferences, Snapshot, Status}, +}; +use pebrel_mobile_link::{endpoint::RelayAccess, qr::PairingQr}; + +mod relay; +#[cfg(all(test, feature = "gpui-test-support"))] +mod tests; +mod view; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(super) enum Phase { + Off, + Pairing, + Paired, +} + +pub(super) struct MobileState { + initialized: bool, + loading: bool, + syncing: bool, + snapshot: Option, + mode: Mode, + pairing_open: bool, + pairing_baseline: Vec, + operation: bool, + sequence: u64, + generation: Option, + failure: Option, + addresses: Vec, + address_select: SharedSelect, + port_input: Entity, + qr_payload: Option, + qr: Option>, + expires_at: Option, + copy_feedback: Entity, + monitor: Option>, + relay_open: bool, + relay_focus: FocusHandle, + relay_inputs: Vec>, + relay_edit: u64, + relay_loading: bool, + relay_result: Option, + saved_relay: Option, + server_select: SharedSelect, + server_hosts: Vec<(String, String)>, +} + +impl MobileState { + pub(super) fn new(window: &mut Window, cx: &mut Context) -> Self { + Self { + initialized: false, + loading: false, + syncing: false, + snapshot: None, + mode: Mode::Lan, + pairing_open: false, + pairing_baseline: Vec::new(), + operation: false, + sequence: 0, + generation: None, + failure: None, + addresses: Vec::new(), + address_select: cx + .new(|cx| SelectState::new(Vec::::new(), None, window, cx)), + port_input: cx.new(|cx| { + InputState::new(window, cx) + .pattern(regex::Regex::new(r"^\d{0,5}$").expect("port pattern")) + }), + qr_payload: None, + qr: None, + expires_at: None, + copy_feedback: cx.new(|_| CopyFeedback::new()), + monitor: None, + relay_open: false, + relay_focus: cx.focus_handle(), + relay_inputs: (0..5) + .map(|i| cx.new(|cx| InputState::new(window, cx).masked(matches!(i, 2 | 3)))) + .collect(), + relay_edit: 0, + relay_loading: false, + relay_result: None, + saved_relay: None, + server_select: cx + .new(|cx| SelectState::new(Vec::::new(), None, window, cx)), + server_hosts: Vec::new(), + } + } + + pub(super) fn phase(&self) -> Phase { + if !self.snapshot.as_ref().is_some_and(|s| s.preferences.enabled) { + Phase::Off + } else if self.pairing_open || self.snapshot.as_ref().is_none_or(|s| s.devices.is_empty()) { + Phase::Pairing + } else { + Phase::Paired + } + } + + fn preferences(&self) -> Preferences { + self.snapshot.as_ref().map(|s| s.preferences.clone()).unwrap_or_default() + } + + fn display_snapshot(&mut self, snapshot: Snapshot) { + if self.pairing_open + && snapshot + .devices + .iter() + .any(|d| d.connected && !self.pairing_baseline.contains(&d.id)) + { + self.pairing_open = false; + } + let payload = snapshot.connection(self.mode).and_then(|c| c.invitation.clone()); + if self.qr_payload != payload { + self.expires_at = payload + .as_deref() + .and_then(|p| serde_json::from_str::(p).ok()) + .and_then(|v| v["secure"]["expiresAt"].as_u64()); + self.qr = payload.as_deref().and_then(|payload| { + let qr = PairingQr::encode(payload.as_bytes()).ok()?; + let scale = (512 / qr.width()).clamp(1, 8); + let side = (qr.width() * scale) as u32; + let pixels = image::RgbaImage::from_raw(side, side, qr.rgba(scale).ok()?)?; + Some(Arc::new(RenderImage::new([image::Frame::new(pixels)]))) + }); + self.qr_payload = payload; + } + self.snapshot = Some(snapshot); + } + + fn valid_qr(&self) -> bool { + // 切换监听地址时旧邀请已经失效,完成事务前不再展示或复制旧二维码。 + !self.operation + && self.qr.is_some() + && self.expires_at.is_some_and(|until| until > now()) + && self + .snapshot + .as_ref() + .and_then(|s| s.connection(self.mode)) + .is_some_and(|c| matches!(c.status, Status::Waiting | Status::Connected)) + } +} + +impl Drop for MobileState { + fn drop(&mut self) { + if let Some(generation) = self.generation { + connection::cancel(generation); + } + } +} + +fn now() -> u64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|v| v.as_secs()) + .unwrap_or(0) +} + +impl SettingsPane { + fn mobile_initialize(&mut self, window: &mut Window, cx: &mut Context) { + if self.mobile.initialized { + return; + } + self.mobile.initialized = true; + self.mobile.loading = true; + let feedback = self.mobile.copy_feedback.clone(); + self._subscriptions.push(cx.observe(&feedback, |_, _, cx| cx.notify())); + let select = self.mobile.address_select.clone(); + self._subscriptions.push(cx.subscribe_in(&select, window, |this, _, event, window, cx| { + if matches!(event, SelectEvent::Confirm(_)) + && !this.mobile.syncing + && !this.mobile.operation + { + let mut preferences = this.mobile.preferences(); + preferences.address = this.mobile_selected_address(cx); + if preferences.enabled && preferences.lan_enabled { + this.mobile_apply(preferences, None, false, window, cx); + } + } + })); + let input = self.mobile.port_input.clone(); + self._subscriptions.push(cx.subscribe_in(&input, window, |this, _, event, window, cx| { + if matches!(event, InputEvent::Blur | InputEvent::PressEnter { .. }) + && !this.mobile.syncing + && !this.mobile.operation + { + let value = this.mobile.port_input.read(cx).value(); + let port = + if value.trim().is_empty() { Ok(0) } else { value.trim().parse::() }; + match port { + Ok(port) if port != this.mobile.preferences().port => { + let mut preferences = this.mobile.preferences(); + preferences.port = port; + this.mobile_apply(preferences, None, false, window, cx); + }, + Err(_) => { + this.mobile.failure = Some(Failure::Invalid); + cx.notify(); + }, + _ => {}, + } + } + })); + for input in self.mobile.relay_inputs.clone() { + self._subscriptions.push(cx.subscribe(&input, |this, _, event, _| { + if matches!(event, InputEvent::Change) && !this.mobile.syncing { + this.mobile.relay_edit = this.mobile.relay_edit.wrapping_add(1); + this.mobile.relay_result = None; + } + })); + } + let load = cx.background_executor().spawn(async { + (connection::snapshot(), connection::addresses(), connection::saved_relay()) + }); + cx.spawn_in(window, async move |this, cx| { + let (snapshot, addresses, relay) = load.await; + let _ = this.update_in(cx, |this, window, cx| { + this.mobile.loading = false; + match snapshot { + Ok(snapshot) => this.mobile.display_snapshot(snapshot), + Err(error) => this.mobile.failure = Some(error), + } + if let Ok(addresses) = addresses { + this.mobile_set_addresses(addresses, window, cx); + } + if let Ok(relay) = relay { + this.mobile.saved_relay = relay; + } + this.mobile_sync_servers(window, cx); + let port = this.mobile.preferences().port; + this.mobile.syncing = true; + this.mobile.port_input.update(cx, |input, cx| { + input.set_value( + if port == 0 { String::new() } else { port.to_string() }, + window, + cx, + ) + }); + this.mobile.syncing = false; + cx.notify(); + }); + }) + .detach(); + self.mobile.monitor = Some(cx.spawn(async move |this, cx| { + loop { + cx.background_executor().timer(Duration::from_secs(1)).await; + let Ok(sequence) = this.update(cx, |this, _| { + (!this.mobile.operation && !this.mobile.loading).then_some(this.mobile.sequence) + }) else { + break; + }; + let Some(sequence) = sequence else { continue }; + let result = cx.background_executor().spawn(async { connection::snapshot() }).await; + if this + .update(cx, |this, cx| { + if this.mobile.sequence != sequence || this.mobile.operation { + return; + } + if let Ok(snapshot) = result { + this.mobile.display_snapshot(snapshot); + } + if this.active_section == MOBILE_SECTION { + cx.notify(); + } + }) + .is_err() + { + break; + } + } + })); + } + + fn mobile_run( + &mut self, + generation: Option, + close_relay: bool, + work: impl FnOnce() -> Result + Send + 'static, + window: &mut Window, + cx: &mut Context, + ) { + if let Some(previous) = self.mobile.generation.take() { + connection::cancel(previous); + } + self.mobile.sequence = self.mobile.sequence.wrapping_add(1); + let sequence = self.mobile.sequence; + self.mobile.generation = generation; + self.mobile.operation = true; + self.mobile.failure = None; + let task = cx.background_executor().spawn(async move { work() }); + cx.spawn_in(window, async move |this, cx| { + let result = task.await; + let _ = this.update_in(cx, |this, window, cx| { + if this.mobile.sequence != sequence { + return; + } + this.mobile.operation = false; + this.mobile.generation = None; + match result { + Ok(snapshot) => { + this.mobile.display_snapshot(snapshot); + if close_relay { + this.mobile.saved_relay = this.mobile_relay_json(cx).ok(); + this.mobile.relay_open = false; + window.focus(&this.focus_handle, cx); + } + }, + Err(Failure::Cancelled) => {}, + Err(error) => this.mobile.failure = Some(error), + } + cx.notify(); + }); + }) + .detach(); + cx.notify(); + } + + fn mobile_apply( + &mut self, + preferences: Preferences, + relay: Option, + close_relay: bool, + window: &mut Window, + cx: &mut Context, + ) { + let generation = connection::begin(); + self.mobile_run( + Some(generation), + close_relay, + move || connection::apply(generation, preferences, relay), + window, + cx, + ); + } + + fn mobile_selected_address(&self, cx: &App) -> Option { + self.mobile + .address_select + .read(cx) + .selected_index(cx) + .and_then(|i| self.mobile.addresses.get(i.row)) + .map(|a| a.address) + } + + fn mobile_enable(&mut self, window: &mut Window, cx: &mut Context) { + let mut preferences = self.mobile.preferences(); + preferences.enabled = true; + preferences.address = self.mobile_selected_address(cx).or(preferences.address); + if !preferences.lan_enabled && !preferences.relay_enabled { + preferences.lan_enabled = true; + } + self.mobile.pairing_open = + self.mobile.snapshot.as_ref().is_none_or(|s| s.devices.is_empty()); + self.mobile.pairing_baseline = self + .mobile + .snapshot + .as_ref() + .map(|s| s.devices.iter().map(|d| d.id.clone()).collect()) + .unwrap_or_default(); + self.mobile_apply(preferences, None, false, window, cx); + } + + fn mobile_pause(&mut self, window: &mut Window, cx: &mut Context) { + let mut preferences = self.mobile.preferences(); + preferences.enabled = false; + self.mobile.pairing_open = false; + self.mobile_apply(preferences, None, false, window, cx); + } + + fn mobile_pair(&mut self, window: &mut Window, cx: &mut Context) { + self.mobile.pairing_open = true; + self.mobile.pairing_baseline = self + .mobile + .snapshot + .as_ref() + .map(|s| s.devices.iter().map(|d| d.id.clone()).collect()) + .unwrap_or_default(); + let mode = self.mobile.mode; + if self.mobile.snapshot.as_ref().and_then(|s| s.connection(mode)).is_some() { + self.mobile_run(None, false, move || connection::refresh_invitation(mode), window, cx); + } else { + self.mobile_mode(mode, window, cx); + } + } + + fn mobile_mode(&mut self, mode: Mode, window: &mut Window, cx: &mut Context) { + self.mobile.mode = mode; + if let Some(snapshot) = self.mobile.snapshot.clone() { + self.mobile.display_snapshot(snapshot); + } + if self.mobile.snapshot.as_ref().and_then(|s| s.connection(mode)).is_none() { + if mode == Mode::Relay && self.mobile.saved_relay.is_none() { + self.mobile_open_relay(window, cx); + return; + } + let mut preferences = self.mobile.preferences(); + preferences.enabled = true; + match mode { + Mode::Lan => { + preferences.lan_enabled = true; + preferences.address = self.mobile_selected_address(cx); + }, + Mode::Relay => preferences.relay_enabled = true, + } + self.mobile_apply(preferences, None, false, window, cx); + } else { + cx.notify(); + } + } + + fn mobile_set_addresses( + &mut self, + addresses: Vec, + window: &mut Window, + cx: &mut Context, + ) { + let previous = self.mobile_selected_address(cx).or(self.mobile.preferences().address); + let selected = previous + .and_then(|ip| addresses.iter().position(|a| a.address == ip)) + .or_else(|| (!addresses.is_empty()).then_some(0)); + let labels = addresses + .iter() + .map(|a| SharedString::from(format!("{} · {}", a.name, a.address))) + .collect(); + self.mobile.addresses = addresses; + self.mobile.syncing = true; + self.mobile.address_select.update(cx, |select, cx| { + select.set_items(labels, window, cx); + select.set_selected_index( + selected.map(|row| IndexPath::default().row(row)), + window, + cx, + ); + }); + self.mobile.syncing = false; + } + + fn mobile_refresh_addresses(&mut self, window: &mut Window, cx: &mut Context) { + if self.mobile.loading { + return; + } + self.mobile.loading = true; + let task = cx.background_executor().spawn(async { connection::addresses() }); + cx.spawn_in(window, async move |this, cx| { + let result = task.await; + let _ = this.update_in(cx, |this, window, cx| { + this.mobile.loading = false; + match result { + Ok(addresses) => { + this.mobile_set_addresses(addresses, window, cx); + }, + Err(_) => this.mobile.failure = Some(Failure::Address), + } + cx.notify(); + }); + }) + .detach(); + } + + fn mobile_copy(&mut self, cx: &mut Context) { + if !self.mobile.valid_qr() { + return; + } + if let Some(payload) = self.mobile.qr_payload.clone() { + cx.write_to_clipboard(gpui::ClipboardItem::new_string(payload)); + self.mobile.copy_feedback.update(cx, |feedback, cx| feedback.mark_copied(cx)); + } + } + + fn mobile_remove( + &mut self, + id: String, + name: String, + window: &mut Window, + cx: &mut Context, + ) { + let language = crate::gpui_shell::config::ui_language(cx); + let owner = cx.entity().downgrade(); + window.open_dialog(cx, move |dialog, window, _| { + let owner = owner.clone(); + let id = id.clone(); + confirm_dialog( + dialog, + window, + language.format(Message::MobileRemoveTitle, &[("name", &name)]), + language.text(Message::MobileRemoveDescription), + language.text(Message::MobileRemove), + language.text(Message::CommonCancel), + ButtonVariant::Danger, + ) + .on_ok(move |_, window, cx| { + let id = id.clone(); + let _ = owner.update(cx, |this, cx| { + this.mobile_run(None, false, move || connection::revoke_device(&id), window, cx) + }); + true + }) + }); + } +} diff --git a/nebula_app/src/gpui_shell/settings_pane/mobile/relay.rs b/nebula_app/src/gpui_shell/settings_pane/mobile/relay.rs new file mode 100644 index 0000000..8a739af --- /dev/null +++ b/nebula_app/src/gpui_shell/settings_pane/mobile/relay.rs @@ -0,0 +1,436 @@ +//! 中转配置抽屉:草稿与运行连接分离,校验通过后才保存和启用。 + +use super::*; + +impl SettingsPane { + pub(super) fn mobile_sync_servers(&mut self, window: &mut Window, cx: &mut Context) { + let hosts: Vec<_> = self + .ssh_hosts + .merged() + .into_iter() + .map(|destination| { + let label = self + .ssh_hosts + .profiles + .for_destination(&destination) + .label + .filter(|label| !label.is_empty()) + .unwrap_or_else(|| destination.clone()); + (destination, label) + }) + .collect(); + let labels = hosts + .iter() + .map(|(destination, label)| { + SharedString::from(if label == destination { + destination.clone() + } else { + format!("{label} · {destination}") + }) + }) + .collect(); + let selected = (!hosts.is_empty()).then_some(IndexPath::default().row(0)); + self.mobile.server_hosts = hosts; + self.mobile.server_select.update(cx, |select, cx| { + select.set_items(labels, window, cx); + select.set_selected_index(selected, window, cx); + }); + } + + pub(super) fn mobile_open_relay(&mut self, window: &mut Window, cx: &mut Context) { + self.mobile_sync_servers(window, cx); + self.mobile.relay_open = true; + self.mobile.relay_result = None; + self.mobile.relay_edit = self.mobile.relay_edit.wrapping_add(1); + if let Some(saved) = self.mobile.saved_relay.clone() { + self.mobile_import_relay(&saved, window, cx); + } + window.focus(&self.mobile.relay_inputs[0].read(cx).focus_handle(cx), cx); + cx.notify(); + } + + fn mobile_close_relay(&mut self, window: &mut Window, cx: &mut Context) { + self.mobile.relay_open = false; + self.mobile.relay_loading = false; + self.mobile.relay_edit = self.mobile.relay_edit.wrapping_add(1); + if let Some(generation) = self.mobile.generation.take() { + connection::cancel(generation); + self.mobile.sequence = self.mobile.sequence.wrapping_add(1); + self.mobile.operation = false; + } + window.focus(&self.focus_handle, cx); + cx.notify(); + } + + pub(super) fn mobile_relay_json(&self, cx: &App) -> Result { + let values: Vec<_> = + self.mobile.relay_inputs.iter().map(|i| i.read(cx).value().trim().to_owned()).collect(); + let json = serde_json::json!({"version":2,"url":values[0],"room":values[1],"desktopToken":values[2],"mobileToken":values[3],"tlsPin":values[4]}).to_string(); + RelayAccess::parse(json.as_bytes()).map_err(|_| Failure::Invalid)?; + Ok(json) + } + + fn mobile_import_relay(&mut self, json: &str, window: &mut Window, cx: &mut Context) { + match RelayAccess::parse(json.as_bytes()) { + Ok(access) => { + self.mobile.syncing = true; + for (input, value) in self.mobile.relay_inputs.iter().zip([ + &access.url, + &access.room, + &access.desktop_token, + &access.mobile_token, + &access.tls_pin, + ]) { + input.update(cx, |input, cx| input.set_value(value.clone(), window, cx)); + } + self.mobile.syncing = false; + self.mobile.relay_edit = self.mobile.relay_edit.wrapping_add(1); + self.mobile.relay_result = Some(Message::MobileRelayImported); + self.mobile.failure = None; + }, + Err(_) => self.mobile.relay_result = Some(Message::MobileRelayInvalid), + } + cx.notify(); + } + + fn mobile_read_server(&mut self, window: &mut Window, cx: &mut Context) { + let destination = self + .mobile + .server_select + .read(cx) + .selected_index(cx) + .and_then(|i| self.mobile.server_hosts.get(i.row)) + .map(|h| h.0.clone()); + let Some(destination) = destination else { return }; + self.mobile.relay_loading = true; + self.mobile.relay_result = None; + let sequence = self.mobile.relay_edit; + let task = + cx.background_executor().spawn(async move { connection::relay_from_ssh(&destination) }); + cx.spawn_in(window, async move |this, cx| { + let result = task.await; + let _ = this.update_in(cx, |this, window, cx| { + this.mobile.relay_loading = false; + if !this.mobile.relay_open || this.mobile.relay_edit != sequence { + return; + } + match result { + Ok(json) => this.mobile_import_relay(&json, window, cx), + Err(_) => this.mobile.relay_result = Some(Message::MobileRelayReadFailed), + } + cx.notify(); + }); + }) + .detach(); + cx.notify(); + } + + fn mobile_import_file(&mut self, window: &mut Window, cx: &mut Context) { + let picked = cx.prompt_for_paths(gpui::PathPromptOptions { + files: true, + directories: false, + multiple: false, + prompt: Some( + crate::gpui_shell::config::ui_language(cx).text(Message::MobileImportFile).into(), + ), + }); + let sequence = self.mobile.relay_edit; + cx.spawn_in(window, async move |this, cx| { + let Ok(Ok(Some(paths))) = picked.await else { return }; + let Some(path) = paths.into_iter().next() else { return }; + let read = cx + .background_executor() + .spawn(async move { + use std::io::Read; + let mut data = String::new(); + std::fs::File::open(path)?.take(8193).read_to_string(&mut data)?; + if data.len() > 8192 { + return Err(std::io::Error::other("configuration_too_large")); + } + Ok::<_, std::io::Error>(data) + }) + .await; + let _ = this.update_in(cx, |this, window, cx| { + if !this.mobile.relay_open || this.mobile.relay_edit != sequence { + return; + } + match read { + Ok(json) => this.mobile_import_relay(&json, window, cx), + Err(_) => this.mobile.relay_result = Some(Message::MobileRelayInvalid), + } + cx.notify(); + }); + }) + .detach(); + } + + fn mobile_save_relay(&mut self, window: &mut Window, cx: &mut Context) { + let json = match self.mobile_relay_json(cx) { + Ok(json) => json, + Err(_) => { + self.mobile.relay_result = Some(Message::MobileRelayInvalid); + cx.notify(); + return; + }, + }; + let mut preferences = self.mobile.preferences(); + preferences.enabled = true; + preferences.relay_enabled = true; + self.mobile.mode = Mode::Relay; + self.mobile_apply(preferences, Some(json), true, window, cx); + } + + pub(in crate::gpui_shell::settings_pane) fn mobile_relay_modal( + &self, + cx: &mut Context, + ) -> Option { + if self.active_section != MOBILE_SECTION || !self.mobile.relay_open { + return None; + } + let language = crate::gpui_shell::config::ui_language(cx); + let text = |message| language.text(message); + let busy = self.mobile.operation || self.mobile.relay_loading; + let mut fields = v_flex().w_full().gap(px(18.0)); + for (index, label) in [ + Message::MobileRelayUrl, + Message::MobileRelayRoom, + Message::MobileDesktopToken, + Message::MobilePhoneToken, + Message::MobileTlsPin, + ] + .into_iter() + .enumerate() + { + let mut field = v_flex() + .w_full() + .gap(px(6.0)) + .child( + div() + .text_size(px(13.0)) + .font_weight(gpui::FontWeight::MEDIUM) + .child(text(label)), + ) + .child(Input::new(&self.mobile.relay_inputs[index]).w_full().disabled(busy)); + if index == 1 || index == 3 || index == 4 { + field = field.child(view::description( + text(match index { + 1 => Message::MobileRelayRoomHint, + 3 => Message::MobileRelayTokensHint, + _ => Message::MobileRelayPinHint, + }), + cx, + )); + } + fields = fields.child(field); + } + let drawer = v_flex() + .id("mobile-relay-drawer") + .debug_selector(|| "mobile-relay-drawer".into()) + .track_focus(&self.mobile.relay_focus) + .w(px(520.0)) + .max_w_full() + .h_full() + .bg(crate::gpui_shell::theme::settings_panel_bg(cx)) + .border_l_1() + .border_color(cx.theme().border) + .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()) + .child( + h_flex() + .w_full() + .items_start() + .gap_3() + .px(px(28.0)) + .py(px(24.0)) + .border_b_1() + .border_color(cx.theme().border) + .child( + v_flex() + .flex_1() + .min_w_0() + .gap_1() + .child( + div() + .text_size(px(18.0)) + .font_weight(gpui::FontWeight::SEMIBOLD) + .child(text(Message::MobileRelay)), + ) + .child(view::description(text(Message::MobileRelayDrawerSubtitle), cx)), + ) + .child( + Button::new("mobile-relay-close") + .ghost() + .icon(IconName::Close) + .tooltip(text(Message::MobileClose)) + .on_click(cx.listener(|this, _, window, cx| { + this.mobile_close_relay(window, cx) + })), + ), + ) + .child( + v_flex().flex_1().min_h_0().overflow_y_scrollbar().child( + v_flex() + .w_full() + .px(px(28.0)) + .py(px(24.0)) + .gap(px(28.0)) + .child( + v_flex() + .w_full() + .gap_3() + .child(view::group_heading(text(Message::MobileReadServer), cx)) + .child(view::description(text(Message::MobileReadServerHint), cx)) + .child( + h_flex() + .w_full() + .gap_2() + .child( + Select::new(&self.mobile.server_select) + .flex_1() + .min_w_0() + .disabled(busy), + ) + .child( + Button::new("mobile-relay-read") + .outline() + .label(text(if self.mobile.relay_loading { + Message::MobileReading + } else { + Message::MobileRead + })) + .disabled( + busy || self.mobile.server_hosts.is_empty(), + ) + .on_click(cx.listener(|this, _, window, cx| { + this.mobile_read_server(window, cx) + })), + ), + ), + ) + .child( + v_flex() + .w_full() + .gap_4() + .child( + h_flex() + .w_full() + .flex_wrap() + .gap_2() + .child(view::group_heading( + text(Message::MobileConnectionInfo), + cx, + )) + .child(div().flex_1()) + .child( + Button::new("mobile-relay-paste") + .ghost() + .small() + .label(text(Message::MobileImportClipboard)) + .disabled(busy) + .on_click(cx.listener(|this, _, window, cx| { + if let Some(data) = cx + .read_from_clipboard() + .and_then(|item| item.text()) + { + this.mobile_import_relay(&data, window, cx); + } else { + this.mobile.relay_result = + Some(Message::MobileRelayInvalid); + cx.notify(); + } + })), + ) + .child( + Button::new("mobile-relay-file") + .ghost() + .small() + .label(text(Message::MobileImportFile)) + .disabled(busy) + .on_click(cx.listener(|this, _, window, cx| { + this.mobile_import_file(window, cx) + })), + ), + ) + .child(fields), + ) + .when_some(self.mobile.relay_result, |body, result| { + body.child(view::description(text(result), cx)) + }) + .when_some(self.mobile.failure, |body, failure| { + body.child( + div() + .text_size(px(13.0)) + .text_color(cx.theme().danger) + .child(text(view::failure_message(failure))), + ) + }) + .child(view::description(text(Message::MobileEncryptedHint), cx)), + ), + ) + .child( + h_flex() + .w_full() + .px(px(28.0)) + .py(px(18.0)) + .gap_2() + .border_t_1() + .border_color(cx.theme().border) + .child( + Button::new("mobile-relay-disable") + .ghost() + .label(text(Message::MobileDisableRelay)) + .disabled(busy || !self.mobile.preferences().relay_enabled) + .on_click(cx.listener(|this, _, window, cx| { + let mut preferences = this.mobile.preferences(); + preferences.relay_enabled = false; + this.mobile_apply(preferences, None, true, window, cx); + })), + ) + .child(div().flex_1()) + .child( + Button::new("mobile-relay-cancel") + .ghost() + .label(text(Message::CommonCancel)) + .on_click(cx.listener(|this, _, window, cx| { + this.mobile_close_relay(window, cx) + })), + ) + .child( + Button::new("mobile-relay-save") + .primary() + .label(text(if self.mobile.operation { + Message::MobileChecking + } else { + Message::MobileCheckSave + })) + .disabled(busy) + .on_click(cx.listener(|this, _, window, cx| { + this.mobile_save_relay(window, cx) + })), + ), + ); + Some( + div() + .absolute() + .inset_0() + .flex() + .justify_end() + .bg(cx.theme().background.opacity(0.5)) + .occlude() + .on_mouse_down( + MouseButton::Left, + cx.listener(|this, _, window, cx| { + cx.stop_propagation(); + this.mobile_close_relay(window, cx); + }), + ) + .on_key_down(cx.listener(|this, event: &KeyDownEvent, window, cx| { + if event.keystroke.key.eq_ignore_ascii_case("escape") { + cx.stop_propagation(); + this.mobile_close_relay(window, cx); + } + })) + .child(drawer) + .into_any_element(), + ) + } +} diff --git a/nebula_app/src/gpui_shell/settings_pane/mobile/tests.rs b/nebula_app/src/gpui_shell/settings_pane/mobile/tests.rs new file mode 100644 index 0000000..0b9dea9 --- /dev/null +++ b/nebula_app/src/gpui_shell/settings_pane/mobile/tests.rs @@ -0,0 +1,311 @@ +use super::*; +use crate::gpui_shell::copy_feedback::COPY_FEEDBACK_TTL; +use crate::mobile_connection::{ConnectionSnapshot, DeviceSummary}; + +fn fixture(enabled: bool, paired: bool) -> Snapshot { + let invitation = serde_json::json!({ + "fixture": "complete invitation", "secure": { "expiresAt": now() + 600 } + }) + .to_string(); + Snapshot { + preferences: Preferences { enabled, ..Default::default() }, + lan: enabled.then_some(ConnectionSnapshot { + status: Status::Waiting, + invitation: Some(invitation), + address: "wss://192.0.2.1:4567".into(), + pairing_code: Some("48271936".into()), + discoverable: true, + }), + relay: None, + devices: if paired { + vec![DeviceSummary { + id: "fixture-phone".into(), + name: "Fixture phone".into(), + allow_input: false, + connected: true, + route: Some(Mode::Lan), + }] + } else { + Vec::new() + }, + requests: Vec::new(), + } +} + +fn fixture_addresses() -> Vec { + [("Ethernet", "192.0.2.1"), ("Tailscale", "100.64.0.8")] + .into_iter() + .map(|(name, address)| connection::LanAddress { + address: address.parse().unwrap(), + name: name.into(), + preferred: name == "Ethernet", + }) + .collect() +} + +#[gpui::test] +fn mobile_three_states_and_manual_copy_use_the_rendered_controls(cx: &mut gpui::TestAppContext) { + cx.update(|cx| { + gpui_component::init(cx); + cx.set_global(crate::gpui_shell::config::Settings::load(ThemeName::Nord)); + }); + let mut owner = None; + let (_, cx) = cx.add_window_view(|window, cx| { + let pane = cx.new(|cx| SettingsPane::new(window, cx)); + pane.update(cx, |pane, cx| { + pane.active_section = 11; + // 布局夹具不启动真实监听,也不读取或修改用户的手机授权。 + pane.mobile.initialized = true; + pane.mobile.display_snapshot(fixture(false, false)); + pane.mobile_set_addresses(fixture_addresses(), window, cx); + }); + owner = Some(pane.clone()); + gpui_component::Root::new(pane, window, cx) + }); + let pane = owner.unwrap(); + cx.simulate_resize(gpui::size(px(1280.0), px(1000.0))); + cx.run_until_parked(); + cx.update(|window, cx| { + let _ = window.draw(cx); + }); + let off = cx.debug_bounds("mobile-off").expect("off state has actual layout"); + let enable = cx.debug_bounds("mobile-enable").expect("enable action is rendered"); + assert!(enable.size.width < off.size.width / 2.0, "enable action keeps its content width"); + assert!(cx.debug_bounds("mobile-pairing").is_none()); + assert!(cx.debug_bounds("mobile-interface-row").is_none()); + + cx.update(|_, cx| { + pane.update(cx, |pane, cx| { + pane.mobile.display_snapshot(fixture(true, false)); + cx.notify(); + }) + }); + cx.run_until_parked(); + cx.update(|window, cx| { + let _ = window.draw(cx); + }); + assert!(cx.debug_bounds("mobile-off").is_none()); + assert!(cx.debug_bounds("mobile-pairing").is_some()); + assert!(cx.debug_bounds("mobile-short-code").is_some()); + let network = cx + .debug_bounds("mobile-interface-row") + .expect("network selection is available before the first pairing"); + let port = + cx.debug_bounds("mobile-port-row").expect("port is editable before the first pairing"); + let pairing = cx.debug_bounds("mobile-pairing").unwrap(); + let modes = cx.debug_bounds("mobile-pairing-modes").unwrap(); + let steps = cx.debug_bounds("mobile-pairing-steps").unwrap(); + assert!( + pairing.origin.y <= modes.origin.y + && modes.bottom() <= network.origin.y + && network.bottom() <= port.origin.y + && port.bottom() <= steps.origin.y + && steps.bottom() <= pairing.bottom(), + "endpoint settings stay inside the pairing card, after the connection type and before the steps" + ); + let qr = cx.debug_bounds("mobile-qr").expect("QR has actual layout"); + let copy = + cx.debug_bounds("mobile-copy-invitation").expect("manual pairing action is rendered"); + assert!(copy.size.height >= px(28.0)); + cx.simulate_click(copy.center(), gpui::Modifiers::default()); + let expected = pane.read_with(cx, |pane, _| pane.mobile.qr_payload.clone().unwrap()); + assert_eq!(cx.read_from_clipboard().unwrap().text().unwrap(), expected); + assert!(pane.read_with(cx, |pane, cx| pane.mobile.copy_feedback.read(cx).is_copied())); + cx.run_until_parked(); + cx.executor().advance_clock(COPY_FEEDBACK_TTL / 2); + cx.simulate_click(copy.center(), gpui::Modifiers::default()); + cx.run_until_parked(); + cx.executor().advance_clock(COPY_FEEDBACK_TTL / 2); + cx.run_until_parked(); + assert!(pane.read_with(cx, |pane, cx| pane.mobile.copy_feedback.read(cx).is_copied())); + cx.executor().advance_clock(COPY_FEEDBACK_TTL); + cx.run_until_parked(); + assert!(!pane.read_with(cx, |pane, cx| pane.mobile.copy_feedback.read(cx).is_copied())); + cx.update(|window, cx| { + let _ = window.draw(cx); + }); + assert_eq!(cx.debug_bounds("mobile-qr").unwrap(), qr, "copy feedback must not move the QR"); + + // 网络切换尚未完成时,不能再把旧邀请交给手机。 + cx.update(|_, cx| { + pane.update(cx, |pane, cx| { + pane.mobile.operation = true; + cx.notify(); + }); + cx.write_to_clipboard(gpui::ClipboardItem::new_string("unchanged while switching".into())); + }); + cx.run_until_parked(); + cx.update(|window, cx| { + let _ = window.draw(cx); + }); + let blocked_copy = cx.debug_bounds("mobile-copy-invitation").unwrap(); + cx.simulate_click(blocked_copy.center(), gpui::Modifiers::default()); + assert_eq!(cx.read_from_clipboard().unwrap().text().unwrap(), "unchanged while switching"); + cx.update(|_, cx| { + pane.update(cx, |pane, cx| { + pane.mobile.operation = false; + cx.notify(); + }); + }); + + cx.simulate_resize(gpui::size(px(820.0), px(1200.0))); + cx.run_until_parked(); + cx.update(|window, cx| { + let _ = window.draw(cx); + }); + let narrow_network = cx + .debug_bounds("mobile-interface-row") + .expect("network selection remains reachable in a narrow window"); + assert!(narrow_network.origin.x >= px(0.0) && narrow_network.right() <= px(820.0)); + let narrow_qr = cx.debug_bounds("mobile-qr").unwrap(); + assert!(narrow_qr.origin.x >= px(0.0) && narrow_qr.right() <= px(820.0)); + assert_eq!(narrow_qr.size, qr.size, "narrow layouts stack instead of shrinking the QR"); + + cx.update(|_, cx| { + pane.update(cx, |pane, cx| { + pane.mobile.display_snapshot(fixture(true, true)); + cx.notify(); + }) + }); + cx.run_until_parked(); + cx.update(|window, cx| { + let _ = window.draw(cx); + }); + assert_eq!(pane.read_with(cx, |pane, _| pane.mobile.phase()), Phase::Paired); + assert!(cx.debug_bounds("mobile-pairing").is_none()); + assert!(cx.debug_bounds("mobile-add-phone").is_some()); + + cx.simulate_resize(gpui::size(px(1280.0), px(1600.0))); + cx.run_until_parked(); + cx.update(|window, cx| { + let _ = window.draw(cx); + }); + let notifications = std::rc::Rc::new(std::cell::Cell::new(0)); + let observed = notifications.clone(); + let _subscription = + cx.update(|_, cx| cx.observe(&pane, move |_, _| observed.set(observed.get() + 1))); + for selector in [ + "mobile-device-row", + "mobile-lan-row", + "mobile-interface-row", + "mobile-port-row", + "mobile-relay-row", + "mobile-default-permission-row", + "mobile-notifications-row", + "mobile-pause-row", + ] { + let bounds = cx.debug_bounds(selector).expect("settings row has actual layout"); + let outside = gpui::point(px(1279.0), px(1599.0)); + cx.simulate_mouse_move(outside, None, gpui::Modifiers::default()); + cx.run_until_parked(); + notifications.set(0); + + // 指针落在行的留白,避免按钮的重绘掩盖行自身缺少 hover 通知;不推进轮询时钟。 + let inside = gpui::point(bounds.origin.x + px(4.0), bounds.center().y); + cx.simulate_mouse_move(inside, None, gpui::Modifiers::default()); + cx.run_until_parked(); + assert!(notifications.get() > 0, "{selector} must repaint on hover without networking"); + + notifications.set(0); + cx.simulate_mouse_move( + inside + gpui::point(px(1.0), px(1.0)), + None, + gpui::Modifiers::default(), + ); + cx.run_until_parked(); + assert_eq!(notifications.get(), 0, "{selector} must not redraw for every pointer move"); + + notifications.set(0); + cx.simulate_mouse_move(outside, None, gpui::Modifiers::default()); + cx.run_until_parked(); + assert!(notifications.get() > 0, "{selector} must repaint when the pointer leaves"); + } +} + +#[test] +#[ignore = "requires a native Windows desktop and PEBREL_MOBILE_QA_DIR"] +fn native_mobile_three_state_preview() { + use gpui::{Bounds, WindowBounds, WindowOptions, point}; + use std::{ + path::PathBuf, + sync::{Arc, Mutex}, + time::Duration, + }; + + let output = PathBuf::from(std::env::var_os("PEBREL_MOBILE_QA_DIR").expect("QA directory")); + std::fs::create_dir_all(&output).unwrap(); + assert!(!output.join("off-ready.json").exists(), "use a fresh QA directory"); + let result = Arc::new(Mutex::new(0)); + let completed = result.clone(); + gpui_platform::application().with_assets(crate::gpui_shell::assets::NebulaAssets).run( + move |cx| { + gpui_component::init(cx); + let mut settings = crate::gpui_shell::config::Settings::load(ThemeName::Nord); + settings.ui_language = crate::display::UiLanguage::ZhCn; + cx.set_global(settings); + crate::gpui_shell::theme::apply_chrome_theme(cx); + let mut owner = None; + let handle = cx + .open_window( + WindowOptions { + window_bounds: Some(WindowBounds::Windowed(Bounds::new( + point(px(60.0), px(60.0)), + gpui::size(px(1080.0), px(860.0)), + ))), + focus: false, + show: true, + ..Default::default() + }, + |window, cx| { + let pane = cx.new(|cx| SettingsPane::new(window, cx)); + pane.update(cx, |pane, cx| { + pane.active_section = 11; + // Snapshot-only preview: no listeners, credentials or pairing records. + pane.mobile.initialized = true; + pane.mobile.display_snapshot(fixture(false, false)); + pane.mobile_set_addresses(fixture_addresses(), window, cx); + }); + owner = Some(pane.clone()); + cx.new(|cx| gpui_component::Root::new(pane, window, cx)) + }, + ) + .unwrap(); + let pane = owner.unwrap(); + cx.spawn(async move |cx| { + for (name, enabled, paired) in + [("off", false, false), ("pairing", true, false), ("paired", true, true)] + { + cx.update_window(handle.into(), |_, window, cx| { + pane.update(cx, |pane, cx| { + pane.mobile.display_snapshot(fixture(enabled, paired)); + cx.notify(); + }); + window.refresh(); + }) + .unwrap(); + cx.background_executor().timer(Duration::from_millis(700)).await; + std::fs::write( + output.join(format!("{name}-ready.json")), + serde_json::to_vec( + &serde_json::json!({"pid": std::process::id(), "state": name}), + ) + .unwrap(), + ) + .unwrap(); + for _ in 0..100 { + if output.join(format!("{name}-captured")).exists() { + *result.lock().unwrap() += 1; + break; + } + cx.background_executor().timer(Duration::from_millis(200)).await; + } + } + cx.update_window(handle.into(), |_, window, _| window.remove_window()).unwrap(); + drop(pane); + cx.update(|cx| cx.quit()); + }) + .detach(); + }, + ); + assert_eq!(*completed.lock().unwrap(), 3, "all three native states were captured"); +} diff --git a/nebula_app/src/gpui_shell/settings_pane/mobile/view.rs b/nebula_app/src/gpui_shell/settings_pane/mobile/view.rs new file mode 100644 index 0000000..e67d213 --- /dev/null +++ b/nebula_app/src/gpui_shell/settings_pane/mobile/view.rs @@ -0,0 +1,794 @@ +//! 三态共享同一份运行快照;二维码、在线状态和批准请求均来自实际连接。 + +use super::*; +use crate::gpui_shell::widgets::NebulaSwitch; +use gpui_component::menu::PopupMenuItem; + +pub(super) fn description(text: impl Into, cx: &App) -> gpui::Div { + div() + .text_size(px(13.0)) + .line_height(px(21.0)) + .text_color(cx.theme().muted_foreground) + .child(text.into()) +} + +pub(super) fn group_heading(text: impl Into, cx: &App) -> gpui::Div { + div() + .text_size(px(13.0)) + .font_weight(gpui::FontWeight::SEMIBOLD) + .text_color(cx.theme().muted_foreground) + .child(text.into()) +} + +pub(super) fn failure_message(failure: Failure) -> Message { + match failure { + Failure::Invalid => Message::MobileInvalid, + Failure::Address => Message::MobileAddressFailed, + Failure::Port => Message::MobilePortFailed, + Failure::Credentials => Message::MobileCredentialsFailed, + Failure::Connection => Message::MobileConnectionFailed, + Failure::Cancelled => Message::MobileCancelled, + } +} + +fn status_message(status: Status) -> Message { + match status { + Status::Starting => Message::MobileStarting, + Status::Waiting => Message::MobileWaiting, + Status::Connected => Message::MobileConnected, + Status::Reconnecting => Message::MobileReconnecting, + Status::Stopped => Message::MobileStopped, + Status::Failed => Message::MobileConnectionFailed, + } +} + +fn row( + id: &'static str, + title: &'static str, + hint: impl Into, + control: impl IntoElement, + cx: &App, +) -> gpui::Stateful { + // GPUI 需用稳定 ID 保存 hover 状态,否则颜色只能等后台状态轮询触发重绘。 + h_flex() + .id(id) + .debug_selector(move || id.into()) + .w_full() + .gap(px(24.0)) + .px(px(13.0)) + .py(px(12.0)) + .rounded(px(8.0)) + .hover(|style| style.bg(crate::gpui_shell::theme::settings_hover_bg(cx, false))) + .child( + v_flex() + .flex_1() + .min_w_0() + .gap(px(3.0)) + .child(div().text_size(px(14.0)).child(title)) + .child(description(hint, cx)), + ) + .child(h_flex().flex_shrink_0().gap_2().child(control)) +} + +impl SettingsPane { + pub(in crate::gpui_shell::settings_pane) fn section_mobile( + &mut self, + window: &mut Window, + cx: &mut Context, + ) -> gpui::Div { + self.mobile_initialize(window, cx); + let language = crate::gpui_shell::config::ui_language(cx); + let phase = self.mobile.phase(); + // 原型的 812 包含两侧 56px 留白;窄窗收紧页边,不缩小二维码或按钮。 + let narrow = f32::from(window.viewport_size().width) < 1000.0; + let mut page = v_flex() + .id("mobile-settings") + .debug_selector(|| "mobile-settings".into()) + .w_full() + .max_w(px(812.0)) + .px(px(if narrow { 24.0 } else { 56.0 })) + .pt(px(44.0)) + .pb(px(140.0)) + .text_size(px(14.0)) + .line_height(px(21.0)) + .child( + v_flex() + .w_full() + .gap(px(6.0)) + .child( + div() + .text_size(px(22.0)) + .line_height(px(30.0)) + .font_weight(gpui::FontWeight::SEMIBOLD) + .child(language.text(Message::MobileTitle)), + ) + .child(description(language.text(Message::MobileSubtitle), cx)), + ); + if let Some(failure) = self.mobile.failure { + page = page.child( + div() + .id("mobile-error") + .mt(px(18.0)) + .text_size(px(13.0)) + .text_color(cx.theme().danger) + .child(language.text(failure_message(failure))), + ); + } + page = match phase { + Phase::Off => page.child(self.mobile_off(cx)), + Phase::Pairing => page.child(self.mobile_pairing(narrow, cx)), + Phase::Paired => page, + }; + if phase != Phase::Off { + if let Some(snapshot) = &self.mobile.snapshot { + for request in &snapshot.requests { + page = page.child(self.mobile_request(request, cx)); + } + } + page = page.child(self.mobile_devices(cx)); + } + if phase == Phase::Paired { + page = page.child(self.mobile_connections(cx)).child(self.mobile_permissions(cx)); + } + div().w_full().flex().justify_center().child(page) + } + + fn mobile_off(&self, cx: &Context) -> gpui::Stateful { + let language = crate::gpui_shell::config::ui_language(cx); + let busy = self.mobile.operation || self.mobile.loading; + v_flex() + .id("mobile-off") + .debug_selector(|| "mobile-off".into()) + .w_full() + .mt(px(28.0)) + .p(px(24.0)) + .border_1() + .border_color(cx.theme().border) + .rounded(px(10.0)) + .gap_1() + .child( + div() + .text_size(px(18.0)) + .line_height(px(26.0)) + .font_weight(gpui::FontWeight::SEMIBOLD) + .child(language.text(Message::MobileOffTitle)), + ) + .child(description(language.text(Message::MobileOffDescription), cx)) + .child( + h_flex().mt(px(20.0)).child( + Button::new("mobile-enable") + .debug_selector(|| "mobile-enable".into()) + .primary() + .label(language.text(if busy { + Message::MobileStarting + } else { + Message::MobileEnable + })) + .disabled(busy) + .on_click( + cx.listener(|this, _, window, cx| this.mobile_enable(window, cx)), + ), + ), + ) + } + + fn mobile_pairing(&self, narrow: bool, cx: &Context) -> gpui::Stateful { + let language = crate::gpui_shell::config::ui_language(cx); + let busy = self.mobile.operation; + let mode = self.mobile.mode; + let mut modes = h_flex() + .id("mobile-pairing-modes") + .debug_selector(|| "mobile-pairing-modes".into()) + .mt(px(18.0)) + .p(px(2.0)) + .rounded(px(8.0)) + .border_1() + .border_color(cx.theme().border) + .bg(cx.theme().secondary); + for (route, label) in [(Mode::Lan, Message::MobileLan), (Mode::Relay, Message::MobileRelay)] + { + modes = modes.child( + Button::new(if route == Mode::Lan { + "mobile-mode-lan" + } else { + "mobile-mode-relay" + }) + .ghost() + .small() + .h(px(28.0)) + .rounded(px(6.0)) + .selected(mode == route) + .when(mode == route, |button| { + button.bg(crate::gpui_shell::theme::settings_panel_bg(cx)) + }) + .label(language.text(label)) + .disabled(busy) + .on_click( + cx.listener(move |this, _, window, cx| this.mobile_mode(route, window, cx)), + ), + ); + } + let mut steps = v_flex() + .id("mobile-pairing-steps") + .debug_selector(|| "mobile-pairing-steps".into()) + .w_full() + .mt(px(18.0)) + .gap(px(10.0)); + let messages = match mode { + Mode::Lan => { + [Message::MobileStepOpen, Message::MobileStepScan, Message::MobileStepApprove] + }, + Mode::Relay => { + [Message::MobileStepRelay, Message::MobileStepScan, Message::MobileStepApprove] + }, + }; + for (index, message) in messages.into_iter().enumerate() { + let mut body = v_flex().flex_1().min_w_0().child(language.text(message)); + if index == 0 { + body = body.child(description( + language.text(if mode == Mode::Lan { + Message::MobileLanHint + } else { + Message::MobileRelayOutbound + }), + cx, + )); + } + steps = steps.child( + h_flex() + .w_full() + .items_start() + .gap(px(10.0)) + .child( + div() + .size(px(20.0)) + .flex_shrink_0() + .flex() + .items_center() + .justify_center() + .border_1() + .border_color(cx.theme().border) + .rounded_full() + .text_size(px(11.5)) + .child((index + 1).to_string()), + ) + .child(body), + ); + } + let valid = self.mobile.valid_qr(); + let mut qr = v_flex().w(px(232.0)).flex_shrink_0().items_center().gap(px(10.0)); + let code = div() + .id("mobile-qr") + .debug_selector(|| "mobile-qr".into()) + .size(px(196.0)) + .p(px(12.0)) + .rounded(px(10.0)) + .bg(gpui::rgb(0xffffff)) + .flex() + .items_center() + .justify_center(); + qr = qr.child(if valid { + code.child(img(self.mobile.qr.as_ref().expect("valid QR").clone()).size_full()) + } else { + code.child(div().text_size(px(13.0)).text_color(gpui::rgb(0x333333)).child( + language.text(if busy { + Message::MobileStarting + } else { + Message::MobileQrUnavailable + }), + )) + }); + let remaining = self.mobile.expires_at.unwrap_or(0).saturating_sub(now()); + let timer = format!("{:02}:{:02}", remaining / 60, remaining % 60); + qr = qr.child( + h_flex() + .gap_1() + .child(description( + if valid { + language.format(Message::MobileExpires, &[("time", &timer)]) + } else { + language.text(Message::MobileExpired).to_owned() + }, + cx, + )) + .child( + Button::new("mobile-refresh-qr") + .ghost() + .small() + .label(language.text(Message::MobileRefreshQr)) + .disabled(busy) + .on_click(cx.listener(|this, _, window, cx| this.mobile_pair(window, cx))), + ), + ); + if valid && mode == Mode::Lan { + if let Some(connection) = self.mobile.snapshot.as_ref().and_then(|s| s.connection(mode)) + { + if let Some(code) = + connection.pairing_code.as_deref().filter(|code| code.len() == 8) + { + qr = qr.child( + h_flex() + .gap_1() + .child(description(language.text(Message::MobileShortCodePrompt), cx)) + .child( + div() + .id("mobile-short-code") + .debug_selector(|| "mobile-short-code".into()) + .text_size(px(13.0)) + .font_weight(gpui::FontWeight::SEMIBOLD) + .child(format!("{} {}", &code[..4], &code[4..])), + ), + ); + if !connection.discoverable { + qr = qr.child(description( + language.text(Message::MobileDiscoveryUnavailable), + cx, + )); + } + } else { + qr = qr.child(description(language.text(Message::MobileShortCodeUsed), cx)); + } + } + } + let copied = self.mobile.copy_feedback.read(cx).is_copied(); + qr = qr.child( + Button::new("mobile-copy-invitation") + .debug_selector(|| "mobile-copy-invitation".into()) + .ghost() + .small() + .h(px(32.0)) + .icon(if copied { IconName::Check } else { IconName::Copy }) + .label(language.text(if copied { + Message::MobileCopied + } else { + Message::MobileManualPair + })) + .disabled(!valid) + .on_click(cx.listener(|this, _, _, cx| this.mobile_copy(cx))), + ); + let mut left = v_flex() + .flex_1() + .min_w_0() + .child( + div() + .text_size(px(18.0)) + .line_height(px(26.0)) + .font_weight(gpui::FontWeight::SEMIBOLD) + .child(language.text(Message::MobilePairTitle)), + ) + .child(description(language.text(Message::MobilePairDescription), cx)) + .child(h_flex().child(modes)) + // 网卡决定当前邀请的地址,应和连接类型、扫码步骤放在同一配对卡片里。 + .when(mode == Mode::Lan, |left| { + left.child(self.mobile_lan_options(true, cx).mt(px(14.0))) + }) + .child(steps); + if let Some(connection) = self.mobile.snapshot.as_ref().and_then(|s| s.connection(mode)) { + left = left.child( + description( + format!( + "{} · {}", + language.text(status_message(connection.status)), + connection.address + ), + cx, + ) + .mt(px(14.0)), + ); + } else if mode == Mode::Relay { + left = left.child( + Button::new("mobile-pair-configure") + .ghost() + .label(language.text(Message::MobileConfigureRelay)) + .on_click( + cx.listener(|this, _, window, cx| this.mobile_open_relay(window, cx)), + ), + ); + } + h_flex() + .id("mobile-pairing") + .debug_selector(|| "mobile-pairing".into()) + .w_full() + .mt(px(28.0)) + .p(px(24.0)) + .gap(px(32.0)) + .items_start() + .border_1() + .border_color(cx.theme().border) + .rounded(px(10.0)) + .when(narrow, |hero| hero.flex_col()) + .child(left) + .child(qr) + } + + fn mobile_request( + &self, + request: &connection::PairingRequest, + cx: &Context, + ) -> gpui::Stateful { + let language = crate::gpui_shell::config::ui_language(cx); + let approve_id = request.id.clone(); + let deny_id = request.id.clone(); + let busy = self.mobile.operation || request.approving; + let route = language.text(if request.route == Mode::Lan { + Message::MobileLan + } else { + Message::MobileRelay + }); + let detail = language.format( + Message::MobileRequestDescription, + &[ + ("route", route), + ("peer", request.peer.as_deref().unwrap_or("")), + ("code", &request.verification_code), + ], + ); + h_flex() + .id(SharedString::from(format!("mobile-request-{}", request.id))) + .debug_selector(|| "mobile-pairing-request".into()) + .w_full() + .mt(px(16.0)) + .px(px(16.0)) + .py(px(14.0)) + .gap(px(14.0)) + .border_1() + .border_color(cx.theme().border) + .rounded(px(10.0)) + .flex_wrap() + .child(Icon::default().path(crate::gpui_shell::assets::nav::PHONE).size(px(20.0))) + .child( + v_flex() + .flex_1() + .min_w(px(160.0)) + .gap_1() + .child(div().text_size(px(14.0)).font_weight(gpui::FontWeight::SEMIBOLD).child( + language.format(Message::MobileRequestTitle, &[("name", &request.name)]), + )) + .child(description(detail, cx)), + ) + .child( + Button::new(SharedString::from(format!("mobile-deny-{}", request.id))) + .ghost() + .label(language.text(Message::MobileDeny)) + .disabled(busy) + .on_click(cx.listener(move |this, _, window, cx| { + let id = deny_id.clone(); + this.mobile_run( + None, + false, + move || connection::decide_pairing(&id, None), + window, + cx, + ); + })), + ) + .child( + Button::new(SharedString::from(format!("mobile-approve-{}", request.id))) + .primary() + .label(language.text(if request.approving { + Message::MobileApproving + } else { + Message::MobileApprove + })) + .disabled(busy) + .on_click(cx.listener(move |this, _, window, cx| { + let id = approve_id.clone(); + let permission = this.mobile.preferences().default_input; + this.mobile_run( + None, + false, + move || connection::decide_pairing(&id, Some(permission)), + window, + cx, + ); + })), + ) + } + + fn mobile_permission_menu( + &self, + device: Option<&connection::DeviceSummary>, + cx: &Context, + ) -> impl IntoElement { + let language = crate::gpui_shell::config::ui_language(cx); + let selected = + device.map(|d| d.allow_input).unwrap_or(self.mobile.preferences().default_input); + let device_id = device.map(|d| d.id.clone()); + let owner = cx.entity().downgrade(); + Button::new(SharedString::from(format!( + "mobile-permission-{}", + device_id.as_deref().unwrap_or("default") + ))) + .outline() + .small() + .w(px(132.0)) + .h(px(32.0)) + .justify_between() + .label(language.text(if selected { + Message::MobileAllowInput + } else { + Message::MobileReadOnly + })) + .dropdown_caret(true) + .disabled(self.mobile.operation) + .dropdown_menu(move |mut menu, _, _| { + for (value, label) in + [(false, Message::MobileReadOnly), (true, Message::MobileAllowInput)] + { + let owner = owner.clone(); + let device_id = device_id.clone(); + menu = menu.item( + PopupMenuItem::new(language.text(label)).checked(value == selected).on_click( + move |_, window, cx| { + let id = device_id.clone(); + let _ = owner.update(cx, |this, cx| { + if let Some(id) = id { + this.mobile_run( + None, + false, + move || connection::set_permission(&id, value), + window, + cx, + ); + } else { + let mut preferences = this.mobile.preferences(); + preferences.default_input = value; + this.mobile_apply(preferences, None, false, window, cx); + } + }); + }, + ), + ); + } + menu + }) + } + + fn mobile_devices(&self, cx: &Context) -> gpui::Stateful { + let language = crate::gpui_shell::config::ui_language(cx); + let mut group = v_flex().id("mobile-devices").w_full().mt(px(36.0)).child( + h_flex() + .w_full() + .justify_between() + .pb(px(6.0)) + .child(group_heading(language.text(Message::MobileDevices), cx)) + .when(self.mobile.phase() == Phase::Paired, |head| { + head.child( + Button::new("mobile-add-phone") + .debug_selector(|| "mobile-add-phone".into()) + .ghost() + .small() + .icon(IconName::Plus) + .label(language.text(Message::MobileAddPhone)) + .disabled(self.mobile.operation) + .on_click( + cx.listener(|this, _, window, cx| this.mobile_pair(window, cx)), + ), + ) + }), + ); + let devices = + self.mobile.snapshot.as_ref().map(|s| s.devices.as_slice()).unwrap_or_default(); + if devices.is_empty() { + return group.child( + description(language.text(Message::MobileNoDevices), cx).px(px(13.0)).py(px(12.0)), + ); + } + for device in devices { + let id = device.id.clone(); + let name = device.name.clone(); + let status = language.text(if device.connected { + Message::MobileOnline + } else { + Message::MobileOffline + }); + let detail = if let Some(route) = device.route.filter(|_| device.connected) { + format!( + "{status} · {}", + language.text(if route == Mode::Lan { + Message::MobileLan + } else { + Message::MobileRelay + }) + ) + } else { + status.to_owned() + }; + group = group.child( + h_flex() + .id(SharedString::from(format!("mobile-device-{id}"))) + .debug_selector(|| "mobile-device-row".into()) + .w_full() + .px(px(13.0)) + .py(px(12.0)) + .gap(px(14.0)) + .rounded(px(8.0)) + .hover(|style| style.bg(crate::gpui_shell::theme::settings_hover_bg(cx, false))) + .child( + Icon::default() + .path(crate::gpui_shell::assets::nav::PHONE) + .size(px(20.0)) + .flex_shrink_0(), + ) + .child( + v_flex() + .flex_1() + .min_w_0() + .gap(px(2.0)) + .child( + div() + .text_size(px(14.0)) + .font_weight(gpui::FontWeight::MEDIUM) + .child(name.clone()), + ) + .child(description(detail, cx)), + ) + .child(self.mobile_permission_menu(Some(device), cx)) + .child( + Button::new(SharedString::from(format!("mobile-remove-{id}"))) + .ghost() + .small() + .label(language.text(Message::MobileRemove)) + .disabled(self.mobile.operation) + .on_click(cx.listener(move |this, _, window, cx| { + this.mobile_remove(id.clone(), name.clone(), window, cx) + })), + ), + ); + } + group + } + + fn mobile_lan_options(&self, compact: bool, cx: &Context) -> gpui::Div { + let language = crate::gpui_shell::config::ui_language(cx); + let busy = self.mobile.operation; + v_flex() + .w_full() + .when(compact, |fields| fields.gap(px(12.0))) + .child( + row( + "mobile-interface-row", + language.text(Message::MobileInterface), + language.text(Message::MobileInterfaceHint), + h_flex() + .gap_2() + .child( + gpui::Styled::h( + Select::new(&self.mobile.address_select).small(), + px(32.0), + ) + .w(px(204.0)) + .disabled(busy), + ) + .child( + Button::new("mobile-refresh-addresses") + .ghost() + .small() + .icon(IconName::Redo) + .tooltip(language.text(Message::MobileRefreshAddresses)) + .disabled(busy || self.mobile.loading) + .on_click(cx.listener(|this, _, window, cx| { + this.mobile_refresh_addresses(window, cx) + })), + ), + cx, + ) + .when(compact, |field| { + field.flex_col().items_stretch().gap(px(6.0)).px(px(0.0)).py(px(0.0)) + }), + ) + .child( + row( + "mobile-port-row", + language.text(Message::MobilePort), + language.text(Message::MobilePortHint), + gpui::Styled::h(Input::new(&self.mobile.port_input).small(), px(32.0)) + .w(px(120.0)) + .disabled(busy), + cx, + ) + .when(compact, |field| { + field.flex_col().items_stretch().gap(px(6.0)).px(px(0.0)).py(px(0.0)) + }), + ) + } + + fn mobile_connections(&self, cx: &Context) -> gpui::Div { + let language = crate::gpui_shell::config::ui_language(cx); + let preferences = self.mobile.preferences(); + let busy = self.mobile.operation; + let mut group = v_flex() + .w_full() + .mt(px(36.0)) + .child( + h_flex() + .w_full() + .justify_between() + .gap_3() + .pb(px(6.0)) + .child(group_heading(language.text(Message::MobileConnections), cx)) + .child(description(language.text(Message::MobileConnectionsHint), cx)), + ) + .child(row( + "mobile-lan-row", + language.text(Message::MobileLanDirect), + language.text(Message::MobileLanHint), + NebulaSwitch::new("mobile-lan-enabled") + .checked(preferences.lan_enabled) + .disabled(busy) + .on_click(cx.listener(|this, enabled: &bool, window, cx| { + let mut preferences = this.mobile.preferences(); + preferences.lan_enabled = *enabled; + this.mobile_apply(preferences, None, false, window, cx); + })), + cx, + )); + if preferences.lan_enabled { + group = group.child(self.mobile_lan_options(false, cx)); + } + let relay = self.mobile.snapshot.as_ref().and_then(|s| s.relay.as_ref()); + let hint = relay + .map(|relay| { + format!("{} · {}", language.text(status_message(relay.status)), relay.address) + }) + .unwrap_or_else(|| language.text(Message::MobileRelayHint).to_owned()); + group.child(row( + "mobile-relay-row", + language.text(Message::MobileRelay), + hint, + Button::new("mobile-configure-relay") + .outline() + .small() + .label(language.text(if preferences.relay_enabled { + Message::MobileManageRelay + } else { + Message::MobileConfigureRelay + })) + .disabled(busy) + .on_click(cx.listener(|this, _, window, cx| this.mobile_open_relay(window, cx))), + cx, + )) + } + + fn mobile_permissions(&self, cx: &Context) -> gpui::Div { + let language = crate::gpui_shell::config::ui_language(cx); + v_flex() + .w_full() + .mt(px(36.0)) + .child(group_heading(language.text(Message::MobilePermissions), cx).pb(px(6.0))) + .child(row( + "mobile-default-permission-row", + language.text(Message::MobileDefaultPermission), + language.text(Message::MobileDefaultPermissionHint), + self.mobile_permission_menu(None, cx), + cx, + )) + .child(row( + "mobile-notifications-row", + language.text(Message::MobileNotifications), + language.text(Message::MobileNotificationsHint), + NebulaSwitch::new("mobile-notifications") + .checked(self.mobile.preferences().notifications) + .disabled(self.mobile.operation) + .on_click(cx.listener(|this, enabled: &bool, window, cx| { + let mut preferences = this.mobile.preferences(); + preferences.notifications = *enabled; + this.mobile_apply(preferences, None, false, window, cx); + })), + cx, + )) + .child(row( + "mobile-pause-row", + language.text(Message::MobilePauseTitle), + language.text(Message::MobilePauseHint), + Button::new("mobile-pause") + .debug_selector(|| "mobile-pause".into()) + .outline() + .small() + .label(language.text(Message::MobilePause)) + .disabled(self.mobile.operation) + .on_click(cx.listener(|this, _, window, cx| this.mobile_pause(window, cx))), + cx, + )) + } +} diff --git a/nebula_app/src/gpui_shell/settings_pane/navigation.rs b/nebula_app/src/gpui_shell/settings_pane/navigation.rs index f99bd74..573871e 100644 --- a/nebula_app/src/gpui_shell/settings_pane/navigation.rs +++ b/nebula_app/src/gpui_shell/settings_pane/navigation.rs @@ -6,10 +6,9 @@ pub(super) const THEME_VALUES: [&str; ThemeName::BUILTIN.len()] = ThemeName::BUI pub(super) const REPOSITORY_URL: &str = "https://github.com/Kuddev/pebrel"; pub(super) const BUG_REPORT_TEMPLATE: &str = "bug_report.yml"; -/// 左侧分区的稳定路由表。2026-08-28 产品裁定:默认 GPUI 导航收敛为常用项, -/// 暂时隐藏“AI 供应商”和“备份”;页面实现与索引继续保留。后续恢复入口时只改 -/// [`HIDDEN_NAV_SECTIONS`],不得删除或重排这里的条目。 -pub(super) const SECTION_IDS: [&str; 11] = [ +/// 稳定路由只追加;视觉顺序独立放在 NAV_GROUPS,避免旧入口指向另一页。 +pub(super) const MOBILE_SECTION: usize = 11; +pub(super) const SECTION_IDS: [&str; 12] = [ "application", "appearance", "profiles", @@ -21,12 +20,13 @@ pub(super) const SECTION_IDS: [&str; 11] = [ "advanced", "backup", "agents", + "mobile", ]; /// Bilingual search aliases for the stable section routes. Search is a route /// finder, so a query such as "font", "opacity", or "更新" lands on the /// section that owns the control instead of merely filtering the current page. -pub(super) const SECTION_SEARCH_TERMS: [&str; 11] = [ +pub(super) const SECTION_SEARCH_TERMS: [&str; 12] = [ "application app 应用 update 更新 version 版本 github support 支持", "appearance ligatures 连字 外观 theme 主题 custom 自定义 template 模板 import 导入 export 导出 font 字体 opacity 透明度 background 背景 cursor 光标 icon 图标 dim inactive panes 调暗非活动窗格 分屏变暗 scrollback scrolling speed history 回滚 滚动 速度 历史 滚轮 ctrl wheel zoom 缩放", "profiles 配置文件 shell terminal 终端 completion 补全 startup 启动 ai message notifications toast alerts bell 提醒 通知 弹窗 消息 右下角 ai消息通知 ai 消息通知 ai消息弹窗 ai 消息弹窗 铃声 duration timeout persistent auto-dismiss 时长 秒 常驻 自动关闭", @@ -38,14 +38,19 @@ pub(super) const SECTION_SEARCH_TERMS: [&str; 11] = [ "advanced 高级 session 会话 tray 托盘 restore 恢复 startup autostart login silent 自启动 静默启动 开机 登录", "backup cloud sync 云备份 云同步 云存储 备份 export 导出 restore 恢复 webdav s3 sftp 坚果云 123 123云盘 nextcloud synology 群晖 nas r2 minio snapshots 快照", "agents agent hook hooks 智能体 钩子 接入 claude codex opencode cursor kimi pi omp copilot grok", + "mobile phone android ios remote pairing qr lan relay 手机 远程 配对 二维码 局域网 中转 网卡 只看 手机通知", ]; -pub(super) const HIDDEN_NAV_SECTIONS: &[usize] = &[3, 9]; +// 暂时隐藏供应商入口,保留路由编号,避免其他设置入口发生偏移。 +pub(super) const HIDDEN_NAV_SECTIONS: &[usize] = &[3]; /// 保留原来的分组展开顺序,组名不再渲染;数组里仍保存稳定的 [`SECTION_IDS`] /// 下标,不复制设置状态或路由。 -pub(super) const NAV_GROUPS: [(&str, &[usize]); 3] = - [("workspace", &[0, 1, 2, 10, 6, 7]), ("connections", &[3, 4, 5]), ("system", &[8, 9])]; +pub(super) const NAV_GROUPS: [(&str, &[usize]); 3] = [ + ("workspace", &[0, 1, 2, 10, 6, 7]), + ("connections", &[3, 4, 5, MOBILE_SECTION]), + ("system", &[8, 9]), +]; pub(super) fn section_label(index: usize, language: crate::display::UiLanguage) -> &'static str { match SECTION_IDS.get(index).copied() { @@ -60,6 +65,7 @@ pub(super) fn section_label(index: usize, language: crate::display::UiLanguage) Some("advanced") => language.tr("settings.sidebar.advanced"), Some("backup") => language.tr("settings.sidebar.backup"), Some("agents") => language.text(crate::i18n::Message::SettingsSidebarAgents), + Some("mobile") => language.text(crate::i18n::Message::MobileTitle), _ => "", } } @@ -76,7 +82,7 @@ pub(super) fn visible_nav_sections() -> impl Iterator { } // 导航使用组件的小字号,与搜索菜单一致;尺寸是逻辑像素,由窗口统一处理 DPI。 -pub(super) const SETTINGS_NAV_WIDTH: f32 = 256.0; +pub(super) const SETTINGS_NAV_WIDTH: f32 = 248.0; pub(super) const SETTINGS_NAV_ROW_HEIGHT: f32 = 34.0; pub(super) const SETTINGS_NAV_ICON_SIZE: f32 = 16.0; @@ -118,8 +124,9 @@ pub(super) fn section_icon(index: usize) -> SharedString { 6 => crate::gpui_shell::assets::nav::MOUSE_POINTER.into(), 7 => crate::gpui_shell::assets::nav::KEYMAP.into(), 8 => crate::gpui_shell::assets::nav::SLIDERS.into(), - 9 => IconName::HardDrive.path(), + 9 => crate::gpui_shell::assets::backup::DRIVE.into(), 10 => IconName::Bot.path(), + MOBILE_SECTION => crate::gpui_shell::assets::nav::PHONE.into(), _ => IconName::Inbox.path(), } } diff --git a/nebula_app/src/gpui_shell/settings_pane/search_header.rs b/nebula_app/src/gpui_shell/settings_pane/search_header.rs index 3ebec7a..59b159b 100644 --- a/nebula_app/src/gpui_shell/settings_pane/search_header.rs +++ b/nebula_app/src/gpui_shell/settings_pane/search_header.rs @@ -56,7 +56,7 @@ impl SettingsPane { .cleanable(true) .prefix( Icon::new(IconName::Search) - .xsmall() + .size(px(SETTINGS_NAV_ICON_SIZE)) .text_color(cx.theme().muted_foreground), ) .aria_label(language.pick("在全部设置中搜索", "Search all settings")), @@ -186,9 +186,11 @@ mod tests { assert_eq!(matching_sections(" FONT ", en), vec![1]); assert_eq!(matching_sections("字体", zh), vec![1]); assert_eq!(matching_sections("quick terminal", en), vec![7]); - assert!(matching_sections("clod backup", en).is_empty()); - assert!(matching_sections("webdav", en).is_empty()); - assert!(matching_sections("123", zh).is_empty()); + assert_eq!(matching_sections("clod backup", en), vec![9]); + assert_eq!(matching_sections("webdav", en), vec![9]); + assert_eq!(matching_sections("123", zh), vec![9]); + assert_eq!(matching_sections("手机", zh), vec![MOBILE_SECTION]); + assert_eq!(matching_sections("pairing", en), vec![MOBILE_SECTION]); assert_eq!(matching_sections("透度", zh).first(), Some(&1)); assert!(matching_sections("no such setting", en).is_empty()); assert_eq!(matching_sections("", en), visible_nav_sections().collect::>()); diff --git a/nebula_app/src/gpui_shell/settings_pane/status.rs b/nebula_app/src/gpui_shell/settings_pane/status.rs index 5792754..0a8784c 100644 --- a/nebula_app/src/gpui_shell/settings_pane/status.rs +++ b/nebula_app/src/gpui_shell/settings_pane/status.rs @@ -81,6 +81,7 @@ pub(super) enum BackupCompletion { #[derive(Clone, Debug)] pub(super) enum BackupStatus { + Notice(crate::i18n::Message), PassphraseTooShort, SelectionRequired, Processing, @@ -106,6 +107,7 @@ impl BackupStatus { pub(in crate::gpui_shell) fn text(&self, language: crate::display::UiLanguage) -> String { match self { + Self::Notice(message) => language.text(*message).into(), Self::PassphraseTooShort => language .pick("备份密码至少 8 位", "The backup password must be at least 8 characters") .into(), @@ -204,6 +206,7 @@ pub(in crate::gpui_shell) enum SshStatus { LibraryImported { added: usize, skipped: usize }, LibraryImportedPartial { added: usize, error: String }, Saved(String), + Copied(String), Pinned, Imported(usize), Opening(String), @@ -256,6 +259,7 @@ impl SshStatus { Self::Saved(destination) => { format!("{} {destination}", language.pick("已保存", "Saved")) }, + Self::Copied(label) => language.format(crate::i18n::Message::HostsCopied, &[("name", label)]), Self::Pinned => language.pick("置顶状态已更新", "Pin status updated").into(), Self::Imported(count) => format!( "{} {count} {}", diff --git a/nebula_app/src/gpui_shell/settings_pane/tests.rs b/nebula_app/src/gpui_shell/settings_pane/tests.rs index 615bea9..344f543 100644 --- a/nebula_app/src/gpui_shell/settings_pane/tests.rs +++ b/nebula_app/src/gpui_shell/settings_pane/tests.rs @@ -186,11 +186,11 @@ fn ctrl_wheel_font_zoom_setting_is_searchable_and_has_a_visible_switch( } #[test] -fn settings_nav_visibility_keeps_stable_routes_and_hides_backup() { +fn settings_nav_visibility_hides_providers_and_keeps_stable_routes() { let visibility: Vec<_> = (0..SECTION_IDS.len()).map(is_nav_section_visible).collect(); assert_eq!( visibility, - vec![true, true, true, false, true, true, true, true, true, false, true] + vec![true, true, true, false, true, true, true, true, true, true, true, true] ); assert_eq!( SECTION_IDS, @@ -206,6 +206,7 @@ fn settings_nav_visibility_keeps_stable_routes_and_hides_backup() { "advanced", "backup", "agents", + "mobile", ] ); } @@ -213,14 +214,26 @@ fn settings_nav_visibility_keeps_stable_routes_and_hides_backup() { #[test] fn settings_nav_starts_with_application_then_frequent_options() { let visible: Vec<_> = visible_nav_sections().collect(); - assert_eq!(visible, vec![0, 1, 2, 10, 6, 7, 4, 5, 8]); + assert_eq!(visible, vec![0, 1, 2, 10, 6, 7, 4, 5, 11, 8, 9]); let zh_labels: Vec<_> = visible .iter() .map(|index| section_label(*index, crate::display::UiLanguage::ZhCn)) .collect(); assert_eq!( zh_labels, - vec!["应用", "外观", "终端", "Agents", "交互", "按键映射", "SSH", "网络", "高级"] + vec![ + "应用", + "外观", + "终端", + "Agents", + "交互", + "按键映射", + "SSH", + "网络", + "手机远程", + "高级", + "备份" + ] ); let en_labels: Vec<_> = visible .iter() @@ -237,7 +250,9 @@ fn settings_nav_starts_with_application_then_frequent_options() { "Key Bindings", "SSH", "Network", + "Phone Remote", "Advanced", + "Backup", ] ); } diff --git a/nebula_app/src/gpui_shell/ssh_settings.rs b/nebula_app/src/gpui_shell/ssh_settings.rs index 16238dd..a11259f 100644 --- a/nebula_app/src/gpui_shell/ssh_settings.rs +++ b/nebula_app/src/gpui_shell/ssh_settings.rs @@ -215,6 +215,9 @@ impl SettingsPane { status: SshStatus, cx: &mut Context, ) { + if self.ssh_library.busy { + return; + } // 列表将被改写,未决删除的快照会过期:先提交它。 self.commit_pending_ssh_delete(); let mut updated = self.ssh_hosts.clone(); @@ -233,6 +236,9 @@ impl SettingsPane { /// 二次确认后的删除:立刻移出列表并写盘,但 Profile 与凭据留到 8 秒 /// 撤销窗口结束(旧壳 Undo 合同——期内撤销可完整恢复)。 pub(super) fn delete_ssh_host(&mut self, host: &str, cx: &mut Context) { + if self.ssh_library.busy { + return; + } let profile_path = crate::display::nebula_data_dir().join("ssh_profiles.json"); let profiles = match crate::ssh_profiles::SshProfiles::load(&profile_path) { Ok(profiles) => profiles, @@ -368,6 +374,9 @@ impl SettingsPane { window: &mut Window, cx: &mut Context, ) { + if self.ssh_library.busy { + return; + } let adding = destination.is_none(); let profile_path = crate::display::nebula_data_dir().join("ssh_profiles.json"); let profiles = @@ -376,8 +385,12 @@ impl SettingsPane { crate::ssh_profiles::SshProfiles::default() }); let profile = destination.as_deref().map(|host| profiles.for_destination(host)); - let (address_with_user, port) = - destination.as_deref().map(crate::display::split_destination_port).unwrap_or_default(); + let (address_with_user, port) = destination + .as_deref() + .map(|identity| { + crate::display::split_destination_port(profiles.connection_destination(identity)) + }) + .unwrap_or_default(); let (mut username, address) = crate::display::split_destination_user(&address_with_user); // 新增主机的常用服务器账号直接给出可编辑实值;placeholder 不会参与 // destination 拼装,用户只填 IP 时此前实际会退回本机账号。 @@ -712,6 +725,15 @@ impl SettingsPane { }, }; let previous_connection = original.as_deref().map(|_| editor.original_connection.clone()); + let destination = match profiles.edited_identity(original.as_deref(), &destination) { + Ok(identity) => identity, + Err(error) => { + self.ssh_status = Some(SshStatus::Error(error)); + self.ssh_editor = Some(editor); + cx.notify(); + return; + }, + }; // 保存会改列表:未决删除的快照过期,先提交。 self.commit_pending_ssh_delete(); // 先在副本里计算新列表,直到 Profile 与 settings 两份数据都写成功 @@ -727,7 +749,8 @@ impl SettingsPane { ), value => value.to_owned(), }; - let (address, _) = crate::display::split_destination_port(&destination); + let (address, _) = + crate::display::split_destination_port(profiles.connection_destination(&destination)); let (username, _) = crate::display::split_destination_user(&address); profiles.remember_username(&username); profiles.upsert(crate::ssh_profiles::SshProfileAuth { @@ -837,11 +860,12 @@ impl SettingsPane { self.ssh_username_picker_open = false; editor.private_keys.clear(); self.ssh_editor = None; + let saved_address = profiles.connection_destination(&destination).to_owned(); self.ssh_status = credential_cleanup_error.map_or_else( - || Some(SshStatus::Saved(destination.clone())), + || Some(SshStatus::Saved(saved_address.clone())), |error| { Some(SshStatus::SavedWithCleanupError { - destination: destination.clone(), + destination: saved_address.clone(), error: error.to_string(), }) }, diff --git a/nebula_app/src/gpui_shell/ssh_settings/library.rs b/nebula_app/src/gpui_shell/ssh_settings/library.rs index 2760251..9259868 100644 --- a/nebula_app/src/gpui_shell/ssh_settings/library.rs +++ b/nebula_app/src/gpui_shell/ssh_settings/library.rs @@ -2,16 +2,58 @@ use super::*; use gpui::AppContext as _; -use gpui_component::menu::PopupMenuItem; +use gpui_component::menu::{PopupMenu, PopupMenuItem}; mod exchange; #[cfg(test)] mod tests; -// Each virtual item includes its bottom gutter, so scrolling and hit tests use -// the actual card extent rather than the previous contiguous table-row height. -const HOST_CARD_HEIGHT: f32 = 68.0; -const HOST_CARD_EXTENT: f32 = HOST_CARD_HEIGHT + 8.0; +// Virtual rows share the prototype panel separators; hit testing uses the full row. +const HOST_ROW_HEIGHT: f32 = 61.0; + +/// Keep the dropdown's own selected state authoritative while revealing the +/// trigger for row hover or keyboard focus. Opacity preserves its tab stop and +/// fixed hit area, so showing the menu never shifts the Connect button. +#[derive(IntoElement)] +struct HostMoreTrigger { + button: Button, + hover_group: SharedString, +} + +impl gpui::Styled for HostMoreTrigger { + fn style(&mut self) -> &mut gpui::StyleRefinement { + self.button.style() + } +} + +impl gpui::InteractiveElement for HostMoreTrigger { + fn interactivity(&mut self) -> &mut gpui::Interactivity { + self.button.interactivity() + } +} + +impl gpui_component::Selectable for HostMoreTrigger { + fn selected(mut self, selected: bool) -> Self { + self.button = self.button.selected(selected); + self + } + + fn is_selected(&self) -> bool { + self.button.is_selected() + } +} + +impl gpui_component::menu::DropdownMenu for HostMoreTrigger {} + +impl gpui::RenderOnce for HostMoreTrigger { + fn render(self, _window: &mut Window, _cx: &mut gpui::App) -> impl IntoElement { + let open = self.button.is_selected(); + self.button + .opacity(if open { 1.0 } else { 0.0 }) + .group_hover(self.hover_group, |style| style.opacity(1.0)) + .focus(|style| style.opacity(1.0)) + } +} /// Center the glyph's ink, not its monospace advance or the surrounding text line. fn host_icon(glyph: char, family: SharedString, color: gpui::Hsla) -> impl IntoElement { @@ -65,7 +107,6 @@ fn host_icon(glyph: char, family: SharedString, color: gpui::Hsla) -> impl IntoE pub(in crate::gpui_shell) enum HostScope { All, Pinned, - Managed, Recent, } @@ -86,7 +127,8 @@ impl HostLibraryState { let language = crate::gpui_shell::config::ui_language(cx); Self { search: cx.new(|cx| { - InputState::new(window, cx).placeholder(language.text(Message::HostsSearch)) + InputState::new(window, cx) + .placeholder(language.text(Message::HostsSearchPlaceholder)) }), group: cx.new(|cx| { InputState::new(window, cx).placeholder(language.text(Message::HostsGroup)) @@ -121,34 +163,6 @@ impl SettingsPane { self.ssh_library.reset_scroll(); } - fn duplicate_ssh_host(&mut self, host: String, window: &mut Window, cx: &mut Context) { - let label = self - .ssh_hosts - .profiles - .for_destination(&host) - .label - .filter(|label| !label.trim().is_empty()) - .unwrap_or_else(|| host.clone()); - let copy_label = self.ssh_hosts.profiles.next_default_label(label.trim()); - - self.open_ssh_editor(Some(host.clone()), window, cx); - if let Some(editor) = self.ssh_editor.as_mut() { - editor.original_destination = None; - // 复制不会重命名原主机,编辑路径排除的原地址仍应可用作新主机的跳板。 - editor.jump_choices.push((host, label)); - } - self.ssh_destination_input.update(cx, |input, cx| input.set_value("", window, cx)); - self.ssh_label_input.update(cx, |input, cx| input.set_value(copy_label, window, cx)); - self.ssh_password_input.update(cx, |input, cx| { - input.set_placeholder( - crate::gpui_shell::config::ui_language(cx) - .pick("留空则连接时询问", "Leave empty to ask when connecting"), - window, - cx, - ); - }); - } - fn filtered_library_hosts(&self, cx: &gpui::App) -> Vec { let query = self.ssh_library.search.read(cx).value(); let mut hosts = if self.ssh_library.scope == HostScope::Recent { @@ -167,14 +181,14 @@ impl SettingsPane { self.ssh_hosts.profiles.filter_hosts( hosts, &query, - self.ssh_library.scope == HostScope::Managed, + false, self.ssh_library.group_filter.as_deref(), ) } fn library_controls(&self, cx: &mut Context) -> gpui::AnyElement { let language = crate::gpui_shell::config::ui_language(cx); - let weak = cx.entity().downgrade(); + let owner = cx.entity().downgrade(); let group_filter = self.ssh_library.group_filter.clone(); let groups = self.ssh_hosts.profiles.groups(); let group_label = match group_filter.as_deref() { @@ -184,82 +198,91 @@ impl SettingsPane { }; h_flex() .id("ssh-library-controls") + .debug_selector(|| "ssh-library-controls".into()) + .w_full() .gap_2() .items_center() .flex_wrap() .child( - h_flex().gap_1().flex_wrap().children( - [ - (HostScope::All, Message::HostsAll), - (HostScope::Pinned, Message::HostsPinned), - (HostScope::Managed, Message::HostsManaged), - (HostScope::Recent, Message::HostsRecent), - ] - .into_iter() - .enumerate() - .map(|(index, (scope, label))| { - Button::new(("host-scope", index)) - .debug_selector(move || format!("host-scope-{index}")) - .label(language.text(label)) - .small() - .ghost() - .selected(self.ssh_library.scope == scope) - .on_click(cx.listener(move |this, _, _, cx| { - this.ssh_library.scope = scope; - this.ssh_library.reset_scroll(); - cx.notify(); - })) + h_flex() + .p(px(3.0)) + .gap(px(2.0)) + .rounded(px(8.0)) + .bg(cx.theme().secondary) + .children( + [ + (HostScope::All, Message::HostsScopeAll), + (HostScope::Pinned, Message::HostsPinned), + (HostScope::Recent, Message::HostsRecent), + ] + .into_iter() + .enumerate() + .map(|(index, (scope, label))| { + let selected = self.ssh_library.scope == scope; + Button::new(("host-scope", index)) + .debug_selector(move || format!("host-scope-{index}")) + .label(language.text(label)) + .ghost() + .small() + .h(px(28.0)) + .rounded(px(6.0)) + .selected(selected) + .when(selected, |button| { + button.bg(crate::gpui_shell::theme::settings_panel_bg(cx)) + }) + .on_click(cx.listener(move |this, _, _, cx| { + this.ssh_library.scope = scope; + this.ssh_library.reset_scroll(); + cx.notify(); + })) + }), + ), + ) + .child( + Button::new("host-group-filter") + .debug_selector(|| "host-group-filter".into()) + .label(group_label) + .small() + .w(px(140.0)) + .h(px(32.0)) + .dropdown_caret(true) + .dropdown_menu(move |mut menu, _, _| { + let choices = std::iter::once(( + None, + language.text(Message::HostsAllGroups).to_owned(), + )) + .chain(std::iter::once(( + Some(String::new()), + language.text(Message::HostsUngrouped).to_owned(), + ))) + .chain(groups.iter().map(|group| (Some(group.clone()), group.clone()))); + for (choice, label) in choices { + let owner = owner.clone(); + menu = menu.item( + PopupMenuItem::new(label).checked(choice == group_filter).on_click( + move |_, _, cx| { + let _ = owner.update(cx, |this, cx| { + this.ssh_library.group_filter = choice.clone(); + this.ssh_library.reset_scroll(); + cx.notify(); + }); + }, + ), + ); + } + menu }), - ), ) .child(div().flex_1()) .child( - h_flex() - .gap_2() - .items_center() - .flex_wrap() + div() + .id("ssh-inline-filter") + .debug_selector(|| "ssh-inline-filter".into()) + .w(px(240.0)) + .max_w_full() .child( - Button::new("host-group-filter").label(group_label).small().dropdown_menu( - move |mut menu, _, _| { - let choices = std::iter::once(( - None, - language.text(Message::HostsAllGroups).to_owned(), - )) - .chain(std::iter::once(( - Some(String::new()), - language.text(Message::HostsUngrouped).to_owned(), - ))) - .chain( - groups.iter().map(|group| (Some(group.clone()), group.clone())), - ); - for (choice, label) in choices { - let owner = weak.clone(); - menu = menu.item( - PopupMenuItem::new(label) - .checked(choice == group_filter) - .on_click(move |_, _, cx| { - let _ = owner.update(cx, |this, cx| { - this.ssh_library.group_filter = choice.clone(); - this.ssh_library.reset_scroll(); - cx.notify(); - }); - }), - ); - } - menu - }, - ), - ) - .child( - div() - .id("ssh-inline-filter") - .debug_selector(|| "ssh-inline-filter".into()) - .w(px(210.0)) - .child( - Input::new(&self.ssh_library.search) - .small() - .prefix(Icon::new(IconName::Search).size(px(14.0))), - ), + gpui::Styled::h(Input::new(&self.ssh_library.search).small(), px(32.0)) + .prefix(Icon::new(IconName::Search).size(px(14.0))), ), ) .into_any_element() @@ -268,108 +291,134 @@ impl SettingsPane { fn library_header(&self, cx: &mut Context) -> gpui::AnyElement { let language = crate::gpui_shell::config::ui_language(cx); let owner = cx.entity().downgrade(); - h_flex() - .gap_3() - .items_center() - .flex_wrap() + v_flex() + .w_full() + .gap(px(6.0)) .child( - v_flex() - .flex_1() - .min_w(px(220.0)) - .gap_1() + h_flex() + .w_full() + .gap_3() + .items_center() + .flex_wrap() .child( div() - .text_size(px(18.0)) + .flex_1() + .min_w(px(160.0)) + .text_size(px(22.0)) + .line_height(px(30.0)) .font_weight(gpui::FontWeight::SEMIBOLD) .child(language.text(Message::HostsTitle)), ) .child( - div() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(language.text(Message::HostsSubtitle)), + h_flex() + .gap_3() + .child( + Button::new("hosts-exchange") + .label(language.text(Message::HostsExchange)) + .ghost() + .small() + .h(px(32.0)) + .dropdown_caret(true) + .disabled(self.ssh_library.busy) + .dropdown_menu(move |menu, _, _| { + let import_owner = owner.clone(); + let export_owner = owner.clone(); + menu.item( + PopupMenuItem::new(language.text(Message::HostsImport)) + .on_click(move |_, window, cx| { + let _ = import_owner.update(cx, |this, cx| { + this.import_host_csv(window, cx) + }); + }), + ) + .item( + PopupMenuItem::new(language.text(Message::HostsExport)) + .on_click(move |_, window, cx| { + let _ = export_owner.update(cx, |this, cx| { + this.export_host_csv(window, cx) + }); + }), + ) + }), + ) + .child( + Button::new("ssh-add-host") + .debug_selector(|| "ssh-add-host".into()) + .icon(IconName::Plus) + .label(language.text(Message::HostsAdd)) + .small() + .h(px(32.0)) + .primary() + .disabled(self.ssh_library.busy) + .on_click(cx.listener(|this, _, window, cx| { + this.open_ssh_editor(None, window, cx) + })), + ), ), ) .child( - h_flex() - .gap_2() - .child( - Button::new("hosts-exchange") - .label(language.text(Message::HostsExchange)) - .small() - .disabled(self.ssh_library.busy) - .dropdown_menu(move |menu, _, _| { - let import_owner = owner.clone(); - let export_owner = owner.clone(); - menu.item( - PopupMenuItem::new(language.text(Message::HostsImport)) - .on_click(move |_, window, cx| { - let _ = import_owner.update(cx, |this, cx| { - this.import_host_csv(window, cx) - }); - }), - ) - .item( - PopupMenuItem::new(language.text(Message::HostsExport)) - .on_click(move |_, window, cx| { - let _ = export_owner.update(cx, |this, cx| { - this.export_host_csv(window, cx) - }); - }), - ) - }), - ) - .child( - Button::new("ssh-add-host") - .icon(IconName::Plus) - .label(language.text(Message::HostsAdd)) - .small() - .primary() - .disabled(self.ssh_library.busy) - .on_click(cx.listener(|this, _, window, cx| { - this.open_ssh_editor(None, window, cx) - })), - ), + div() + .max_w(px(598.0)) + .text_size(px(13.0)) + .line_height(px(21.0)) + .text_color(cx.theme().muted_foreground) + .child(language.text(Message::HostsListSubtitle)), ) .into_any_element() } fn library_config_banner(&self, cx: &mut Context) -> gpui::AnyElement { let language = crate::gpui_shell::config::ui_language(cx); - let count = self.ssh_hosts.configured.len(); - let text = if count == 0 { - language.text(Message::HostsConfigEmpty).to_owned() - } else { - language.format(Message::HostsConfigShared, &[("count", &count.to_string())]) - }; + let hosts = self.ssh_hosts.merged(); + let configured = hosts.iter().filter(|host| self.ssh_hosts.is_from_config(host)).count(); + let hidden = self.ssh_hosts.hidden_hosts().len(); h_flex() .id("ssh-config-banner") - .gap_2() - .px_3() - .py_2() + .debug_selector(|| "ssh-config-banner".into()) + .w_full() + .gap_1() .items_center() - .rounded(px(6.0)) - .border_1() - .border_color(cx.theme().border) - .bg(cx.theme().secondary.opacity(0.35)) - .child(Icon::new(IconName::Info).size(px(14.0)).text_color(cx.theme().muted_foreground)) - .child( - div() - .flex_1() - .min_w_0() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(text), - ) + .flex_wrap() + .child(div().text_size(px(12.5)).text_color(cx.theme().muted_foreground).child( + language.format( + Message::HostsSummary, + &[("total", &hosts.len().to_string()), ("config", &configured.to_string())], + ), + )) .child( Button::new("ssh-import") .label(language.text(Message::HostsReload)) .small() .ghost() + .h(px(28.0)) + .text_color(cx.theme().link) .loading(self.ssh_library.busy) .disabled(self.ssh_library.busy) .on_click(cx.listener(|this, _, _, cx| this.reload_host_library(cx))), ) + .child(div().flex_1()) + .when(hidden > 0, |footer| { + footer.child( + Button::new("ssh-toggle-hidden") + .debug_selector(|| "ssh-toggle-hidden".into()) + .ghost() + .small() + .h(px(28.0)) + .text_color(cx.theme().link) + .label(if self.ssh_show_hidden { + language.text(Message::HostsCollapseHidden).to_owned() + } else { + language.format( + Message::HostsHiddenCount, + &[("count", &hidden.to_string())], + ) + }) + .on_click(cx.listener(|this, _, _, cx| { + this.ssh_show_hidden = !this.ssh_show_hidden; + cx.notify(); + })), + ) + }) .into_any_element() } @@ -421,275 +470,204 @@ impl SettingsPane { &self, host: String, ix: usize, - _host_count: usize, + host_count: usize, labels: &std::collections::HashMap, icons: &std::collections::HashMap, cx: &mut Context, ) -> gpui::AnyElement { let language = crate::gpui_shell::config::ui_language(cx); let theme = cx.theme(); - let hover_bg = crate::gpui_shell::theme::settings_hover_bg(cx, false); let muted = theme.muted_foreground; - let symbol_family: SharedString = crate::font_install::REQUIRED_FONT_FAMILY.into(); - let font_px = cx - .try_global::() - .map(|s| s.ui_font_size_px) - .unwrap_or(15.0); - let title_h = font_px * 0.9; - let subtitle_h = font_px * 0.8; - let delete_confirm = self.ssh_delete_confirm.clone(); - + let hairline = crate::gpui_shell::theme::settings_hairline(cx); let pinned = self.ssh_hosts.is_pinned(&host); let from_config = self.ssh_hosts.is_from_config(&host); - let confirm = delete_confirm.as_deref() == Some(host.as_str()); + let confirm = self.ssh_delete_confirm.as_deref() == Some(host.as_str()); let label = labels.get(&host).cloned().unwrap_or_else(|| host.clone()); let group = self.ssh_hosts.profiles.organization(&host).group.clone(); let profile = self.ssh_hosts.profiles.for_destination(&host); - let auth = host_auth_label(&profile, language); - // 行首 OS 图标(旧壳裁定 2026-08-09):id 取自 ssh_profiles 存储, - // 未认出回落通用终端形状;mono 字体渲染 Nerd Font 字位。 + let mut detail = self.ssh_hosts.profiles.connection_destination(&host).to_owned(); + if profile.connection.jump_mode == crate::ssh_profiles::SshHostJumpMode::Host { + detail.push_str(" · "); + detail.push_str( + &language.format(Message::HostsVia, &[("host", &profile.connection.jump_host)]), + ); + } + detail.push_str(" · "); + detail.push_str(&host_auth_label(&profile, language)); let os_icon = crate::display::ui::os_icons::resolve(icons.get(&host).map(String::as_str)); - let context_owner = cx.entity().downgrade(); + let owner = cx.entity().downgrade(); + let context_owner = owner.clone(); let context_host = host.clone(); + let menu_host = host.clone(); + let hover_group: SharedString = format!("ssh-host-actions-{ix}").into(); let connect_host = host.clone(); - let edit_host = host.clone(); - let pin_host = host.clone(); - let delete_host = host.clone(); - div() - .h(px(HOST_CARD_EXTENT)) - .w_full() - .pb(px(8.0)) + let delete_host = host; + let chip = |label: String| { + div() + .max_w(px(110.0)) + .truncate() + .px(px(6.0)) + .py(px(1.0)) + .rounded(px(5.0)) + .text_size(px(11.0)) + .line_height(px(16.0)) + .text_color(muted) + .border_1() + .border_color(theme.border) + .child(label) + }; + let title = h_flex() + .min_w_0() + .items_center() + .gap(px(8.0)) .child( - h_flex() - .id(SharedString::from(format!("ssh-host-row-{ix}"))) - .debug_selector(move || format!("ssh-host-row-{ix}")) - .h(px(HOST_CARD_HEIGHT)) - .w_full() - .px_3() - .items_center() - .gap_3() - .rounded(px(6.0)) - .border_1() - .border_color(theme.border) - .bg(theme.secondary.opacity(0.25)) - .hover(move |row| row.bg(hover_bg)) - .context_menu(move |menu, _, _| { - let connect_owner = context_owner.clone(); - let edit_owner = context_owner.clone(); - let duplicate_owner = context_owner.clone(); - let delete_owner = context_owner.clone(); - let connect_host = context_host.clone(); - let edit_host = context_host.clone(); - let duplicate_host = context_host.clone(); - let delete_host = context_host.clone(); - menu.item( - PopupMenuItem::new(language.text(Message::LauncherConnect)).on_click( - move |_, _, cx| { - let _ = connect_owner.update(cx, |this, cx| { - cx.emit(SettingsPaneEvent::LaunchSsh(connect_host.clone())); - this.ssh_status = - Some(SshStatus::Opening(connect_host.clone())); - cx.notify(); - }); - }, - ), - ) - .item(PopupMenuItem::new(language.text(Message::LauncherEdit)).on_click( - move |_, window, cx| { - let _ = edit_owner.update(cx, |this, cx| { - this.open_ssh_editor(Some(edit_host.clone()), window, cx); - }); - }, - )) - .item(PopupMenuItem::new(language.text(Message::CommonCopy)).on_click( - move |_, window, cx| { - let _ = duplicate_owner.update(cx, |this, cx| { - this.duplicate_ssh_host(duplicate_host.clone(), window, cx); - }); - }, - )) - .item( - PopupMenuItem::new(language.text(Message::LauncherDelete)).on_click( - move |_, _, cx| { - let _ = delete_owner.update(cx, |this, cx| { - this.ssh_delete_confirm = Some(delete_host.clone()); - cx.notify(); - }); - }, - ), - ) - }) - .child( - crate::gpui_shell::widgets::device_icon_container(cx) - .id(SharedString::from(format!("ssh-host-icon-{ix}"))) - .debug_selector(move || format!("ssh-host-icon-{ix}")) - .child(host_icon(os_icon.glyph, symbol_family, muted)), - ) - .child( - v_flex() - .flex_1() - .min_w_0() - .justify_center() - .gap(px(3.0)) - .child( - h_flex() - .min_w_0() - .items_center() - .text_size(px(title_h)) - .line_height(px(title_h * 1.25)) - .gap_2() - .child( - div() - .min_w_0() - .truncate() - .font_weight(gpui::FontWeight::MEDIUM) - .child(label), - ) - .when(!group.is_empty(), |line| { - line.child( - div() - .max_w(px(120.0)) - .truncate() - .px(px(5.0)) - .rounded_sm() - .text_xs() - .text_color(muted) - .bg(theme.secondary) - .child(group), - ) - }) - .when(from_config, |line| { - line.child( - div() - .flex_shrink_0() - .px(px(5.0)) - .rounded_sm() - .text_size(px(10.0)) - .text_color(muted) - .border_1() - .border_color(theme.border) - .child("ssh-config"), - ) - }), - ) - .child( - h_flex() - .min_w_0() - .gap_2() - .items_center() - .child( - div() - .min_w_0() - .text_size(px(subtitle_h)) - .line_height(px(subtitle_h * 1.25)) - .text_color(muted) - .truncate() - .child(host.clone()), - ) - .child(div().text_color(muted).child("·")) - .child( - div() - .min_w_0() - .truncate() - .text_size(px(subtitle_h)) - .text_color(muted) - .child(auth), - ), - ), - ) - .child( - h_flex() - .gap_1() - .flex_shrink_0() - .child( - Button::new(SharedString::from(format!("ssh-connect-{ix}"))) - .debug_selector(move || format!("ssh-connect-{ix}")) - .label(language.text(Message::HostsConnect)) - .small() - .on_click(cx.listener(move |this, _, _, cx| { - cx.emit(SettingsPaneEvent::LaunchSsh(connect_host.clone())); - this.ssh_status = - Some(SshStatus::Opening(connect_host.clone())); - cx.notify(); - })), - ) - .child( - Button::new(SharedString::from(format!("ssh-edit-{ix}"))) - .debug_selector(move || format!("ssh-edit-{ix}")) - .icon(IconName::Settings2) - .ghost() - .small() - .size(px(32.0)) - .tooltip(language.pick("编辑主机", "Edit host")) - .on_click(cx.listener(move |this, _, window, cx| { - this.open_ssh_editor(Some(edit_host.clone()), window, cx); - })), - ) - .child( - Button::new(SharedString::from(format!("ssh-pin-{ix}"))) - .debug_selector(move || format!("ssh-pin-{ix}")) - .icon(Icon::default().path(crate::gpui_shell::assets::nav::PIN)) - .ghost() - .small() - .size(px(32.0)) - .selected(pinned) - .toggled(pinned) - .tooltip(if pinned { - language.pick("取消置顶", "Unpin") - } else { - language.pick("置顶", "Pin") - }) - .on_click(cx.listener(move |this, _, _, cx| { - this.ssh_apply( - |lists| lists.toggle_pin(&pin_host), - SshStatus::Pinned, - cx, - ); - })), - ) - .child( - Button::new(SharedString::from(format!("ssh-delete-{ix}"))) - .debug_selector(move || format!("ssh-delete-{ix}")) - .map(|button| { - if confirm { - button - .label(language.pick("确认删除", "Confirm delete")) - .danger() - .small() - } else { - button - .icon( - Icon::default().path( - crate::gpui_shell::assets::nav::TRASH, - ), - ) - .ghost() - .small() - .size(px(32.0)) - .tooltip(if from_config { - language.tr("settings.ssh.hide_config_host") - } else { - language.pick("删除", "Delete") - }) - } - }) - .on_click(cx.listener(move |this, _, _, cx| { - if this.ssh_delete_confirm.as_deref() - == Some(delete_host.as_str()) - { - this.delete_ssh_host(&delete_host, cx); - } else { - this.ssh_delete_confirm = Some(delete_host.clone()); - cx.notify(); - } - })), - ), - ), + div() + .min_w_0() + .truncate() + .text_size(px(14.0)) + .line_height(px(21.0)) + .font_weight(gpui::FontWeight::MEDIUM) + .child(label), ) + .when(pinned, |line| { + line.child( + Icon::default() + .path(crate::gpui_shell::assets::nav::PIN) + .size(px(12.0)) + .text_color(theme.link), + ) + }) + .when(!group.is_empty(), |line| line.child(chip(group))) + .when(from_config, |line| line.child(chip("ssh config".into()))); + let actions = if confirm { + h_flex() + .gap(px(4.0)) + .flex_shrink_0() + .child( + Button::new(SharedString::from(format!("ssh-delete-{ix}"))) + .debug_selector(move || format!("ssh-delete-{ix}")) + .label(language.text(Message::HostsConfirmDelete)) + .danger() + .small() + .h(px(32.0)) + .on_click(cx.listener(move |this, _, _, cx| { + this.delete_ssh_host(&delete_host, cx) + })), + ) + .child( + Button::new(SharedString::from(format!("ssh-cancel-delete-{ix}"))) + .debug_selector(move || format!("ssh-cancel-delete-{ix}")) + .label(language.text(Message::CommonCancel)) + .ghost() + .small() + .h(px(32.0)) + .on_click(cx.listener(|this, _, _, cx| { + this.ssh_delete_confirm = None; + cx.notify(); + })), + ) + } else { + h_flex() + .gap(px(4.0)) + .flex_shrink_0() + .child( + HostMoreTrigger { + hover_group: hover_group.clone(), + button: Button::new(SharedString::from(format!("ssh-more-{ix}"))) + .debug_selector(move || format!("ssh-more-{ix}")) + .icon(IconName::Ellipsis) + .ghost() + .small() + .size(px(32.0)) + .tooltip(language.text(Message::HostsMore)), + } + .dropdown_menu(move |menu, _, _| { + host_actions( + menu, + owner.clone(), + menu_host.clone(), + pinned, + from_config, + language, + ) + }), + ) + .child( + Button::new(SharedString::from(format!("ssh-connect-{ix}"))) + .debug_selector(move || format!("ssh-connect-{ix}")) + .label(language.text(Message::HostsConnect)) + .small() + .h(px(32.0)) + .on_click(cx.listener(move |this, _, _, cx| { + cx.emit(SettingsPaneEvent::LaunchSsh(connect_host.clone())); + this.ssh_status = Some(SshStatus::Opening( + this.ssh_hosts + .profiles + .connection_destination(&connect_host) + .to_owned(), + )); + cx.notify(); + })), + ) + }; + let icon = div() + .id(SharedString::from(format!("ssh-host-icon-{ix}"))) + .debug_selector(move || format!("ssh-host-icon-{ix}")) + .size(px(32.0)) + .flex_shrink_0() + .rounded(px(8.0)) + .bg(theme.secondary) + .flex() + .items_center() + .justify_center() + .child(host_icon( + os_icon.glyph, + crate::font_install::REQUIRED_FONT_FAMILY.into(), + muted, + )); + h_flex() + .id(SharedString::from(format!("ssh-host-row-{ix}"))) + .debug_selector(move || format!("ssh-host-row-{ix}")) + .group(hover_group) + .h(px(HOST_ROW_HEIGHT)) + .w_full() + .px(px(14.0)) + .items_center() + .gap(px(14.0)) + .when(ix + 1 < host_count, |row| row.border_b_1().border_color(hairline)) + .hover(|row| row.bg(crate::gpui_shell::theme::settings_hover_bg(cx, false))) + .context_menu(move |menu, _, _| { + host_actions( + menu, + context_owner.clone(), + context_host.clone(), + pinned, + from_config, + language, + ) + }) + .child(icon) + .child( + v_flex().flex_1().min_w_0().justify_center().gap(px(1.0)).child(title).child( + div() + .min_w_0() + .truncate() + .text_size(px(12.0)) + .line_height(px(18.0)) + .font_family(crate::font_install::REQUIRED_FONT_FAMILY) + .text_color(muted) + .child(detail), + ), + ) + .child(actions) .into_any_element() } - pub(in crate::gpui_shell) fn section_ssh(&mut self, cx: &mut Context) -> gpui::Div { + pub(in crate::gpui_shell) fn section_ssh( + &mut self, + window: &Window, + cx: &mut Context, + ) -> gpui::Div { let language = crate::gpui_shell::config::ui_language(cx); let controls = self.library_controls(cx); let header = self.library_header(cx); @@ -723,7 +701,7 @@ impl SettingsPane { ) .track_scroll(&self.ssh_library.scroll) .w_full() - .h(px(HOST_CARD_EXTENT * host_count.clamp(1, 8) as f32)); + .h(px(HOST_ROW_HEIGHT * host_count.clamp(1, 8) as f32)); let hidden_rows = self.ssh_show_hidden.then(|| { hidden @@ -732,7 +710,9 @@ impl SettingsPane { .map(|(ix, host)| { let restore_host = host.clone(); h_flex() - .h(px(32.0)) + .id(("ssh-hidden-row", ix)) + .debug_selector(move || format!("ssh-hidden-row-{ix}")) + .h(px(40.0)) .w_full() .px_3() .items_center() @@ -764,73 +744,65 @@ impl SettingsPane { .collect::>() }); - let hidden_count = self.ssh_hosts.hidden_hosts().len(); let undo_bar = self.ssh_delete_undo.as_ref().map(|undo| (undo.host.clone(), undo.seq)); div() .flex() .flex_col() - .gap_3() + .gap(px(14.0)) + .w_full() + .max_w(px(1000.0)) + .px(px(if f32::from(window.viewport_size().width) < 1000.0 { 12.0 } else { 36.0 })) + .pt(px(16.0)) + .pb(px(110.0)) .child(header) - .child(config_banner) - .child(controls) + .child(div().mt(px(10.0)).child(controls)) .children( self.ssh_hosts .load_error .as_ref() .map(|error| div().text_sm().text_color(theme.danger).child(error.clone())), ) - .child(v_flex().w_full().when(host_count > 0, |card| card.child(host_rows)).when( - host_count == 0, - |card| { - card.child( - v_flex() - .py_6() - .gap_1() - .items_center() - .child( - div() - .text_color(muted) - .child(language.text(Message::HostsNoMatches)), - ) - .child( - div() - .text_xs() - .text_color(muted) - .child(language.tr("settings.ssh.empty_hint")), - ), - ) - }, - )) - .when(hidden_count > 0, |group| { - let show = self.ssh_show_hidden; - group.child( - NebulaButton::new("ssh-toggle-hidden") - .label(if show { - SharedString::from(language.pick("收起已隐藏", "Collapse hidden")) - } else { - SharedString::from(format!( - "{} {hidden_count}", - language.pick("已隐藏", "Hidden") - )) - }) - .on_click(cx.listener(|this, _, _, cx| { - this.ssh_show_hidden = !this.ssh_show_hidden; - cx.notify(); - })), - ) - }) - .when_some(hidden_rows, |group, rows| { - group.child(div().h(px(8.0))).child( - v_flex() - .w_full() - .rounded(px(8.0)) - .border_1() - .border_color(theme.border) - .overflow_hidden() - .children(rows), - ) - }) + .child( + v_flex() + .w_full() + .border_1() + .border_color(crate::gpui_shell::theme::settings_hairline(cx)) + .rounded(px(10.0)) + .overflow_hidden() + .bg(theme.foreground.opacity(0.028)) + .when(host_count > 0, |card| card.child(host_rows)) + .when(host_count == 0, |card| { + card.child( + v_flex() + .py_6() + .gap_1() + .items_center() + .child( + div() + .text_color(muted) + .child(language.text(Message::HostsNoMatches)), + ) + .child( + div() + .text_xs() + .text_color(muted) + .child(language.tr("settings.ssh.empty_hint")), + ), + ) + }) + .when_some(hidden_rows, |panel, rows| { + panel.child(v_flex().w_full().children(rows)) + }) + .child( + div() + .px_3() + .py_2() + .border_t_1() + .border_color(crate::gpui_shell::theme::settings_hairline(cx)) + .child(config_banner), + ), + ) .when_some(undo_bar, |group, (host, _)| { group.child(div().h(px(8.0))).child( h_flex() @@ -876,7 +848,7 @@ fn host_auth_label( use crate::ssh_profiles::SshAuthMode; if profile.auth == SshAuthMode::PublicKey { if let Some(name) = profile.private_keys.first().and_then(|path| path.file_name()) { - return name.to_string_lossy().into_owned(); + return language.format(Message::HostsKeyFile, &[("name", &name.to_string_lossy())]); } } language @@ -888,3 +860,68 @@ fn host_auth_label( }) .to_owned() } + +/// Both the right-click menu and keyboard-accessible row button use these actions. +fn host_actions( + menu: PopupMenu, + owner: gpui::WeakEntity, + host: String, + pinned: bool, + from_config: bool, + language: crate::display::UiLanguage, +) -> PopupMenu { + let connect_owner = owner.clone(); + let edit_owner = owner.clone(); + let copy_owner = owner.clone(); + let pin_owner = owner.clone(); + let connect_host = host.clone(); + let edit_host = host.clone(); + let copy_host = host.clone(); + let pin_host = host.clone(); + menu.item(PopupMenuItem::new(language.text(Message::LauncherConnect)).on_click( + move |_, _, cx| { + let _ = connect_owner.update(cx, |this, cx| { + cx.emit(SettingsPaneEvent::LaunchSsh(connect_host.clone())); + this.ssh_status = Some(SshStatus::Opening( + this.ssh_hosts.profiles.connection_destination(&connect_host).to_owned(), + )); + cx.notify(); + }); + }, + )) + .item(PopupMenuItem::new(language.text(Message::LauncherEdit)).on_click( + move |_, window, cx| { + let _ = edit_owner + .update(cx, |this, cx| this.open_ssh_editor(Some(edit_host.clone()), window, cx)); + }, + )) + .item(PopupMenuItem::new(language.text(Message::CommonCopy)).on_click(move |_, _, cx| { + let _ = copy_owner.update(cx, |this, cx| this.duplicate_ssh_host(copy_host.clone(), cx)); + })) + .item( + PopupMenuItem::new(language.text(if pinned { + Message::HostsUnpin + } else { + Message::HostsPin + })) + .on_click(move |_, _, cx| { + let _ = pin_owner.update(cx, |this, cx| { + this.ssh_apply(|lists| lists.toggle_pin(&pin_host), SshStatus::Pinned, cx) + }); + }), + ) + .separator() + .item( + PopupMenuItem::new(language.text(if from_config { + Message::SettingsSshHideConfigHost + } else { + Message::LauncherDelete + })) + .on_click(move |_, _, cx| { + let _ = owner.update(cx, |this, cx| { + this.ssh_delete_confirm = Some(host.clone()); + cx.notify(); + }); + }), + ) +} diff --git a/nebula_app/src/gpui_shell/ssh_settings/library/exchange.rs b/nebula_app/src/gpui_shell/ssh_settings/library/exchange.rs index 1fc598e..0901f7e 100644 --- a/nebula_app/src/gpui_shell/ssh_settings/library/exchange.rs +++ b/nebula_app/src/gpui_shell/ssh_settings/library/exchange.rs @@ -15,6 +15,11 @@ enum Completion { visibility_error: Option, }, Exported, + Duplicated { + profiles: SshProfiles, + identity: String, + label: String, + }, } impl SettingsPane { @@ -58,11 +63,58 @@ impl SettingsPane { cx.emit(SettingsPaneEvent::Changed); }, Ok(Completion::Exported) => self.ssh_status = Some(SshStatus::LibraryExported), + Ok(Completion::Duplicated { profiles, identity, label }) => { + self.ssh_hosts.profiles = profiles; + self.ssh_hosts.load_error = None; + self.ssh_library.scope = HostScope::All; + if let Some(index) = + self.filtered_library_hosts(cx).iter().position(|host| host == &identity) + { + self.ssh_library.scroll.scroll_to_item(index, gpui::ScrollStrategy::Top); + } + self.ssh_status = Some(SshStatus::Copied(label)); + cx.emit(SettingsPaneEvent::Changed); + }, Err(error) => self.ssh_status = Some(SshStatus::Error(error)), } cx.notify(); } + pub(super) fn duplicate_ssh_host(&mut self, host: String, cx: &mut Context) { + let Some(sequence) = self.begin_host_exchange(cx) else { return }; + let profiles = self.ssh_hosts.profiles.clone(); + let occupied = self.ssh_hosts.merged(); + let load_error = self.ssh_hosts.load_error.clone(); + let task = cx.background_executor().spawn(async move { + if let Some(error) = load_error { + return Err(error); + } + let mut bytes = [0u8; 16]; + getrandom::fill(&mut bytes).map_err(|error| error.to_string())?; + let identity = format!("pebrel-host-{:032x}", u128::from_be_bytes(bytes)); + if occupied.contains(&identity) { + return Err("Host identity already exists".into()); + } + let path = crate::display::nebula_data_dir().join("ssh_profiles.json"); + let (profiles, label) = crate::ssh_profiles::duplication::save_duplicate( + profiles, + &path, + &host, + &identity, + crate::ssh_credentials::load_generic_secret, + crate::ssh_credentials::store_generic_secret, + crate::ssh_credentials::delete_generic_secret, + ) + .map_err(|error| error.to_string())?; + Ok(Completion::Duplicated { profiles, identity, label }) + }); + cx.spawn(async move |this, cx| { + let result = task.await; + let _ = this.update(cx, |this, cx| this.finish_host_exchange(sequence, result, cx)); + }) + .detach(); + } + pub(super) fn reload_host_library(&mut self, cx: &mut Context) { let Some(sequence) = self.begin_host_exchange(cx) else { return; diff --git a/nebula_app/src/gpui_shell/ssh_settings/library/tests.rs b/nebula_app/src/gpui_shell/ssh_settings/library/tests.rs index 3aeae33..3f48ed9 100644 --- a/nebula_app/src/gpui_shell/ssh_settings/library/tests.rs +++ b/nebula_app/src/gpui_shell/ssh_settings/library/tests.rs @@ -10,12 +10,12 @@ fn pairing_design_ssh_auth_description_uses_metadata_not_a_saved_password_claim( assert_eq!(host_auth_label(&profile, language), "Password"); profile.auth = crate::ssh_profiles::SshAuthMode::PublicKey; profile.private_keys.push(std::path::PathBuf::from("private/location/id_ed25519")); - assert_eq!(host_auth_label(&profile, language), "id_ed25519"); + assert_eq!(host_auth_label(&profile, language), "Private key id_ed25519"); } #[cfg(feature = "gpui-test-support")] #[gpui::test] -fn pairing_design_ssh_cards_keep_icon_anchors_and_compact_filter(cx: &mut gpui::TestAppContext) { +fn prototype_ssh_rows_keep_icon_anchors_and_compact_filter(cx: &mut gpui::TestAppContext) { cx.update(|cx| { gpui_component::init(cx); cx.set_global(crate::gpui_shell::config::Settings::load(nebula_settings::ThemeName::Nord)); @@ -29,6 +29,8 @@ fn pairing_design_ssh_cards_keep_icon_anchors_and_compact_filter(cx: &mut gpui:: pane.ssh_hosts = crate::gpui_shell::ssh_hosts::SshHostLists { saved: vec!["nebula-test".into(), "second-host".into()], pinned: vec!["second-host".into()], + configured: vec!["hidden-config".into()], + hidden: vec!["hidden-config".into()], ..Default::default() }; let mut profile = pane.ssh_hosts.profiles.for_destination("nebula-test"); @@ -47,40 +49,100 @@ fn pairing_design_ssh_cards_keep_icon_anchors_and_compact_filter(cx: &mut gpui:: window.draw(cx).clear(cx); }); let icon = cx.debug_bounds("ssh-host-icon-0").expect("card anchor"); - assert_eq!(icon.size, gpui::size(px(36.0), px(36.0))); + assert_eq!(icon.size, gpui::size(px(32.0), px(32.0))); let first = cx.debug_bounds("ssh-host-row-0").unwrap(); - assert_eq!(icon.center().y, first.center().y); + assert!(cx.debug_bounds("ssh-host-time-0").is_none(), "unknown time column is removed"); + // A one-pixel bottom divider shifts the content center by half a pixel. + assert!((f32::from(icon.center().y - first.center().y)).abs() <= 0.5); let next = cx.debug_bounds("ssh-host-row-1").unwrap(); let next_icon = cx.debug_bounds("ssh-host-icon-1").unwrap(); assert_eq!(icon.center().x, next_icon.center().x); - assert!(next.origin.y - first.bottom() >= px(8.0)); + assert_eq!(next.origin.y, first.bottom(), "rows share one panel without card gutters"); let filter = cx.debug_bounds("ssh-inline-filter").unwrap(); - assert!(filter.size.width <= px(210.0)); + assert!(filter.size.width <= px(240.0)); + assert!(filter.size.height >= px(32.0), "single-line search keeps the toolbar height"); assert!(filter.right() <= px(width)); - for name in ["ssh-edit-0", "ssh-pin-0", "ssh-delete-0"] { + let controls = cx.debug_bounds("ssh-library-controls").unwrap(); + assert!(controls.bottom() <= first.top(), "filters stay above the list panel"); + let footer = cx.debug_bounds("ssh-config-banner").unwrap(); + assert!(footer.top() >= next.bottom()); + assert!(cx.debug_bounds("host-scope-2").is_some()); + assert!(cx.debug_bounds("host-scope-3").is_none()); + assert!(cx.debug_bounds("ssh-toggle-hidden").is_some()); + for name in ["ssh-more-0", "ssh-connect-0"] { let action = cx.debug_bounds(name).expect("visible management action"); assert!(action.size.width >= px(32.0) && action.size.height >= px(32.0)); assert!(action.right() <= first.right()); } } + let more = cx.debug_bounds("ssh-more-1").unwrap(); + let connect_before = cx.debug_bounds("ssh-connect-1").unwrap(); + let hovered_row = cx.debug_bounds("ssh-host-row-1").unwrap(); + cx.simulate_mouse_move(hovered_row.center(), None, gpui::Modifiers::default()); + cx.run_until_parked(); cx.update(|window, cx| { + window.refresh(); + window.draw(cx).clear(cx); + }); + assert_eq!(cx.debug_bounds("ssh-connect-1").unwrap(), connect_before); + cx.simulate_click(more.center(), gpui::Modifiers::default()); + cx.run_until_parked(); + // Move away from the row into the menu area; the open dropdown owns its + // visibility and keyboard navigation must continue to work. + cx.simulate_mouse_move(gpui::point(px(10.0), px(10.0)), None, gpui::Modifiers::default()); + cx.run_until_parked(); + // 复制失败也不能退回编辑弹窗;用明确的加载错误阻止测试触碰用户凭据和磁盘。 + pane.update(cx, |pane, _| pane.ssh_hosts.load_error = Some("fixture load failure".into())); + cx.simulate_keystrokes("down down down enter"); + cx.run_until_parked(); + cx.update(|_, cx| { pane.update(cx, |pane, cx| { - pane.duplicate_ssh_host("nebula-test".into(), window, cx); - let editor = pane.ssh_editor.as_ref().expect("copied host draft"); - assert!(editor.original_destination.is_none()); - assert!( - editor - .jump_choices - .iter() - .any(|(host, label)| host == "nebula-test" && label == "Alpha"), - "the copied-from host remains available as a jump host" - ); - assert_eq!(pane.ssh_destination_input.read(cx).value(), ""); - assert_eq!(pane.ssh_label_input.read(cx).value(), "Alpha 1"); - pane.close_ssh_editor(window, cx); + assert!(pane.ssh_editor.is_none(), "copy does not open an editor"); + assert_eq!(pane.ssh_hosts.profiles.destinations().count(), 1); + assert!(matches!(&pane.ssh_status, Some(SshStatus::Error(error)) if error == "fixture load failure")); + pane.ssh_hosts.load_error = None; + cx.notify(); }); }); + cx.run_until_parked(); + cx.update(|window, cx| { + window.refresh(); + window.draw(cx).clear(cx); + }); + let row = cx.debug_bounds("ssh-host-row-0").unwrap(); + cx.simulate_mouse_down(row.center(), MouseButton::Right, gpui::Modifiers::default()); + cx.simulate_mouse_up(row.center(), MouseButton::Right, gpui::Modifiers::default()); + cx.run_until_parked(); + cx.simulate_keystrokes("down down down down down enter"); + cx.run_until_parked(); + cx.update(|window, cx| { + window.refresh(); + window.draw(cx).clear(cx); + }); + pane.read_with(cx, |pane, _| { + assert_eq!(pane.ssh_delete_confirm.as_deref(), Some("second-host")); + assert_eq!(pane.ssh_hosts.saved.len(), 2, "menu only requests confirmation"); + assert!(pane.ssh_delete_undo.is_none()); + }); + let cancel = cx.debug_bounds("ssh-cancel-delete-0").unwrap(); + cx.simulate_click(cancel.center(), gpui::Modifiers::default()); + cx.run_until_parked(); + assert!(pane.read_with(cx, |pane, _| pane.ssh_delete_confirm.is_none())); + cx.update(|window, cx| { + window.refresh(); + window.draw(cx).clear(cx); + }); + let hidden = cx.debug_bounds("ssh-toggle-hidden").unwrap(); + cx.simulate_click(hidden.center(), gpui::Modifiers::default()); + cx.run_until_parked(); + cx.update(|window, cx| { + window.refresh(); + window.draw(cx).clear(cx); + }); + assert!(cx.debug_bounds("ssh-hidden-row-0").is_some()); + assert!(pane.read_with(cx, |pane, _| pane.ssh_show_hidden)); + let pin_filter = cx.debug_bounds("host-scope-1").unwrap(); let point = gpui::point(pin_filter.origin.x + px(4.0), pin_filter.center().y); cx.simulate_mouse_down(point, MouseButton::Left, gpui::Modifiers::default()); @@ -122,9 +184,14 @@ fn native_ssh_copy_context_menu_preview() { gpui_platform::application().with_assets(crate::gpui_shell::assets::NebulaAssets).run( move |cx| { gpui_component::init(cx); - cx.set_global(crate::gpui_shell::config::Settings::load( - nebula_settings::ThemeName::Nord, - )); + let theme = if std::env::var("PEBREL_SSH_QA_THEME").as_deref() == Ok("paper") { + nebula_settings::ThemeName::Paper + } else { + nebula_settings::ThemeName::Nord + }; + let mut settings = crate::gpui_shell::config::Settings::load(theme); + settings.ui_language = crate::display::UiLanguage::ZhCn; + cx.set_global(settings); crate::gpui_shell::theme::apply_chrome_theme(cx); let mut pane = None; @@ -142,25 +209,54 @@ fn native_ssh_copy_context_menu_preview() { |window, cx| { let view = cx.new(|cx| SettingsPane::new(window, cx)); view.update(cx, |pane, cx| { - pane.manage_launcher_ssh("root@192.0.2.10".into(), false, window, cx); + pane.manage_launcher_ssh(String::new(), false, window, cx); pane.ssh_delete_confirm = None; + let fixtures = [ + ("kud@nas.example", "家里 NAS", "家里"), + ("deploy@192.0.2.21", "生产 API", "公司"), + ("bastion", "bastion", "公司"), + ("kud@relay.example", "中转服务器", ""), + ("gpu-box", "gpu-box", "家里"), + ("pi@raspberrypi.example", "raspberrypi", "家里"), + ]; pane.ssh_hosts = crate::gpui_shell::ssh_hosts::SshHostLists { - saved: vec![ - "root@192.0.2.10".into(), - "deploy@198.51.100.20".into(), + saved: fixtures.iter().map(|(host, _, _)| (*host).into()).collect(), + pinned: fixtures[..2] + .iter() + .map(|(host, _, _)| (*host).into()) + .collect(), + configured: vec![ + "bastion".into(), + "gpu-box".into(), + "old-vps".into(), ], + hidden: vec!["old-vps".into()], ..Default::default() }; - let mut alpha = - pane.ssh_hosts.profiles.for_destination("root@192.0.2.10"); - alpha.label = Some("Alpha".into()); - alpha.auth = crate::ssh_profiles::SshAuthMode::PublicKey; - alpha.private_keys.push("C:\\Keys\\alpha_ed25519".into()); - pane.ssh_hosts.profiles.upsert(alpha); - let mut beta = - pane.ssh_hosts.profiles.for_destination("deploy@198.51.100.20"); - beta.label = Some("Beta".into()); - pane.ssh_hosts.profiles.upsert(beta); + for (index, (host, label, group)) in fixtures.into_iter().enumerate() { + let mut profile = pane.ssh_hosts.profiles.for_destination(host); + profile.label = Some(label.into()); + if matches!(index, 0 | 3) { + profile.auth = crate::ssh_profiles::SshAuthMode::PublicKey; + profile.private_keys.push("C:\\Keys\\id_ed25519".into()); + } + if index == 1 { + profile.connection.jump_mode = + crate::ssh_profiles::SshHostJumpMode::Host; + profile.connection.jump_host = "bastion".into(); + } + pane.ssh_hosts.profiles.upsert(profile); + pane.ssh_hosts + .profiles + .set_organization( + host, + crate::ssh_profiles::HostOrganization { + group: group.into(), + ..Default::default() + }, + ) + .unwrap(); + } }); pane = Some(view.clone()); cx.new(|cx| gpui_component::Root::new(view, window, cx)) @@ -185,7 +281,7 @@ fn native_ssh_copy_context_menu_preview() { serde_json::to_vec(&serde_json::json!({ "pid": std::process::id(), "state": "host-row-ready", - "host": "Alpha", + "host": "家里 NAS", })) .unwrap(), ) diff --git a/nebula_app/src/gpui_shell/terminal/confirmation/choices.rs b/nebula_app/src/gpui_shell/terminal/confirmation/choices.rs index 4f298ba..eff6408 100644 --- a/nebula_app/src/gpui_shell/terminal/confirmation/choices.rs +++ b/nebula_app/src/gpui_shell/terminal/confirmation/choices.rs @@ -1,6 +1,7 @@ //! Codex's displayed one-key choices. A question digit submits the current //! answer itself; appending Enter could accidentally submit the next question. use super::*; +use unicode_width::UnicodeWidthChar as _; type Choices = (Fingerprint, String, Vec, Vec); @@ -34,17 +35,36 @@ pub(super) fn describe( let row = ROW.get_or_init(|| Regex::new(r"^(?:[›❯>]\s*)?([1-9])\.\s+(.+)$").unwrap()); static KEY: OnceLock = OnceLock::new(); let key = KEY.get_or_init(|| Regex::new(r"\(([a-z])\)$").unwrap()); - let mut labels = Vec::new(); + let mut labels: Vec = Vec::new(); let mut replies = Vec::new(); let mut first_row = None; + let mut continuation = false; for (ix, line) in context[..context.len() - 1].iter().enumerate() { - let Some(captures) = row.captures(line) else { continue }; + let Some(captures) = row.captures(line) else { + if continuation { + let label = labels.last_mut()?; + let next = line.split_whitespace().collect::>().join(" "); + // 桌面按列排版的续行也属于选项;宽字符之间的折行不插入多余空格。 + let wide_wrap = label.chars().next_back().zip(next.chars().next()).is_some_and( + |(left, right)| left.width() == Some(2) && right.width() == Some(2), + ); + if !wide_wrap { + label.push(' '); + } + label.push_str(&next); + if label.chars().count() > 240 { + return None; + } + } + continue; + }; + continuation = false; let number = captures[1].parse::().ok()?; if number != labels.len() + 1 || labels.len() >= 9 { return None; } first_row.get_or_insert(ix); - let label = captures[2].trim(); + let label = captures[2].split_whitespace().collect::>().join(" "); if label.chars().count() > 240 { return None; } @@ -55,9 +75,10 @@ pub(super) fn describe( continue; } replies.push(number.to_string()); - labels.push(label.to_owned()); + labels.push(label); + continuation = true; } else { - let shortcut = key.captures(label)?; + let shortcut = key.captures(&label)?; replies.push(shortcut[1].to_owned()); labels.push(label[..shortcut.get(0)?.start()].trim().to_owned()); } @@ -127,4 +148,20 @@ mod tests { assert!(describe(prompt, "claude", None, TermMode::empty(), true).is_none()); assert!(describe(prompt, "codex", None, TermMode::empty(), false).is_none()); } + + #[test] + fn wrapped_choice_descriptions_keep_their_text_without_terminal_column_padding() { + let screen = "Question 1/1 (1 unanswered)\n选择预览主题。\n› 1. Light (Recommended) Keeps the selected preview\n readable.\n 2. 深色 适合低光环境并保持正\n 常。\n 3. None of the above Optionally, add details in\n notes (tab).\ntab to add notes | enter to submit answer | esc to interrupt"; + let (_, question, labels, replies) = + describe(screen, "codex", Some("s"), TermMode::empty(), true).unwrap(); + assert_eq!(question, "选择预览主题。"); + assert_eq!( + labels, + [ + "Light (Recommended) Keeps the selected preview readable.", + "深色 适合低光环境并保持正常。" + ] + ); + assert_eq!(replies, ["1", "2"]); + } } diff --git a/nebula_app/src/gpui_shell/terminal/session.rs b/nebula_app/src/gpui_shell/terminal/session.rs index 4d0870c..583b9a1 100644 --- a/nebula_app/src/gpui_shell/terminal/session.rs +++ b/nebula_app/src/gpui_shell/terminal/session.rs @@ -25,6 +25,7 @@ use nebula_terminal::tty::EventedPty as _; pub struct EventProxy { events: super::event_mailbox::EventSender, stages: UnboundedSender, + remote_reader: Arc>>, } impl EventListener for EventProxy { @@ -34,6 +35,11 @@ impl EventListener for EventProxy { } impl crate::ssh_session::SshEventHost for EventProxy { + fn ssh_transcript_reader(&self, reader: Option) { + if let Ok(mut current) = self.remote_reader.lock() { + *current = reader; + } + } fn ssh_stage(&self, stage: crate::ssh_session::SshStage) { let _ = self.stages.unbounded_send(stage); } @@ -67,6 +73,7 @@ pub struct TerminalSession { /// PTY 直系 shell PID;关闭确认沿用旧壳 `busy_child(shell_pid)` 判据。 /// SSH 没有本地 shell 进程,固定为 0。 pub shell_pid: u32, + pub(super) remote_reader: Arc>>, } #[cfg(all(test, feature = "gpui-test-support"))] @@ -85,7 +92,7 @@ pub(super) fn test_session_with_events() -> ( ) { let (events, event_rx) = super::event_mailbox::channel(); let (stages, _) = unbounded(); - let proxy = EventProxy { events, stages }; + let proxy = EventProxy { events, stages, remote_reader: Default::default() }; let term = Term::new(Config::default(), &GridSize { columns: 80, screen_lines: 24 }, proxy.clone()); let (sender, receiver) = nebula_terminal::event_loop::EventLoopSender::standalone().unwrap(); @@ -95,6 +102,7 @@ pub(super) fn test_session_with_events() -> ( notifier: Notifier(sender), native_prompt: proxy.events.native_prompt.clone(), shell_pid: 0, + remote_reader: proxy.remote_reader.clone(), }, receiver, event_rx, @@ -169,7 +177,7 @@ pub fn spawn( ) -> std::io::Result { let (tx, rx) = super::event_mailbox::channel(); let (stage_tx, stage_rx) = unbounded(); - let proxy = EventProxy { events: tx, stages: stage_tx }; + let proxy = EventProxy { events: tx, stages: stage_tx, remote_reader: Default::default() }; let grid = GridSize { columns: window_size.num_cols as usize, @@ -185,6 +193,7 @@ pub fn spawn( // 用于 resize 锚定问题的字节级取证。 let record = std::env::var_os("NEBULA_PTY_RECORD").is_some(); let native_prompt = proxy.events.native_prompt.clone(); + let remote_reader = proxy.remote_reader.clone(); let mut event_loop = EventLoop::new(Arc::clone(&term), proxy, pty, options.drain_on_exit, record)?; if let Some(token) = options.env.get(crate::ai_hook::remote::TOKEN_ENV) { @@ -193,7 +202,7 @@ pub fn spawn( let notifier = Notifier(event_loop.channel()); let _io_thread = event_loop.spawn(); - Ok((TerminalSession { term, notifier, native_prompt, shell_pid }, rx, stage_rx)) + Ok((TerminalSession { term, notifier, native_prompt, shell_pid, remote_reader }, rx, stage_rx)) } /// 启动一个 SSH 直连会话(russh,与旧壳 `create_ssh_pane` 同一业务层): @@ -208,7 +217,7 @@ pub fn spawn_ssh( let term_config = crate::ssh_session::terminal_config(term_config); let (tx, rx) = super::event_mailbox::channel(); let (stage_tx, stage_rx) = unbounded(); - let proxy = EventProxy { events: tx, stages: stage_tx }; + let proxy = EventProxy { events: tx, stages: stage_tx, remote_reader: Default::default() }; let grid = GridSize { columns: window_size.num_cols as usize, @@ -216,6 +225,7 @@ pub fn spawn_ssh( }; let term = Arc::new(FairMutex::new(Term::new(term_config, &grid, proxy.clone()))); let native_prompt = proxy.events.native_prompt.clone(); + let remote_reader = proxy.remote_reader.clone(); let sender = crate::ssh_session::spawn_session_at( destination, initial_remote_cwd, @@ -225,7 +235,13 @@ pub fn spawn_ssh( )?; Ok(( - TerminalSession { term, notifier: Notifier(sender), native_prompt, shell_pid: 0 }, + TerminalSession { + term, + notifier: Notifier(sender), + native_prompt, + shell_pid: 0, + remote_reader, + }, rx, stage_rx, )) diff --git a/nebula_app/src/gpui_shell/terminal/view.rs b/nebula_app/src/gpui_shell/terminal/view.rs index eddb5c6..f8818f7 100644 --- a/nebula_app/src/gpui_shell/terminal/view.rs +++ b/nebula_app/src/gpui_shell/terminal/view.rs @@ -6,6 +6,7 @@ mod agent_activity; mod broadcast; mod completion; mod confirmation; +mod conversation; mod cwd_report; mod image_paste; mod layout; diff --git a/nebula_app/src/gpui_shell/terminal/view/conversation.rs b/nebula_app/src/gpui_shell/terminal/view/conversation.rs new file mode 100644 index 0000000..9594ae6 --- /dev/null +++ b/nebula_app/src/gpui_shell/terminal/view/conversation.rs @@ -0,0 +1,240 @@ +//! The chat view is a projection of this live CLI, not another Agent session. + +use gpui::Context; +use serde::Serialize; +use serde_json::{Value, json}; +use sha2::{Digest, Sha256}; + +use super::{InputOrigin, TerminalView}; +use crate::{ + assistant_answer::conversation::{Location, Source}, + runtime_api::{ + ApiError, RuntimeTaskState, + conversation::{Identity, Request}, + }, +}; + +#[derive(Serialize)] +struct Prompt { + id: String, + text: String, + options: Vec, + selected: usize, + binary: bool, +} + +fn project_confirmation(question: super::super::confirmation::Confirmation) -> Prompt { + let binary = question.choices.is_empty(); + let id = Sha256::digest(question.id.to_le_bytes()) + .iter() + .map(|byte| format!("{byte:02x}")) + .collect(); + Prompt { id, text: question.question, options: question.choices, selected: 0, binary } +} + +impl TerminalView { + pub(crate) fn runtime_conversation_identity(&self) -> Result { + self.ensure_runtime_readable()?; + let agent = self.runtime_chat_agent().ok_or_else(|| { + ApiError::new("conversation_ended", "this pane no longer contains a live Agent") + })?; + let session_id = agent.session_id.filter(|id| !id.is_empty()).ok_or_else(|| { + ApiError::new( + "conversation_unavailable", + "the Agent has not reported its native conversation identity", + ) + })?; + Ok(Identity { kind: agent.kind, session_id, epoch: Some(self.ai_session_probe_epoch) }) + } + + pub(crate) fn runtime_conversation_check( + &self, + expected: &Identity, + ) -> Result { + let current = self.runtime_conversation_identity()?; + if !expected.matches(¤t) { + return Err(ApiError::new( + "conversation_identity_changed", + "the Agent conversation was replaced", + )); + } + Ok(current) + } + + pub(crate) fn runtime_conversation_source( + &self, + expected: &Identity, + ) -> Result { + let identity = self.runtime_conversation_check(expected)?; + let target = self + .session_agent() + .filter(|target| { + target.source == identity.kind + && target.session_id.as_deref() == Some(identity.session_id.as_str()) + }) + .ok_or_else(|| { + ApiError::new("conversation_unavailable", "native conversation metadata is pending") + })?; + let path = target + .session_file + .filter(|path| crate::session::valid_native_session_file(path)) + .ok_or_else(|| { + ApiError::new( + "conversation_unavailable", + "the Agent has not reported a transcript path", + ) + })?; + let location = if self.ssh_destination.is_some() { + let reader = self + .session + .as_ref() + .and_then(|session| session.remote_reader.lock().ok()?.clone()) + .ok_or_else(|| { + ApiError::new( + "conversation_unavailable", + "the SSH transcript connection is unavailable", + ) + })?; + Location::Ssh(reader) + } else { + Location::Local(self.exec_context.clone().ok_or_else(|| { + ApiError::new("conversation_unavailable", "the pane environment is unavailable") + })?) + }; + Ok(Source { identity, path, location, cwd: self.cwd.clone() }) + } + + fn conversation_prompt(&mut self) -> Option<(u64, Prompt)> { + let question = self.capture_confirmation()?; + Some((question.id, project_confirmation(question))) + } + + pub(crate) fn runtime_conversation_status( + &mut self, + expected: &Identity, + ) -> Result { + let identity = self.runtime_conversation_check(expected)?; + let prompt = self.conversation_prompt().map(|(_, prompt)| prompt); + Ok(json!({"identity": identity, "state": self.runtime_task_state(), "prompt": prompt, + "can_send": self.pending_runtime_submit.is_none() && matches!(self.runtime_task_state(), RuntimeTaskState::Idle | RuntimeTaskState::Finished)})) + } + + pub(crate) fn runtime_conversation_apply( + &mut self, + request: &Request, + cx: &mut Context, + ) -> Result { + let identity = self.runtime_conversation_check(request.identity())?; + if self.pending_runtime_submit.is_some() { + return Err(ApiError::new( + "input_in_progress", + "the previous message is still being committed", + )); + } + match request { + Request::Send { text, .. } => { + if !matches!( + self.runtime_task_state(), + RuntimeTaskState::Idle | RuntimeTaskState::Finished + ) { + return Err(ApiError::new( + "agent_busy", + "the Agent is working or awaiting a terminal interaction", + )); + } + self.runtime_chat_message(text.clone(), InputOrigin::User, cx)?; + }, + Request::Choose { prompt_id, option, .. } => { + let (request_id, _) = self + .conversation_prompt() + .filter(|(_, prompt)| &prompt.id == prompt_id) + .ok_or_else(|| { + ApiError::new( + "prompt_changed", + "the visible question changed; read its current options", + ) + })?; + // 和桌面确认通知共用问题身份、一次性消费及按键编码;不另建屏幕解析器。 + if !self.answer_choice(request_id, *option, cx) { + return Err(ApiError::new( + "prompt_changed", + "the visible question changed; read its current options", + )); + } + }, + Request::Key { key, modifiers, .. } => { + self.runtime_send_key(*key, *modifiers, 1, cx)?; + }, + Request::Read { .. } => { + return Err(ApiError::new( + "runtime_unavailable", + "conversation reads require the background dispatcher", + )); + }, + } + Ok(json!({"identity": identity, "accepted": true})) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use nebula_terminal::term::TermMode; + + #[test] + fn conversation_projection_keeps_native_question_identity_and_single_key_semantics() { + let mut state = super::super::super::confirmation::ConfirmationState::default(); + let screen = "Question 1/1 (1 unanswered)\nChoose a scope.\n› 1. Current\n 2. All\ntab to add notes | enter to submit answer | esc to interrupt"; + state.observe_waiting(true); + let native = + state.capture(screen, "codex", Some("session"), TermMode::empty(), true).unwrap(); + let first = project_confirmation(native.clone()); + assert_eq!(first.options, ["Current", "All"]); + assert_eq!( + state.answer_choice( + native.id, + 1, + screen, + "codex", + Some("session"), + TermMode::empty(), + true + ), + Some(b"2".to_vec()) + ); + assert!(state.capture(screen, "codex", Some("session"), TermMode::empty(), true).is_none()); + state.observe_waiting(false); + state.observe_waiting(true); + let repeated = project_confirmation( + state.capture(screen, "codex", Some("session"), TermMode::empty(), true).unwrap(), + ); + assert_ne!( + first.id, repeated.id, + "a new round of the same question must invalidate the old phone button" + ); + assert!( + state + .answer_choice( + native.id, + 0, + screen, + "codex", + Some("session"), + TermMode::empty(), + true + ) + .is_none() + ); + assert!( + state + .capture( + "Read this list:\n1. Yes\n2. No", + "codex", + Some("session"), + TermMode::empty(), + true + ) + .is_none() + ); + } +} diff --git a/nebula_app/src/gpui_shell/terminal/view/runtime.rs b/nebula_app/src/gpui_shell/terminal/view/runtime.rs index 1940225..30f17f9 100644 --- a/nebula_app/src/gpui_shell/terminal/view/runtime.rs +++ b/nebula_app/src/gpui_shell/terminal/view/runtime.rs @@ -335,7 +335,7 @@ impl TerminalView { } } - fn ensure_runtime_readable(&self) -> Result<(), crate::runtime_api::ApiError> { + pub(super) fn ensure_runtime_readable(&self) -> Result<(), crate::runtime_api::ApiError> { if self.ssh_destination.is_none() { return Ok(()); } @@ -378,6 +378,7 @@ impl TerminalView { &self, window_id: u64, lines: usize, + screen: bool, ) -> Result { self.ensure_runtime_readable()?; let Some(session) = &self.session else { @@ -387,7 +388,7 @@ impl TerminalView { )); }; let term = session.term.lock(); - Ok(crate::runtime_api::capture_terminal_tail( + let mut read = crate::runtime_api::capture_terminal_tail( &term, window_id, self.pane_id, @@ -395,7 +396,13 @@ impl TerminalView { self.runtime_task_state(), self.exited.is_some(), self.exited.clone(), - )) + ); + if screen { + read.screen = Some(crate::runtime_api::capture_terminal_screen(&term, |index| { + self.palette.query_reply(index, term.colors()) + })?); + } + Ok(read) } pub fn runtime_procs( @@ -529,7 +536,6 @@ impl TerminalView { )); } let agent = self.runtime_agent(); - let recognized_agent = submit && agent.is_some(); let codex_submit = submit && agent.as_ref().is_some_and(|agent| agent.kind == "codex"); if submit { self.capture_runtime_prompt(); @@ -566,8 +572,8 @@ impl TerminalView { // 仍盯着提交前 Idle;真实 shell/hook 结束事件负责把它归位。 self.awaiting_input = false; self.mark_command_running(); - if recognized_agent { - self.agent_activity.submitted(); + if let Some(agent) = agent { + self.agent_activity.submitted_text(&agent.kind, &text); } cx.emit(TerminalViewEvent::TitleChanged); cx.notify(); @@ -646,8 +652,8 @@ impl TerminalView { cx: &mut Context, ) -> Result<(), crate::runtime_api::ApiError> { self.ensure_local_context_allowed(origin)?; - let recognized_agent = self.runtime_chat_agent().is_some(); - if require_agent && !recognized_agent { + let agent = self.runtime_chat_agent(); + if require_agent && agent.is_none() { return Err(crate::runtime_api::ApiError::new( "invalid_target", "multi-line Agent input requires a live Agent pane", @@ -704,8 +710,8 @@ impl TerminalView { if submit { self.awaiting_input = false; self.mark_command_running(); - if recognized_agent { - self.agent_activity.submitted(); + if let Some(agent) = agent { + self.agent_activity.submitted_text(&agent.kind, &text); } cx.emit(TerminalViewEvent::TitleChanged); cx.notify(); diff --git a/nebula_app/src/gpui_shell/terminal/view/startup_command.rs b/nebula_app/src/gpui_shell/terminal/view/startup_command.rs index 2e8224e..834dbc3 100644 --- a/nebula_app/src/gpui_shell/terminal/view/startup_command.rs +++ b/nebula_app/src/gpui_shell/terminal/view/startup_command.rs @@ -88,7 +88,7 @@ impl TerminalView { let Some(id) = target.session_id.as_deref().filter(|_| target.source == "codex") else { return false; }; - self.runtime_read(0, 80).is_ok_and(|read| { + self.runtime_read(0, 80, false).is_ok_and(|read| { let compact: String = read.text.split_whitespace().collect(); compact.contains(&format!("ERROR:NosavedsessionfoundwithID{id}.")) }) diff --git a/nebula_app/src/gpui_shell/workspace.rs b/nebula_app/src/gpui_shell/workspace.rs index 04da1d7..c634334 100644 --- a/nebula_app/src/gpui_shell/workspace.rs +++ b/nebula_app/src/gpui_shell/workspace.rs @@ -70,6 +70,8 @@ mod rename; use rename::apply_cancel_rename; use rename::{PaneRename, TabRename, apply_commit_rename}; mod residency; +mod runtime_conversation; +mod runtime_tabs; mod send_to_chat; mod session_persistence; mod session_recovery; @@ -85,6 +87,7 @@ mod tab_drag; mod tab_duplication; mod tab_menu; mod tab_presentation; +use tab_presentation::TabMeta; use tab_presentation::TabPresentation; mod tab_scroll; mod top_tabs; @@ -603,31 +606,6 @@ fn ssh_host_icon_ids(data_dir: &Path) -> std::collections::HashMap, - /// 色标(右键菜单的标签颜色);`None` = 不画色条。 - color: Option, - /// 本 Tab 创建时实际采用的 shell 短标。默认 shell 是“新建时参数”, - /// 不是全局实时主题;设置改变后既有 PTY 不会换进程,这个标签也不能 - /// 跟着全局值漂移。非终端 Tab 为 `None`。 - shell_tag: Option, - /// 与旧壳 `TabEntry::launch` 同义:保存“这个 Tab 创建时实际采用什么 - /// 启动方式”。共享 session v4 已有完整 schema,GPUI 只需把它保留下来, - /// 不能在快照时把所有本地 Tab 都降级成 `None`。 - launch: Option, - /// 后台 tab 响过 BEL(旧壳 `has_bell`)。激活即清。 - has_bell: bool, -} - pub struct NebulaWorkspace { tabs: Vec, /// 与 `tabs` 同下标的用户元数据,见 [`TabMeta`]。 diff --git a/nebula_app/src/gpui_shell/workspace/agents.rs b/nebula_app/src/gpui_shell/workspace/agents.rs index 8c5027b..6465ee4 100644 --- a/nebula_app/src/gpui_shell/workspace/agents.rs +++ b/nebula_app/src/gpui_shell/workspace/agents.rs @@ -370,6 +370,7 @@ impl NebulaWorkspace { at, tab, TabMeta { + runtime_id: Default::default(), custom_name: agent.map(|agent| format!("{} 分叉", agent.display_name())), color, shell_tag, diff --git a/nebula_app/src/gpui_shell/workspace/residency.rs b/nebula_app/src/gpui_shell/workspace/residency.rs index 8b5f3fa..29af20d 100644 --- a/nebula_app/src/gpui_shell/workspace/residency.rs +++ b/nebula_app/src/gpui_shell/workspace/residency.rs @@ -108,6 +108,9 @@ impl NebulaWorkspace { } fn queue_or_answer_runtime(&mut self, dispatch: Arc, cx: &mut Context) { + if super::runtime_tabs::dispatch_read(&dispatch, &cx.entity().downgrade(), cx) { + return; + } match &dispatch.command { RuntimeCommand::NewWindow { .. } => { dispatch.respond(Err(ApiError::new( @@ -115,16 +118,15 @@ impl NebulaWorkspace { "the GPUI runtime currently owns one workspace window; window.create is unavailable", ))); }, - RuntimeCommand::Exec { pane_id, argv, timeout_ms, max_output_bytes, .. } => { - match self.runtime_exec_context(*pane_id, cx) { - Ok((context, cwd)) => crate::runtime_exec::spawn( - dispatch.clone(), - context, - cwd, - argv.clone(), - *timeout_ms, - *max_output_bytes, - ), + RuntimeCommand::Exec { window_id, pane_id, .. } + | RuntimeCommand::Git { window_id, pane_id, .. } => { + match self + .runtime_window_requested(*window_id) + .and_then(|()| self.runtime_exec_context(*pane_id, cx)) + { + Ok((context, cwd)) => { + crate::runtime_exec::spawn(dispatch.clone(), context, cwd) + }, Err(error) => dispatch.respond(Err(error)), } }, @@ -142,6 +144,8 @@ impl NebulaWorkspace { self.runtime_pending.push(dispatch); }, RuntimeCommand::Snapshot + | RuntimeCommand::Conversation { .. } + | RuntimeCommand::Tab { .. } | RuntimeCommand::CloseWindow { .. } | RuntimeCommand::CloseTab { .. } | RuntimeCommand::RenameTab { .. } @@ -173,6 +177,12 @@ impl NebulaWorkspace { cx: &mut Context, ) -> Result { match command { + RuntimeCommand::Conversation { window_id, pane_id, request } => self + .conversation_view(*window_id, *pane_id, cx)? + .update(cx, |view, cx| view.runtime_conversation_apply(request, cx)), + RuntimeCommand::Tab { window_id, request } => { + self.execute_tab_request(*window_id, request, window, cx) + }, RuntimeCommand::Snapshot => { serde_json::to_value(self.publish_runtime_snapshot(window, cx)) .map_err(|error| ApiError::new("serialization_failed", error.to_string())) @@ -260,37 +270,7 @@ impl NebulaWorkspace { }, RuntimeCommand::CloseTab { window_id, tab_index } => { self.runtime_window_requested(*window_id)?; - if *tab_index >= self.tabs.len() { - return Err(ApiError::new( - "target_not_found", - format!( - "tab {tab_index} does not exist in window {}", - self.runtime_window_id - ), - )); - } - if let Some(process) = self.busy_process_in_tab(*tab_index, None, cx) { - return Err(runtime_close_confirmation( - process, - json!({ - "target": "tab", - "window_id": self.runtime_window_id, - "tab_index": tab_index - }), - )); - } - self.close_tab(*tab_index, window, cx); - let action = json!({ - "window_id": self.runtime_window_id, - "tab_index": tab_index, - "closed": true - }); - if self.tabs.is_empty() { - let snapshot = super::windowing::publish_runtime_snapshot(cx); - Ok(json!({ "action": action, "snapshot": snapshot })) - } else { - self.runtime_result(action, window, cx) - } + self.close_runtime_tab(*tab_index, window, cx) }, RuntimeCommand::RenameTab { window_id, tab_index, name } => { self.runtime_window_requested(*window_id)?; @@ -550,7 +530,7 @@ impl NebulaWorkspace { cx, ) }, - RuntimeCommand::ReadPane { window_id, pane_id, lines } => { + RuntimeCommand::ReadPane { window_id, pane_id, lines, screen } => { self.runtime_window_requested(*window_id)?; let Some(tab_ix) = self.tab_of_pane(*pane_id) else { return Err(ApiError::new( @@ -565,7 +545,7 @@ impl NebulaWorkspace { .expect("tab_of_pane resolved a terminal pane") .view .read(cx) - .runtime_read(self.runtime_window_id, *lines), + .runtime_read(self.runtime_window_id, *lines, *screen), _ => unreachable!("tab_of_pane only resolves terminal tabs"), }?; serde_json::to_value(read) @@ -651,7 +631,7 @@ impl NebulaWorkspace { cx, ) }, - RuntimeCommand::Exec { .. } => Err(ApiError::new( + RuntimeCommand::Exec { .. } | RuntimeCommand::Git { .. } => Err(ApiError::new( "invalid_runtime_command", "pane.exec must be prepared before entering the synchronous GPUI dispatcher", )), @@ -801,7 +781,7 @@ impl NebulaWorkspace { .expect("tab_of_pane resolved a terminal pane") .view .read(cx) - .runtime_read(self.runtime_window_id, *lines), + .runtime_read(self.runtime_window_id, *lines, false), _ => unreachable!("tab_of_pane only resolves terminal tabs"), }?; Ok(json!({ "agent": managed, "read": read })) @@ -826,7 +806,7 @@ impl NebulaWorkspace { } } - fn runtime_window_requested(&self, requested: Option) -> Result<(), ApiError> { + pub(super) fn runtime_window_requested(&self, requested: Option) -> Result<(), ApiError> { if requested.is_none_or(|id| id == self.runtime_window_id) { Ok(()) } else { @@ -837,7 +817,7 @@ impl NebulaWorkspace { } } - fn runtime_result( + pub(super) fn runtime_result( &self, action: serde_json::Value, window: &Window, @@ -898,6 +878,8 @@ impl NebulaWorkspace { WorkspaceTab::Code { .. } => ("code", None, None, None, Vec::new()), }; RuntimeTab { + tab_id: self.tab_meta.get(index).map(|meta| meta.runtime_id.0.clone()), + file: tab.runtime_file_info(cx), index, active: index == self.active, label: self.tab_title(index, cx).to_string(), diff --git a/nebula_app/src/gpui_shell/workspace/runtime_conversation.rs b/nebula_app/src/gpui_shell/workspace/runtime_conversation.rs new file mode 100644 index 0000000..2d5f01d --- /dev/null +++ b/nebula_app/src/gpui_shell/workspace/runtime_conversation.rs @@ -0,0 +1,85 @@ +//! Capture UI-owned identity, read native files off-thread, recheck before publishing. + +use std::sync::Arc; + +use super::{NebulaWorkspace, WorkspaceTab}; +use crate::{ + gpui_shell::terminal::view::TerminalView, + runtime_api::{ApiError, RuntimeCommand, RuntimeDispatch, conversation::Request}, +}; +use gpui::{App, Entity, WeakEntity}; + +impl NebulaWorkspace { + pub(super) fn conversation_view( + &self, + window_id: u64, + pane_id: u64, + cx: &App, + ) -> Result, ApiError> { + self.runtime_window_requested(Some(window_id))?; + self.tabs + .iter() + .find_map(|tab| match tab { + WorkspaceTab::Terminal { panes, .. } => { + panes.iter().find(|pane| pane.id == pane_id).map(|pane| pane.view.clone()) + }, + _ => None, + }) + .filter(|view| view.read(cx).runtime_conversation_identity().is_ok()) + .ok_or_else(|| { + ApiError::new("conversation_ended", "the Agent pane was closed or exited") + }) + } +} + +pub(super) fn dispatch_read( + dispatch: &Arc, + workspace: &WeakEntity, + cx: &mut App, +) -> bool { + let RuntimeCommand::Conversation { + window_id, + pane_id, + request: Request::Read { identity, before, revision }, + } = &dispatch.command + else { + return false; + }; + let prepared = workspace + .upgrade() + .ok_or_else(|| ApiError::new("target_not_found", "the workspace was closed")) + .and_then(|workspace| workspace.read(cx).conversation_view(*window_id, *pane_id, cx)) + .and_then(|view| { + view.read(cx) + .runtime_conversation_source(identity) + .map(|source| (view.downgrade(), source)) + }); + let (view, source) = match prepared { + Ok(prepared) => prepared, + Err(error) => { + dispatch.respond(Err(error)); + return true; + }, + }; + let current = source.identity.clone(); + let (before, revision, dispatch) = (*before, revision.clone(), dispatch.clone()); + let work = cx.background_executor().spawn(async move { + crate::assistant_answer::conversation::read(source, before, revision.as_deref()) + }); + cx.spawn(async move |cx| { + let result = work.await.and_then(|page| { + view.update(cx, |view, _| { + let mut result = view.runtime_conversation_status(¤t)?; + result + .as_object_mut() + .expect("conversation status") + .extend(page.as_object().expect("conversation page").clone()); + Ok(result) + }) + .map_err(|_| ApiError::new("conversation_ended", "the Agent pane was closed"))? + }); + dispatch.respond(result); + }) + .detach(); + true +} diff --git a/nebula_app/src/gpui_shell/workspace/runtime_tabs.rs b/nebula_app/src/gpui_shell/workspace/runtime_tabs.rs new file mode 100644 index 0000000..f5a5059 --- /dev/null +++ b/nebula_app/src/gpui_shell/workspace/runtime_tabs.rs @@ -0,0 +1,326 @@ +//! Mobile/Runtime adapters for existing tab lifecycles and immutable reader snapshots. + +use std::{ + fs::File, + io::{Read, Seek, SeekFrom}, + path::PathBuf, + sync::Arc, +}; + +use base64::Engine as _; +use gpui::{App, Context, WeakEntity, Window}; +use gpui_component::Rope; +use serde_json::{Value, json}; + +use super::{NebulaWorkspace, WorkspaceTab}; +use crate::runtime_api::{ + ApiError, RuntimeCommand, RuntimeDispatch, + tabs::{self, FileInfo, Request}, +}; + +impl WorkspaceTab { + pub(super) fn runtime_file_info(&self, cx: &App) -> Option { + if let Some(file) = self.file_editor(cx) { + let view = file.read(cx); + return Some(FileInfo { + path: view.path.to_string_lossy().into_owned(), + remote: view.reader_is_remote(), + dirty: view.is_dirty(), + saving: view.is_saving(), + ready: view.reader_revision().is_some(), + revision: view.reader_revision(), + }); + } + match self { + Self::Image { view } => Some(FileInfo { + path: view.read(cx).path.to_string_lossy().into_owned(), + remote: false, + dirty: false, + saving: false, + ready: true, + revision: None, + }), + _ => None, + } + } +} + +impl NebulaWorkspace { + fn runtime_tab_index(&self, id: &str) -> Result { + self.tab_meta.iter().position(|meta| meta.runtime_id.0 == id).ok_or_else(|| { + ApiError::new( + "target_not_found", + "the requested tab was closed or moved to another window", + ) + }) + } + + pub(super) fn execute_tab_request( + &mut self, + window_id: u64, + request: &Request, + window: &mut Window, + cx: &mut Context, + ) -> Result { + self.runtime_window_requested(Some(window_id))?; + match request { + Request::Close { tab_id } => { + let index = self.runtime_tab_index(tab_id)?; + self.close_runtime_tab(index, window, cx) + }, + Request::Focus { tab_id } => { + let index = self.runtime_tab_index(tab_id)?; + self.activate_tab(index, window, cx); + self.runtime_result(json!({"window_id": window_id, "tab_id": tab_id}), window, cx) + }, + Request::Open { path } => { + // 此入口只打开应用内阅读器,未知扩展名绝不交给系统关联程序执行。 + if !crate::gpui_shell::doc_tabs::openable_in_app(path) { + return Err(ApiError::new( + "unsupported_file", + "this file type has no in-app reader", + )); + } + self.open_document_path(path.clone(), window, cx); + let id = self.tab_meta[self.active].runtime_id.0.clone(); + self.runtime_result(json!({"window_id": window_id, "tab_id": id}), window, cx) + }, + Request::Read { .. } => Err(ApiError::new( + "runtime_unavailable", + "file reads require the background dispatcher", + )), + } + } + + pub(super) fn close_runtime_tab( + &mut self, + index: usize, + window: &mut Window, + cx: &mut Context, + ) -> Result { + let tab = self + .tabs + .get(index) + .ok_or_else(|| ApiError::new("target_not_found", "the requested tab does not exist"))?; + if let Some(file) = tab.file_editor(cx) { + let file = file.read(cx); + if file.is_saving() || file.is_dirty() { + // 普通关闭会异步弹桌面对话框;RPC 在真正关闭之前必须保留失败状态。 + return Err(ApiError::new( + if file.is_saving() { "file_saving" } else { "unsaved_changes" }, + "save or resolve this document on the computer before closing its shared tab", + )); + } + } + if let Some(process) = self.busy_process_in_tab(index, None, cx) { + return Err(ApiError::new( + "confirmation_required", + format!("{process} is still running; confirm closure on the computer"), + )); + } + let tab_id = self.tab_meta[index].runtime_id.0.clone(); + self.close_tab(index, window, cx); + if self.tab_meta.iter().any(|meta| meta.runtime_id.0 == tab_id) { + return Err(ApiError::new("close_pending", "the tab has not closed")); + } + let action = json!({"window_id": self.runtime_window_id, "tab_index": index, "tab_id": tab_id, "closed": true}); + if self.tabs.is_empty() { + Ok( + json!({"action": action, "snapshot": super::windowing::publish_runtime_snapshot(cx)}), + ) + } else { + self.runtime_result(action, window, cx) + } + } + + fn reader_source( + &self, + window_id: u64, + tab_id: &str, + cx: &App, + ) -> Result { + self.runtime_window_requested(Some(window_id))?; + let tab = &self.tabs[self.runtime_tab_index(tab_id)?]; + if let Some(file) = tab.file_editor(cx) { + return file + .read(cx) + .reader_snapshot(cx) + .map(|(text, revision)| ReaderSource::Text(text, revision)) + .ok_or_else(|| { + ApiError::new("file_not_ready", "the computer has not loaded this document") + }); + } + if let WorkspaceTab::Image { view } = tab { + return Ok(ReaderSource::Image(view.read(cx).path.clone())); + } + Err(ApiError::new("unsupported_file", "this tab has no readable file content")) + } +} + +enum ReaderSource { + Text(Rope, u64), + Image(PathBuf), +} + +pub(super) fn dispatch_read( + dispatch: &Arc, + workspace: &WeakEntity, + cx: &App, +) -> bool { + let RuntimeCommand::Tab { + window_id, + request: Request::Read { tab_id, offset, limit, revision }, + } = &dispatch.command + else { + return false; + }; + let source = workspace + .upgrade() + .ok_or_else(|| ApiError::new("target_not_found", "the workspace was closed")) + .and_then(|workspace| workspace.read(cx).reader_source(*window_id, tab_id, cx)); + let (dispatch, window_id, tab_id, offset, limit, revision) = + (dispatch.clone(), *window_id, tab_id.clone(), *offset, *limit, revision.clone()); + cx.background_executor() + .spawn(async move { + let result = source + .and_then(|source| read_chunk(source, offset, limit, revision.as_deref())) + .map(|mut result| { + result["window_id"] = json!(window_id); + result["tab_id"] = json!(tab_id); + result + }); + dispatch.respond(result); + }) + .detach(); + true +} + +fn read_chunk( + source: ReaderSource, + offset: usize, + limit: usize, + expected: Option<&str>, +) -> Result { + let (bytes, total, revision, kind) = match source { + ReaderSource::Text(text, revision) => { + let revision = format!("text:{revision}"); + check_revision(expected, &revision)?; + let total = text.len(); + check_size(total, offset)?; + let end = offset.saturating_add(limit).min(total); + // 线协议按字节计数;中文和 emoji 跨块时,结束点退回完整 UTF-8 字符边界。 + let part = (0..4) + .filter_map(|back| end.checked_sub(back)) + .filter(|end| *end >= offset) + .find_map(|end| text.try_slice(offset..end).ok()) + .ok_or_else(|| ApiError::invalid_params("offset is not a UTF-8 boundary"))?; + (part.to_string().into_bytes(), total, revision, "text") + }, + ReaderSource::Image(path) => { + let mut file = File::open(path).map_err(file_error)?; + let before = file.metadata().map_err(file_error)?; + if !before.is_file() { + return Err(ApiError::new("unsupported_file", "expected a regular image file")); + } + let total = usize::try_from(before.len()) + .map_err(|_| ApiError::new("file_too_large", "image exceeds the reader budget"))?; + check_size(total, offset)?; + let revision = image_revision(&before)?; + check_revision(expected, &revision)?; + file.seek(SeekFrom::Start(offset as u64)).map_err(file_error)?; + let mut bytes = vec![0; limit.min(total - offset)]; + file.read_exact(&mut bytes).map_err(file_error)?; + check_revision( + Some(&revision), + &image_revision(&file.metadata().map_err(file_error)?)?, + )?; + (bytes, total, revision, "image") + }, + }; + let next_offset = offset + bytes.len(); + Ok(json!({"kind": kind, "revision": revision, "offset": offset, "next_offset": next_offset, + "total_bytes": total, "eof": next_offset == total, + "data": base64::engine::general_purpose::STANDARD.encode(bytes)})) +} + +fn check_revision(expected: Option<&str>, actual: &str) -> Result<(), ApiError> { + if expected.is_some_and(|revision| revision != actual) { + Err(ApiError::new("file_changed", "the document changed while reading; refresh it")) + } else { + Ok(()) + } +} + +fn check_size(total: usize, offset: usize) -> Result<(), ApiError> { + if total > tabs::MAX_FILE_BYTES { + Err(ApiError::new("file_too_large", "file exceeds the 16 MiB reader budget")) + } else if offset > total { + Err(ApiError::invalid_params("offset exceeds the file length")) + } else { + Ok(()) + } +} + +fn image_revision(metadata: &std::fs::Metadata) -> Result { + let modified = metadata + .modified() + .map_err(file_error)? + .duration_since(std::time::UNIX_EPOCH) + .map_err(|error| ApiError::new("file_read_failed", error.to_string()))?; + Ok(format!("image:{}:{}", metadata.len(), modified.as_nanos())) +} + +fn file_error(error: std::io::Error) -> ApiError { + ApiError::new("file_read_failed", error.to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn reader_chunks_preserve_utf8_and_require_the_same_revision() { + let original = "a中文😀z"; + let mut bytes = Vec::new(); + loop { + let part = read_chunk( + ReaderSource::Text(Rope::from(original), 9), + bytes.len(), + 4, + Some("text:9"), + ) + .unwrap(); + bytes.extend( + base64::engine::general_purpose::STANDARD + .decode(part["data"].as_str().unwrap()) + .unwrap(), + ); + if part["eof"] == true { + break; + } + } + assert_eq!(String::from_utf8(bytes).unwrap(), original); + let error = read_chunk(ReaderSource::Text(Rope::from(original), 10), 4, 4, Some("text:9")) + .unwrap_err(); + assert_eq!(error.code, "file_changed"); + assert!(read_chunk(ReaderSource::Text(Rope::from(original), 9), 2, 4, None).is_err()); + } + + #[test] + fn image_reads_detect_replacement_and_enforce_the_budget() { + use std::io::Write as _; + let mut file = tempfile::NamedTempFile::new().unwrap(); + file.write_all(b"abcdefgh").unwrap(); + let part = read_chunk(ReaderSource::Image(file.path().into()), 0, 4, None).unwrap(); + let next = + read_chunk(ReaderSource::Image(file.path().into()), 4, 4, part["revision"].as_str()) + .unwrap(); + assert_eq!(next["eof"], true); + file.write_all(b"changed").unwrap(); + let error = + read_chunk(ReaderSource::Image(file.path().into()), 4, 4, part["revision"].as_str()) + .unwrap_err(); + assert_eq!(error.code, "file_changed"); + assert_eq!(check_size(tabs::MAX_FILE_BYTES + 1, 0).unwrap_err().code, "file_too_large"); + } +} diff --git a/nebula_app/src/gpui_shell/workspace/session_recovery.rs b/nebula_app/src/gpui_shell/workspace/session_recovery.rs index 767ce4d..1f925c9 100644 --- a/nebula_app/src/gpui_shell/workspace/session_recovery.rs +++ b/nebula_app/src/gpui_shell/workspace/session_recovery.rs @@ -141,6 +141,7 @@ impl NebulaWorkspace { at, WorkspaceTab::Terminal { panes, tree, focused, zoomed: false, broadcast: false }, TabMeta { + runtime_id: Default::default(), custom_name: tab.custom_name.clone(), color: tab.color, shell_tag: Self::launch_shell_tag(&saved_launch), diff --git a/nebula_app/src/gpui_shell/workspace/tab_presentation.rs b/nebula_app/src/gpui_shell/workspace/tab_presentation.rs index 27d3382..0f6d6dc 100644 --- a/nebula_app/src/gpui_shell/workspace/tab_presentation.rs +++ b/nebula_app/src/gpui_shell/workspace/tab_presentation.rs @@ -2,6 +2,30 @@ use super::*; +/// 标签元数据:用户名字、色标沿用 session v4;运行时身份只随活体标签移动, +/// 不写入 session,避免重启后的请求命中新标签。 +/// 与 `tabs` **同下标**。长度必须一致,所以增删移三种结构性改动只允许走 +/// `insert_tab_at` / `remove_tab_at` / `move_tab`——别处直接 `self.tabs.push` +/// 会让色条和名字错位到邻居身上。 +#[derive(Clone, Debug, Default)] +pub(super) struct TabMeta { + pub(super) runtime_id: crate::runtime_api::tabs::TabId, + /// 用户重命名过的标签名;`None` = 跟着 cwd/文件名自动走。 + pub(super) custom_name: Option, + /// 色标(右键菜单的标签颜色);`None` = 不画色条。 + pub(super) color: Option, + /// 本 Tab 创建时实际采用的 shell 短标。默认 shell 是“新建时参数”, + /// 不是全局实时主题;设置改变后既有 PTY 不会换进程,这个标签也不能 + /// 跟着全局值漂移。非终端 Tab 为 `None`。 + pub(super) shell_tag: Option, + /// 与旧壳 `TabEntry::launch` 同义:保存“这个 Tab 创建时实际采用什么 + /// 启动方式”。共享 session v4 已有完整 schema,GPUI 只需把它保留下来, + /// 不能在快照时把所有本地 Tab 都降级成 `None`。 + pub(super) launch: Option, + /// 后台 tab 响过 BEL(旧壳 `has_bell`)。激活即清。 + pub(super) has_bell: bool, +} + /// 两种 tab 布局共用的只读展示数据。状态与动作仍由 `NebulaWorkspace` /// 持有;这里只集中 cwd 标题、程序图标、AI 活动和用户元数据的解释。 pub(super) struct TabPresentation { diff --git a/nebula_app/src/gpui_shell/workspace/windowing.rs b/nebula_app/src/gpui_shell/workspace/windowing.rs index 4aa0234..089e5e9 100644 --- a/nebula_app/src/gpui_shell/workspace/windowing.rs +++ b/nebula_app/src/gpui_shell/workspace/windowing.rs @@ -109,6 +109,8 @@ fn runtime_window_policy(command: &RuntimeCommand) -> RuntimeWindowPolicy { RuntimeCommand::Focus { .. } => RuntimeWindowPolicy::Focus, RuntimeCommand::NewWindow { .. } => RuntimeWindowPolicy::CreateWithoutActivation, RuntimeCommand::Snapshot + | RuntimeCommand::Conversation { .. } + | RuntimeCommand::Tab { .. } | RuntimeCommand::CloseWindow { .. } | RuntimeCommand::NewTab { .. } | RuntimeCommand::CloseTab { .. } @@ -125,6 +127,7 @@ fn runtime_window_policy(command: &RuntimeCommand) -> RuntimeWindowPolicy { | RuntimeCommand::SendKey { .. } | RuntimeCommand::Run { .. } | RuntimeCommand::Exec { .. } + | RuntimeCommand::Git { .. } | RuntimeCommand::AgentStart { .. } | RuntimeCommand::AgentFork { .. } | RuntimeCommand::AgentPrompt { .. } @@ -831,6 +834,10 @@ fn entry_with_pane(pane_id: u64, cx: &mut App) -> Option { fn route_entry(command: &RuntimeCommand, cx: &mut App) -> Result { let runtime_hub = cx.global::().runtime_hub.clone(); let (window_id, pane_id) = match command { + RuntimeCommand::Conversation { window_id, pane_id, .. } => { + (Some(*window_id), Some(*pane_id)) + }, + RuntimeCommand::Tab { window_id, .. } => (Some(*window_id), None), RuntimeCommand::Focus { window_id, pane_id } | RuntimeCommand::Split { window_id, pane_id, .. } | RuntimeCommand::AgentStart { window_id, pane_id, .. } => (*window_id, *pane_id), @@ -848,7 +855,8 @@ fn route_entry(command: &RuntimeCommand, cx: &mut App) -> Result (*window_id, Some(*pane_id)), + | RuntimeCommand::Exec { window_id, pane_id, .. } + | RuntimeCommand::Git { window_id, pane_id, .. } => (*window_id, Some(*pane_id)), RuntimeCommand::AgentFork { window_id, source_pane_id, .. } => { (*window_id, *source_pane_id) }, @@ -1054,9 +1062,7 @@ fn dispatch_runtime(dispatch: Arc, cx: &mut App) { _ => {}, } - if let RuntimeCommand::Exec { pane_id, argv, timeout_ms, max_output_bytes, .. } = - &dispatch.command - { + if let Some((window_id, pane_id)) = dispatch.command.execution_target() { let entry = match route_entry(&dispatch.command, cx) { Ok(entry) => entry, Err(error) => { @@ -1071,15 +1077,12 @@ fn dispatch_runtime(dispatch: Arc, cx: &mut App) { ))); return; }; - match workspace.read(cx).runtime_exec_context(*pane_id, cx) { - Ok((context, cwd)) => crate::runtime_exec::spawn( - dispatch.clone(), - context, - cwd, - argv.clone(), - *timeout_ms, - *max_output_bytes, - ), + let prepared = workspace + .read(cx) + .runtime_window_requested(window_id) + .and_then(|()| workspace.read(cx).runtime_exec_context(pane_id, cx)); + match prepared { + Ok((context, cwd)) => crate::runtime_exec::spawn(dispatch.clone(), context, cwd), Err(error) => dispatch.respond(Err(error)), } return; @@ -1110,6 +1113,12 @@ fn dispatch_runtime(dispatch: Arc, cx: &mut App) { return; }, }; + if super::runtime_tabs::dispatch_read(&dispatch, &entry.workspace, cx) { + return; + } + if super::runtime_conversation::dispatch_read(&dispatch, &entry.workspace, cx) { + return; + } let command = dispatch.command.clone(); let workspace = entry.workspace.clone(); let result = entry.handle.update(cx, move |_, window, cx| { @@ -1913,7 +1922,7 @@ mod tests { text: "paste".to_owned(), submit: false, }, - RuntimeCommand::ReadPane { window_id: Some(1), pane_id: 2, lines: 20 }, + RuntimeCommand::ReadPane { window_id: Some(1), pane_id: 2, lines: 20, screen: false }, RuntimeCommand::Procs { window_id: Some(1), pane_id: 2 }, RuntimeCommand::SendKey { window_id: Some(1), diff --git a/nebula_app/src/main.rs b/nebula_app/src/main.rs index 94b4aa0..8e7cf61 100644 --- a/nebula_app/src/main.rs +++ b/nebula_app/src/main.rs @@ -84,6 +84,7 @@ mod markdown; mod math; mod message_bar; mod migrate; +mod mobile_connection; mod motion; mod mux; mod nebula_history; @@ -91,6 +92,7 @@ mod notify; #[cfg(windows)] mod panic; mod platform; +mod plugins; #[cfg(all(unix, feature = "legacy-shell"))] mod polling; mod process_tree; @@ -258,6 +260,7 @@ fn main() -> Result<(), Box> { Some(Subcommands::Msg(options)) => msg(options)?, Some(Subcommands::Migrate(options)) => migrate::migrate(options), Some(Subcommands::Config(options)) => std::process::exit(config_cli::run(options)), + Some(Subcommands::Plugin(options)) => std::process::exit(plugins::cli::run(options)), #[cfg(windows)] Some(Subcommands::NotifyTest) => std::process::exit(crate::notify::notify_test()), #[cfg(windows)] diff --git a/nebula_app/src/mobile_connection.rs b/nebula_app/src/mobile_connection.rs new file mode 100644 index 0000000..def42b3 --- /dev/null +++ b/nebula_app/src/mobile_connection.rs @@ -0,0 +1,524 @@ +//! 手机连接由进程持有,设置页只发命令并读取快照。关闭页面不停止已配对手机。 + +use crate::runtime_api::mobile_bridge::BridgeSession; +use pebrel_mobile_link::{ + endpoint::{self, HostState, RelayAccess}, + preview::{self, LanCredentials}, +}; +use std::{ + io, + net::IpAddr, + sync::{ + Arc, Mutex, OnceLock, + atomic::{AtomicBool, AtomicU64, Ordering}, + }, +}; + +mod preferences; +mod recovery; +pub(crate) use endpoint::runtime::Status; +pub(crate) use endpoint::{DeviceSummary, PairingRequest, Route as Mode}; +pub(crate) use preferences::Preferences; +pub(crate) use preview::network::{LanAddress, addresses}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum Failure { + Invalid, + Address, + Port, + Credentials, + Connection, + Cancelled, +} + +fn classify(error: io::Error) -> Failure { + match error.kind() { + io::ErrorKind::AddrInUse => Failure::Port, + io::ErrorKind::AddrNotAvailable => Failure::Address, + io::ErrorKind::PermissionDenied => Failure::Credentials, + _ => Failure::Connection, + } +} + +#[derive(Clone, PartialEq, Eq)] +pub(crate) struct ConnectionSnapshot { + pub status: Status, + pub invitation: Option, + pub address: String, + pub pairing_code: Option, + pub discoverable: bool, +} + +#[derive(Clone, PartialEq, Eq)] +pub(crate) struct Snapshot { + pub preferences: Preferences, + pub lan: Option, + pub relay: Option, + pub devices: Vec, + pub requests: Vec, +} + +impl Snapshot { + pub fn connection(&self, mode: Mode) -> Option<&ConnectionSnapshot> { + match mode { + Mode::Lan => self.lan.as_ref(), + Mode::Relay => self.relay.as_ref(), + } + } +} + +struct Active { + handle: Arc, + identity: String, + enabled: Arc, +} + +impl Drop for Active { + fn drop(&mut self) { + self.enabled.store(false, Ordering::Release); + self.handle.shutdown.cancel(); + } +} + +#[derive(Default)] +struct Manager { + initialized: bool, + configuration: preferences::Configuration, + lan: Option, + relay: Option, +} + +fn manager() -> &'static Mutex { + static MANAGER: OnceLock> = OnceLock::new(); + MANAGER.get_or_init(|| Mutex::new(Manager::default())) +} + +static OPERATIONS: Mutex<()> = Mutex::new(()); +static GENERATION: AtomicU64 = AtomicU64::new(0); +static NOTIFICATIONS: AtomicBool = AtomicBool::new(true); + +fn runtime() -> Result<&'static tokio::runtime::Runtime, Failure> { + static RUNTIME: OnceLock> = OnceLock::new(); + RUNTIME + .get_or_init(|| { + tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .thread_name("pebrel-mobile-network") + .enable_all() + .build() + .ok() + }) + .as_ref() + .ok_or(Failure::Connection) +} + +fn persist_host() -> endpoint::PersistHost { + Arc::new(|bytes| { + preferences::store("host", bytes).map_err(|_| io::Error::other("mobile_credentials_failed")) + }) +} + +fn host() -> Result>, Failure> { + static HOST: OnceLock>> = OnceLock::new(); + static LOAD: Mutex<()> = Mutex::new(()); + if let Some(host) = HOST.get() { + return Ok(host.clone()); + } + let _load = LOAD.lock().map_err(|_| Failure::Credentials)?; + if let Some(host) = HOST.get() { + return Ok(host.clone()); + } + let host = match preferences::load("host")? { + Some(bytes) => HostState::restore(&bytes).map_err(|_| Failure::Credentials)?, + None => { + let host = HostState::generate().map_err(|_| Failure::Credentials)?; + preferences::store("host", &host.encode().map_err(|_| Failure::Credentials)?)?; + host + }, + }; + Ok(HOST.get_or_init(|| Arc::new(Mutex::new(host))).clone()) +} + +fn initialize() -> Result<(), Failure> { + if manager().lock().map_err(|_| Failure::Connection)?.initialized { + return Ok(()); + } + let configuration = preferences::Configuration::load()?; + let mut manager = manager().lock().map_err(|_| Failure::Connection)?; + if !manager.initialized { + NOTIFICATIONS.store(configuration.preferences.notifications, Ordering::Release); + manager.configuration = configuration; + manager.initialized = true; + } + Ok(()) +} + +/// 以下查询在后台调用;UI 始终渲染已有快照,不等待凭据存储或 host 锁。 +pub(crate) fn snapshot() -> Result { + initialize()?; + let host = host()?; + let (devices, requests) = { + let host = host.lock().map_err(|_| Failure::Credentials)?; + (host.devices(), host.pairing_requests(now())) + }; + let (preferences, lan, relay) = { + let manager = manager().lock().map_err(|_| Failure::Connection)?; + ( + manager.configuration.preferences.clone(), + manager.lan.as_ref().map(|a| a.handle.clone()), + manager.relay.as_ref().map(|a| a.handle.clone()), + ) + }; + let view = |handle: Arc| ConnectionSnapshot { + status: *handle.status.lock().unwrap_or_else(|e| e.into_inner()), + invitation: handle.invitation(), + address: handle.address().to_owned(), + pairing_code: handle.pairing_code(), + discoverable: handle.discoverable(), + }; + Ok(Snapshot { preferences, lan: lan.map(view), relay: relay.map(view), devices, requests }) +} + +pub(crate) fn begin() -> u64 { + GENERATION.fetch_add(1, Ordering::AcqRel).wrapping_add(1) +} + +pub(crate) fn cancel(generation: u64) { + // UI 取消不等待后台凭据写入或连接关闭;事务在提交点检查代际。 + let _ = GENERATION.compare_exchange( + generation, + generation.wrapping_add(1), + Ordering::AcqRel, + Ordering::Acquire, + ); +} + +fn current(generation: u64) -> Result<(), Failure> { + if GENERATION.load(Ordering::Acquire) == generation { Ok(()) } else { Err(Failure::Cancelled) } +} + +struct OwnedSession { + session: BridgeSession, + authorization: endpoint::runtime::Authorization, + enabled: Arc, +} + +impl endpoint::runtime::RuntimeSession for OwnedSession { + fn request(&mut self, bytes: &[u8]) -> io::Result<()> { + if !self.enabled.load(Ordering::Acquire) || !self.authorization.is_active() { + return Err(io::Error::other("mobile_closed")); + } + self.session.set_input_permission(self.authorization.allow_input()); + self.session.request(bytes) + } +} + +fn factory(enabled: Arc) -> endpoint::runtime::AuthorizedFactory { + Arc::new(move |authorization| { + let enabled = enabled.clone(); + Arc::new(move |reply| { + if !enabled.load(Ordering::Acquire) || !authorization.is_active() { + return Err(io::Error::other("mobile_closed")); + } + let access = authorization.clone(); + let scope = enabled.clone(); + let output = Arc::new(move |bytes: Vec| { + if !scope.load(Ordering::Acquire) || !access.is_active() { + return Err(io::Error::other("mobile_closed")); + } + let mut frame: serde_json::Value = + serde_json::from_slice(&bytes).map_err(io::Error::other)?; + if frame["event"] == "runtime.snapshot" { + frame["data"]["mobile_policy"] = serde_json::json!({ + "allow_input": access.allow_input(), "notifications": NOTIFICATIONS.load(Ordering::Acquire), + }); + } + reply(serde_json::to_vec(&frame).map_err(io::Error::other)?) + }); + Ok(Box::new(OwnedSession { + session: BridgeSession::open(authorization.allow_input(), output)?, + authorization: authorization.clone(), + enabled: enabled.clone(), + })) + }) + }) +} + +pub(crate) fn saved_relay() -> Result, Failure> { + initialize()?; + Ok(manager().lock().map_err(|_| Failure::Connection)?.configuration.relay.clone()) +} + +fn route_enabled(preferences: &Preferences, mode: Mode) -> bool { + preferences.enabled + && match mode { + Mode::Lan => preferences.lan_enabled, + Mode::Relay => preferences.relay_enabled, + } +} + +fn route_identity(configuration: &preferences::Configuration, mode: Mode) -> String { + match mode { + Mode::Lan => { + format!("{:?}:{}", configuration.preferences.address, configuration.preferences.port) + }, + Mode::Relay => { + use sha2::{Digest, Sha256}; + Sha256::digest(configuration.relay.as_deref().unwrap_or_default().as_bytes()) + .iter() + .map(|byte| format!("{byte:02x}")) + .collect() + }, + } +} + +fn start_route( + mode: Mode, + configuration: &mut preferences::Configuration, +) -> Result { + let preferences = &configuration.preferences; + let host = host()?; + let scope = Arc::new(AtomicBool::new(false)); + let runtime = runtime()?; + let handle = match mode { + Mode::Lan => { + let address = preferences + .address + .filter(|v| preview::network::usable(*v)) + .ok_or(Failure::Address)?; + let stored = configuration + .lan + .as_ref() + .map(|bytes| { + LanCredentials::parse(bytes.as_bytes()).map_err(|_| Failure::Credentials) + }) + .transpose()?; + let mut credentials = match stored { + Some(mut credentials) => { + credentials.relocate(address, preferences.port).map_err(classify)?; + credentials + }, + None => LanCredentials::generate(address, preferences.port).map_err(classify)?, + }; + let handle = runtime + .block_on(endpoint::start_lan( + &mut credentials, + host, + persist_host(), + "Pebrel", + preferences.default_input, + factory(scope.clone()), + )) + .map_err(classify)?; + configuration.lan = Some( + String::from_utf8(credentials.encode().map_err(|_| Failure::Credentials)?.to_vec()) + .map_err(|_| Failure::Credentials)?, + ); + handle + }, + Mode::Relay => runtime + .block_on(endpoint::start_relay( + RelayAccess::parse( + configuration.relay.as_deref().ok_or(Failure::Invalid)?.as_bytes(), + ) + .map_err(|_| Failure::Invalid)?, + host, + persist_host(), + "Pebrel", + preferences.default_input, + factory(scope.clone()), + )) + .map_err(classify)?, + }; + Ok(Active { + handle: Arc::new(handle), + identity: route_identity(configuration, mode), + enabled: scope, + }) +} + +/// 串行后台事务;失败/取消恢复旧配置,只重建确实更换过的传输。 +pub(crate) fn apply( + generation: u64, + preferences: Preferences, + relay_json: Option, +) -> Result { + let _operation = OPERATIONS.lock().map_err(|_| Failure::Connection)?; + apply_locked(generation, preferences, relay_json) +} + +fn apply_locked( + generation: u64, + mut preferences: Preferences, + relay_json: Option, +) -> Result { + initialize()?; + current(generation)?; + if preferences.enabled && !preferences.lan_enabled && !preferences.relay_enabled { + preferences.enabled = false; + } + if route_enabled(&preferences, Mode::Lan) { + let available = addresses().map_err(|_| Failure::Address)?; + preferences.address = recovery::available_address(preferences.address, &available); + if preferences.address.is_none() { + return Err(Failure::Address); + } + } + let previous = manager().lock().map_err(|_| Failure::Connection)?.configuration.clone(); + let mut configuration = previous.clone(); + configuration.preferences = preferences; + if let Some(relay) = relay_json { + RelayAccess::parse(relay.as_bytes()).map_err(|_| Failure::Invalid)?; + configuration.relay = Some(relay); + } + if route_enabled(&configuration.preferences, Mode::Relay) { + RelayAccess::parse(configuration.relay.as_deref().ok_or(Failure::Invalid)?.as_bytes()) + .map_err(|_| Failure::Invalid)?; + } + let mut replaced = Vec::new(); + let mut written = false; + let result = (|| { + let mut candidates = Vec::new(); + for mode in [Mode::Lan, Mode::Relay] { + if !route_enabled(&configuration.preferences, mode) { + continue; + } + let identity = route_identity(&configuration, mode); + let reuse = { + let mut manager = manager().lock().map_err(|_| Failure::Connection)?; + let active = match mode { + Mode::Lan => &mut manager.lan, + Mode::Relay => &mut manager.relay, + }; + let reuse = active.as_ref().is_some_and(|a| { + a.identity == identity + && !matches!( + *a.handle.status.lock().unwrap_or_else(|e| e.into_inner()), + Status::Failed | Status::Stopped + ) + }); + if !reuse && active.take().is_some() { + replaced.push(mode); + } + reuse + }; + if !reuse { + current(generation)?; + candidates.push((mode, start_route(mode, &mut configuration)?)); + } + } + current(generation)?; + configuration.store()?; + written = true; + let mut manager = manager().lock().map_err(|_| Failure::Connection)?; + current(generation)?; + for (mode, active) in candidates { + active.enabled.store(true, Ordering::Release); + *match mode { + Mode::Lan => &mut manager.lan, + Mode::Relay => &mut manager.relay, + } = Some(active); + } + if !route_enabled(&configuration.preferences, Mode::Lan) { + manager.lan.take(); + } + if !route_enabled(&configuration.preferences, Mode::Relay) { + manager.relay.take(); + } + NOTIFICATIONS.store(configuration.preferences.notifications, Ordering::Release); + manager.configuration = configuration; + Ok(()) + })(); + if let Err(error) = result { + if written { + previous.store()?; + } + let mut previous = previous; + for mode in replaced { + match start_route(mode, &mut previous) { + Ok(active) => { + active.enabled.store(true, Ordering::Release); + let mut manager = manager().lock().map_err(|_| Failure::Connection)?; + *match mode { + Mode::Lan => &mut manager.lan, + Mode::Relay => &mut manager.relay, + } = Some(active); + }, + Err(failure) => log::warn!("mobile route restoration: {failure:?}"), + } + } + return Err(error); + } + snapshot() +} + +pub(crate) fn refresh_invitation(mode: Mode) -> Result { + let _operation = OPERATIONS.lock().map_err(|_| Failure::Connection)?; + let (handle, allow_input) = { + let manager = manager().lock().map_err(|_| Failure::Connection)?; + let active = match mode { + Mode::Lan => &manager.lan, + Mode::Relay => &manager.relay, + } + .as_ref() + .ok_or(Failure::Connection)?; + (active.handle.clone(), manager.configuration.preferences.default_input) + }; + handle.refresh_invitation(allow_input).map_err(classify)?; + snapshot() +} + +pub(crate) fn decide_pairing(id: &str, allow_input: Option) -> Result { + host()? + .lock() + .map_err(|_| Failure::Credentials)? + .decide_pairing(id, allow_input, now()) + .map_err(classify)?; + snapshot() +} + +pub(crate) fn set_permission(id: &str, allow_input: bool) -> Result { + if !host()? + .lock() + .map_err(|_| Failure::Credentials)? + .set_input_permission(id, allow_input, &persist_host()) + .map_err(|_| Failure::Credentials)? + { + return Err(Failure::Invalid); + } + snapshot() +} + +pub(crate) fn revoke_device(id: &str) -> Result { + host()? + .lock() + .map_err(|_| Failure::Credentials)? + .revoke(id, &persist_host()) + .map_err(|_| Failure::Credentials)?; + snapshot() +} + +pub(crate) fn relay_from_ssh(destination: &str) -> Result { + crate::ssh_profiles::validate_ssh_destination(destination).map_err(|_| Failure::Invalid)?; + let bytes = crate::ssh_session::runtime() + .map_err(|_| Failure::Connection)? + .block_on(crate::ssh_session::exec_private( + destination, + "/opt/pebrel-relay/pebrel-relay export-access --directory /etc/pebrel-relay", + std::time::Duration::from_secs(45), + )) + .map_err(|_| Failure::Connection)?; + RelayAccess::parse(&bytes).map_err(|_| Failure::Invalid)?; + String::from_utf8(bytes).map_err(|_| Failure::Invalid) +} + +pub(crate) fn resume_saved() { + recovery::start(); +} + +fn now() -> u64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_secs()) + .unwrap_or(0) +} diff --git a/nebula_app/src/mobile_connection/preferences.rs b/nebula_app/src/mobile_connection/preferences.rs new file mode 100644 index 0000000..9ef5c97 --- /dev/null +++ b/nebula_app/src/mobile_connection/preferences.rs @@ -0,0 +1,69 @@ +use super::*; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(default, deny_unknown_fields)] +pub(crate) struct Preferences { + pub enabled: bool, + pub lan_enabled: bool, + pub relay_enabled: bool, + pub address: Option, + pub port: u16, + pub default_input: bool, + pub notifications: bool, +} + +impl Default for Preferences { + fn default() -> Self { + Self { + enabled: false, + lan_enabled: true, + relay_enabled: false, + address: None, + port: 0, + default_input: false, + notifications: true, + } + } +} + +fn key(name: &str) -> String { + // 独立配置实例和管理员实例不继承另一实例的手机授权。 + let path = crate::display::nebula_data_dir(); + let identity = + format!("{}:{}", path.display(), crate::platform::elevation::requires_isolation()); + let digest = Sha256::digest(identity.as_bytes()); + let scope: String = digest[..12].iter().map(|b| format!("{b:02x}")).collect(); + format!("Pebrel/Mobile/V2/{scope}/{name}") +} + +pub(super) fn load(name: &str) -> Result>, Failure> { + crate::platform::credentials::load(&key(name)).map_err(|_| Failure::Credentials) +} + +pub(super) fn store(name: &str, bytes: &[u8]) -> Result<(), Failure> { + crate::platform::credentials::store(&key(name), bytes).map_err(|_| Failure::Credentials) +} + +/// 凭据由一次系统写入提交,避免「偏好已保存、令牌仍是旧值」的半成功状态。 +#[derive(Clone, Default, Serialize, Deserialize)] +#[serde(default, deny_unknown_fields)] +pub(super) struct Configuration { + pub preferences: Preferences, + pub relay: Option, + pub lan: Option, +} + +impl Configuration { + pub(super) fn load() -> Result { + load("configuration")? + .map(|bytes| serde_json::from_slice(&bytes).map_err(|_| Failure::Credentials)) + .transpose() + .map(Option::unwrap_or_default) + } + + pub(super) fn store(&self) -> Result<(), Failure> { + store("configuration", &serde_json::to_vec(self).map_err(|_| Failure::Invalid)?) + } +} diff --git a/nebula_app/src/mobile_connection/recovery.rs b/nebula_app/src/mobile_connection/recovery.rs new file mode 100644 index 0000000..d8fad72 --- /dev/null +++ b/nebula_app/src/mobile_connection/recovery.rs @@ -0,0 +1,107 @@ +//! 进程级 LAN 恢复:网卡枚举和监听重建留在后台,不依赖设置页是否打开。 + +use super::*; +use std::time::Duration; + +pub(super) fn available_address(saved: Option, available: &[LanAddress]) -> Option { + saved + .filter(|address| available.iter().any(|entry| entry.address == *address)) + .or_else(|| available.first().map(|entry| entry.address)) +} + +fn repair_lan() -> Result<(), Failure> { + // 用户设置事务优先;监测不递增代际,也不拿旧偏好覆盖已经提交的新设置。 + let Ok(_operation) = OPERATIONS.try_lock() else { return Ok(()) }; + let generation = GENERATION.load(Ordering::Acquire); + initialize()?; + let (mut preferences, healthy) = { + let manager = manager().lock().map_err(|_| Failure::Connection)?; + let healthy = manager.lan.as_ref().is_some_and(|active| { + !matches!( + *active.handle.status.lock().unwrap_or_else(|error| error.into_inner()), + Status::Failed | Status::Stopped + ) + }); + (manager.configuration.preferences.clone(), healthy) + }; + if !route_enabled(&preferences, Mode::Lan) { + return Ok(()); + } + let available = addresses().map_err(|_| Failure::Address)?; + let address = available_address(preferences.address, &available).ok_or(Failure::Address)?; + if healthy && preferences.address == Some(address) { + return Ok(()); + } + preferences.address = Some(address); + apply_locked(generation, preferences, None).map(|_| ()) +} + +pub(super) fn start() { + static STARTED: AtomicBool = AtomicBool::new(false); + if STARTED.swap(true, Ordering::AcqRel) { + return; + } + let generation = begin(); + let started = + std::thread::Builder::new().name("pebrel-mobile-recovery".into()).spawn(move || { + let initial = initialize().and_then(|_| { + let preferences = manager() + .lock() + .map_err(|_| Failure::Connection)? + .configuration + .preferences + .clone(); + if preferences.enabled { + apply(generation, preferences, None).map(|_| ()) + } else { + Ok(()) + } + }); + if let Err(error) = initial { + log::warn!("mobile startup: {error:?}"); + } + let mut previous_failure = initial.err(); + loop { + // 只枚举本机网卡,不做探测连接;禁用 LAN 时跳过枚举,不占用 UI 帧预算。 + std::thread::sleep(Duration::from_secs(5)); + let failure = repair_lan().err(); + if failure != previous_failure { + if let Some(error) = failure.filter(|error| *error != Failure::Cancelled) { + log::warn!("mobile LAN recovery: {error:?}"); + } + previous_failure = failure; + } + } + }); + if let Err(error) = started { + STARTED.store(false, Ordering::Release); + log::warn!("mobile recovery thread: {}", error.kind()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn address(value: &str) -> LanAddress { + LanAddress { address: value.parse().unwrap(), name: "fixture".into(), preferred: true } + } + + #[test] + fn preserves_the_selected_interface_while_it_exists() { + let available = [address("192.168.1.7"), address("100.64.0.8")]; + assert_eq!( + available_address(Some(available[1].address), &available), + Some(available[1].address) + ); + } + + #[test] + fn an_absent_interface_uses_the_current_order_without_inventing_an_address() { + let saved = Some("192.168.0.7".parse().unwrap()); + let available = [address("192.168.1.7")]; + assert_eq!(available_address(saved, &available), Some(available[0].address)); + assert_eq!(available_address(saved, &[]), None); + assert_eq!(available_address(None, &available), Some(available[0].address)); + } +} diff --git a/nebula_app/src/platform/distribution.rs b/nebula_app/src/platform/distribution.rs new file mode 100644 index 0000000..192a5c7 --- /dev/null +++ b/nebula_app/src/platform/distribution.rs @@ -0,0 +1,192 @@ +//! 安装渠道只控制应用自更新,不改变用户设置、插件或终端热路径。 +use std::sync::OnceLock; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum Distribution { + Direct, + Scoop, + Msix, + Unrecognized, +} + +impl Distribution { + pub(crate) fn externally_managed(self) -> bool { + self != Self::Direct + } + + pub(crate) fn label(self) -> &'static str { + match self { + Self::Direct => "Stable", + Self::Scoop => "Scoop", + Self::Msix => "MSIX", + Self::Unrecognized => "—", + } + } + + pub(crate) fn message(self) -> Option { + use crate::i18n::Message; + match self { + Self::Direct => None, + Self::Scoop => Some(Message::UpdateManagedScoop), + Self::Msix => Some(Message::UpdateManagedMsix), + Self::Unrecognized => Some(Message::UpdateManagedUnknown), + } + } +} + +/// 在 GUI 启动锁检查时初始化;随后设置页仅访问这一个不可变值。 +pub(crate) fn current() -> Distribution { + static CHANNEL: OnceLock = OnceLock::new(); + *CHANNEL.get_or_init(detect) +} + +pub(crate) fn require_direct_update() -> Result<(), String> { + match current() { + Distribution::Direct => Ok(()), + Distribution::Scoop => Err("Update this installation with: scoop update pebrel".into()), + Distribution::Msix => Err( + "Update this MSIX package through its distribution source (Microsoft Store for Store installations).".into(), + ), + Distribution::Unrecognized => { + Err("Installation ownership could not be established; use its original distribution source.".into()) + }, + } +} + +fn detect() -> Distribution { + #[cfg(windows)] + { + use windows_sys::Win32::Foundation::{ + APPMODEL_ERROR_NO_PACKAGE, ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS, + }; + use windows_sys::Win32::Storage::Packaging::Appx::GetCurrentPackageFullName; + let mut length = 0; + // 只查询长度即可确认真实包身份,不按 WindowsApps 路径猜测,也不分配名称缓冲。 + // SAFETY: length 指向可写 UINT32;零长度查询允许空输出指针。 + let status = unsafe { GetCurrentPackageFullName(&mut length, std::ptr::null_mut()) }; + match status { + ERROR_INSUFFICIENT_BUFFER | ERROR_SUCCESS => return Distribution::Msix, + APPMODEL_ERROR_NO_PACKAGE => {}, + error => { + log::warn!("Package identity query failed: {error}"); + return Distribution::Unrecognized; + }, + } + return std::env::current_exe() + .and_then(std::fs::canonicalize) + .map(|executable| from_marker(&executable)) + .unwrap_or(Distribution::Unrecognized); + } + #[cfg(not(windows))] + Distribution::Direct +} + +#[cfg(any(windows, test))] +fn from_marker(executable: &std::path::Path) -> Distribution { + use std::io::Read as _; + let Some(directory) = executable.parent() else { return Distribution::Unrecognized }; + let marker = directory.join("pebrel-distribution"); + let file = match std::fs::File::open(marker) { + Ok(file) => file, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Distribution::Direct, + Err(_) => return Distribution::Unrecognized, + }; + let mut bytes = Vec::with_capacity(33); + if file.take(33).read_to_end(&mut bytes).is_err() { + return Distribution::Unrecognized; + } + parse_marker(&bytes) +} + +#[cfg(any(windows, test))] +fn parse_marker(bytes: &[u8]) -> Distribution { + if bytes.len() <= 32 && bytes.trim_ascii() == b"scoop" { + Distribution::Scoop + } else { + // 未知/损坏标记不得悄悄切回普通安装器;标记只收窄更新能力。 + Distribution::Unrecognized + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn only_explicit_scoop_marker_is_recognized() { + for value in [b"scoop".as_slice(), b"scoop\r\n", b" scoop "] { + assert_eq!(parse_marker(value), Distribution::Scoop); + } + for value in [b"".as_slice(), b"installer", b"msix", b"Scoop", &[b' '; 33]] { + assert_eq!(parse_marker(value), Distribution::Unrecognized); + } + } + + #[test] + fn ownership_is_local_to_installation_not_directory_name() { + let root = tempfile::tempdir().unwrap(); + let executable = root.path().join("pebrel.exe"); + assert_eq!(from_marker(&executable), Distribution::Direct); + std::fs::write(root.path().join("pebrel-distribution"), b"scoop\n").unwrap(); + assert_eq!(from_marker(&executable), Distribution::Scoop); + std::fs::write(root.path().join("pebrel-distribution"), b"other").unwrap(); + assert_eq!(from_marker(&executable), Distribution::Unrecognized); + } + + #[test] + fn external_channels_never_own_setup_updates() { + assert!(!Distribution::Direct.externally_managed()); + assert!(Distribution::Direct.message().is_none()); + for channel in [Distribution::Scoop, Distribution::Msix, Distribution::Unrecognized] { + assert!(channel.externally_managed()); + assert!(channel.message().is_some()); + } + } + + #[cfg(windows)] + #[test] + fn managed_process_stops_before_update_io() { + const PROBE: &str = "PEBREL_DISTRIBUTION_TEST_CHILD"; + if let Some(marker) = std::env::var_os(PROBE) { + let expected = + if marker == "scoop" { Distribution::Scoop } else { Distribution::Unrecognized }; + assert_eq!(current(), expected); + assert!(require_direct_update().is_err()); + assert!(crate::update_check::check_now().is_err()); + let asset = crate::update_check::UpdateAsset { + version: "0.0.0".into(), + name: String::new(), + download_url: String::new(), + size: None, + sha256: None, + }; + assert!(crate::update_download::begin(&asset).is_err()); + assert!(crate::update_download::handoff::prepare(&asset).is_err()); + assert!(crate::update_download::handoff::schedule(&asset).is_err()); + assert!(!crate::update_download::handoff::apply_scheduled()); + assert!(!crate::update_download::handoff::installation_in_progress().unwrap()); + crate::update_download::hydrate(); + assert!(crate::update_download::cached_asset().is_none()); + let executable = std::env::current_exe().unwrap(); + assert!(!executable.parent().unwrap().join(".pebrel-update.nebula-lock").exists()); + return; + } + let executable = std::env::current_exe().unwrap(); + for marker in ["scoop", "unknown"] { + // 同卷硬链接避免复制大型测试二进制;子进程拥有独立 OnceLock 和更新状态。 + let root = tempfile::tempdir_in(executable.parent().unwrap()).unwrap(); + let child = root.path().join("distribution-test.exe"); + std::fs::hard_link(&executable, &child).unwrap(); + std::fs::write(root.path().join("pebrel-distribution"), marker).unwrap(); + let output = std::process::Command::new(child) + .args([ + "--exact", + "platform::distribution::tests::managed_process_stops_before_update_io", + ]) + .env(PROBE, marker) + .output() + .unwrap(); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stdout)); + } + } +} diff --git a/nebula_app/src/platform/mod.rs b/nebula_app/src/platform/mod.rs index 4b832d6..84f993d 100644 --- a/nebula_app/src/platform/mod.rs +++ b/nebula_app/src/platform/mod.rs @@ -15,6 +15,7 @@ pub(crate) mod ai_session_identity; pub mod capabilities; pub mod credentials; pub mod dirs; +pub(crate) mod distribution; pub(crate) mod elevation; pub(crate) mod environment; pub(crate) mod file_drag; diff --git a/nebula_app/src/platform/process.rs b/nebula_app/src/platform/process.rs index 5e94f07..f36624a 100644 --- a/nebula_app/src/platform/process.rs +++ b/nebula_app/src/platform/process.rs @@ -1,4 +1,4 @@ -//! 从本进程启动控制台子进程时的统一抑制入口。 +//! 非交互子进程的平台适配:抑制控制台窗口,隔离并回收一次性进程树。 //! //! Pebrel 是 `windows_subsystem = "windows"` 的 GUI 进程(见 `main.rs`), //! **自己没有控制台可以给子进程继承**;`cargo test --bin pebrel` 的测试二进制 @@ -19,7 +19,8 @@ //! 靠继承父控制台工作,见 `ssh::run`);同理,`notepad` / `explorer` / `open` //! 那几处是**故意**要给用户看见窗口的。 -use std::process::Command; +use std::io; +use std::process::{Child, Command}; /// `CREATE_NO_WINDOW` 的**唯一定义处**。 /// @@ -52,6 +53,131 @@ pub(crate) fn hidden_command_with(command: &mut Command, extra_flags: u32) -> &m command } +#[cfg(unix)] +pub(crate) fn configure_process_group(command: &mut Command) { + use std::os::unix::process::CommandExt as _; + command.process_group(0); +} + +/// Windows:`pane.exec` 的子进程绝不允许弹出控制台窗口。 +/// +/// Pebrel 自己是 `windows_subsystem = "windows"` 的 GUI 进程(见 `main.rs`), +/// **没有控制台**可给子进程继承;不抑制的话 Windows 会给每条 exec 命令分配一个 +/// 新控制台,而默认终端应用会托管新控制台的机器上那就是**弹一整扇窗口** +/// (同 [`crate::ssh_session`] 里 `ssh.exe -G` 那条注释说的现象)。 +/// +/// exec 的 stdin 是 null、stdout/stderr 走管道,从头到尾没有交互,也就不需要 +/// 控制台——和 wsl/git 那些 spawn 用 `CREATE_NO_WINDOW` 是同一条规矩。 +#[cfg(windows)] +pub(crate) fn configure_process_group(command: &mut Command) { + crate::platform::process::hidden_command(command); +} + +#[cfg(not(any(unix, windows)))] +pub(crate) fn configure_process_group(_: &mut Command) {} + +pub(crate) struct ProcessGroup { + #[cfg(windows)] + job: windows_sys::Win32::Foundation::HANDLE, + #[cfg(unix)] + process_group: i32, +} + +impl ProcessGroup { + pub(crate) fn attach(child: &Child) -> io::Result { + #[cfg(windows)] + { + use std::mem::{size_of, zeroed}; + use std::os::windows::io::AsRawHandle as _; + use std::ptr; + use windows_sys::Win32::Foundation::{CloseHandle, HANDLE}; + use windows_sys::Win32::System::JobObjects::{ + AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, + JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation, + SetInformationJobObject, + }; + + // SAFETY: all pointers reference initialized POD values for the duration of each + // call. The returned job handle is owned by ProcessGroup and closed exactly once. + unsafe { + let job = CreateJobObjectW(ptr::null(), ptr::null()); + if job.is_null() { + return Err(io::Error::last_os_error()); + } + let mut limits: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = zeroed(); + limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + if SetInformationJobObject( + job, + JobObjectExtendedLimitInformation, + (&raw const limits).cast(), + size_of::() as u32, + ) == 0 + { + let error = io::Error::last_os_error(); + CloseHandle(job); + return Err(error); + } + if AssignProcessToJobObject(job, child.as_raw_handle() as HANDLE) == 0 { + let error = io::Error::last_os_error(); + CloseHandle(job); + return Err(error); + } + return Ok(Self { job }); + } + } + #[cfg(unix)] + { + Ok(Self { process_group: child.id() as i32 }) + } + #[cfg(not(any(unix, windows)))] + { + let _ = child; + Ok(Self {}) + } + } + + pub(crate) fn terminate(&self, child: &mut Child) { + #[cfg(windows)] + unsafe { + use windows_sys::Win32::System::JobObjects::TerminateJobObject; + let _ = TerminateJobObject(self.job, 1); + } + #[cfg(unix)] + unsafe { + let _ = libc::kill(-self.process_group, libc::SIGKILL); + } + let _ = child.kill(); + } + + pub(crate) fn finish(self) { + #[cfg(unix)] + unsafe { + let _ = libc::kill(-self.process_group, libc::SIGKILL); + } + // Windows uses JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE in Drop. + } +} + +impl Drop for ProcessGroup { + fn drop(&mut self) { + #[cfg(windows)] + { + // SAFETY: `job` is the live handle created and exclusively owned by this guard. + unsafe { + let _ = windows_sys::Win32::Foundation::CloseHandle(self.job); + } + } + #[cfg(unix)] + { + // A reader-thread creation failure must not leave the child tree + // alive with one inherited pipe still open. + unsafe { + let _ = libc::kill(-self.process_group, libc::SIGKILL); + } + } + } +} + #[cfg(all(test, windows))] mod tests { use super::*; diff --git a/nebula_app/src/plugins/AGENTS.md b/nebula_app/src/plugins/AGENTS.md new file mode 100644 index 0000000..26c7f4c --- /dev/null +++ b/nebula_app/src/plugins/AGENTS.md @@ -0,0 +1,7 @@ +# Plugin command foundation + +- 本模块目前是显式 CLI 命令的冷路径,不是常驻插件宿主;GUI 启动、绘制和输入不得调用同步执行器。 +- 清单是元数据权威;原生命令分支不读取 Lua 源码、不创建插件 VM。保持既有配置 Lua 独立。 +- Lua 只交出可验证的普通数据与宿主请求;VM 配额之外还需约束 JSON 展开、包文件与协议帧。 +- 不增加新语言运行时、后台扫描、全局缓存或空的生命周期框架;新增持续资源时再明确 owner、取消与代次。 +- 当前事实和命令示例见 README;设计依据见 [命令地基](../../../architecture/notes/nebula_app/plugins/2026-09-28-bounded-command-foundation.md)。 diff --git a/nebula_app/src/plugins/README.md b/nebula_app/src/plugins/README.md new file mode 100644 index 0000000..d54d4a1 --- /dev/null +++ b/nebula_app/src/plugins/README.md @@ -0,0 +1,84 @@ +# Plugin command foundation + +This module implements an explicit, command-scoped plugin entry. It does not install packages, +keep plugins resident, register UI panels, or start a plugin worker during normal GUI startup. +The product adds no dependency or new language runtime for this entry. + +## Public entry + +From the repository root, using a newly built `pebrel` binary: + +```text +pebrel plugin check nebula_app/examples/plugins/native-overview --pretty +pebrel plugin run nebula_app/examples/plugins/native-overview describe --pretty +pebrel plugin check nebula_app/examples/plugins/lua-overview --pretty +pebrel plugin run nebula_app/examples/plugins/lua-overview summary --pretty +``` + +`check` validates the UTF-8 TOML manifest, contribution rules, declared entry path and source-size +metadata. It does not read, compile or execute Lua. Its result explicitly identifies this scope. +`run` invokes exactly one declared command. `--args` accepts a JSON object overriding that +command's default `params`; `--timeout-ms` accepts 1 through 30000, defaulting to 3000. +Both commands output JSON and return a nonzero exit code on failure. + +The example commands query `runtime.describe`, so execution requires a running Pebrel instance. +The check command is offline. The examples do not create panes or modify settings. + +## Package and invocation contracts + +- `plugin.toml` owns `manifest_version`, `api_version`, `id`, `name`, `version`, optional `entry`, + `permissions`, and `commands`. Both numeric versions are currently 1; `version` is bounded + package metadata, not a dependency-range expression. Unknown fields are rejected. +- Each command declares `id`, `title`, optional object `params`, and exactly one `method` or + `handler`. A `method` directly invokes a declared Runtime method in Rust; a `handler` names + a function in the table returned by `entry`. An entry exists exactly when Lua handlers exist. +- Native commands never construct a plugin Lua VM, including native commands in a mixed package. + Only a selected Lua command reads its entry and creates a VM. The VM is dropped when that + invocation returns, including errors. There is no shared module or VM cache. +- Entry paths are portable package-relative paths. Traversal, alternate roots and package-internal + links/reparse points are rejected. The root itself is the directory explicitly selected by the user. + These filesystem checks are not an OS sandbox against concurrent same-user directory replacement. +- The Lua entry is a single UTF-8 text file returning a command-function table. A handler receives + `(ctx, args)` and returns one JSON-compatible value. Arbitrary module loading is not part of this + initial entry; the existing configuration Lua module loader is unchanged. +- `ctx.runtime.call(method, params)` yields to the Rust driver. The manifest grants exact method + names; `events.subscribe` is excluded because this entry handles one-shot replies. Runtime owns + semantic argument validation, state and target identity. The discovery token remains in Rust. +- `ctx.null` represents JSON null. `ctx.array()` marks an empty array; nonempty arrays must have + dense integer keys. Mixed, sparse, non-UTF-8 and unsupported values fail rather than lose data. +- Timeouts and errors do not roll back previously completed external actions. No request is + automatically retried. Transport errors after submitting input retain the existing uncertainty + description from the Runtime client. Unhandled Runtime errors retain their code and details, + including partial-operation and deferred-cleanup receipts, across the Lua boundary. + +## Resource and execution budgets + +| Boundary | Current limit | +| --- | --- | +| Manifest | 32 KiB, 64 commands, 32 permissions | +| Lua source | 256 KiB | +| Lua allocator | 8 MiB per invocation | +| Lua instruction sampling | Yield every 10000 instructions; stop at 1000000 accounted instructions | +| Runtime calls | 16 per Lua invocation | +| Structured data | Depth 32, 4096 visitation/allocation units, 64 KiB cumulative key/string bytes | +| CLI argument JSON | 64 KiB before parsing | +| Runtime wire | 128 KiB request; 256 KiB complete response line | + +The Lua memory limit is not a process RSS limit. File buffers, validated Rust values, interpreter +stack and existing application resources have separate costs. Conversion reserves output container +slots before allocation so nested/shared tables do not expand outside the structured-data budget. + +Entry initialization and handlers both run as managed coroutines. The Rust driver regains control +through instruction yields, including loops wrapped in `pcall`. Filesystem loading, raw OS access, +unmanaged coroutines, dynamic Lua loading and direct output are not exposed to plugin code. +Instruction hooks do not preempt an arbitrary C operation; host I/O is outside Lua execution and +the one-shot Runtime transport applies a cumulative deadline to its connect/write/read loop. + +This CLI driver performs that yielded Runtime request synchronously on the command process. +It is not an asynchronous worker adapter and must not be called from a GUI callback or a shared +resident worker. A persistent/event-driven host is a separate next slice, not implied by this CLI. + +`execution.lua_memory_bytes` reports the VM's current allocator usage before it is dropped; +`instruction_ticks` is sampled VM work, not CPU cycles, peak memory or total process allocation. +The native branch reports zero Lua usage. Focused regressions live in `tests.rs`, with transport +and command-line integration assertions beside their existing owners. diff --git a/nebula_app/src/plugins/cli.rs b/nebula_app/src/plugins/cli.rs new file mode 100644 index 0000000..bc54fbb --- /dev/null +++ b/nebula_app/src/plugins/cli.rs @@ -0,0 +1,105 @@ +use std::io::Write; +use std::path::PathBuf; +use std::time::Duration; + +use clap::{Args, Subcommand}; +use serde_json::{Value, json}; + +use super::{Error, MAX_JSON_BYTES, Package}; + +#[derive(Args, Debug)] +pub(crate) struct Options { + #[clap(subcommand)] + pub command: Command, + /// Pretty-print the JSON result. + #[clap(long, global = true)] + pub pretty: bool, +} + +#[derive(Subcommand, Debug)] +pub(crate) enum Command { + /// Validate package metadata and entry paths without executing Lua. + Check { path: PathBuf }, + /// Invoke one declared native or Lua command; no resident plugin is started. + Run { + path: PathBuf, + command: String, + /// JSON object overriding this command's default parameters. + #[clap(long, default_value = "{}")] + args: String, + #[clap(long, default_value_t = 3000, value_parser = clap::value_parser!(u64).range(1..=30_000))] + timeout_ms: u64, + }, +} + +pub(crate) fn run(options: Options) -> i32 { + let result = execute(&options.command); + let success = result.is_ok(); + let output = match result { + Ok(value) => json!({ "ok": true, "result": value }), + Err(error) => json!({ "ok": false, "error": error }), + }; + let mut stdout = std::io::stdout().lock(); + let written = if options.pretty { + serde_json::to_writer_pretty(&mut stdout, &output) + } else { + serde_json::to_writer(&mut stdout, &output) + }; + if let Err(error) = written { + eprintln!("Plugin output failed: {error}"); + return 1; + } + if stdout.write_all(b"\n").and_then(|()| stdout.flush()).is_err() { + return 1; + } + i32::from(!success) +} + +fn execute(command: &Command) -> super::Result { + let path = match command { + Command::Check { path } | Command::Run { path, .. } => path, + }; + let package = Package::open(path)?; + match command { + Command::Check { .. } => Ok(json!({ + "manifest": package.manifest, + "lua_executed": false, + "validation": "manifest_and_entry_paths", + })), + Command::Run { command, args, timeout_ms, .. } => { + if args.len() > MAX_JSON_BYTES { + return Err(Error::new("arguments_too_large", "arguments exceed 64 KiB")); + } + let args = serde_json::from_str(args) + .map_err(|error| Error::new("invalid_arguments", error))?; + let execution = super::execute( + &package, + command, + args, + Duration::from_millis(*timeout_ms), + &mut |method, params, timeout| { + let response = + crate::runtime_api::request_once_bounded(method, params, timeout) + .map_err(runtime_error)?; + if !response.ok { + let error = response.error.ok_or_else(|| { + Error::new("invalid_runtime_response", "missing error") + })?; + return Err(runtime_error(error)); + } + response + .result + .ok_or_else(|| Error::new("invalid_runtime_response", "missing result")) + }, + )?; + Ok(json!({ "plugin": package.manifest.id, "command": command, "execution": execution })) + }, + } +} + +fn runtime_error(error: crate::runtime_api::ApiError) -> Error { + let mut result = Error::new(error.code, error.message); + // 部分完成与清理回执属于 Runtime 的结果,插件适配层不得将它们折叠成字符串。 + result.details = error.details; + result +} diff --git a/nebula_app/src/plugins/json.rs b/nebula_app/src/plugins/json.rs new file mode 100644 index 0000000..53aadfc --- /dev/null +++ b/nebula_app/src/plugins/json.rs @@ -0,0 +1,142 @@ +//! Lua 与宿主间的数据必须同时限制深度、节点和文本;仅限制 VM 内存会漏掉展开复制。 + +use mlua::{LuaSerdeExt, Value as LuaValue}; +use serde_json::{Map, Number, Value}; + +use super::{Error, MAX_JSON_BYTES, MAX_JSON_DEPTH, MAX_JSON_NODES, Result}; + +#[derive(Default)] +struct Budget { + nodes: usize, + bytes: usize, + slots: usize, +} + +impl Budget { + fn reserve_slots(&mut self, count: usize) -> Result<()> { + self.slots = self.slots.saturating_add(count); + if self.slots > MAX_JSON_NODES { + return Err(Error::new( + "payload_limit", + "payload container allocation budget exhausted", + )); + } + Ok(()) + } + + fn visit(&mut self, depth: usize, bytes: usize) -> Result<()> { + self.nodes += 1; + self.bytes = self.bytes.saturating_add(bytes); + if depth > MAX_JSON_DEPTH || self.nodes > MAX_JSON_NODES || self.bytes > MAX_JSON_BYTES { + return Err(Error::new( + "payload_limit", + "payload exceeds its depth, node, or text budget", + )); + } + Ok(()) + } +} + +pub(super) fn validate(value: &Value) -> Result<()> { + fn walk(value: &Value, depth: usize, budget: &mut Budget) -> Result<()> { + budget.visit(depth, 0)?; + match value { + Value::String(value) => budget.visit(depth, value.len())?, + Value::Array(values) => { + for value in values { + walk(value, depth + 1, budget)?; + } + }, + Value::Object(values) => { + for (key, value) in values { + budget.visit(depth, key.len())?; + walk(value, depth + 1, budget)?; + } + }, + _ => {}, + } + Ok(()) + } + walk(value, 0, &mut Budget::default()) +} + +pub(super) fn from_lua(lua: &mlua::Lua, value: LuaValue) -> Result { + fn walk( + value: LuaValue, + depth: usize, + budget: &mut Budget, + array_marker: *const std::ffi::c_void, + ) -> Result { + budget.visit(depth, 0)?; + Ok(match value { + LuaValue::Nil => Value::Null, + LuaValue::LightUserData(value) if value.0.is_null() => Value::Null, + LuaValue::Boolean(value) => Value::Bool(value), + LuaValue::Integer(value) => Value::Number(value.into()), + LuaValue::Number(value) => Value::Number(Number::from_f64(value).ok_or_else(|| { + Error::new("invalid_payload", "non-finite numbers are not JSON values") + })?), + LuaValue::String(value) => { + let text = value.to_str().map_err(|error| Error::new("invalid_utf8", error))?; + budget.visit(depth, text.len())?; + Value::String(text.to_string()) + }, + LuaValue::Table(table) => { + let length = table.raw_len(); + if length > MAX_JSON_NODES { + return Err(Error::new( + "payload_limit", + "array length exceeds the node budget", + )); + } + let array = length > 0 + || table.metatable().is_some_and(|value| value.to_pointer() == array_marker); + if array { + // 先预留整棵输出树的槽位预算,再分配,避免深层数组逐层按上限扩容。 + budget.reserve_slots(length)?; + } + let mut object = Map::new(); + let mut values = if array { vec![Value::Null; length] } else { Vec::new() }; + let mut count = 0; + for pair in table.pairs::() { + let (key, value) = + pair.map_err(|error| Error::new("invalid_payload", error))?; + count += 1; + match (array, key) { + (true, LuaValue::Integer(index)) + if index >= 1 && (index as usize) <= length => + { + values[index as usize - 1] = + walk(value, depth + 1, budget, array_marker)?; + }, + (false, LuaValue::String(key)) => { + budget.reserve_slots(1)?; + let key = + key.to_str().map_err(|error| Error::new("invalid_utf8", error))?; + budget.visit(depth, key.len())?; + let value = walk(value, depth + 1, budget, array_marker)?; + object.insert(key.to_string(), value); + }, + _ => { + return Err(Error::new( + "invalid_payload", + "tables must be string-keyed objects or dense arrays", + )); + }, + } + } + if array && count != length { + return Err(Error::new("invalid_payload", "sparse arrays are not supported")); + } + if array { Value::Array(values) } else { Value::Object(object) } + }, + _ => { + return Err(Error::new( + "invalid_payload", + "functions, threads, and userdata are not JSON values", + )); + }, + }) + } + walk(value, 0, &mut Budget::default(), lua.array_metatable().to_pointer()) +} diff --git a/nebula_app/src/plugins/lua.rs b/nebula_app/src/plugins/lua.rs new file mode 100644 index 0000000..ecf8b0e --- /dev/null +++ b/nebula_app/src/plugins/lua.rs @@ -0,0 +1,222 @@ +//! 托管协程把宿主调用让出到 Rust;读取 Runtime 时不持有 Lua 的执行栈。 + +use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::{Duration, Instant}; + +use mlua::{ + ChunkMode, Function, HookTriggers, Lua, LuaOptions, LuaSerdeExt, MultiValue, StdLib, Table, + ThreadStatus, Value as LuaValue, VmState, +}; +use serde_json::Value; + +use super::{ + Error, Execution, INSTRUCTION_QUANTUM, MAX_INSTRUCTIONS, MAX_LUA_MEMORY, MAX_RUNTIME_CALLS, + Package, Result, RuntimeCall, json, +}; + +const REQUEST: &str = "pebrel.runtime.request"; + +#[cfg(test)] +thread_local! { + static VM_CREATIONS: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + +#[cfg(test)] +pub(super) fn vm_creation_count() -> usize { + VM_CREATIONS.with(std::cell::Cell::get) +} + +struct Budget { + deadline: Instant, + ticks: Arc, + calls: usize, +} + +impl Budget { + fn remaining(&self) -> Result { + if self.ticks.load(Ordering::Relaxed) >= MAX_INSTRUCTIONS { + return Err(Error::new("lua_instruction_limit", "Lua instruction budget exhausted")); + } + self.deadline + .checked_duration_since(Instant::now()) + .filter(|remaining| !remaining.is_zero()) + .ok_or_else(|| Error::new("plugin_timeout", "plugin execution deadline reached")) + } +} + +pub(super) fn execute( + package: &Package, + handler: &str, + arguments: Value, + timeout: Duration, + call: &mut RuntimeCall<'_>, +) -> Result { + let mut budget = Budget { + deadline: Instant::now() + timeout, + ticks: Arc::new(AtomicUsize::new(0)), + calls: 0, + }; + let source = package.source()?; + budget.remaining()?; + #[cfg(test)] + VM_CREATIONS.with(|count| count.set(count.get() + 1)); + let lua = Lua::new_with( + StdLib::TABLE | StdLib::STRING | StdLib::MATH | StdLib::UTF8 | StdLib::COROUTINE, + LuaOptions::default(), + ) + .map_err(lua_error)?; + lua.set_memory_limit(MAX_LUA_MEMORY).map_err(lua_error)?; + let request: Function = lua + .load( + r#" + local yield, raise = coroutine.yield, error + return function(method, params) + local ok, result = yield('pebrel.runtime.request', method, params or {}) + if not ok then raise(result, 0) end + return result + end + "#, + ) + .eval() + .map_err(lua_error)?; + let globals = lua.globals(); + // 不留绕过宿主的文件入口,也不让非托管 coroutine 绕过指令分片。 + for name in + ["coroutine", "dofile", "loadfile", "load", "require", "print", "warn", "collectgarbage"] + { + globals.raw_set(name, LuaValue::Nil).map_err(lua_error)?; + } + let entry = lua + .load(source.strip_prefix('\u{feff}').unwrap_or(&source)) + .set_mode(ChunkMode::Text) + .set_name(package.manifest.entry.as_deref().unwrap_or("plugin")) + .into_function() + .map_err(lua_error)?; + let exports = drive(&lua, entry, MultiValue::new(), package, &mut budget, call)?; + let LuaValue::Table(exports) = exports else { + return Err(Error::new( + "invalid_lua_entry", + "entry must return a table of command functions", + )); + }; + // raw_get 避免未托管的 __index 在查找 handler 时执行插件代码。 + let function: Function = exports.raw_get(handler).map_err(lua_error)?; + let runtime = lua.create_table().map_err(lua_error)?; + runtime.raw_set("call", request).map_err(lua_error)?; + let context = lua.create_table().map_err(lua_error)?; + context.raw_set("runtime", runtime).map_err(lua_error)?; + context.raw_set("null", lua.null()).map_err(lua_error)?; + context + .raw_set( + "array", + lua.create_function(|lua, values: Option
    | { + let values = match values { + Some(values) => values, + None => lua.create_table()?, + }; + values.set_metatable(Some(lua.array_metatable()))?; + Ok(values) + }) + .map_err(lua_error)?, + ) + .map_err(lua_error)?; + let arguments = lua.to_value(&arguments).map_err(lua_error)?; + let args = MultiValue::from_vec(vec![LuaValue::Table(context), arguments]); + let result = drive(&lua, function, args, package, &mut budget, call)?; + let result = json::from_lua(&lua, result)?; + Ok(Execution { + result, + engine: "lua", + lua_memory_bytes: lua.used_memory(), + instruction_ticks: budget.ticks.load(Ordering::Relaxed), + }) + // 命令作用域结束即释放 VM;这里没有全局缓存或常驻插件对象。 +} + +fn drive( + lua: &Lua, + function: Function, + mut args: MultiValue, + package: &Package, + budget: &mut Budget, + call: &mut RuntimeCall<'_>, +) -> Result { + let thread = lua.create_thread(function).map_err(lua_error)?; + let ticks = budget.ticks.clone(); + thread + .set_hook( + HookTriggers::new().every_nth_instruction(INSTRUCTION_QUANTUM as u32), + move |_, _| { + ticks.fetch_add(INSTRUCTION_QUANTUM, Ordering::Relaxed); + // 使用 yield 而非可被 pcall 吞掉的异常,让预算裁定回到 Rust。 + Ok(VmState::Yield) + }, + ) + .map_err(lua_error)?; + loop { + budget.remaining()?; + let values: MultiValue = thread.resume(args).map_err(lua_error)?; + budget.remaining()?; + if thread.status() != ThreadStatus::Resumable { + if values.len() > 1 { + return Err(Error::new( + "invalid_lua_result", + "commands return one JSON-compatible value", + )); + } + return Ok(values.into_iter().next().unwrap_or(LuaValue::Nil)); + } + args = MultiValue::new(); + if values.is_empty() { + continue; + } + let mut values = values.into_iter(); + let valid_tag = matches!(values.next(), Some(LuaValue::String(tag)) if tag.to_str().is_ok_and(|tag| tag == REQUEST)); + let Some(LuaValue::String(method)) = values.next() else { + return Err(Error::new("invalid_runtime_request", "runtime method must be a string")); + }; + let method = method.to_str().map_err(lua_error)?; + let params = + values.next().ok_or_else(|| Error::new("invalid_runtime_request", "missing params"))?; + if !valid_tag || values.next().is_some() { + return Err(Error::new("invalid_runtime_request", "unexpected coroutine yield")); + } + package.allow_method(&method)?; + budget.calls += 1; + if budget.calls > MAX_RUNTIME_CALLS { + return Err(Error::new( + "runtime_call_limit", + "at most 16 runtime calls per invocation", + )); + } + let params = json::from_lua(lua, params)?; + if !params.is_object() { + return Err(Error::new("invalid_runtime_request", "runtime params must be an object")); + } + let result = call(&method, params, budget.remaining()?); + budget.remaining()?; + args = match result { + Ok(value) => { + json::validate(&value)?; + MultiValue::from_vec(vec![ + LuaValue::Boolean(true), + lua.to_value(&value).map_err(lua_error)?, + ]) + }, + Err(error) => MultiValue::from_vec(vec![ + LuaValue::Boolean(false), + LuaValue::Error(Box::new(mlua::Error::external(error))), + ]), + }; + } +} + +fn lua_error(error: mlua::Error) -> Error { + if let Some(original) = error.chain().find_map(|source| source.downcast_ref::()) { + return original.clone(); + } + let code = + if matches!(error, mlua::Error::MemoryError(_)) { "lua_memory_limit" } else { "lua_error" }; + Error::new(code, error) +} diff --git a/nebula_app/src/plugins/mod.rs b/nebula_app/src/plugins/mod.rs new file mode 100644 index 0000000..9d73c16 --- /dev/null +++ b/nebula_app/src/plugins/mod.rs @@ -0,0 +1,109 @@ +//! 插件命令的冷路径入口;普通 GUI 启动不构造插件实例、VM 或工作线程。 + +pub(crate) mod cli; +mod json; +mod lua; +mod package; + +use std::fmt; +use std::time::Duration; + +use serde::Serialize; +use serde_json::Value; + +use package::Package; + +const MAX_JSON_BYTES: usize = 64 * 1024; +const MAX_JSON_NODES: usize = 4096; +const MAX_JSON_DEPTH: usize = 32; +const MAX_LUA_MEMORY: usize = 8 * 1024 * 1024; +const MAX_INSTRUCTIONS: usize = 1_000_000; +const INSTRUCTION_QUANTUM: usize = 10_000; +const MAX_RUNTIME_CALLS: usize = 16; + +#[derive(Clone, Debug, Serialize)] +struct Error { + pub code: String, + pub message: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub details: Option, +} + +impl Error { + pub(super) fn new(code: impl Into, message: impl fmt::Display) -> Self { + Self { + code: code.into(), + message: message.to_string().chars().take(2048).collect(), + details: None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(output, "{}: {}", self.code, self.message) + } +} + +impl std::error::Error for Error {} + +type Result = std::result::Result; +type RuntimeCall<'a> = dyn FnMut(&str, Value, Duration) -> Result + 'a; + +#[derive(Debug, Serialize)] +struct Execution { + pub result: Value, + pub engine: &'static str, + /// 只报告本次 Lua VM 的当前分配量,不将其冒充整个进程的 RSS 或峰值。 + pub lua_memory_bytes: usize, + pub instruction_ticks: usize, +} + +fn execute( + package: &Package, + command_id: &str, + arguments: Value, + timeout: Duration, + call: &mut RuntimeCall<'_>, +) -> Result { + if timeout.is_zero() || timeout > Duration::from_secs(30) { + return Err(Error::new("invalid_timeout", "timeout must be within 1..=30000 ms")); + } + let command = package + .manifest + .commands + .iter() + .find(|command| command.id == command_id) + .ok_or_else(|| Error::new("command_not_found", command_id))?; + let mut parameters = command.params.as_object().cloned().expect("validated command params"); + let Value::Object(arguments) = arguments else { + return Err(Error::new("invalid_arguments", "command arguments must be an object")); + }; + parameters.extend(arguments); + let parameters = Value::Object(parameters); + json::validate(¶meters)?; + + if let Some(method) = &command.method { + package.allow_method(method)?; + let result = call(method, parameters, timeout)?; + json::validate(&result)?; + // 资源命令在此返回,路径上没有 Lua::new、脚本读取或调度器初始化。 + return Ok(Execution { + result, + engine: "native", + lua_memory_bytes: 0, + instruction_ticks: 0, + }); + } + + lua::execute( + package, + command.handler.as_deref().expect("validated Lua command"), + parameters, + timeout, + call, + ) +} + +#[cfg(test)] +mod tests; diff --git a/nebula_app/src/plugins/package.rs b/nebula_app/src/plugins/package.rs new file mode 100644 index 0000000..382b7db --- /dev/null +++ b/nebula_app/src/plugins/package.rs @@ -0,0 +1,242 @@ +//! 只读取显式声明的文件;不扫描插件目录树,也不根据 Lua 内容发现元数据。 + +use std::fs::{self, File}; +use std::io::Read; +use std::path::{Path, PathBuf}; + +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; + +use super::{Error, Result, json}; + +const MAX_MANIFEST_BYTES: usize = 32 * 1024; +pub(super) const MAX_SOURCE_BYTES: usize = 256 * 1024; + +#[derive(Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub(super) struct Manifest { + pub manifest_version: u16, + pub api_version: u16, + pub id: String, + pub name: String, + pub version: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub entry: Option, + #[serde(default)] + pub permissions: Vec, + pub commands: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub(super) struct Command { + pub id: String, + pub title: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub method: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub handler: Option, + #[serde(default = "empty_params")] + pub params: Value, +} + +fn empty_params() -> Value { + Value::Object(Map::new()) +} + +#[derive(Debug)] +pub(super) struct Package { + pub manifest: Manifest, + pub root: PathBuf, +} + +impl Package { + pub fn open(path: &Path) -> Result { + let directory = if path.is_dir() { + path + } else if path.file_name().is_some_and(|name| name == "plugin.toml") { + path.parent() + .filter(|parent| !parent.as_os_str().is_empty()) + .unwrap_or_else(|| Path::new(".")) + } else { + return Err(Error::new("invalid_package", "expected a directory or plugin.toml")); + }; + let root = directory.canonicalize().map_err(|error| Error::new("package_io", error))?; + let source = read_file(&root, "plugin.toml", MAX_MANIFEST_BYTES)?; + let source = source.strip_prefix('\u{feff}').unwrap_or(&source); + let manifest: Manifest = + toml::from_str(source).map_err(|error| Error::new("invalid_manifest", error))?; + manifest.validate()?; + let package = Self { manifest, root }; + if let Some(entry) = &package.manifest.entry { + // check 只核实文件与大小,不读取或执行入口;混合包的原生命令仍免建 VM。 + let path = package_file(&package.root, entry)?; + let size = fs::metadata(&path).map_err(|error| Error::new("package_io", error))?.len(); + if size > MAX_SOURCE_BYTES as u64 { + return Err(Error::new("source_too_large", "Lua entry exceeds 256 KiB")); + } + } + Ok(package) + } + + pub(super) fn source(&self) -> Result { + let entry = self + .manifest + .entry + .as_deref() + .ok_or_else(|| Error::new("missing_entry", "Lua commands require an entry"))?; + read_file(&self.root, entry, MAX_SOURCE_BYTES) + } + + pub(super) fn allow_method(&self, method: &str) -> Result<()> { + if method == "events.subscribe" { + return Err(Error::new( + "stream_not_supported", + "this entry supports one-shot requests", + )); + } + if !self.manifest.permissions.iter().any(|allowed| allowed == method) { + return Err(Error::new( + "permission_denied", + format!("undeclared runtime method: {method}"), + )); + } + Ok(()) + } +} + +impl Manifest { + fn validate(&self) -> Result<()> { + if self.manifest_version != 1 || self.api_version != 1 { + return Err(Error::new("unsupported_version", "supported manifest/API version: 1")); + } + if !identifier(&self.id) || !label(&self.name, 256) || !label(&self.version, 64) { + return Err(Error::new("invalid_metadata", "invalid id, name, or version")); + } + if self.commands.is_empty() || self.commands.len() > 64 || self.permissions.len() > 32 { + return Err(Error::new( + "manifest_limit", + "declare 1..=64 commands and at most 32 permissions", + )); + } + if self.permissions.iter().enumerate().any(|(index, method)| { + !identifier(method) + || method == "events.subscribe" + || self.permissions[..index].contains(method) + }) { + return Err(Error::new( + "invalid_permissions", + "permissions must be unique one-shot method names", + )); + } + let mut has_lua = false; + for (index, command) in self.commands.iter().enumerate() { + if !identifier(&command.id) + || !label(&command.title, 256) + || self.commands[..index].iter().any(|other| other.id == command.id) + { + return Err(Error::new("invalid_command", "command IDs must be valid and unique")); + } + match (&command.method, &command.handler) { + (Some(method), None) if self.permissions.contains(method) => {}, + (None, Some(handler)) if identifier(handler) => has_lua = true, + _ => { + return Err(Error::new( + "invalid_command", + "declare exactly one permitted method or Lua handler", + )); + }, + } + if !command.params.is_object() { + return Err(Error::new("invalid_params", "command params must be an object")); + } + json::validate(&command.params)?; + } + if has_lua != self.entry.is_some() { + return Err(Error::new( + "invalid_entry", + "entry must be present exactly when Lua handlers are declared", + )); + } + if let Some(entry) = &self.entry { + valid_relative_path(entry)?; + if !entry.ends_with(".lua") { + return Err(Error::new("invalid_entry", "entry must end in .lua")); + } + } + Ok(()) + } +} + +fn identifier(value: &str) -> bool { + !value.is_empty() + && value.len() <= 96 + && value.bytes().next().is_some_and(|byte| byte.is_ascii_lowercase()) + && value.bytes().all(|byte| { + byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"._-".contains(&byte) + }) +} + +fn label(value: &str, limit: usize) -> bool { + !value.trim().is_empty() && value.len() <= limit && !value.chars().any(char::is_control) +} + +fn valid_relative_path(value: &str) -> Result<()> { + if value.is_empty() + || value.len() > 512 + || value.contains(['\\', ':', '\0']) + || value.split('/').any(|part| part.is_empty() || matches!(part, "." | "..")) + { + return Err(Error::new( + "invalid_entry_path", + "use a package-relative path with normal /-separated components", + )); + } + Ok(()) +} + +fn package_file(root: &Path, relative: &str) -> Result { + valid_relative_path(relative)?; + let mut path = root.to_path_buf(); + for component in relative.split('/') { + path.push(component); + let metadata = + fs::symlink_metadata(&path).map_err(|error| Error::new("package_io", error))?; + let linked = metadata.file_type().is_symlink(); + #[cfg(windows)] + let linked = { + use std::os::windows::fs::MetadataExt; + linked || metadata.file_attributes() & 0x400 != 0 + }; + if linked { + return Err(Error::new( + "linked_package_file", + "package entries must not traverse links or reparse points", + )); + } + } + let resolved = path.canonicalize().map_err(|error| Error::new("package_io", error))?; + if !resolved.starts_with(root) || !resolved.is_file() { + return Err(Error::new( + "invalid_package_file", + "expected a regular file inside the package", + )); + } + Ok(resolved) +} + +fn read_file(root: &Path, relative: &str, limit: usize) -> Result { + let path = package_file(root, relative)?; + let file = File::open(path).map_err(|error| Error::new("package_io", error))?; + let mut bytes = Vec::new(); + file.take((limit + 1) as u64) + .read_to_end(&mut bytes) + .map_err(|error| Error::new("package_io", error))?; + if bytes.len() > limit { + return Err(Error::new( + "package_file_too_large", + format!("{relative} exceeds {limit} bytes"), + )); + } + String::from_utf8(bytes).map_err(|error| Error::new("invalid_utf8", error)) +} diff --git a/nebula_app/src/plugins/tests.rs b/nebula_app/src/plugins/tests.rs new file mode 100644 index 0000000..a6aa845 --- /dev/null +++ b/nebula_app/src/plugins/tests.rs @@ -0,0 +1,296 @@ +use std::fs; +use std::time::Duration; + +use serde_json::{Value, json}; + +use super::*; + +const NATIVE: &str = r#" +manifest_version = 1 +api_version = 1 +id = "example.overview" +name = "运行时概览" +version = "0.1.0" +permissions = ["runtime.describe"] +[[commands]] +id = "describe" +title = "运行时概览" +method = "runtime.describe" +"#; + +fn package(manifest: &str, source: Option<&[u8]>) -> (tempfile::TempDir, Package) { + let dir = tempfile::tempdir().unwrap(); + fs::write(dir.path().join("plugin.toml"), manifest).unwrap(); + if let Some(source) = source { + fs::write(dir.path().join("init.lua"), source).unwrap(); + } + let package = Package::open(dir.path()).unwrap(); + (dir, package) +} + +fn lua_manifest() -> String { + NATIVE + .replace("permissions =", "entry = 'init.lua'\npermissions =") + .replace("method = \"runtime.describe\"", "handler = \"describe\"") +} + +fn run_lua(source: &str) -> Result { + let (_dir, package) = package(&lua_manifest(), Some(source.as_bytes())); + execute(&package, "describe", json!({}), Duration::from_secs(5), &mut |_, _, _| { + panic!("this fixture must not call Runtime") + }) +} + +#[test] +fn resource_command_uses_native_result_without_a_lua_vm() { + let (_dir, package) = package(NATIVE, None); + let initial_vms = lua::vm_creation_count(); + let execution = execute( + &package, + "describe", + json!({"label":"中文"}), + Duration::from_secs(1), + &mut |method, params, _| { + assert_eq!(method, "runtime.describe"); + assert_eq!(params, json!({"label":"中文"})); + Ok(json!({"version": 1})) + }, + ) + .unwrap(); + assert_eq!(execution.engine, "native"); + assert_eq!(execution.lua_memory_bytes, 0); + assert_eq!(execution.instruction_ticks, 0); + assert_eq!(lua::vm_creation_count(), initial_vms); +} + +#[test] +fn mixed_package_native_command_does_not_parse_lua() { + let manifest = format!( + "{}\n[[commands]]\nid='dynamic'\ntitle='动态'\nhandler='dynamic'\n", + NATIVE.replace("permissions =", "entry = 'init.lua'\npermissions =") + ); + let (_dir, package) = package(&manifest, Some(b"not valid Lua !!")); + let initial_vms = lua::vm_creation_count(); + let execution = + execute(&package, "describe", json!({}), Duration::from_secs(1), &mut |_, _, _| { + Ok(Value::Null) + }) + .unwrap(); + assert_eq!(execution.lua_memory_bytes, 0); + assert_eq!(lua::vm_creation_count(), initial_vms); +} + +#[test] +fn lua_command_yields_runtime_requests_and_returns_utf8_data() { + let (_dir, package) = package(&lua_manifest(), Some(br#" + return { describe = function(ctx, args) + local result = ctx.runtime.call('runtime.describe', {label=args.label}) + return {label=args.label, count=#result.capabilities, empty=ctx.array(), nothing=ctx.null} + end } + "#)); + let mut calls = 0; + let execution = execute( + &package, + "describe", + json!({"label":"工作台"}), + Duration::from_secs(1), + &mut |_, params, _| { + calls += 1; + assert_eq!(params["label"], "工作台"); + Ok(json!({"capabilities":["one","two"]})) + }, + ) + .unwrap(); + assert_eq!(calls, 1); + assert_eq!(execution.result, json!({"label":"工作台", "count":2, "empty":[], "nothing":null})); + assert!(execution.lua_memory_bytes > 0 && execution.lua_memory_bytes <= MAX_LUA_MEMORY); +} + +#[test] +fn missing_capability_fails_before_native_dispatch() { + let (_dir, package) = package( + &lua_manifest(), + Some( + br#" + return { describe = function(ctx) return ctx.runtime.call('pane.close', {}) end } + "#, + ), + ); + let error = execute(&package, "describe", json!({}), Duration::from_secs(1), &mut |_, _, _| { + panic!("denied call reached the host") + }) + .unwrap_err(); + assert_eq!(error.code, "permission_denied"); +} + +#[test] +fn native_error_receipts_survive_the_lua_boundary() { + let (_dir, first_package) = package( + &lua_manifest(), + Some( + br#" + return {describe=function(ctx) return ctx.runtime.call('runtime.describe', {}) end} + "#, + ), + ); + let mut expected = Error::new("action_failed", "native operation failed"); + expected.details = Some(json!({"partial":true, "cleanup_deferred":true})); + let error = + execute(&first_package, "describe", json!({}), Duration::from_secs(1), &mut |_, _, _| { + Err(expected.clone()) + }) + .unwrap_err(); + assert_eq!(error.code, expected.code); + assert_eq!(error.details, expected.details); + + let (_dir, package) = package( + &lua_manifest(), + Some( + br#" + return {describe=function(ctx) + local ok = pcall(function() ctx.runtime.call('runtime.describe', {}) end) + return {caught = not ok} + end} + "#, + ), + ); + let result = + execute(&package, "describe", json!({}), Duration::from_secs(1), &mut |_, _, _| { + Err(expected.clone()) + }) + .unwrap(); + assert_eq!(result.result, json!({"caught":true})); +} + +#[test] +fn instruction_yields_stop_loops_even_inside_pcall() { + let error = run_lua("return {describe=function() while true do pcall(function() while true do end end) end end}").unwrap_err(); + assert_eq!(error.code, "lua_instruction_limit"); +} + +#[test] +fn source_initialization_is_budgeted_too() { + let error = run_lua("while true do end").unwrap_err(); + assert_eq!(error.code, "lua_instruction_limit"); +} + +#[test] +fn lua_memory_is_bounded() { + let error = + run_lua("return {describe=function() return string.rep('x', 32 * 1024 * 1024) end}") + .unwrap_err(); + assert_eq!(error.code, "lua_memory_limit"); +} + +#[test] +fn payload_text_and_runtime_call_count_are_bounded() { + assert_eq!( + run_lua("return {describe=function() return string.rep('x', 65537) end}").unwrap_err().code, + "payload_limit" + ); + let (_dir, package) = package( + &lua_manifest(), + Some( + br#" + return {describe=function(ctx) + for i=1,17 do ctx.runtime.call('runtime.describe', {}) end + end} + "#, + ), + ); + let mut calls = 0; + let error = execute(&package, "describe", json!({}), Duration::from_secs(1), &mut |_, _, _| { + calls += 1; + Ok(Value::Null) + }) + .unwrap_err(); + assert_eq!(error.code, "runtime_call_limit"); + assert_eq!(calls, MAX_RUNTIME_CALLS); +} + +#[test] +fn filesystem_and_unmanaged_execution_are_not_exposed() { + let execution = run_lua("return {describe=function() return {restricted=(io==nil and os==nil and package==nil and debug==nil and load==nil and loadfile==nil and dofile==nil and coroutine==nil)} end}").unwrap(); + assert_eq!(execution.result, json!({"restricted":true})); +} + +#[test] +fn conversion_bounds_cycles_deep_data_and_shared_expansion() { + for source in [ + "return {describe=function() local t={}; t.self=t; return t end}", + "return {describe=function() local t={}; for i=1,40 do t={t} end; return t end}", + "return {describe=function() local t={1}; for i=1,20 do t={t,t} end; return t end}", + ] { + assert_eq!(run_lua(source).unwrap_err().code, "payload_limit"); + } +} + +#[test] +fn conversion_preserves_arrays_and_rejects_ambiguous_values() { + assert_eq!( + run_lua("return {describe=function() return {1,2,{name='ok'}} end}").unwrap().result, + json!([1,2,{"name":"ok"}]) + ); + for source in [ + "return {describe=function() return {[1]=true, extra=true} end}", + "return {describe=function() return {[2]=true} end}", + "return {describe=function() return 0/0 end}", + "return {describe=function() return function() end end}", + ] { + assert_eq!(run_lua(source).unwrap_err().code, "invalid_payload"); + } + assert_eq!( + run_lua("return {describe=function() return string.char(255) end}").unwrap_err().code, + "invalid_utf8" + ); +} + +#[test] +fn metadata_versions_unknown_fields_and_duplicate_commands_fail() { + for manifest in [ + NATIVE.replace("api_version = 1", "api_version = 2"), + NATIVE.replace("api_version = 1", "api_version = 1\nunknown = true"), + format!( + "{NATIVE}\n[[commands]]\nid='describe'\ntitle='duplicate'\nmethod='runtime.describe'" + ), + NATIVE.replace("method = \"runtime.describe\"", "method = \"pane.close\""), + ] { + let dir = tempfile::tempdir().unwrap(); + fs::write(dir.path().join("plugin.toml"), manifest).unwrap(); + assert!(Package::open(dir.path()).is_err()); + } +} + +#[test] +fn package_reads_are_bounded_and_utf8_only() { + let dir = tempfile::tempdir().unwrap(); + fs::write(dir.path().join("plugin.toml"), vec![b' '; 32 * 1024 + 1]).unwrap(); + assert_eq!(Package::open(dir.path()).unwrap_err().code, "package_file_too_large"); + fs::write(dir.path().join("plugin.toml"), b"\xff").unwrap(); + assert_eq!(Package::open(dir.path()).unwrap_err().code, "invalid_utf8"); + fs::write(dir.path().join("plugin.toml"), lua_manifest()).unwrap(); + fs::write(dir.path().join("init.lua"), vec![b' '; 256 * 1024 + 1]).unwrap(); + assert_eq!(Package::open(dir.path()).unwrap_err().code, "source_too_large"); +} + +#[test] +fn entry_paths_are_portable_and_package_relative() { + for entry in ["../init.lua", "/init.lua", "C:/init.lua", "a\\init.lua", "a//init.lua"] { + let dir = tempfile::tempdir().unwrap(); + let manifest = lua_manifest().replace("entry = 'init.lua'", &format!("entry = '{entry}'")); + fs::write(dir.path().join("plugin.toml"), manifest).unwrap(); + assert_eq!(Package::open(dir.path()).unwrap_err().code, "invalid_entry_path"); + } +} + +#[cfg(unix)] +#[test] +fn entry_symlinks_are_rejected() { + let dir = tempfile::tempdir().unwrap(); + let outside = tempfile::tempdir().unwrap(); + fs::write(dir.path().join("plugin.toml"), lua_manifest()).unwrap(); + fs::write(outside.path().join("other.lua"), "return {}").unwrap(); + std::os::unix::fs::symlink(outside.path().join("other.lua"), dir.path().join("init.lua")) + .unwrap(); + assert_eq!(Package::open(dir.path()).unwrap_err().code, "linked_package_file"); +} diff --git a/nebula_app/src/runtime_api.rs b/nebula_app/src/runtime_api.rs index 64940d3..97e2f31 100644 --- a/nebula_app/src/runtime_api.rs +++ b/nebula_app/src/runtime_api.rs @@ -8,8 +8,14 @@ mod agent_api; mod cli; mod command; +pub(crate) mod conversation; +pub(crate) mod git; +pub(crate) mod mobile_bridge; +mod mobile_screen; mod orchestrate; mod server; +pub(crate) mod tabs; +mod terminal_read; mod transport; use transport::*; @@ -17,14 +23,15 @@ pub mod shortcuts; #[cfg(test)] mod tests; +pub(crate) use cli::request_once_bounded; pub use cli::run_cli; use command::{ RuntimeWaitState, SubscribeParams, WaitParams, default_read_lines, default_true, parse_params, validate_agent_name, validate_agent_selector, wait_matches, }; pub(crate) use command::{ - capture_process_tree, capture_terminal_tail, validate_chat_message, validate_command_line, - validate_paste_text, validate_prompt, + capture_process_tree, validate_chat_message, validate_command_line, validate_paste_text, + validate_prompt, }; #[cfg(feature = "legacy-shell")] pub use server::dispatch_prompt; @@ -34,6 +41,8 @@ pub use server::{ RuntimeServer, try_open_default_tab_existing, try_open_directory_existing, try_open_window_existing, }; +pub use terminal_read::RuntimePaneRead; +pub(crate) use terminal_read::{capture_terminal_screen, capture_terminal_tail}; use std::error::Error; use std::fmt; @@ -52,11 +61,6 @@ use serde_json::{Value, json}; #[cfg(feature = "legacy-shell")] use winit::event_loop::EventLoopProxy; -use nebula_terminal::event::EventListener; -use nebula_terminal::grid::Dimensions as _; -use nebula_terminal::index::{Column, Line, Point}; -use nebula_terminal::term::Term; - use crate::cli::{ ControlCommand as CliCommand, ControlOptions, ControlSplitDirection, ControlWaitState, }; @@ -136,12 +140,17 @@ pub struct ApiResponse { pub version: u16, pub id: String, pub ok: bool, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, deserialize_with = "present_json", skip_serializing_if = "Option::is_none")] pub result: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, } +// 显式 null 是有效结果;保留它与“回复缺少 result 字段”的区别。 +fn present_json<'de, D: serde::Deserializer<'de>>(input: D) -> Result, D::Error> { + Value::deserialize(input).map(Some) +} + impl ApiResponse { fn success(id: impl Into, result: Value) -> Self { Self { @@ -430,6 +439,10 @@ pub struct RuntimeWindow { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct RuntimeTab { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tab_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub file: Option, pub index: usize, pub active: bool, pub label: String, @@ -496,21 +509,6 @@ pub struct RuntimeAgentPane { pub state_change_seq: u64, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct RuntimePaneRead { - pub window_id: u64, - pub pane_id: u64, - pub text: String, - pub requested_lines: usize, - pub returned_lines: usize, - pub history_available: usize, - pub truncated: bool, - pub task_state: RuntimeTaskState, - pub exited: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub exit_reason: Option, -} - #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct RuntimeProcess { pub pid: u32, @@ -755,6 +753,15 @@ pub struct RuntimeKeyModifiers { #[derive(Debug, Clone)] pub enum RuntimeCommand { Snapshot, + Conversation { + window_id: u64, + pane_id: u64, + request: conversation::Request, + }, + Tab { + window_id: u64, + request: tabs::Request, + }, NewWindow { /// 普通启动 / Explorer 右键要求新开窗口时,把目标目录一路带到首个标签。 cwd: Option, @@ -824,6 +831,7 @@ pub enum RuntimeCommand { window_id: Option, pane_id: u64, lines: usize, + screen: bool, }, Procs { window_id: Option, @@ -850,6 +858,11 @@ pub enum RuntimeCommand { timeout_ms: u64, max_output_bytes: usize, }, + Git { + window_id: Option, + pane_id: u64, + request: git::Request, + }, AgentStart { window_id: Option, pane_id: Option, @@ -1927,7 +1940,7 @@ pub(crate) fn read_pane_tail_text( ) -> Option<(String, usize)> { let requested = lines.saturating_add(TAIL_SCAN_EXTRA_LINES).min(MAX_READ_LINES); let result = dispatch_runtime_command( - RuntimeCommand::ReadPane { window_id, pane_id, lines: requested }, + RuntimeCommand::ReadPane { window_id, pane_id, lines: requested, screen: false }, sink, hub, ) diff --git a/nebula_app/src/runtime_api/cli.rs b/nebula_app/src/runtime_api/cli.rs index 0c3394f..33c6a85 100644 --- a/nebula_app/src/runtime_api/cli.rs +++ b/nebula_app/src/runtime_api/cli.rs @@ -29,6 +29,110 @@ pub(super) fn request_once( .map_err(|error| input_transport_error(method, error)) } +/// 插件的命令入口使用相同协议,但为响应保留量和整次 I/O 设置独立上限。 +pub(crate) fn request_once_bounded( + method: &str, + params: Value, + timeout: Duration, +) -> Result { + let endpoint = read_endpoint() + .ok_or_else(|| ApiError::new("runtime_unavailable", "no resident Pebrel runtime found"))?; + let request = ApiRequest::new(endpoint.token.clone(), method, params); + let mut bytes = serde_json::to_vec(&request) + .map_err(|error| ApiError::invalid_params(error.to_string()))?; + if bytes.len() > MAX_REQUEST_BYTES { + return Err(ApiError::new("request_too_large", "plugin request exceeds 128 KiB")); + } + bytes.push(b'\n'); + let response = bounded_exchange(&endpoint, &bytes, timeout) + .and_then(|bytes| decode_plugin_response(&request, &bytes)); + response.map_err(|error| { + let error = input_transport_error(method, error); + if let Some(error) = error.downcast_ref::() { + ApiError::new(error.code(), error.message.clone()) + } else { + ApiError::new("runtime_transport", error.to_string()) + } + }) +} + +fn decode_plugin_response( + request: &ApiRequest, + bytes: &[u8], +) -> Result> { + let response: ApiResponse = serde_json::from_slice(bytes)?; + if response.protocol != PROTOCOL_NAME + || response.version != PROTOCOL_VERSION + || response.id != request.id + || (response.ok && (response.result.is_none() || response.error.is_some())) + || (!response.ok && (response.error.is_none() || response.result.is_some())) + { + return Err(CliError::new( + "invalid_response", + "runtime response identity or envelope mismatch", + ) + .into()); + } + Ok(response) +} + +fn bounded_exchange( + endpoint: &Endpoint, + request: &[u8], + timeout: Duration, +) -> Result, Box> { + const MAX_RESPONSE: usize = 256 * 1024; + let deadline = Instant::now() + timeout; + let remaining = || { + deadline + .checked_duration_since(Instant::now()) + .filter(|remaining| !remaining.is_zero()) + .ok_or_else(|| { + IoError::new(std::io::ErrorKind::TimedOut, "plugin runtime request timed out") + }) + }; + let mut stream = + TcpStream::connect_timeout(&endpoint_addr(endpoint), remaining()?.min(CONNECT_TIMEOUT))?; + let mut pending = request; + while !pending.is_empty() { + stream.set_write_timeout(Some(remaining()?))?; + let written = stream.write(pending)?; + if written == 0 { + return Err(IoError::new(std::io::ErrorKind::WriteZero, "runtime write stopped").into()); + } + pending = &pending[written..]; + } + // The newline frames the complete request. A fast peer may already have + // replied and closed here; shutting down the write half can then fail with + // NotConnected on macOS before we read its buffered response. + let mut bytes = Vec::new(); + let mut buffer = [0; 4096]; + loop { + stream.set_read_timeout(Some(remaining()?))?; + let count = stream.read(&mut buffer)?; + if count == 0 { + return Err(CliError::new( + "runtime_no_response", + "runtime closed before a complete JSON line", + ) + .into()); + } + let newline = buffer[..count].iter().position(|byte| *byte == b'\n'); + let end = newline.unwrap_or(count); + if bytes.len() + end > MAX_RESPONSE { + return Err(CliError::new( + "runtime_response_too_large", + "plugin response exceeds 256 KiB", + ) + .into()); + } + bytes.extend_from_slice(&buffer[..end]); + if newline.is_some() { + return Ok(bytes); + } + } +} + fn read_response( endpoint: &Endpoint, request: &ApiRequest, @@ -592,6 +696,76 @@ impl Error for CliError {} mod output_tests { use super::*; + #[test] + fn plugin_response_checks_identity_and_envelope() { + let request = ApiRequest::new("fixture".to_owned(), "runtime.describe", json!({})); + let response = ApiResponse::success(&request.id, json!({"name":"中文"})); + let value = serde_json::to_value(&response).unwrap(); + assert_eq!( + decode_plugin_response(&request, &serde_json::to_vec(&value).unwrap()).unwrap(), + response + ); + let null = ApiResponse::success(&request.id, Value::Null); + assert_eq!( + decode_plugin_response(&request, &serde_json::to_vec(&null).unwrap()).unwrap(), + null + ); + let mut missing_result = value.clone(); + missing_result.as_object_mut().unwrap().remove("result"); + assert!( + decode_plugin_response(&request, &serde_json::to_vec(&missing_result).unwrap()) + .is_err() + ); + for (key, replacement) in [ + ("id", json!("other")), + ("protocol", json!("other")), + ("version", json!(2)), + ("ok", json!(false)), + ] { + let mut invalid = value.clone(); + invalid[key] = replacement; + assert!( + decode_plugin_response(&request, &serde_json::to_vec(&invalid).unwrap()).is_err() + ); + } + } + + #[test] + fn plugin_exchange_bounds_response_and_requires_complete_line() { + for (response, expected_error) in [ + (b"{\"ok\":true}\n".to_vec(), None), + (vec![b'x'; 256 * 1024 + 1], Some("runtime_response_too_large")), + (b"{}".to_vec(), Some("runtime_no_response")), + ] { + let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap(); + let endpoint = Endpoint { + port: listener.local_addr().unwrap().port(), + token: "fixture".to_owned(), + }; + let server = std::thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + stream.set_read_timeout(Some(Duration::from_secs(2))).unwrap(); + stream.set_write_timeout(Some(Duration::from_secs(2))).unwrap(); + let mut request = String::new(); + BufReader::new(&mut stream).read_line(&mut request).unwrap(); + assert_eq!(request, "{}\n"); + // 超额回复的接收方可提前关流;写方无需等待对方接受被丢弃的数据。 + let _ = stream.write_all(&response); + }); + let result = bounded_exchange(&endpoint, b"{}\n", Duration::from_secs(2)); + server.join().unwrap(); + if let Some(code) = expected_error { + let error = result.unwrap_err(); + let response_error = error + .downcast_ref::() + .unwrap_or_else(|| panic!("expected {code}, got {error:?}")); + assert_eq!(response_error.code(), code); + } else { + assert_eq!(result.unwrap(), b"{\"ok\":true}"); + } + } + } + #[test] fn runtime_submission_missing_or_zero_baseline_cannot_wait_on_old_idle() { for baseline in [None, Some(0)] { diff --git a/nebula_app/src/runtime_api/command.rs b/nebula_app/src/runtime_api/command.rs index b3bfb8b..66f66a1 100644 --- a/nebula_app/src/runtime_api/command.rs +++ b/nebula_app/src/runtime_api/command.rs @@ -99,6 +99,8 @@ struct ReadParams { pane_id: u64, #[serde(default = "default_read_lines")] lines: usize, + #[serde(default)] + screen: bool, } #[derive(Debug, Deserialize)] @@ -222,9 +224,24 @@ fn default_exec_output_bytes() -> usize { } impl RuntimeCommand { + pub(crate) fn execution_target(&self) -> Option<(Option, u64)> { + match self { + Self::Exec { window_id, pane_id, .. } | Self::Git { window_id, pane_id, .. } => { + Some((*window_id, *pane_id)) + }, + _ => None, + } + } + pub(super) fn from_request(request: &ApiRequest) -> Result { match request.method.as_str() { "runtime.snapshot" => Ok(Self::Snapshot), + "conversation.read" + | "conversation.send" + | "conversation.choose" + | "conversation.key" => super::conversation::parse(request), + "tab.focus" | "tab.open" | "tab.read" => super::tabs::parse(request), + "tab.close" if request.params.get("tab_id").is_some() => super::tabs::parse(request), "window.create" => { let params: WindowParams = parse_params(&request.params)?; Ok(Self::NewWindow { cwd: params.cwd, shell_id: params.shell }) @@ -332,6 +349,7 @@ impl RuntimeCommand { window_id: params.window_id, pane_id: params.pane_id, lines: params.lines, + screen: params.screen, }) }, "pane.procs" => { @@ -398,6 +416,7 @@ impl RuntimeCommand { "agent.start" | "agent.fork" | "agent.prompt" | "agent.paste" | "agent.read" => { agent_api::command_from_request(request) }, + method if method.starts_with("git.") => super::git::command(request), method => Err(ApiError::new( "method_not_found", format!("runtime API method {method:?} does not exist"), @@ -406,78 +425,6 @@ impl RuntimeCommand { } } -/// Read the logical tail of the terminal model. The range is anchored at the -/// buffer bottom, never at `display_offset`, so a user scrolling through -/// history cannot change what an external agent observes. -pub(crate) fn capture_terminal_tail( - term: &Term, - window_id: u64, - pane_id: u64, - requested_lines: usize, - task_state: RuntimeTaskState, - exited: bool, - exit_reason: Option, -) -> RuntimePaneRead { - let columns = term.columns(); - let screen_lines = term.screen_lines(); - let total_lines = term.total_lines(); - let history_available = total_lines.saturating_sub(screen_lines); - if columns == 0 || screen_lines == 0 || total_lines == 0 { - return RuntimePaneRead { - window_id, - pane_id, - text: String::new(), - requested_lines, - returned_lines: 0, - history_available, - truncated: false, - task_state, - exited, - exit_reason, - }; - } - - let mut returned_lines = requested_lines.min(total_lines); - let end = Point::new(Line(screen_lines as i32 - 1), Column(columns - 1)); - let capture = |lines: usize| { - let start_line = screen_lines as i64 - lines as i64; - let start = Point::new(Line(start_line.max(-(history_available as i64)) as i32), Column(0)); - term.bounds_to_string(start, end) - }; - let mut text = capture(returned_lines); - - // Reduce by whole terminal rows first, preserving exact returned_lines. - // Only a pathological single row can fall through to UTF-8 byte slicing. - while text.len() > MAX_READ_BYTES && returned_lines > 1 { - let estimated = ((returned_lines as u128 * MAX_READ_BYTES as u128) / text.len() as u128) - .clamp(1, (returned_lines - 1) as u128) as usize; - returned_lines = estimated; - text = capture(returned_lines); - } - let mut byte_truncated = false; - if text.len() > MAX_READ_BYTES { - let mut start = text.len() - MAX_READ_BYTES; - while !text.is_char_boundary(start) { - start += 1; - } - text = text[start..].to_owned(); - byte_truncated = true; - } - - RuntimePaneRead { - window_id, - pane_id, - text, - requested_lines, - returned_lines, - history_available, - truncated: returned_lines < total_lines || byte_truncated, - task_state, - exited, - exit_reason, - } -} - pub(crate) fn capture_process_tree( window_id: u64, pane_id: u64, diff --git a/nebula_app/src/runtime_api/conversation.rs b/nebula_app/src/runtime_api/conversation.rs new file mode 100644 index 0000000..8f057d4 --- /dev/null +++ b/nebula_app/src/runtime_api/conversation.rs @@ -0,0 +1,135 @@ +//! Identity-bound access to the conversation already running in a pane. + +use serde::{Deserialize, Serialize}; + +use super::{ApiError, ApiRequest, RuntimeCommand, RuntimeKey, RuntimeKeyModifiers, parse_params}; + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(deny_unknown_fields)] +pub struct Identity { + pub kind: String, + pub session_id: String, + /// First reads resolve the epoch; all mutations require the returned value. + pub epoch: Option, +} + +impl Identity { + pub fn matches(&self, current: &Self) -> bool { + self.kind == current.kind + && self.session_id == current.session_id + && self.epoch.is_none_or(|epoch| current.epoch == Some(epoch)) + } +} + +#[derive(Clone, Debug)] +pub enum Request { + Read { identity: Identity, before: Option, revision: Option }, + Send { identity: Identity, text: String }, + Choose { identity: Identity, prompt_id: String, option: usize }, + Key { identity: Identity, key: RuntimeKey, modifiers: RuntimeKeyModifiers }, +} + +impl Request { + pub fn identity(&self) -> &Identity { + match self { + Self::Read { identity, .. } + | Self::Send { identity, .. } + | Self::Choose { identity, .. } + | Self::Key { identity, .. } => identity, + } + } +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Params { + window_id: u64, + pane_id: u64, + identity: Identity, + before: Option, + revision: Option, + text: Option, + prompt_id: Option, + option: Option, + key: Option, +} + +pub(super) fn parse(request: &ApiRequest) -> Result { + let p: Params = parse_params(&request.params)?; + if p.window_id == 0 + || p.pane_id == 0 + || !matches!(p.identity.kind.as_str(), "codex" | "claude") + || p.identity.session_id.is_empty() + || p.identity.session_id.len() > 128 + || p.identity.session_id.chars().any(char::is_control) + || p.revision.as_ref().is_some_and(|s| s.len() > 128) + { + return Err(ApiError::invalid_params( + "a current pane and native conversation identity are required", + )); + } + let read_only = + p.text.is_none() && p.prompt_id.is_none() && p.option.is_none() && p.key.is_none(); + let request = match request.method.as_str() { + "conversation.read" if read_only => { + Request::Read { identity: p.identity, before: p.before, revision: p.revision } + }, + "conversation.send" + if p.identity.epoch.is_some() + && p.before.is_none() + && p.revision.is_none() + && p.prompt_id.is_none() + && p.option.is_none() + && p.key.is_none() => + { + let text = p.text.ok_or_else(|| ApiError::invalid_params("text is required"))?; + super::validate_chat_message(&text)?; + Request::Send { identity: p.identity, text } + }, + "conversation.choose" + if p.identity.epoch.is_some() + && p.before.is_none() + && p.revision.is_none() + && p.text.is_none() + && p.key.is_none() => + { + let prompt_id = p + .prompt_id + .filter(|id| id.len() == 64 && id.bytes().all(|b| b.is_ascii_hexdigit())) + .ok_or_else(|| ApiError::invalid_params("the current prompt_id is required"))?; + let option = p + .option + .filter(|index| *index < 9) + .ok_or_else(|| ApiError::invalid_params("a visible option is required"))?; + Request::Choose { identity: p.identity, prompt_id, option } + }, + "conversation.key" + if p.identity.epoch.is_some() + && p.before.is_none() + && p.revision.is_none() + && p.text.is_none() + && p.prompt_id.is_none() + && p.option.is_none() => + { + let key = match p.key.as_deref() { + Some("Ctrl+C") => RuntimeKey::C, + Some("Esc") => RuntimeKey::Escape, + Some("Tab") => RuntimeKey::Tab, + Some("←") => RuntimeKey::Left, + Some("→") => RuntimeKey::Right, + Some("↑") => RuntimeKey::Up, + Some("↓") => RuntimeKey::Down, + _ => { + return Err(ApiError::invalid_params( + "a visible conversation shortcut is required", + )); + }, + }; + let modifiers = + RuntimeKeyModifiers { control: key == RuntimeKey::C, ..Default::default() }; + Request::Key { identity: p.identity, key, modifiers } + }, + _ => return Err(ApiError::invalid_params("invalid conversation operation fields")), + }; + Ok(RuntimeCommand::Conversation { window_id: p.window_id, pane_id: p.pane_id, request }) +} diff --git a/nebula_app/src/runtime_api/git.rs b/nebula_app/src/runtime_api/git.rs new file mode 100644 index 0000000..cca639b --- /dev/null +++ b/nebula_app/src/runtime_api/git.rs @@ -0,0 +1,330 @@ +//! 已配对手机的 Git 请求仍由电脑解析窗格、确定工作目录并执行,不暴露任意命令。 + +use std::collections::HashMap; +use std::sync::{Arc, Mutex, OnceLock, Weak}; +use std::time::{Duration, Instant}; + +use serde::Deserialize; +use serde_json::{Value, json}; +use sha2::{Digest, Sha256}; + +use super::{ApiError, ApiRequest, RuntimeCommand, parse_params}; +use crate::git_worktree::status::{GitStatus, STATUS_ARGS, parse_status}; +use crate::runtime_exec::PaneExecContext; + +const LIMIT: usize = 256 * 1024; +const DEADLINE: Duration = Duration::from_secs(25); + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Request { + window_id: u64, + pane_id: u64, + expected_cwd: String, + #[serde(default)] + path: String, + #[serde(default)] + staged: bool, + #[serde(default)] + all: bool, + #[serde(default)] + revision: String, + #[serde(default)] + message: String, + #[serde(default)] + commit: String, + #[serde(default)] + offset: usize, + #[serde(skip)] + operation: String, +} + +pub(super) fn command(request: &ApiRequest) -> Result { + let mut params: Request = parse_params(&request.params)?; + params.operation = request.method.strip_prefix("git.").unwrap_or_default().into(); + if !matches!( + params.operation.as_str(), + "status" | "diff" | "history" | "stage" | "unstage" | "commit" | "fetch" | "pull" | "push" + ) { + return Err(ApiError::new("method_not_found", "unknown Git operation")); + } + if params.window_id == 0 + || params.pane_id == 0 + || params.expected_cwd.is_empty() + || params.expected_cwd.len() > 4096 + || params.expected_cwd.contains('\0') + { + return Err(ApiError::invalid_params( + "an explicit pane and working directory are required", + )); + } + if params.path.len() > 4096 + || params.path.contains('\0') + || params.message.len() > 16 * 1024 + || params.message.contains('\0') + || params.offset > 10_000 + { + return Err(ApiError::invalid_params("Git parameter exceeds its limit")); + } + if !params.commit.is_empty() + && (!matches!(params.commit.len(), 40 | 64) + || !params.commit.bytes().all(|b| b.is_ascii_hexdigit())) + { + return Err(ApiError::invalid_params("commit must be a full object ID")); + } + if params.is_write() + && (params.revision.len() != 64 || !params.revision.bytes().all(|b| b.is_ascii_hexdigit())) + { + return Err(ApiError::invalid_params("a current Git status revision is required")); + } + if params.operation == "commit" && params.message.trim().is_empty() { + return Err(ApiError::invalid_params("commit message is empty")); + } + Ok(RuntimeCommand::Git { + window_id: Some(params.window_id), + pane_id: params.pane_id, + request: params, + }) +} + +impl Request { + fn is_write(&self) -> bool { + !matches!(self.operation.as_str(), "status" | "diff" | "history") + } +} + +struct Repository { + context: PaneExecContext, + cwd: String, + root: String, + started: Instant, +} + +impl Repository { + fn run(&self, args: &[&str], diff_exit: bool) -> Result { + let remaining = DEADLINE.saturating_sub(self.started.elapsed()).as_millis() as u64; + if remaining == 0 { + return Err(ApiError::new( + "git_outcome_unknown", + "Git deadline reached; refresh before another operation", + )); + } + let mut argv: Vec = [ + "git", + "--no-pager", + "--literal-pathspecs", + "--no-optional-locks", + "-c", + "credential.interactive=never", + "-c", + "core.askPass=", + "-C", + &self.root, + ] + .into_iter() + .map(str::to_owned) + .collect(); + argv.extend(args.iter().map(|s| (*s).into())); + let result = crate::runtime_exec::execute( + self.context.clone(), + self.cwd.clone(), + argv, + remaining, + LIMIT, + )?; + if result["timed_out"] == true { + return Err(ApiError::new( + "git_outcome_unknown", + "Git timed out; refresh before another operation", + )); + } + if result["success"] != true && !(diff_exit && result["exit_code"] == 1) { + return Err(ApiError::new("git_failed", "Git did not complete the operation") + .details(json!({"stderr": result["stderr"], "exit_code": result["exit_code"]}))); + } + if result["capture"]["stdout"]["truncated"] == true { + return Err(ApiError::new("git_too_large", "Git output exceeds the mobile view limit")); + } + if result["capture"]["stdout"]["encoding"] != "utf-8" { + return Err(ApiError::new("git_encoding", "Git output is not UTF-8")); + } + Ok(result["stdout"].as_str().unwrap_or_default().into()) + } + + fn status(&self) -> Result<(GitStatus, String), ApiError> { + let mut args = STATUS_ARGS.to_vec(); + args.push("--untracked-files=all"); + let raw = self.run(&args, false)?; + let status = parse_status(&raw) + .map_err(|code| ApiError::new(code, "invalid or oversized Git status"))?; + if status.entries.len() > 8_000 { + return Err(ApiError::new("git_too_large", "too many changes for the mobile view")); + } + // 同名文件被重新暂存也会改变 blob ID,提交前据此拒绝旧页面里的整仓库操作。 + let index = self.run( + &["diff", "--cached", "--raw", "--no-abbrev", "--no-ext-diff", "--no-textconv", "-z"], + false, + )?; + let mut digest = Sha256::new(); + digest.update(self.root.as_bytes()); + digest.update([0]); + digest.update(raw.as_bytes()); + digest.update(index.as_bytes()); + let revision: String = digest.finalize().iter().map(|byte| format!("{byte:02x}")).collect(); + Ok((status, revision)) + } + + fn diff(&self, request: &Request) -> Result { + let mut args = vec!["diff", "--no-ext-diff", "--no-textconv", "--no-color"]; + let mut no_index = false; + if !request.commit.is_empty() { + args = vec![ + "show", + "--format=", + "--first-parent", + "--no-ext-diff", + "--no-textconv", + "--no-color", + &request.commit, + "--", + ]; + } else { + let (status, _) = self.status()?; + let entry = + status.entries.iter().find(|e| e.path == request.path).ok_or_else(|| { + ApiError::new("git_stale", "the selected change no longer exists") + })?; + if entry.index == '?' { + no_index = true; + args.extend(["--no-index", "--", "/dev/null", &request.path]); + } else { + if request.staged { + args.push("--cached"); + } + args.push("--"); + args.push(&request.path); + if let Some(original) = &entry.original { + args.push(original); + } + } + return Ok(json!({"text": self.run(&args, no_index)?})); + } + Ok(json!({"text": self.run(&args, no_index)?})) + } + + fn mutate(&self, request: &Request, status: &GitStatus) -> Result { + let mut args = match request.operation.as_str() { + "stage" => vec!["add"], + "unstage" if status.head == "(initial)" => vec!["rm", "--cached", "-r"], + "unstage" => vec!["restore", "--staged"], + "commit" => { + if status.entries.iter().any(|e| e.conflict) + || !status.entries.iter().any(|e| e.staged()) + { + return Err(ApiError::new( + "git_not_committable", + "resolve conflicts and stage changes before committing", + )); + } + vec!["commit", "-m", &request.message] + }, + "fetch" => vec!["fetch"], + "pull" => vec!["pull", "--ff-only"], + "push" => vec!["push"], + _ => return Err(ApiError::invalid_params("unknown Git mutation")), + }; + if matches!(request.operation.as_str(), "stage" | "unstage") { + if request.all { + args.extend(["--", "."]); + } else { + let entry = + status.entries.iter().find(|e| e.path == request.path).ok_or_else(|| { + ApiError::new("git_stale", "the selected change no longer exists") + })?; + args.extend(["--", &entry.path]); + if request.operation == "unstage" { + if let Some(original) = &entry.original { + args.push(original); + } + } + } + } + let output = self.run(&args, false)?; + // 写入回执与刷新分开;随后刷新失败不能把已经成功的提交变成“可重试”。 + Ok(json!({"success": true, "output": output})) + } +} + +fn repository_lock(key: String) -> Arc> { + static LOCKS: OnceLock>>>> = OnceLock::new(); + let mut locks = LOCKS.get_or_init(Mutex::default).lock().unwrap_or_else(|e| e.into_inner()); + locks.retain(|_, value| value.strong_count() > 0); + if let Some(lock) = locks.get(&key).and_then(Weak::upgrade) { + return lock; + } + let lock = Arc::new(Mutex::new(())); + locks.insert(key, Arc::downgrade(&lock)); + lock +} + +pub(crate) fn execute( + context: PaneExecContext, + cwd: String, + request: &Request, +) -> Result { + if cwd != request.expected_cwd { + return Err(ApiError::new( + "git_target_changed", + "the pane working directory changed; reopen Git", + )); + } + let mut repo = + Repository { context: context.for_git(), root: cwd.clone(), cwd, started: Instant::now() }; + let root = repo.run(&["rev-parse", "--show-toplevel"], false)?; + repo.root = root.strip_suffix('\n').unwrap_or(&root).into(); + let lock = repository_lock(format!( + "{:?}:{:?}:{}", + repo.context.wsl_distribution(), + repo.context.wsl_user(), + repo.root + )); + let _guard = lock.try_lock().map_err(|_| { + ApiError::new("git_busy", "another Git operation is running for this repository") + })?; + if request.operation == "diff" { + return repo.diff(request); + } + let (status, revision) = repo.status()?; + if request.operation == "status" { + return Ok(json!({"root": repo.root, "status": status, "revision": revision})); + } + if request.operation == "history" { + if status.head == "(initial)" { + return Ok(json!({"commits": [], "has_more": false})); + } + let skip = format!("--skip={}", request.offset); + let output = repo.run( + &[ + "log", + "--all", + "--date-order", + "--decorate=full", + "--max-count=51", + &skip, + "--format=%x1e%H%x1f%h%x1f%D%x1f%s%x1f%an%x1f%ct%x1f%P", + ], + false, + )?; + let commits = crate::display::side_panel::parse_git_history(&output); + let values: Vec<_> = commits.iter().take(50).map(|c| json!({"hash": c.full_hash, "short_hash": c.short_hash, + "subject": c.subject, "author": c.author, "timestamp": c.timestamp, "parents": c.parent_hashes, "refs": c.decorations})).collect(); + return Ok(json!({"commits": values, "has_more": commits.len() > 50})); + } + if revision != request.revision { + return Err(ApiError::new("git_stale", "repository state changed; refresh before writing")); + } + repo.mutate(request, &status) +} + +#[cfg(test)] +mod tests; diff --git a/nebula_app/src/runtime_api/git/tests.rs b/nebula_app/src/runtime_api/git/tests.rs new file mode 100644 index 0000000..c7d922e --- /dev/null +++ b/nebula_app/src/runtime_api/git/tests.rs @@ -0,0 +1,138 @@ +use super::*; +use std::path::Path; + +fn git(cwd: &Path, args: &[&str]) -> String { + let mut command = std::process::Command::new("git"); + crate::platform::process::hidden_command(&mut command); + let output = command.arg("-C").arg(cwd).args(args).output().expect("run Git"); + assert!(output.status.success(), "{args:?}: {}", String::from_utf8_lossy(&output.stderr)); + String::from_utf8(output.stdout).unwrap() +} + +fn repository() -> tempfile::TempDir { + let dir = tempfile::tempdir().unwrap(); + git(dir.path(), &["init", "--initial-branch=main"]); + git(dir.path(), &["config", "user.name", "Mobile Git Test"]); + git(dir.path(), &["config", "user.email", "mobile@example.invalid"]); + git(dir.path(), &["config", "commit.gpgsign", "false"]); + dir +} + +fn request(cwd: &Path, operation: &str, mut params: Value) -> Request { + params["window_id"] = json!(1); + params["pane_id"] = json!(2); + params["expected_cwd"] = json!(cwd.to_str().unwrap()); + let command = + command(&ApiRequest::new("test".into(), format!("git.{operation}"), params)).unwrap(); + let RuntimeCommand::Git { request, .. } = command else { panic!("Git command") }; + request +} + +fn call(cwd: &Path, operation: &str, params: Value) -> Result { + let options = nebula_terminal::tty::Options { + working_directory: Some(cwd.to_path_buf()), + ..Default::default() + }; + execute( + PaneExecContext::from_pty_options(&options), + cwd.to_str().unwrap().into(), + &request(cwd, operation, params), + ) +} + +fn revision(cwd: &Path) -> Value { + call(cwd, "status", json!({})).unwrap()["revision"].clone() +} + +#[test] +fn literal_paths_unborn_unstage_and_stale_commit() { + let dir = repository(); + let path = dir.path(); + let name = "-中文 [draft].txt"; + std::fs::write(path.join(name), "first\n").unwrap(); + let status = call(path, "status", json!({})).unwrap(); + assert_eq!(status["status"]["entries"][0]["path"], name); + assert!( + call(path, "diff", json!({"path": name})).unwrap()["text"] + .as_str() + .unwrap() + .contains("+first") + ); + call(path, "stage", json!({"path": name, "revision": status["revision"]})).unwrap(); + call(path, "unstage", json!({"path": name, "revision": revision(path)})).unwrap(); + assert!(git(path, &["ls-files"]).is_empty()); + call(path, "stage", json!({"all": true, "revision": revision(path)})).unwrap(); + let stale = revision(path); + std::fs::write(path.join(name), "restaged\n").unwrap(); + git(path, &["add", "--", name]); + assert_eq!( + call(path, "commit", json!({"message": "stale", "revision": stale})).unwrap_err().code, + "git_stale" + ); + call(path, "commit", json!({"message": "initial", "revision": revision(path)})).unwrap(); + let history = call(path, "history", json!({})).unwrap(); + assert_eq!(history["commits"][0]["subject"], "initial"); + assert!( + call(path, "diff", json!({"commit": history["commits"][0]["hash"]})).unwrap()["text"] + .as_str() + .unwrap() + .contains("+restaged") + ); + git(path, &["mv", "--", name, "renamed.txt"]); + let status = call(path, "status", json!({})).unwrap(); + assert_eq!(status["status"]["entries"][0]["original"], name); + call(path, "unstage", json!({"path": "renamed.txt", "revision": status["revision"]})).unwrap(); + assert!(git(path, &["diff", "--cached", "--name-only"]).is_empty()); + assert!(path.join("renamed.txt").is_file()); +} + +#[test] +fn local_remote_fetch_pull_push_and_nested_cwd() { + let dir = repository(); + let path = dir.path(); + std::fs::create_dir(path.join("src")).unwrap(); + std::fs::write(path.join("src/file.txt"), "one\n").unwrap(); + call(path, "stage", json!({"all": true, "revision": revision(path)})).unwrap(); + call(path, "commit", json!({"message": "initial", "revision": revision(path)})).unwrap(); + let remote = tempfile::tempdir().unwrap(); + git(remote.path(), &["init", "--bare", "--initial-branch=main"]); + git(path, &["remote", "add", "origin", remote.path().to_str().unwrap()]); + git(path, &["push", "--set-upstream", "origin", "main"]); + std::fs::write(path.join("src/file.txt"), "two\n").unwrap(); + let nested = path.join("src"); + call(&nested, "stage", json!({"path": "src/file.txt", "revision": revision(&nested)})).unwrap(); + call(&nested, "commit", json!({"message": "from phone", "revision": revision(&nested)})) + .unwrap(); + call(&nested, "push", json!({"revision": revision(&nested)})).unwrap(); + assert_eq!(git(path, &["rev-parse", "HEAD"]), git(remote.path(), &["rev-parse", "main"])); + call(path, "fetch", json!({"revision": revision(path)})).unwrap(); + call(path, "pull", json!({"revision": revision(path)})).unwrap(); + assert!( + call(path, "status", json!({})).unwrap()["status"]["entries"] + .as_array() + .unwrap() + .is_empty() + ); +} + +#[test] +fn rejects_wrong_target_and_unknown_methods_before_execution() { + let dir = repository(); + let options = nebula_terminal::tty::Options::default(); + let error = execute( + PaneExecContext::from_pty_options(&options), + "another cwd".into(), + &request(dir.path(), "status", json!({})), + ) + .unwrap_err(); + assert_eq!(error.code, "git_target_changed"); + assert!(command(&ApiRequest::new("test".into(), "git.reset", json!({}))).is_err()); + assert!( + command(&ApiRequest::new( + "test".into(), + "git.stage", + json!({"window_id":1,"pane_id":2,"expected_cwd":"/tmp"}) + )) + .is_err() + ); +} diff --git a/nebula_app/src/runtime_api/mobile_bridge.rs b/nebula_app/src/runtime_api/mobile_bridge.rs new file mode 100644 index 0000000..2d67fcf --- /dev/null +++ b/nebula_app/src/runtime_api/mobile_bridge.rs @@ -0,0 +1,543 @@ +//! Mobile's first real desktop transport: one authenticated SSH exec channel. +//! +//! Session ownership and command validation remain in RuntimeHub. This adapter +//! fixes the local endpoint for its entire lifetime, hides its token, bounds wire +//! messages, and exposes a small allowlist. No new public listener or cloud is +//! involved. Device pairing, an input lease, durable notifications and a standalone +//! relay are separate capabilities; this transport does not claim to provide them. + +use super::*; +use std::io; +use std::sync::atomic::AtomicBool; + +const MAX_BRIDGE_FRAME: usize = 2 * 1024 * 1024; +const MAX_BRIDGE_REQUEST: usize = 40 * 1024; + +fn bridge_policy() -> &'static Value { + static POLICY: std::sync::OnceLock = std::sync::OnceLock::new(); + POLICY.get_or_init(|| { + serde_json::from_str(include_str!("../../../mobile/protocol/bridge-policy.json")) + .expect("valid embedded mobile bridge policy") + }) +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct Request { + id: String, + method: String, + #[serde(default = "empty_object")] + params: Value, +} + +impl Request { + fn validate(&self, allow_input: bool) -> Result<(), ApiError> { + if self.id.is_empty() || self.id.len() > 80 || self.id.chars().any(char::is_control) { + return Err(ApiError::invalid_params("id must contain 1..80 bytes without controls")); + } + if !self.params.is_object() { + return Err(ApiError::invalid_params("params must be an object")); + } + let listed = |group: &str| { + bridge_policy()[group] + .as_array() + .expect("method array") + .iter() + .any(|method| method.as_str() == Some(self.method.as_str())) + }; + if !listed("read") { + if !listed("input") { + return Err(ApiError::new("method_not_found", "method is not available on mobile")); + } + if !allow_input { + return Err(ApiError::new("input_not_authorized", "this channel is view-only")); + } + } + if self.method.starts_with("pane.") + || self.method.starts_with("git.") + || self.method.starts_with("conversation.") + { + for key in ["window_id", "pane_id"] { + if self.params.get(key).and_then(Value::as_u64).is_none_or(|id| id == 0) { + return Err(ApiError::invalid_params(format!( + "{key} must be explicit and nonzero" + ))); + } + } + } + if self.method.starts_with("tab.") { + if !(self.method == "tab.new" && self.params.get("window_id").is_none()) + && self.params.get("window_id").and_then(Value::as_u64).is_none_or(|id| id == 0) + { + return Err(ApiError::invalid_params("window_id must be explicit and nonzero")); + } + if !matches!(self.method.as_str(), "tab.new" | "tab.open") + && self.params.get("tab_id").and_then(Value::as_str).is_none_or(str::is_empty) + { + return Err(ApiError::invalid_params( + "mobile tab operations require snapshot tab_id", + )); + } + } + Ok(()) + } +} + +/// Read one bounded line without allocating in proportion to untrusted input. +fn read_frame(reader: &mut impl BufRead, limit: usize) -> io::Result>> { + let mut bytes = Vec::with_capacity(1024.min(limit)); + loop { + let available = reader.fill_buf()?; + if available.is_empty() { + return if bytes.is_empty() { + Ok(None) + } else { + Err(io::Error::new(io::ErrorKind::UnexpectedEof, "incomplete frame")) + }; + } + let newline = available.iter().position(|byte| *byte == b'\n'); + let count = newline.map_or(available.len(), |index| index + 1); + if bytes.len() + count > limit { + return Err(io::Error::new(io::ErrorKind::InvalidData, "frame exceeds limit")); + } + bytes.extend_from_slice(&available[..count]); + reader.consume(count); + if newline.is_some() { + return Ok(Some(bytes)); + } + } +} + +fn connect(endpoint: &Endpoint, request: &ApiRequest) -> io::Result { + let mut stream = TcpStream::connect_timeout(&endpoint_addr(endpoint), CONNECT_TIMEOUT)?; + stream.set_nodelay(true)?; + stream.set_read_timeout(Some(COMMAND_TIMEOUT))?; + stream.set_write_timeout(Some(IO_TIMEOUT))?; + write_json_line(&mut stream, request)?; + stream.shutdown(Shutdown::Write)?; + Ok(stream) +} + +type Output = Arc) -> io::Result<()> + Send + Sync>; + +fn write_frame(output: &Output, value: &impl Serialize) -> io::Result<()> { + let mut bytes = serde_json::to_vec(value).map_err(io::Error::other)?; + if bytes.len() >= MAX_BRIDGE_FRAME { + return Err(io::Error::new(io::ErrorKind::InvalidData, "response exceeds limit")); + } + bytes.push(b'\n'); + output(bytes) +} + +struct Subscription { + id: u64, + cancelled: Arc, + shutdown: TcpStream, + thread: std::thread::JoinHandle<()>, +} + +impl Subscription { + fn stop(self) { + self.cancelled.store(true, Ordering::Release); + let _ = self.shutdown.shutdown(Shutdown::Both); + // A hostile/paused SSH peer can stop reading stdout indefinitely. Do not + // join its blocked writer. This CLI process owns no desktop session; + // exiting closes its remaining I/O and worker with the process. + drop(self.thread); + } +} + +fn stop_screen(endpoint: &Endpoint, target: (u64, u64), subscription: Subscription) { + subscription.cancelled.store(true, Ordering::Release); + // Retire the runtime watcher immediately on replacement/drop. If the local + // runtime already exited, closing the socket still bounds cleanup by heartbeat. + let request = ApiRequest::new( + endpoint.token.clone(), + "pane.screen.unsubscribe", + json!({ + "window_id":target.0,"pane_id":target.1,"subscription_id":subscription.id + }), + ); + let _ = connect(endpoint, &request); + subscription.stop(); +} + +fn start_subscription( + endpoint: &Endpoint, + request: Request, + output: Output, + stopped: Arc, +) -> io::Result { + let screen_only = request.method == "pane.screen.subscribe"; + let mut local = ApiRequest::new(endpoint.token.clone(), request.method, request.params); + local.id = request.id; + let stream = connect(endpoint, &local)?; + let shutdown = stream.try_clone()?; + let mut reader = BufReader::new(stream); + let first = read_frame(&mut reader, MAX_BRIDGE_FRAME)?.ok_or_else(|| { + io::Error::new(io::ErrorKind::UnexpectedEof, "missing subscription response") + })?; + let response: ApiResponse = serde_json::from_slice(&first).map_err(io::Error::other)?; + write_frame(&output, &response)?; + if !response.ok { + return Err(io::Error::other("runtime rejected subscription")); + } + let id = response + .result + .as_ref() + .and_then(|value| value["subscription_id"].as_u64()) + .ok_or_else(|| io::Error::other("invalid_subscription"))?; + let cancelled = Arc::new(AtomicBool::new(false)); + let worker_cancelled = cancelled.clone(); + reader.get_mut().set_read_timeout(None)?; + let thread = + std::thread::Builder::new().name("pebrel-mobile-events".into()).spawn(move || { + while !stopped.load(Ordering::Acquire) && !worker_cancelled.load(Ordering::Acquire) { + let Ok(Some(frame)) = read_frame(&mut reader, MAX_BRIDGE_FRAME) else { break }; + let Ok(event) = serde_json::from_slice::(&frame) else { break }; + if write_frame(&output, &event).is_err() { + break; + } + if screen_only && event["event"] == "pane.screen.error" { + return; + } + } + if screen_only { + if !worker_cancelled.load(Ordering::Acquire) && !stopped.load(Ordering::Acquire) { + let _ = write_frame(&output, &json!({ + "protocol":PROTOCOL_NAME,"version":PROTOCOL_VERSION, + "event":"pane.screen.error","subscription_id":id, + "error":{"code":"screen_stream_lost","message":"resubscribe for a full screen"} + })); + } + } else if !worker_cancelled.load(Ordering::Acquire) && !stopped.swap(true, Ordering::AcqRel) { + let _ = write_frame(&output, &json!({"type":"mobile.disconnected"})); + } + })?; + Ok(Subscription { id, cancelled, shutdown, thread }) +} + +fn screen_capabilities(endpoint: &Endpoint) -> (bool, bool) { + let request = ApiRequest::new(endpoint.token.clone(), "runtime.describe", json!({})); + let result = (|| -> io::Result { + let mut reader = BufReader::new(connect(endpoint, &request)?); + let bytes = read_frame(&mut reader, MAX_BRIDGE_FRAME)? + .ok_or_else(|| io::Error::other("missing_description"))?; + serde_json::from_slice(&bytes).map_err(io::Error::other) + })(); + let Some(value) = result.ok().filter(|reply| reply.ok).and_then(|reply| reply.result) else { + return (false, false); + }; + let stream = value["capabilities"] + .as_array() + .is_some_and(|caps| caps.iter().any(|cap| cap == "pane.screen.subscribe")); + let grid = value["features"] + .as_array() + .is_some_and(|features| features.iter().any(|feature| feature == "pane.read.screen.v1")); + (stream, grid) +} + +pub(crate) fn run(allow_input: bool) -> Result<(), Box> { + let stdout = Mutex::new(io::stdout()); + let output: Output = Arc::new(move |bytes| { + let mut stdout = stdout.lock().map_err(|_| io::Error::other("output lock poisoned"))?; + stdout.write_all(&bytes)?; + stdout.flush() + }); + let mut session = BridgeSession::open(allow_input, output)?; + let stdin = io::stdin(); + let mut input = stdin.lock(); + while !session.stopped.load(Ordering::Acquire) { + let Some(frame) = read_frame(&mut input, MAX_BRIDGE_REQUEST)? else { break }; + session.request(&frame)?; + } + Ok(()) +} + +/// The native settings transport and SSH CLI share this exact authorization, +/// endpoint pinning and subscription owner; networking never forks RPC policy. +pub(crate) struct BridgeSession { + endpoint: Endpoint, + output: Output, + stopped: Arc, + subscription: Option, + screen_subscription: Option<((u64, u64), Subscription)>, + allow_input: bool, + screen: super::mobile_screen::ScreenBaseline, +} + +impl BridgeSession { + pub(crate) fn set_input_permission(&mut self, allow_input: bool) { + self.allow_input = allow_input; + } + + pub(crate) fn open(allow_input: bool, output: Output) -> io::Result { + let endpoint = + read_endpoint().ok_or_else(|| io::Error::other("no resident Pebrel runtime"))?; + let stopped = Arc::new(AtomicBool::new(false)); + let (screen_stream, screen_grid) = screen_capabilities(&endpoint); + write_frame( + &output, + &json!({ + "type": "mobile.ready", "protocol": "pebrel.mobile.ssh", "version": 1, + "capabilities": { + "snapshot": true, "read_tail": true, "state_subscription": true, + "input": allow_input, "exclusive_input": false, "replay_notifications": false, + "terminal_grid_stream": screen_stream, "screen_delta": screen_grid, + "terminal_grid": screen_grid + }, + "max_request_bytes": MAX_BRIDGE_REQUEST, + "max_frame_bytes": MAX_BRIDGE_FRAME + }), + )?; + Ok(Self { + endpoint, + output, + stopped, + subscription: None, + screen_subscription: None, + allow_input, + screen: Default::default(), + }) + } + + pub(crate) fn request(&mut self, frame: &[u8]) -> io::Result<()> { + if self.stopped.load(Ordering::Acquire) || frame.len() > MAX_BRIDGE_REQUEST { + return Err(io::Error::other("mobile_connection_closed")); + } + let Self { + endpoint, + output, + stopped, + subscription, + screen_subscription, + allow_input, + screen, + } = self; + let request: Request = match serde_json::from_slice(&frame) { + Ok(request) => request, + Err(_) => { + write_frame( + &output, + &ApiResponse::failure( + "invalid", + ApiError::new("invalid_request", "invalid mobile request"), + ), + )?; + return Ok(()); + }, + }; + if let Err(error) = request.validate(*allow_input) { + write_frame(&output, &ApiResponse::failure(request.id, error))?; + return Ok(()); + } + if request.method == "events.subscribe" { + if subscription.is_some() { + write_frame( + &output, + &ApiResponse::failure( + request.id, + ApiError::new("already_subscribed", "one state subscription per channel"), + ), + )?; + } else { + *subscription = + Some(start_subscription(endpoint, request, output.clone(), stopped.clone())?); + } + return Ok(()); + } + if request.method == "pane.screen.subscribe" { + let target = ( + request.params["window_id"].as_u64().unwrap(), + request.params["pane_id"].as_u64().unwrap(), + ); + if let Some((target, previous)) = screen_subscription.take() { + stop_screen(endpoint, target, previous); + } + *screen_subscription = Some(( + target, + start_subscription(endpoint, request, output.clone(), stopped.clone())?, + )); + return Ok(()); + } + let unsubscribe = request.method == "pane.screen.unsubscribe"; + if unsubscribe || request.method == "pane.screen.ack" { + let owned = screen_subscription.as_ref().is_some_and(|(target, sub)| { + request.params["subscription_id"].as_u64() == Some(sub.id) + && request.params["window_id"].as_u64() == Some(target.0) + && request.params["pane_id"].as_u64() == Some(target.1) + }); + if !owned { + write_frame( + output, + &ApiResponse::failure( + request.id, + ApiError::invalid_params("screen subscription belongs to another link"), + ), + )?; + return Ok(()); + } + // Prevent intentional teardown from reporting a connection loss. + if unsubscribe { + if let Some((_, sub)) = screen_subscription.as_ref() { + sub.cancelled.store(true, Ordering::Release); + } + } + } + let mut local = ApiRequest::new(endpoint.token.clone(), request.method, request.params); + local.id = request.id.clone(); + let input_reply = matches!(local.method.as_str(), "pane.prompt" | "pane.send_key"); + // This extension belongs to the link, not the resident Runtime API. + let baseline = if local.method == "pane.read" && local.params["screen"] == true { + local + .params + .as_object_mut() + .and_then(|params| params.remove("screen_since")) + .and_then(|value| value.as_u64()) + } else { + None + }; + // Never rediscover the endpoint mid-channel: a runtime replacement + // must cause disconnect, not retarget a queued prompt to a new pane. + let response = (|| -> io::Result { + let mut reader = BufReader::new(connect(&endpoint, &local)?); + let frame = read_frame(&mut reader, MAX_BRIDGE_FRAME)? + .ok_or_else(|| io::Error::new(io::ErrorKind::UnexpectedEof, "missing response"))?; + serde_json::from_slice(&frame).map_err(io::Error::other) + })(); + match response { + Ok(mut response) => { + // Input acknowledgements need the result, not a repeated copy of + // every window/tab. State already has its own ordered subscription. + if input_reply { + if let Some(result) = response.result.as_mut().and_then(Value::as_object_mut) { + result.remove("snapshot"); + } + } + if response.ok + && let (Some(result), Some(sequence)) = (&mut response.result, baseline) + { + screen.encode(result, sequence); + } + write_frame(&output, &response)?; + if unsubscribe { + if let Some((_, sub)) = screen_subscription.take() { + sub.stop(); + } + } + }, + Err(_) => { + write_frame( + &output, + &ApiResponse::failure( + request.id, + ApiError::new( + "runtime_connection_lost", + "delivery may be unknown; do not replay input", + ), + ), + )?; + stopped.store(true, Ordering::Release); + return Err(io::Error::other("runtime_connection_lost")); + }, + } + Ok(()) + } +} + +impl Drop for BridgeSession { + fn drop(&mut self) { + self.stopped.store(true, Ordering::Release); + if let Some(subscription) = self.subscription.take() { + subscription.stop(); + } + if let Some((target, subscription)) = self.screen_subscription.take() { + stop_screen(&self.endpoint, target, subscription); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ssh_and_relay_share_wire_budgets() { + assert_eq!(bridge_policy()["maxFrameBytes"].as_u64(), Some(MAX_BRIDGE_FRAME as u64)); + assert_eq!(bridge_policy()["maxRequestBytes"].as_u64(), Some(MAX_BRIDGE_REQUEST as u64)); + } + + #[test] + fn git_requires_explicit_targets_and_live_write_permission() { + for method in [ + "git.status", + "git.diff", + "git.history", + "git.stage", + "git.unstage", + "git.commit", + "git.fetch", + "git.pull", + "git.push", + ] { + let mut request = Request { + id: "git".into(), + method: method.into(), + params: json!({"window_id":1,"pane_id":2}), + }; + assert!(request.validate(true).is_ok(), "{method}"); + assert_eq!( + request.validate(false).is_ok(), + matches!(method, "git.status" | "git.diff" | "git.history") + ); + request.params["pane_id"] = json!(0); + assert!(request.validate(true).is_err(), "{method}"); + } + } + + #[test] + fn readonly_default_denies_writes_and_unlisted_methods() { + for method in + ["pane.prompt", "pane.send_key", "pane.exec", "window.close", "runtime.orchestrate"] + { + let request = Request { + id: "1".into(), + method: method.into(), + params: json!({"window_id":1,"pane_id":2}), + }; + assert!(request.validate(false).is_err(), "{method}"); + } + } + + #[test] + fn explicit_input_still_requires_complete_target_and_allowlist() { + let mut request = + Request { id: "2".into(), method: "pane.prompt".into(), params: json!({"pane_id":2}) }; + assert!(request.validate(true).is_err()); + request.params["window_id"] = json!(1); + assert!(request.validate(true).is_ok()); + request.method = "pane.exec".into(); + assert!(request.validate(true).is_err()); + request.method = "pane.read".into(); + assert!(request.validate(false).is_ok()); + } + + #[test] + fn frames_are_bounded_and_truncation_is_not_a_valid_request() { + let mut input = io::Cursor::new(b"abc\nnext\n"); + assert_eq!(read_frame(&mut input, 4).unwrap().unwrap(), b"abc\n"); + assert!(read_frame(&mut input, 4).is_err()); + assert!(read_frame(&mut io::Cursor::new(b"abc"), 8).is_err()); + assert!(read_frame(&mut io::Cursor::new(b""), 8).unwrap().is_none()); + } + + #[test] + fn caller_cannot_supply_the_local_runtime_token() { + assert!( + serde_json::from_value::(json!({ + "id":"1", "method":"runtime.snapshot", "token":"attacker" + })) + .is_err() + ); + } +} diff --git a/nebula_app/src/runtime_api/mobile_screen.rs b/nebula_app/src/runtime_api/mobile_screen.rs new file mode 100644 index 0000000..66b7d69 --- /dev/null +++ b/nebula_app/src/runtime_api/mobile_screen.rs @@ -0,0 +1,105 @@ +//! Per-link, single-pane screen baseline. Only acknowledged baselines receive +//! row deltas; switching panes, reconnecting or missing a baseline sends a full +//! snapshot. No terminal bytes or user input are replayed by this adapter. +use serde_json::{Value, json}; + +#[derive(Clone, Default)] +pub(super) struct ScreenBaseline { + target: Option<(u64, u64)>, + screen: Option, + sequence: u64, +} + +impl ScreenBaseline { + pub(super) fn encode(&mut self, result: &mut Value, acknowledged: u64) { + let Some(target) = result["window_id"].as_u64().zip(result["pane_id"].as_u64()) else { + return; + }; + let Some(screen) = result.get("screen").filter(|value| value.is_object()).cloned() else { + return; + }; + let compatible = self.target == Some(target) + && acknowledged == self.sequence + && self.screen.as_ref().is_some_and(|previous| { + previous["version"] == screen["version"] + && previous["columns"] == screen["columns"] + && previous["rows"].as_array().map(Vec::len) + == screen["rows"].as_array().map(Vec::len) + }); + let base = self.sequence; + if self.target != Some(target) || self.screen.as_ref() != Some(&screen) { + self.sequence += 1; + } + result["screen_seq"] = json!(self.sequence); + if compatible { + let previous = self.screen.as_ref().unwrap(); + let rows: Vec<_> = screen["rows"] + .as_array() + .unwrap() + .iter() + .enumerate() + .filter(|(index, row)| previous["rows"][*index] != **row) + .map(|(index, row)| json!([index, row])) + .collect(); + let mut delta = json!({"base":base,"rows":rows,"cursor":screen["cursor"],"palette":screen["palette"]}); + if let Some(wrapped) = screen.get("wrapped") { + delta["wrapped"] = wrapped.clone(); + } + // During a full redraw the normal snapshot can be smaller than indexed rows. + if serde_json::to_vec(&delta).unwrap().len() + < serde_json::to_vec(&screen).unwrap().len() + { + result.as_object_mut().unwrap().remove("screen"); + result["screen_delta"] = delta; + } + } + self.target = Some(target); + self.screen = Some(screen); + } +} + +#[cfg(test)] +mod tests { + use super::*; + fn frame(pane: u64) -> Value { + json!({"window_id":1,"pane_id":pane,"screen":{"version":1,"columns":120, + "rows":vec![vec![json!([" ",1,-257,-258,0]);120];100],"cursor":[0,99,1],"palette":[]}}) + } + + #[test] + fn mobile_latency_one_changed_row_does_not_resend_one_hundred_rows() { + let mut cache = ScreenBaseline::default(); + let mut first = frame(2); + cache.encode(&mut first, 0); + assert!(first.get("screen").is_some()); + let mut changed = frame(2); + changed["screen"]["rows"][99][0][0] = json!("a"); + let full_size = serde_json::to_vec(&changed).unwrap().len(); + cache.encode(&mut changed, first["screen_seq"].as_u64().unwrap()); + assert!(changed.get("screen").is_none()); + assert_eq!(changed["screen_delta"]["rows"].as_array().unwrap().len(), 1); + assert!(serde_json::to_vec(&changed).unwrap().len() < full_size / 50); + let mut missing = frame(2); + cache.encode(&mut missing, 0); + assert!(missing.get("screen").is_some()); + let mut switched = frame(3); + cache.encode(&mut switched, missing["screen_seq"].as_u64().unwrap()); + assert!(switched.get("screen").is_some()); + } + + #[test] + fn mobile_latency_unchanged_screen_keeps_revision_and_resize_requires_snapshot() { + let mut cache = ScreenBaseline::default(); + let mut first = frame(2); + cache.encode(&mut first, 0); + let sequence = first["screen_seq"].as_u64().unwrap(); + let mut same = frame(2); + cache.encode(&mut same, sequence); + assert_eq!(same["screen_seq"], sequence); + assert!(same["screen_delta"]["rows"].as_array().unwrap().is_empty()); + let mut resized = frame(2); + resized["screen"]["rows"].as_array_mut().unwrap().pop(); + cache.encode(&mut resized, sequence); + assert!(resized.get("screen").is_some()); + } +} diff --git a/nebula_app/src/runtime_api/shortcuts.rs b/nebula_app/src/runtime_api/shortcuts.rs index 5953e33..7a14306 100644 --- a/nebula_app/src/runtime_api/shortcuts.rs +++ b/nebula_app/src/runtime_api/shortcuts.rs @@ -888,6 +888,8 @@ mod tests { active_tab: 0, focused_pane_id: Some(pane_id), tabs: vec![RuntimeTab { + tab_id: None, + file: None, index: 0, active: true, label: "test".into(), diff --git a/nebula_app/src/runtime_api/tabs.rs b/nebula_app/src/runtime_api/tabs.rs new file mode 100644 index 0000000..f31f18b --- /dev/null +++ b/nebula_app/src/runtime_api/tabs.rs @@ -0,0 +1,142 @@ +//! Shared-tab addressing and bounded file-read requests. Tab order is not identity. + +use super::{ApiError, ApiRequest, RuntimeCommand, parse_params}; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +pub const MAX_FILE_BYTES: usize = 16 * 1024 * 1024; +pub const MAX_CHUNK_BYTES: usize = 64 * 1024; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TabId(pub String); + +impl Default for TabId { + fn default() -> Self { + use std::sync::{ + LazyLock, + atomic::{AtomicU64, Ordering}, + }; + static NEXT: AtomicU64 = AtomicU64::new(1); + static INSTANCE: LazyLock = LazyLock::new(|| { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_nanos() as u64 + ^ u64::from(std::process::id()) + }); + // 不持久化:重启、关闭后重新打开都必须获得新身份,迟到请求不得命中替身。 + Self(format!("{:016x}{:016x}", *INSTANCE, NEXT.fetch_add(1, Ordering::Relaxed))) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct FileInfo { + pub path: String, + pub remote: bool, + pub dirty: bool, + pub saving: bool, + pub ready: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub revision: Option, +} + +#[derive(Clone, Debug)] +pub enum Request { + Focus { tab_id: String }, + Close { tab_id: String }, + Open { path: PathBuf }, + Read { tab_id: String, offset: usize, limit: usize, revision: Option }, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct TargetParams { + window_id: u64, + tab_id: String, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct OpenParams { + window_id: u64, + path: PathBuf, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct ReadParams { + window_id: u64, + tab_id: String, + #[serde(default)] + offset: usize, + #[serde(default = "default_limit")] + limit: usize, + #[serde(default)] + revision: Option, +} + +fn default_limit() -> usize { + MAX_CHUNK_BYTES +} + +fn validate_target(window_id: u64, tab_id: &str) -> Result<(), ApiError> { + if window_id == 0 || tab_id.len() != 32 || !tab_id.bytes().all(|b| b.is_ascii_hexdigit()) { + return Err(ApiError::invalid_params( + "an explicit window_id and snapshot tab_id are required", + )); + } + Ok(()) +} + +pub(super) fn parse(request: &ApiRequest) -> Result { + let (window_id, request) = match request.method.as_str() { + "tab.focus" | "tab.close" => { + let params: TargetParams = parse_params(&request.params)?; + validate_target(params.window_id, ¶ms.tab_id)?; + let operation = if request.method == "tab.focus" { + Request::Focus { tab_id: params.tab_id } + } else { + Request::Close { tab_id: params.tab_id } + }; + (params.window_id, operation) + }, + "tab.open" => { + let params: OpenParams = parse_params(&request.params)?; + let path = params.path.to_string_lossy(); + if params.window_id == 0 + || !params.path.is_absolute() + || path.len() > 4096 + || path.chars().any(char::is_control) + { + return Err(ApiError::invalid_params( + "window_id and an absolute file path are required", + )); + } + (params.window_id, Request::Open { path: params.path }) + }, + "tab.read" => { + let params: ReadParams = parse_params(&request.params)?; + validate_target(params.window_id, ¶ms.tab_id)?; + if !(4..=MAX_CHUNK_BYTES).contains(¶ms.limit) + || params.offset > MAX_FILE_BYTES + || params.revision.as_ref().is_some_and(|s| s.is_empty() || s.len() > 128) + || (params.offset > 0 && params.revision.is_none()) + { + return Err(ApiError::invalid_params( + "invalid chunk range or missing file revision", + )); + } + ( + params.window_id, + Request::Read { + tab_id: params.tab_id, + offset: params.offset, + limit: params.limit, + revision: params.revision, + }, + ) + }, + _ => return Err(ApiError::new("method_not_found", "unknown tab operation")), + }; + Ok(RuntimeCommand::Tab { window_id, request }) +} diff --git a/nebula_app/src/runtime_api/terminal_read.rs b/nebula_app/src/runtime_api/terminal_read.rs new file mode 100644 index 0000000..6d89919 --- /dev/null +++ b/nebula_app/src/runtime_api/terminal_read.rs @@ -0,0 +1,299 @@ +//! 终端模型的只读投影:纯文本尾部与有界彩色网格共用目标和生命周期。 +use super::{ApiError, MAX_READ_BYTES, RuntimeTaskState}; +use nebula_terminal::event::EventListener; +use nebula_terminal::grid::Dimensions; +use nebula_terminal::index::{Column, Line, Point}; +use nebula_terminal::term::{Term, cell::Flags}; +use nebula_terminal::vte::ansi::{Color, CursorShape, Rgb}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct RuntimePaneRead { + pub window_id: u64, + pub pane_id: u64, + pub text: String, + pub requested_lines: usize, + pub returned_lines: usize, + pub history_available: usize, + pub truncated: bool, + pub task_state: RuntimeTaskState, + pub exited: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub exit_reason: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub screen: Option, +} + +/// Screen v1 uses logical cells, not ANSI replay; wide spacers never become extra glyphs. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct RuntimeTerminalScreen { + pub version: u8, + pub columns: usize, + pub rows: Vec>, + pub palette: Vec<(usize, u32)>, + pub cursor: (usize, usize, u8), + #[serde(default)] + pub wrapped: Vec, +} + +pub(crate) fn capture_terminal_screen( + term: &Term, + palette_color: impl Fn(usize) -> Rgb, +) -> Result { + let columns = term.columns(); + let lines = term.screen_lines(); + if !(1..=400).contains(&columns) || !(1..=200).contains(&lines) || columns * lines > 40_000 { + return Err(ApiError::new( + "screen_too_large", + "screen v1 requires at most 400 columns, 200 rows and 40000 cells; resize the desktop pane", + )); + } + let mut rows = Vec::with_capacity(lines); + let mut text_bytes = 0; + for y in 0..lines { + let mut row = Vec::with_capacity(columns); + let mut x = 0; + while x < columns { + let cell = &term.grid()[Point::new(Line(y as i32), Column(x))]; + let spacer = + cell.flags.intersects(Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER); + let width = if !spacer && cell.flags.contains(Flags::WIDE_CHAR) && x + 1 < columns { + 2 + } else { + 1 + }; + let mut glyph = + if spacer || cell.c.is_control() { " ".to_owned() } else { cell.c.to_string() }; + if !spacer && let Some(extra) = cell.zerowidth() { + glyph.extend(extra.iter().copied().filter(|ch| !ch.is_control())); + } + text_bytes += glyph.len(); + if glyph.len() > 256 || text_bytes > 128 * 1024 { + return Err(ApiError::new( + "screen_too_large", + "screen glyphs exceed the screen v1 text budget", + )); + } + let mut foreground = wire_color(cell.fg, cell.flags.contains(Flags::BOLD)); + let mut background = wire_color(cell.bg, false); + if cell.flags.contains(Flags::INVERSE) { + std::mem::swap(&mut foreground, &mut background); + } + let flags = u8::from(cell.flags.contains(Flags::BOLD)) + | (u8::from(cell.flags.contains(Flags::ITALIC)) << 1) + | (u8::from(cell.flags.intersects(Flags::ALL_UNDERLINES)) << 2) + | (u8::from(cell.flags.contains(Flags::STRIKEOUT)) << 3) + | (u8::from(cell.flags.contains(Flags::DIM)) << 4) + | (u8::from(cell.flags.contains(Flags::HIDDEN)) << 5); + row.push((glyph, width as u8, foreground, background, flags)); + x += width; + } + rows.push(row); + } + let cursor = term.renderable_content().cursor; + let visible = cursor.shape != CursorShape::Hidden + && cursor.point.line.0 >= 0 + && (cursor.point.line.0 as usize) < lines; + // 始终读取当前网格,不随电脑端上翻历史改变手机的实时画面。 + Ok(RuntimeTerminalScreen { + version: 1, + columns, + rows, + wrapped: (0..lines) + .map(|y| { + term.grid()[Point::new(Line(y as i32), Column(columns - 1))] + .flags + .contains(Flags::WRAPLINE) + }) + .collect(), + palette: (0..nebula_terminal::term::color::COUNT) + .map(|index| (index, packed_rgb(palette_color(index)))) + .collect(), + cursor: ( + cursor.point.column.0.min(columns - 1), + cursor.point.line.0.max(0) as usize, + u8::from(visible), + ), + }) +} + +fn packed_rgb(color: Rgb) -> u32 { + (u32::from(color.r) << 16) | (u32::from(color.g) << 8) | u32::from(color.b) +} + +fn wire_color(color: Color, bold: bool) -> i32 { + let index = match color { + Color::Spec(rgb) => return packed_rgb(rgb) as i32, + Color::Named(named) => named as usize, + Color::Indexed(index) => index as usize, + }; + let index = if bold && index < 8 { index + 8 } else { index }; + -(index as i32 + 1) +} + +/// Read the logical tail of the terminal model. The range is anchored at the +/// buffer bottom, never at `display_offset`, so a user scrolling through +/// history cannot change what an external agent observes. +pub(crate) fn capture_terminal_tail( + term: &Term, + window_id: u64, + pane_id: u64, + requested_lines: usize, + task_state: RuntimeTaskState, + exited: bool, + exit_reason: Option, +) -> RuntimePaneRead { + let columns = term.columns(); + let screen_lines = term.screen_lines(); + let total_lines = term.total_lines(); + let history_available = total_lines.saturating_sub(screen_lines); + if columns == 0 || screen_lines == 0 || total_lines == 0 { + return RuntimePaneRead { + window_id, + pane_id, + text: String::new(), + requested_lines, + returned_lines: 0, + history_available, + truncated: false, + task_state, + exited, + exit_reason, + screen: None, + }; + } + + let mut returned_lines = requested_lines.min(total_lines); + let end = Point::new(Line(screen_lines as i32 - 1), Column(columns - 1)); + let capture = |lines: usize| { + let start_line = screen_lines as i64 - lines as i64; + let start = Point::new(Line(start_line.max(-(history_available as i64)) as i32), Column(0)); + term.bounds_to_string(start, end) + }; + let mut text = capture(returned_lines); + + // Reduce by whole terminal rows first, preserving exact returned_lines. + // Only a pathological single row can fall through to UTF-8 byte slicing. + while text.len() > MAX_READ_BYTES && returned_lines > 1 { + let estimated = ((returned_lines as u128 * MAX_READ_BYTES as u128) / text.len() as u128) + .clamp(1, (returned_lines - 1) as u128) as usize; + returned_lines = estimated; + text = capture(returned_lines); + } + let mut byte_truncated = false; + if text.len() > MAX_READ_BYTES { + let mut start = text.len() - MAX_READ_BYTES; + while !text.is_char_boundary(start) { + start += 1; + } + text = text[start..].to_owned(); + byte_truncated = true; + } + + RuntimePaneRead { + window_id, + pane_id, + text, + requested_lines, + returned_lines, + history_available, + truncated: returned_lines < total_lines || byte_truncated, + task_state, + exited, + exit_reason, + screen: None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use nebula_terminal::event::VoidListener; + use nebula_terminal::grid::Scroll; + use nebula_terminal::term::test::TermSize; + use nebula_terminal::vte::ansi::{NamedColor, Processor}; + + fn terminal(columns: usize, lines: usize, text: &str) -> Term { + let mut term = Term::new(Default::default(), &TermSize::new(columns, lines), VoidListener); + let mut parser: Processor = Processor::new(); + parser.advance(&mut term, text.as_bytes()); + term + } + + fn capture(term: &Term) -> RuntimeTerminalScreen { + capture_terminal_screen(term, |index| { + term.colors()[index].unwrap_or(Rgb { r: 1, g: 2, b: 3 }) + }) + .unwrap() + } + + #[test] + fn screen_preserves_wide_combining_ansi_truecolor_and_styles() { + let term = + terminal(12, 2, "\x1b[1;3;4;9;31m中e\u{301}\x1b[0;38;2;12;34;56mX\x1b[7mY\x1b[0m\t"); + let screen = capture(&term); + let row = &screen.rows[0]; + assert_eq!(row[0], ("中".into(), 2, -10, -258, 15)); + assert_eq!(row[1].0, "e\u{301}"); + assert_eq!(row[2].2, 0x0c2238); + assert_eq!((row[3].2, row[3].3), (-258, 0x0c2238)); + assert_eq!(row.iter().map(|cell| usize::from(cell.1)).sum::(), 12); + assert!(row.iter().all(|cell| cell.0.chars().all(|ch| !ch.is_control()))); + assert_eq!(screen.palette.len(), 269); + } + + #[test] + fn screen_preserves_palette_overrides_and_cursor_visibility_without_reading_scrollback() { + let mut term = terminal(12, 2, "old\r\nsecond\r\nlatest\x1b]10;#123456\x07"); + let screen = capture(&term); + assert_eq!(screen.palette[NamedColor::Foreground as usize].1, 0x123456); + assert_eq!(screen.cursor, (6, 1, 1)); + term.scroll_display(Scroll::Top); + assert_eq!(capture(&term), screen); + let mut parser: Processor = Processor::new(); + parser.advance(&mut term, b"\x1b[?25l"); + assert_eq!(capture(&term).cursor.2, 0); + } + + #[test] + fn screen_limits_are_explicit_and_plain_text_remains_compatible() { + let term = terminal(401, 2, "latest"); + assert_eq!( + capture_terminal_screen(&term, |_| Rgb { r: 0, g: 0, b: 0 }).unwrap_err().code, + "screen_too_large" + ); + let tail = capture_terminal_tail(&term, 1, 2, 2, RuntimeTaskState::Idle, false, None); + let json = serde_json::to_value(tail).unwrap(); + assert!(json.get("screen").is_none()); + assert!(json["text"].as_str().unwrap().contains("latest")); + } + + #[test] + fn screen_request_is_opt_in_and_advertised() { + use crate::runtime_api::{ApiRequest, RuntimeCommand}; + for screen in [true, false] { + let request = ApiRequest::new( + "fixture".into(), + "pane.read", + serde_json::json!({"pane_id": 2, "screen":screen}), + ); + assert!( + matches!(RuntimeCommand::from_request(&request).unwrap(), RuntimeCommand::ReadPane { screen: value, .. } if value == screen) + ); + } + assert!( + super::super::transport::runtime_description()["features"] + .as_array() + .unwrap() + .iter() + .any(|feature| feature == "pane.read.screen.v1") + ); + } + + #[test] + fn screen_reports_physical_soft_wrap_without_turning_hard_breaks_into_continuations() { + let term = terminal(6, 3, "abcdefgh\r\nnext"); + let screen = capture(&term); + assert_eq!(screen.wrapped, vec![true, false, false]); + } +} diff --git a/nebula_app/src/runtime_api/tests.rs b/nebula_app/src/runtime_api/tests.rs index 01b4e6a..9d280d0 100644 --- a/nebula_app/src/runtime_api/tests.rs +++ b/nebula_app/src/runtime_api/tests.rs @@ -1,6 +1,85 @@ use super::command::wait_state_matches; use super::*; +#[test] +fn conversation_mutations_require_current_identity_and_read_cannot_smuggle_input() { + let identity = json!({"kind":"codex","session_id":"thread","epoch":3}); + let request = |method: &str, extra: Value| { + let mut params = json!({"window_id":1,"pane_id":2,"identity":identity}); + params.as_object_mut().unwrap().extend(extra.as_object().unwrap().clone()); + ApiRequest::new("token".into(), method, params) + }; + assert!(RuntimeCommand::from_request(&request("conversation.read", json!({}))).is_ok()); + assert!( + RuntimeCommand::from_request(&request("conversation.read", json!({"text":"echo bad"}))) + .is_err() + ); + assert!( + RuntimeCommand::from_request(&request( + "conversation.send", + json!({"text":"你好\nsecond line"}) + )) + .is_ok() + ); + assert!( + RuntimeCommand::from_request(&request( + "conversation.send", + json!({"text":"hello", "identity":{"kind":"codex","session_id":"thread"}}) + )) + .is_err() + ); + assert!( + RuntimeCommand::from_request(&request( + "conversation.choose", + json!({"prompt_id":"a".repeat(64),"option":0}) + )) + .is_ok() + ); + assert!( + RuntimeCommand::from_request(&request( + "conversation.choose", + json!({"prompt_id":"a".repeat(64),"option":9}) + )) + .is_err() + ); + assert!( + RuntimeCommand::from_request(&request("conversation.key", json!({"key":"Ctrl+C"}))).is_ok() + ); + assert!( + RuntimeCommand::from_request(&request("conversation.key", json!({"key":"Enter"}))).is_err() + ); + assert!( + RuntimeCommand::from_request(&request("conversation.read", json!({"key":"Esc"}))).is_err() + ); + assert!( + RuntimeCommand::from_request(&request( + "conversation.key", + json!({"key":"Esc", "identity":{"kind":"codex","session_id":"thread"}}) + )) + .is_err() + ); +} + +#[test] +fn shared_tab_requests_require_identity_and_bounded_revisioned_reads() { + let id = tabs::TabId::default(); + assert_ne!(id, tabs::TabId::default()); + assert_eq!(id, id.clone()); + let parse = |method, params| { + RuntimeCommand::from_request(&ApiRequest::new("token".into(), method, params)) + }; + assert!(matches!( + parse("tab.close", json!({"window_id": 7, "tab_id": id.0})).unwrap(), + RuntimeCommand::Tab { request: tabs::Request::Close { .. }, .. } + )); + assert!(parse("tab.read", json!({"window_id": 7, "tab_id": id.0, "limit": 65536})).is_ok()); + assert!(parse("tab.read", json!({"window_id": 7, "tab_id": id.0, "offset": 1})).is_err()); + assert!(parse("tab.read", json!({"window_id": 7, "tab_id": id.0, "limit": 65537})).is_err()); + assert!(parse("tab.close", json!({"window_id": 0, "tab_id": id.0})).is_err()); + assert!(parse("tab.close", json!({"window_id": 7, "tab_id": id.0, "tab_index": 1})).is_err()); + assert!(parse("tab.focus", json!({"window_id": 7, "tab_id": "stale-index"})).is_err()); +} + fn snapshot(state: RuntimeTaskState) -> RuntimeSnapshot { RuntimeSnapshot::new( 0, @@ -11,6 +90,8 @@ fn snapshot(state: RuntimeTaskState) -> RuntimeSnapshot { active_tab: 0, focused_pane_id: Some(3), tabs: vec![RuntimeTab { + tab_id: None, + file: None, index: 0, active: true, label: "test".into(), @@ -88,6 +169,8 @@ fn delegation_snapshot( focused_pane_id: Some(3), tabs: vec![ RuntimeTab { + tab_id: None, + file: None, index: 0, active: true, label: "origin".into(), @@ -105,6 +188,8 @@ fn delegation_snapshot( )], }, RuntimeTab { + tab_id: None, + file: None, index: 1, active: false, label: "worker".into(), diff --git a/nebula_app/src/runtime_api/transport.rs b/nebula_app/src/runtime_api/transport.rs index dbc0cfc..e0ffb67 100644 --- a/nebula_app/src/runtime_api/transport.rs +++ b/nebula_app/src/runtime_api/transport.rs @@ -137,7 +137,7 @@ pub(super) fn handle_legacy( } pub(super) fn runtime_description() -> Value { - json!({ + let mut description = json!({ "app_version": env!("VERSION"), "protocol": PROTOCOL_NAME, "protocol_version": PROTOCOL_VERSION, @@ -175,7 +175,9 @@ pub(super) fn runtime_description() -> Value { "pane.send_key", "pane.run", "pane.exec", - "pane.wait" + "pane.wait", + "git.status", "git.diff", "git.history", + "git.stage", "git.unstage", "git.commit", "git.fetch", "git.pull", "git.push" ], // Additive params cannot be detected from `capabilities`: an older // build ignores an unknown `after_seq` and still races. Clients that @@ -195,6 +197,7 @@ pub(super) fn runtime_description() -> Value { ,"cli.paste_sources" ,"layout.typed_mutations" ,"pane.exec.non_tty" + ,"pane.read.screen.v1" ], // 环境契约:pane 里的进程靠这些变量发现自己和控制面,不必扫进程或猜 // 端口。写进 describe 是为了让外部客户端能**探测**契约而不是硬编码变量 @@ -217,7 +220,26 @@ pub(super) fn runtime_description() -> Value { "pane": ["list", "read", "send", "paste", "wait", "exec", "close", "zoom", "resize"], "agent": ["list", "send", "delegate", "paste", "read", "wait"] } - }) + }); + if cfg!(feature = "gpui-shell") { + description["capabilities"].as_array_mut().expect("capabilities array").extend( + [ + "tab.focus", + "tab.open", + "tab.read", + "conversation.read", + "conversation.send", + "conversation.choose", + "conversation.key", + ] + .map(|method| json!(method)), + ); + description["features"] + .as_array_mut() + .expect("features array") + .push(json!("tab.identity_and_read")); + } + description } pub(super) fn subscribe_connection( diff --git a/nebula_app/src/runtime_exec.rs b/nebula_app/src/runtime_exec.rs index a02d196..6d63b0f 100644 --- a/nebula_app/src/runtime_exec.rs +++ b/nebula_app/src/runtime_exec.rs @@ -7,14 +7,15 @@ use std::collections::HashMap; use std::io::{self, Read}; use std::path::{Path, PathBuf}; -use std::process::{Child, Command, ExitStatus, Stdio}; +use std::process::{Command, ExitStatus, Stdio}; use std::sync::Arc; use std::time::{Duration, Instant}; use serde::Serialize; use serde_json::{Value, json}; -use crate::runtime_api::{ApiError, RuntimeDispatch}; +use crate::platform::process::{ProcessGroup, configure_process_group}; +use crate::runtime_api::{ApiError, RuntimeCommand, RuntimeDispatch}; #[derive(Clone, Debug)] enum ExecLocation { @@ -35,6 +36,35 @@ pub(crate) struct PaneExecContext { } impl PaneExecContext { + pub(crate) fn for_git(mut self) -> Self { + // 手机没有凭据交互通道;保留宿主 SSH/credential 配置,但不弹出凭据窗口。 + let vars = [ + ("GIT_TERMINAL_PROMPT", "0"), + ("GCM_INTERACTIVE", "never"), + ("SSH_ASKPASS_REQUIRE", "never"), + ]; + if matches!(self.location, ExecLocation::Wsl { .. }) { + let mut forwarded = self + .env + .get("WSLENV") + .cloned() + .or_else(|| std::env::var("WSLENV").ok()) + .unwrap_or_default(); + for (key, _) in vars { + if !forwarded.split(':').any(|entry| entry.split('/').next() == Some(key)) { + if !forwarded.is_empty() { + forwarded.push(':'); + } + forwarded.push_str(key); + forwarded.push_str("/u"); + } + } + self.env.insert("WSLENV".into(), forwarded); + } + self.env.extend(vars.map(|(key, value)| (key.into(), value.into()))); + self + } + pub(crate) fn shell_program(&self) -> Option<&str> { self.shell_program.as_deref() } @@ -226,17 +256,22 @@ fn build_command( Ok((command, execution)) } -pub(crate) fn spawn( - dispatch: Arc, - context: PaneExecContext, - cwd: String, - argv: Vec, - timeout_ms: u64, - max_output_bytes: usize, -) { +pub(crate) fn spawn(dispatch: Arc, context: PaneExecContext, cwd: String) { let worker_dispatch = dispatch.clone(); let result = std::thread::Builder::new().name("nebula-pane-exec".to_owned()).spawn(move || { - worker_dispatch.respond(execute(context, cwd, argv, timeout_ms, max_output_bytes)); + let result = match &worker_dispatch.command { + RuntimeCommand::Exec { argv, timeout_ms, max_output_bytes, .. } => { + execute(context, cwd, argv.clone(), *timeout_ms, *max_output_bytes) + }, + RuntimeCommand::Git { request, .. } => { + crate::runtime_api::git::execute(context, cwd, request) + }, + _ => Err(ApiError::new( + "invalid_runtime_command", + "command does not own a process worker", + )), + }; + worker_dispatch.respond(result); }); if let Err(error) = result { dispatch.respond(Err(ApiError::new( @@ -358,131 +393,6 @@ fn join_capture( }) } -#[cfg(unix)] -fn configure_process_group(command: &mut Command) { - use std::os::unix::process::CommandExt as _; - command.process_group(0); -} - -/// Windows:`pane.exec` 的子进程绝不允许弹出控制台窗口。 -/// -/// Pebrel 自己是 `windows_subsystem = "windows"` 的 GUI 进程(见 `main.rs`), -/// **没有控制台**可给子进程继承;不抑制的话 Windows 会给每条 exec 命令分配一个 -/// 新控制台,而默认终端应用是 Windows Terminal 的机器上那就是**弹一整扇窗口** -/// (同 [`crate::ssh_session`] 里 `ssh.exe -G` 那条注释说的现象)。 -/// -/// exec 的 stdin 是 null、stdout/stderr 走管道,从头到尾没有交互,也就不需要 -/// 控制台——和 wsl/git 那些 spawn 用 `CREATE_NO_WINDOW` 是同一条规矩。 -#[cfg(windows)] -fn configure_process_group(command: &mut Command) { - crate::platform::process::hidden_command(command); -} - -#[cfg(not(any(unix, windows)))] -fn configure_process_group(_: &mut Command) {} - -struct ProcessGroup { - #[cfg(windows)] - job: windows_sys::Win32::Foundation::HANDLE, - #[cfg(unix)] - process_group: i32, -} - -impl ProcessGroup { - fn attach(child: &Child) -> io::Result { - #[cfg(windows)] - { - use std::mem::{size_of, zeroed}; - use std::os::windows::io::AsRawHandle as _; - use std::ptr; - use windows_sys::Win32::Foundation::{CloseHandle, HANDLE}; - use windows_sys::Win32::System::JobObjects::{ - AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, - JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation, - SetInformationJobObject, - }; - - // SAFETY: all pointers reference initialized POD values for the duration of each - // call. The returned job handle is owned by ProcessGroup and closed exactly once. - unsafe { - let job = CreateJobObjectW(ptr::null(), ptr::null()); - if job.is_null() { - return Err(io::Error::last_os_error()); - } - let mut limits: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = zeroed(); - limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; - if SetInformationJobObject( - job, - JobObjectExtendedLimitInformation, - (&raw const limits).cast(), - size_of::() as u32, - ) == 0 - { - let error = io::Error::last_os_error(); - CloseHandle(job); - return Err(error); - } - if AssignProcessToJobObject(job, child.as_raw_handle() as HANDLE) == 0 { - let error = io::Error::last_os_error(); - CloseHandle(job); - return Err(error); - } - return Ok(Self { job }); - } - } - #[cfg(unix)] - { - Ok(Self { process_group: child.id() as i32 }) - } - #[cfg(not(any(unix, windows)))] - { - let _ = child; - Ok(Self {}) - } - } - - fn terminate(&self, child: &mut Child) { - #[cfg(windows)] - unsafe { - use windows_sys::Win32::System::JobObjects::TerminateJobObject; - let _ = TerminateJobObject(self.job, 1); - } - #[cfg(unix)] - unsafe { - let _ = libc::kill(-self.process_group, libc::SIGKILL); - } - let _ = child.kill(); - } - - fn finish(self) { - #[cfg(unix)] - unsafe { - let _ = libc::kill(-self.process_group, libc::SIGKILL); - } - // Windows uses JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE in Drop. - } -} - -impl Drop for ProcessGroup { - fn drop(&mut self) { - #[cfg(windows)] - { - // SAFETY: `job` is the live handle created and exclusively owned by this guard. - unsafe { - let _ = windows_sys::Win32::Foundation::CloseHandle(self.job); - } - } - #[cfg(unix)] - { - // A reader-thread creation failure must not leave the child tree - // alive with one inherited pipe still open. - unsafe { - let _ = libc::kill(-self.process_group, libc::SIGKILL); - } - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/nebula_app/src/ssh.rs b/nebula_app/src/ssh.rs index b4b61f3..fb1872a 100644 --- a/nebula_app/src/ssh.rs +++ b/nebula_app/src/ssh.rs @@ -543,14 +543,18 @@ pub fn ssh_config_hosts() -> Vec { } fn askpass_destination_from_args(args: &[String]) -> Option { + destination_argument(args).map(|(_, destination)| destination) +} + +fn destination_argument(args: &[String]) -> Option<(usize, String)> { let (mut login_user, mut port) = (None, None); let mut i = 0; while i < args.len() { let arg = &args[i]; if arg == "--" { - return args - .get(i + 1) - .map(|dest| format_destination(dest, login_user.as_deref(), port.as_deref())); + return args.get(i + 1).map(|dest| { + (i + 1, format_destination(dest, login_user.as_deref(), port.as_deref())) + }); } let bytes = arg.as_bytes(); if bytes.first() == Some(&b'-') && bytes.len() >= 2 { @@ -575,11 +579,27 @@ fn askpass_destination_from_args(args: &[String]) -> Option { i += if consumed_next { 2 } else { 1 }; continue; } - return Some(format_destination(arg, login_user.as_deref(), port.as_deref())); + return Some((i, format_destination(arg, login_user.as_deref(), port.as_deref()))); } None } +fn resolve_saved_destination( + args: &mut [String], + profiles: &crate::ssh_profiles::SshProfiles, +) -> Option { + let (index, destination) = destination_argument(args)?; + let identity = &args[index]; + let target = profiles.connection_destination(identity); + if target == identity { + return Some(destination); + } + // 系统 SSH 只接收真实地址;AskPass 仍使用副本自己的凭据键。 + let identity = identity.clone(); + args[index] = crate::ssh_session::ssh_config_probe_target(target).into_owned(); + Some(identity) +} + /// Per-pane data for launching SSH from the configured default shell. The /// password stays behind OpenSSH's AskPass contract and never enters this /// command or the terminal scrollback. @@ -683,13 +703,23 @@ fn slash_drive_path(path: &std::path::Path, prefix: &str) -> String { /// `nebula ssh` entrypoint. Returns the process exit code. #[cfg(windows)] -pub fn run(args: Vec) -> i32 { +pub fn run(mut args: Vec) -> i32 { use base64::Engine as _; use std::process::Command; + let profiles = match crate::ssh_profiles::SshProfiles::load( + &crate::display::nebula_data_dir().join("ssh_profiles.json"), + ) { + Ok(profiles) => profiles, + Err(error) => { + eprintln!("pebrel ssh: failed to load host profiles: {error}"); + return 1; + }, + }; + let destination = resolve_saved_destination(&mut args, &profiles); let ssh = find_ssh(); let mut cmd = Command::new(&ssh); - let askpass = askpass_destination_from_args(&args).and_then(|destination| { + let askpass = destination.and_then(|destination| { std::env::current_exe() .ok() .map(|exe| build_askpass_env(&exe, &destination, std::process::id() as u64)) @@ -969,4 +999,19 @@ mod tests { assert_eq!(parse(&["-i", "key file", "host"]), Some("host".into())); assert_eq!(parse(&["-V"]), None); } + + #[test] + fn copied_host_forwards_its_address_and_keeps_its_credential_identity() { + let mut profiles = crate::ssh_profiles::SshProfiles::default(); + profiles.duplicate_host("root@example.com:2222", "copy-host").unwrap(); + // 同名选项值不能被当作目标改写;远端命令也必须原样保留。 + let mut args: Vec = + ["-i", "copy-host", "--", "copy-host", "printf ok"].map(String::from).into(); + assert_eq!(resolve_saved_destination(&mut args, &profiles).as_deref(), Some("copy-host")); + assert_eq!(args, ["-i", "copy-host", "--", "ssh://root@example.com:2222", "printf ok"]); + profiles.duplicate_host("production-alias", "copy-alias").unwrap(); + let mut args = vec!["copy-alias".to_owned()]; + assert_eq!(resolve_saved_destination(&mut args, &profiles).as_deref(), Some("copy-alias")); + assert_eq!(args, ["production-alias"]); + } } diff --git a/nebula_app/src/ssh_profiles.rs b/nebula_app/src/ssh_profiles.rs index b358ea3..a3f874d 100644 --- a/nebula_app/src/ssh_profiles.rs +++ b/nebula_app/src/ssh_profiles.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Deserializer, Serialize}; #[path = "ssh_profiles/connection.rs"] mod connection; +pub(crate) mod duplication; #[path = "ssh_profiles/exchange.rs"] pub(crate) mod exchange; #[path = "ssh_profiles/organization.rs"] @@ -75,6 +76,9 @@ pub struct SshProfiles { usernames: Vec, #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")] organization: std::collections::BTreeMap, + /// 副本的列表/凭据身份独立于连接地址;旧主机没有此项,仍以地址作为身份。 + #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")] + targets: std::collections::BTreeMap, #[serde(skip)] source_bytes: Option>, } @@ -86,6 +90,7 @@ impl Default for SshProfiles { profiles: Vec::new(), usernames: Vec::new(), organization: Default::default(), + targets: Default::default(), source_bytes: None, } } @@ -107,12 +112,17 @@ impl SshProfiles { value.validate().map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; } normalize_usernames(&mut profiles.usernames); + profiles + .validate_targets() + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; profiles.version = PROFILE_VERSION; profiles.source_bytes = Some(data); Ok(profiles) } pub fn save(&mut self, path: &Path) -> io::Result<()> { + self.validate_targets() + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; if let Some(parent) = path.parent() { std::fs::create_dir_all(parent)?; } @@ -182,7 +192,9 @@ impl SshProfiles { pub fn usernames(&self) -> Vec { let mut usernames = self.usernames.clone(); for profile in &self.profiles { - if let Some(username) = destination_username(&profile.destination) { + if let Some(username) = + destination_username(self.connection_destination(&profile.destination)) + { push_unique_username(&mut usernames, username); } } @@ -236,6 +248,7 @@ impl SshProfiles { pub fn remove(&mut self, destination: &str) { self.profiles.retain(|profile| profile.destination != destination); self.organization.remove(destination); + self.targets.remove(destination); } pub fn jump_dependents(&self, destination: &str) -> Vec { @@ -252,6 +265,7 @@ impl SshProfiles { pub fn rename(&mut self, old: &str, new: &str) { let organization = self.organization.remove(old); + let target = self.targets.remove(old); if let Some(mut profile) = self.profiles.iter().find(|profile| profile.destination == old).cloned() { @@ -262,6 +276,9 @@ impl SshProfiles { if let Some(organization) = organization { self.organization.insert(new.to_owned(), organization); } + if let Some(target) = target { + self.targets.insert(new.to_owned(), target); + } for profile in &mut self.profiles { if profile.connection.jump_mode == SshHostJumpMode::Host && profile.connection.jump_host.trim() == old @@ -322,6 +339,106 @@ mod tests { use super::{SshAuthMode, SshProfileAuth, SshProfiles}; use std::path::PathBuf; + #[test] + fn host_copies_keep_independent_targets_metadata_and_csv_roundtrips() { + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join("ssh_profiles.json"); + let mut profiles = SshProfiles::default(); + let mut source = profiles.for_destination("root@example.com:2222"); + source.label = Some("生产".into()); + source.auth = SshAuthMode::PublicKey; + source.private_keys.push("key.pem".into()); + source.icon = Some("debian".into()); + source.connection.jump_mode = super::SshHostJumpMode::Host; + source.connection.jump_host = "bastion".into(); + profiles.upsert(source.clone()); + let organization = + super::HostOrganization::from_inputs("公司", "linux,db", "中文备注").unwrap(); + profiles.set_organization(&source.destination, organization.clone()).unwrap(); + assert_eq!(profiles.duplicate_host(&source.destination, "copy-a").unwrap(), "生产 1"); + assert_eq!(profiles.duplicate_host(&source.destination, "copy-b").unwrap(), "生产 2"); + profiles.duplicate_host("copy-a", "copy-c").unwrap(); + let mut expected = source.clone(); + expected.destination = "copy-a".into(); + expected.label = Some("生产 1".into()); + assert_eq!(profiles.for_destination("copy-a"), expected); + assert_eq!(profiles.organization("copy-a"), &organization); + assert_eq!(profiles.connection_destination("copy-c"), source.destination); + assert_eq!( + profiles.edited_identity(Some("copy-a"), "operator@other:2200").unwrap(), + "copy-a" + ); + assert_eq!(profiles.for_destination(&source.destination), source); + assert_eq!(profiles.connection_destination("copy-b"), source.destination); + profiles.remove("copy-a"); + profiles.save(&path).unwrap(); + let restored = SshProfiles::load(&path).unwrap(); + assert_eq!(restored.connection_destination("copy-b"), source.destination); + let csv = restored.export_csv(); + let mut imported = SshProfiles::default(); + imported.import_missing(&super::exchange::parse_csv(&csv).unwrap()).unwrap(); + assert_eq!(imported.connection_destination("copy-b"), source.destination); + assert_eq!(imported.organization("copy-c"), &organization); + assert_eq!( + imported.filter_hosts(vec!["copy-b".into()], "example.com", false, None), + vec!["copy-b"] + ); + } + + #[test] + fn copying_credentials_commits_only_after_success_and_rolls_back_failed_profile_writes() { + use crate::ssh_credentials::credential_target; + use std::cell::RefCell; + use std::collections::HashMap; + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join("ssh_profiles.json"); + let mut profiles = SshProfiles::default(); + let mut source = profiles.for_destination("root@host"); + source.connection.proxy_mode = super::SshHostProxyMode::Socks5; + source.connection.proxy_host = "proxy".into(); + source.connection.proxy_username = "user".into(); + let proxy_key = source.connection.proxy_credential_target("root@host").unwrap(); + let copied_proxy_key = source.connection.proxy_credential_target("copy-a").unwrap(); + profiles.upsert(source); + profiles.save(&path).unwrap(); + let stale = profiles.clone(); + let original = HashMap::from([ + (credential_target("root@host"), b"password".to_vec()), + (proxy_key, b"proxy-secret".to_vec()), + ]); + let secrets = RefCell::new(original.clone()); + let copy = |profiles, identity| { + super::duplication::save_duplicate( + profiles, + &path, + "root@host", + identity, + |key| Ok(secrets.borrow().get(key).cloned()), + |key, value| { + secrets.borrow_mut().insert(key.to_owned(), value.to_vec()); + Ok(()) + }, + |key| { + secrets.borrow_mut().remove(key); + Ok(()) + }, + ) + }; + let (updated, _) = copy(profiles, "copy-a").unwrap(); + assert_eq!(secrets.borrow()[&credential_target("copy-a")], b"password"); + assert_eq!(secrets.borrow()[&copied_proxy_key], b"proxy-secret"); + let saved = std::fs::read(&path).unwrap(); + let credentials = secrets.borrow().clone(); + assert!(copy(stale, "copy-b").is_err(), "stale writes must not overwrite another copy"); + assert_eq!(*secrets.borrow(), credentials); + assert_eq!(std::fs::read(&path).unwrap(), saved); + assert!(!String::from_utf8(saved).unwrap().contains("proxy-secret")); + assert_eq!(updated.connection_destination("copy-a"), "root@host"); + for (key, value) in original { + assert_eq!(secrets.borrow()[&key], value); + } + } + #[test] fn missing_profile_defaults_to_auto_without_keys() { let profiles = SshProfiles::default(); diff --git a/nebula_app/src/ssh_profiles/duplication.rs b/nebula_app/src/ssh_profiles/duplication.rs new file mode 100644 index 0000000..b956426 --- /dev/null +++ b/nebula_app/src/ssh_profiles/duplication.rs @@ -0,0 +1,118 @@ +//! Independent saved-host identities; endpoint and secret copying stay out of the UI. + +use super::*; + +impl SshProfiles { + pub(crate) fn connection_destination<'a>(&'a self, identity: &'a str) -> &'a str { + self.targets.get(identity).map(String::as_str).unwrap_or(identity) + } + + pub(crate) fn set_connection_destination( + &mut self, + identity: &str, + target: &str, + ) -> Result<(), String> { + validate_ssh_destination(target)?; + if !self.contains(identity) || self.targets.contains_key(target) || identity == target { + return Err("Invalid copied host target".into()); + } + self.targets.insert(identity.to_owned(), target.to_owned()); + Ok(()) + } + + pub(super) fn validate_targets(&self) -> Result<(), String> { + for (identity, target) in &self.targets { + validate_ssh_destination(identity)?; + validate_ssh_destination(target)?; + if !self.contains(identity) || self.targets.contains_key(target) || identity == target { + return Err("Invalid copied host target".into()); + } + } + Ok(()) + } + + /// 编辑副本时只改变它自己的连接目标,不能 upsert 到原主机的地址键上。 + pub(crate) fn edited_identity( + &mut self, + original: Option<&str>, + target: &str, + ) -> Result { + if let Some(identity) = original.filter(|identity| self.targets.contains_key(*identity)) { + self.set_connection_destination(identity, target)?; + Ok(identity.to_owned()) + } else { + Ok(target.to_owned()) + } + } + + pub(crate) fn duplicate_host( + &mut self, + source: &str, + identity: &str, + ) -> Result { + validate_ssh_destination(identity)?; + if identity == source || self.contains(identity) { + return Err("Host identity already exists".into()); + } + let target = self.connection_destination(source).to_owned(); + validate_ssh_destination(&target)?; + if identity == target { + return Err("Host identity already exists".into()); + } + let mut profile = self.for_destination(source); + let prefix = + profile.label.as_deref().filter(|label| !label.trim().is_empty()).unwrap_or(&target); + let label = self.next_default_label(prefix.trim()); + profile.destination = identity.to_owned(); + profile.label = Some(label.clone()); + let organization = self.organization(source).clone(); + self.upsert(profile); + self.set_connection_destination(identity, &target)?; + self.set_organization(identity, organization)?; + Ok(label) + } +} + +/// 写入副本自己的凭据后才提交 Profile;失败只撤回新凭据,原主机始终不动。 +pub(crate) fn save_duplicate( + mut profiles: SshProfiles, + path: &Path, + source: &str, + identity: &str, + mut load: impl FnMut(&str) -> io::Result>>, + mut store: impl FnMut(&str, &[u8]) -> io::Result<()>, + mut delete: impl FnMut(&str) -> io::Result<()>, +) -> io::Result<(SshProfiles, String)> { + let connection = profiles.for_destination(source).connection; + let label = profiles.duplicate_host(source, identity).map_err(io::Error::other)?; + let mut keys = vec![( + crate::ssh_credentials::credential_target(source), + crate::ssh_credentials::credential_target(identity), + )]; + if let Some(old) = connection.proxy_credential_target(source) { + keys.push((old, connection.proxy_credential_target(identity).expect("same proxy options"))); + } + let mut written = Vec::new(); + let result = (|| { + for (old, new) in keys { + if load(&new)?.is_some() { + return Err(io::Error::other("Copied host credential identity already exists")); + } + if let Some(secret) = load(&old)? { + let secret = zeroize::Zeroizing::new(secret); + store(&new, &secret)?; + written.push(new); + } + } + profiles.save(path) + })(); + if let Err(error) = result { + let cleanup: Vec<_> = written.iter().filter_map(|key| delete(key).err()).collect(); + return if cleanup.is_empty() { + Err(error) + } else { + Err(io::Error::other(format!("{error}; copied credential cleanup failed: {cleanup:?}"))) + }; + } + Ok((profiles, label)) +} diff --git a/nebula_app/src/ssh_profiles/exchange.rs b/nebula_app/src/ssh_profiles/exchange.rs index 5943bb4..f338dc6 100644 --- a/nebula_app/src/ssh_profiles/exchange.rs +++ b/nebula_app/src/ssh_profiles/exchange.rs @@ -8,6 +8,7 @@ const MAX_IMPORT_HOSTS: usize = 10_000; #[derive(Clone, Debug)] pub(crate) struct HostRecord { pub destination: String, + pub connect_to: Option, pub label: String, pub organization: HostOrganization, } @@ -30,6 +31,7 @@ pub(crate) fn parse_csv(text: &str) -> Result, String> { let notes = column(&["notes", "description"]); let username = column(&["username", "user"]); let port = column(&["port"]); + let connect_to = column(&["connect_to"]); let secrets: Vec<_> = header .iter() .enumerate() @@ -80,7 +82,11 @@ pub(crate) fn parse_csv(text: &str) -> Result, String> { } let organization = HostOrganization::from_inputs(field(group), field(tags), field(notes)) .map_err(|e| format!("Row {}: {e}", index + 1))?; - records.push(HostRecord { destination: target, label, organization }); + let connect_to = (!field(connect_to).is_empty()).then(|| field(connect_to).to_owned()); + if let Some(address) = &connect_to { + validate_ssh_destination(address).map_err(|e| format!("Row {}: {e}", index + 1))?; + } + records.push(HostRecord { destination: target, connect_to, label, organization }); if records.len() > MAX_IMPORT_HOSTS { return Err("Host import exceeds 10000 entries".into()); } @@ -110,14 +116,18 @@ impl SshProfiles { connection: Default::default(), }); updated.set_organization(&record.destination, record.organization.clone())?; + if let Some(target) = &record.connect_to { + updated.set_connection_destination(&record.destination, target)?; + } added += 1; } + updated.validate_targets()?; *self = updated; Ok(added) } pub(crate) fn export_csv(&self) -> String { - let mut csv = "destination,label,group,tags,notes\r\n".to_owned(); + let mut csv = "destination,label,group,tags,notes,connect_to\r\n".to_owned(); for profile in &self.profiles { let meta = self.organization(&profile.destination); let tags = meta.tags.join(";"); @@ -127,6 +137,7 @@ impl SshProfiles { &meta.group, &tags, &meta.notes, + self.targets.get(&profile.destination).map(String::as_str).unwrap_or_default(), ]; csv.push_str(&fields.into_iter().map(quote_csv).collect::>().join(",")); csv.push_str("\r\n"); diff --git a/nebula_app/src/ssh_profiles/organization.rs b/nebula_app/src/ssh_profiles/organization.rs index 59e28d3..9ce452b 100644 --- a/nebula_app/src/ssh_profiles/organization.rs +++ b/nebula_app/src/ssh_profiles/organization.rs @@ -108,7 +108,7 @@ impl SshProfiles { } let searchable = format!( "{} {} {} {} {}", - destination, + self.connection_destination(destination), profile.and_then(|p| p.label.as_deref()).unwrap_or_default(), organization.group, organization.tags.join(" "), diff --git a/nebula_app/src/ssh_session.rs b/nebula_app/src/ssh_session.rs index 4b8761b..0fef648 100644 --- a/nebula_app/src/ssh_session.rs +++ b/nebula_app/src/ssh_session.rs @@ -31,8 +31,10 @@ mod exec; mod integration; mod lifecycle; mod route; +mod transcript; pub(crate) use integration::setup_cli as setup_ai_cli; use route::{ResolvedRoute, RouteTransport}; +pub use transcript::TranscriptReader; /// Remote terminals have no pre-primed ConPTY handshake or host row anchoring. pub(crate) fn terminal_config( @@ -68,6 +70,9 @@ struct OpenedTransport { pub trait SshEventHost: nebula_terminal::event::EventListener + Clone + Send + Sync + 'static { + /// A read capability belongs to this pane's live authenticated transport. + fn ssh_transcript_reader(&self, _reader: Option) {} + /// 连接阶段变化(连接卡片/横幅的数据源)。默认丢弃。 fn ssh_stage(&self, stage: SshStage) { let _ = stage; @@ -223,6 +228,23 @@ impl SshDestination { /// 使用系统 SSH 的离线配置展开能力解析别名、用户名、端口和 IdentityFile。 /// 这能保持用户现有 `~/.ssh/config` 行为,同时网络连接仍完全由 Rust 传输层承担。 fn resolve(value: &str) -> io::Result { + let path = crate::display::nebula_data_dir().join("ssh_profiles.json"); + let profiles = crate::ssh_profiles::SshProfiles::load(&path)?; + Self::resolve_profile(value, &profiles) + } + + fn resolve_profile( + value: &str, + profiles: &crate::ssh_profiles::SshProfiles, + ) -> io::Result { + let identity = value.trim(); + let mut resolved = Self::resolve_address(profiles.connection_destination(identity))?; + // 连接目标展开后仍保留独立身份,使密码、连接池与断线恢复继续指向该副本。 + resolved.original = identity.to_owned(); + Ok(resolved) + } + + fn resolve_address(value: &str) -> io::Result { let original = value.trim().to_owned(); crate::ssh_profiles::validate_ssh_destination(&original) .map_err(|message| io::Error::new(io::ErrorKind::InvalidInput, message))?; @@ -280,7 +302,7 @@ impl SshDestination { /// 给 `ssh -G` 的离线配置探测目标。Nebula 历史存盘格式允许 /// `user@host:port`,而 OpenSSH 只会从 `ssh://user@host:port` URI 中拆出 /// 端口;无显式端口、已有 URI 与裸 IPv6 都保持原样,避免改变 Host 匹配。 -fn ssh_config_probe_target(value: &str) -> Cow<'_, str> { +pub(crate) fn ssh_config_probe_target(value: &str) -> Cow<'_, str> { if value.starts_with("ssh://") { return Cow::Borrowed(value); } @@ -933,6 +955,27 @@ pub(crate) async fn exec_capture( script: &[u8], budget: Duration, ) -> Result { + let channel = open_exec_channel(raw_destination).await?; + exec::capture(channel, command, script, budget, raw_destination).await +} + +/// 中转配置含访问令牌,复用既有认证连接,但不把任何远端输出写入日志。 +pub(crate) async fn exec_private( + raw_destination: &str, + command: &str, + budget: Duration, +) -> Result, SessionError> { + tokio::time::timeout(budget, async { + let channel = open_exec_channel(raw_destination).await?; + exec::capture_private(channel, command).await + }) + .await + .map_err(|_| io::Error::new(io::ErrorKind::TimedOut, "private_exec_timeout"))? +} + +async fn open_exec_channel( + raw_destination: &str, +) -> Result, SessionError> { let profiles_path = crate::display::nebula_data_dir().join("ssh_profiles.json"); let raw = raw_destination.to_owned(); let (destination, profile) = tokio::task::spawn_blocking(move || { @@ -945,8 +988,7 @@ pub(crate) async fn exec_capture( .map_err(|err| format!("SSH 地址解析任务失败: {err}"))??; let session = authenticated_session(&destination, &profile, None::<&NoopSshEventHost>).await?; - let channel = lifecycle::network("exec channel", session.channel_open_session()).await?; - exec::capture(channel, command, script, budget, raw_destination).await + Ok(lifecycle::network("exec channel", session.channel_open_session()).await?) } /// 在现有认证连接上打开独立 SFTP 子系统;连接池和认证策略仍只有一份。 diff --git a/nebula_app/src/ssh_session/exec.rs b/nebula_app/src/ssh_session/exec.rs index 9053764..430e697 100644 --- a/nebula_app/src/ssh_session/exec.rs +++ b/nebula_app/src/ssh_session/exec.rs @@ -3,6 +3,35 @@ use super::{SessionError, lifecycle}; use russh::{Channel, ChannelMsg, client}; use std::time::Duration; +pub(super) async fn capture_private( + channel: Channel, + command: &str, +) -> Result, SessionError> { + let mut channel = lifecycle::own_channel(channel); + channel.exec(true, command).await?; + let mut stdout = Vec::new(); + let mut exit = None; + while let Some(message) = channel.wait().await { + match message { + ChannelMsg::Data { data } => { + if stdout.len().saturating_add(data.len()) > 8192 { + return Err("private_exec_output_limit".into()); + } + stdout.extend_from_slice(&data); + }, + ChannelMsg::Failure => return Err("private_exec_rejected".into()), + ChannelMsg::ExitStatus { exit_status } => exit = Some(exit_status), + ChannelMsg::Close => break, + _ => {}, + } + } + channel.finish().await?; + if exit != Some(0) { + return Err("private_exec_failed".into()); + } + Ok(stdout) +} + pub(super) async fn capture( channel: Channel, command: &str, diff --git a/nebula_app/src/ssh_session/lifecycle.rs b/nebula_app/src/ssh_session/lifecycle.rs index ebb9916..665a0a8 100644 --- a/nebula_app/src/ssh_session/lifecycle.rs +++ b/nebula_app/src/ssh_session/lifecycle.rs @@ -195,6 +195,9 @@ pub(super) async fn run( }, Err(error) => return Err(error), }; + let (_transcript_scope, reader) = + super::transcript::TranscriptScope::new(&acquired.session); + event_proxy.ssh_transcript_reader(Some(reader)); super::report_stage(Some(&event_proxy), SshStage::Ready); let result = pump(&mut channel, hook_token, initial_size, &terminal, &event_proxy, &mut input).await; @@ -225,6 +228,7 @@ fn finish( terminal: &Arc>>, event_proxy: &H, ) { + event_proxy.ssh_transcript_reader(None); match result { Ok(()) => terminal.lock().exit(), Err(error) => { diff --git a/nebula_app/src/ssh_session/tests.rs b/nebula_app/src/ssh_session/tests.rs index 2b8981b..21a74f5 100644 --- a/nebula_app/src/ssh_session/tests.rs +++ b/nebula_app/src/ssh_session/tests.rs @@ -22,6 +22,18 @@ fn parses_saved_destinations() { assert_eq!((ipv6.host.as_str(), ipv6.port), ("2001:db8::1", 2222)); } +#[test] +fn copied_profile_resolves_the_endpoint_but_retains_its_credential_identity() { + let mut profiles = crate::ssh_profiles::SshProfiles::default(); + profiles.duplicate_host("alice@192.0.2.42:2200", "pebrel-host-copy-test").unwrap(); + let resolved = SshDestination::resolve_profile("pebrel-host-copy-test", &profiles).unwrap(); + assert_eq!(resolved.original, "pebrel-host-copy-test"); + assert_eq!( + (resolved.user.as_str(), resolved.host.as_str(), resolved.port), + ("alice", "192.0.2.42", 2200) + ); +} + #[test] fn parses_resolved_ssh_config() { let config = diff --git a/nebula_app/src/ssh_session/transcript.rs b/nebula_app/src/ssh_session/transcript.rs new file mode 100644 index 0000000..4960c88 --- /dev/null +++ b/nebula_app/src/ssh_session/transcript.rs @@ -0,0 +1,67 @@ +//! Pane-scoped read capability. It never resolves an address or reauthenticates. +use std::sync::{Arc, Weak}; +use std::time::Duration; + +use tokio::sync::watch; + +use super::{ClientSession, SessionError, SharedSession, exec}; + +#[derive(Clone)] +pub struct TranscriptReader { + session: Weak, + closed: watch::Receiver, +} + +pub(super) struct TranscriptScope(watch::Sender); + +impl TranscriptScope { + pub(super) fn new(session: &SharedSession) -> (Self, TranscriptReader) { + let (sender, closed) = watch::channel(false); + (Self(sender), TranscriptReader { session: Arc::downgrade(session), closed }) + } +} + +impl Drop for TranscriptScope { + fn drop(&mut self) { + self.0.send_replace(true); + } +} + +impl TranscriptReader { + pub(crate) async fn capture(&self, script: &[u8]) -> Result { + if *self.closed.borrow() { + return Err("SSH transcript owner closed".into()); + } + let session = self + .session + .upgrade() + .filter(|session| !session.is_closed()) + .ok_or("SSH transcript connection closed")?; + let mut closed = self.closed.clone(); + let budget = Duration::from_secs(5); + // 只借用原 pane 的认证连接;换网/重连/关页后不去连接池寻找替代主机。 + tokio::select! { + biased; + _ = closed.changed() => Err("SSH transcript owner closed".into()), + result = tokio::time::timeout(budget, async { + let channel = session.channel_open_session().await?; + exec::capture(channel, "python3 -", script, budget, "native transcript").await + }) => result.map_err(|_| "SSH transcript read timed out")?, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ended_or_missing_owner_cannot_open_a_replacement_connection() { + let (sender, closed) = watch::channel(false); + let reader = TranscriptReader { session: Weak::new(), closed }; + let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap(); + assert!(runtime.block_on(reader.capture(b"unreachable")).is_err()); + sender.send_replace(true); + assert!(runtime.block_on(reader.capture(b"unreachable")).is_err()); + } +} diff --git a/nebula_app/src/update_check.rs b/nebula_app/src/update_check.rs index 591d991..6d8c3f6 100644 --- a/nebula_app/src/update_check.rs +++ b/nebula_app/src/update_check.rs @@ -122,6 +122,9 @@ struct GitHubReleaseAsset { /// already knows how to display, deduplicate and dismiss. #[cfg(feature = "legacy-shell")] pub fn spawn_once(proxy: EventLoopProxy) { + if crate::platform::distribution::current().externally_managed() { + return; + } static STARTED: AtomicBool = AtomicBool::new(false); if STARTED.swap(true, Ordering::SeqCst) { return; @@ -157,6 +160,9 @@ pub fn spawn_once(proxy: EventLoopProxy) { /// 轻通知,再由用户决定是否打开更新详情弹窗。 #[cfg(feature = "gpui-shell")] pub fn spawn_gpui_once(sender: std::sync::mpsc::Sender) { + if crate::platform::distribution::current().externally_managed() { + return; + } static STARTED: AtomicBool = AtomicBool::new(false); if STARTED.swap(true, Ordering::SeqCst) { return; @@ -215,6 +221,7 @@ pub fn spawn_gpui_once(sender: std::sync::mpsc::Sender Result { + crate::platform::distribution::require_direct_update()?; let release = fetch_latest_release()?; let current = env!("CARGO_PKG_VERSION").to_owned(); let update_available = can_install_version(&release.version)?; diff --git a/nebula_app/src/update_download.rs b/nebula_app/src/update_download.rs index d28dc8b..c879cf7 100644 --- a/nebula_app/src/update_download.rs +++ b/nebula_app/src/update_download.rs @@ -80,6 +80,7 @@ impl DownloadJob { } pub(crate) fn begin(asset: &UpdateAsset) -> Result, String> { + crate::platform::distribution::require_direct_update()?; validate_asset(asset)?; Ok(begin_download_session(asset)) } @@ -140,6 +141,9 @@ pub(crate) fn run(job: DownloadJob, language: UiLanguage) { /// Restore local update state without requiring a successful network check. /// Call once on a background executor; a user-started task always takes priority. pub(crate) fn hydrate() { + if crate::platform::distribution::current().externally_managed() { + return; + } let cached = handoff::failed_update() .map(|(asset, error)| (asset, DownloadStatus::InstallFailed(error))) .or_else(cache::load); @@ -187,6 +191,7 @@ fn download_and_verify( language: UiLanguage, job: Option<&DownloadJob>, ) -> Result<(PathBuf, u64), String> { + crate::platform::distribution::require_direct_update()?; validate_asset(asset)?; let (partial_path, final_path) = download_paths(asset)?; let _download_lock = crate::atomic_file::try_lifetime_lock(&final_path) diff --git a/nebula_app/src/update_download/handoff.rs b/nebula_app/src/update_download/handoff.rs index ba6497c..bcbe659 100644 --- a/nebula_app/src/update_download/handoff.rs +++ b/nebula_app/src/update_download/handoff.rs @@ -92,12 +92,17 @@ fn guard_base(executable: &Path) -> PathBuf { /// Called before starting any resident resources. The lock belongs to the exact /// installation; a copied test/portable application has a different identity. pub(crate) fn installation_in_progress() -> io::Result { + // MSIX 包目录只读,外部渠道也不参与普通安装器事务,因此不创建旁置锁。 + if crate::platform::distribution::current().externally_managed() { + return Ok(false); + } let executable = canonical(&std::env::current_exe()?)?; Ok(crate::atomic_file::try_lifetime_lock(&guard_base(&executable))?.is_none()) } /// All verification and process waiting occurs on a background executor. pub(crate) fn prepare(asset: &UpdateAsset) -> Result { + crate::platform::distribution::require_direct_update()?; if crate::platform::elevation::requires_isolation() { return Err( "Install updates from an ordinary Pebrel window so privileged sessions stay isolated" @@ -305,6 +310,7 @@ pub(super) fn failure_unseen(prompt_state: &Path) -> bool { } pub(crate) fn schedule(asset: &UpdateAsset) -> Result<(), String> { + crate::platform::distribution::require_direct_update()?; if crate::platform::elevation::requires_isolation() { return Err("Schedule updates from an ordinary Pebrel window".into()); } @@ -323,6 +329,9 @@ pub(crate) fn schedule(asset: &UpdateAsset) -> Result<(), String> { /// Before resident resources/windows exist, apply an explicitly armed update. /// A failed attempt is disarmed and normal startup remains available. pub(crate) fn apply_scheduled() -> bool { + if crate::platform::distribution::current().externally_managed() { + return false; + } let path = nebula_settings::settings_dir().join("updates/install-next.json"); let Some(asset) = read_json::(&path) else { return false; diff --git a/nebula_app/src/window_context/runtime.rs b/nebula_app/src/window_context/runtime.rs index 1c126a3..ee93a33 100644 --- a/nebula_app/src/window_context/runtime.rs +++ b/nebula_app/src/window_context/runtime.rs @@ -101,6 +101,8 @@ impl WindowContext { .unwrap_or_else(|| tab_kind(&tab.launch).to_owned()) }); RuntimeTab { + tab_id: None, + file: None, index, active: index == self.active_tab, label, @@ -514,6 +516,7 @@ impl WindowContext { &self, pane_id: u64, lines: usize, + screen: bool, ) -> Result { let Some(pane) = self.pane(pane_id) else { return Err(ApiError::new( @@ -522,7 +525,7 @@ impl WindowContext { )); }; let term = pane.terminal.lock(); - Ok(crate::runtime_api::capture_terminal_tail( + let mut read = crate::runtime_api::capture_terminal_tail( &term, self.id().into(), pane_id, @@ -530,7 +533,13 @@ impl WindowContext { task_state(&pane.nebula_state), false, None, - )) + ); + if screen { + read.screen = Some(crate::runtime_api::capture_terminal_screen(&term, |index| { + term.colors()[index].unwrap_or(*self.display.colors[index]) + })?); + } + Ok(read) } pub(crate) fn runtime_procs(&self, pane_id: u64) -> Result { diff --git a/nebula_app/windows/nebula.rc b/nebula_app/windows/nebula.rc index d8343dd..47465c4 100644 --- a/nebula_app/windows/nebula.rc +++ b/nebula_app/windows/nebula.rc @@ -10,8 +10,8 @@ APP_MANIFEST RT_MANIFEST nebula.manifest #endif 1 VERSIONINFO -FILEVERSION 1,9,1,0 -PRODUCTVERSION 1,9,1,0 +FILEVERSION 2,0,0,0 +PRODUCTVERSION 2,0,0,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS 0x40004L @@ -24,11 +24,11 @@ BEGIN BEGIN VALUE "CompanyName", "Kuddev" VALUE "FileDescription", "Pebrel" - VALUE "FileVersion", "1.9.1" + VALUE "FileVersion", "2.0.0" VALUE "InternalName", "nebula" VALUE "OriginalFilename", "pebrel.exe" VALUE "ProductName", "Pebrel" - VALUE "ProductVersion", "1.9.1" + VALUE "ProductVersion", "2.0.0" END END BLOCK "VarFileInfo" diff --git a/nebula_config/Cargo.toml b/nebula_config/Cargo.toml index 2be0e43..5a4c9c9 100644 --- a/nebula_config/Cargo.toml +++ b/nebula_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nebula_config" -version = "1.9.1" +version = "2.0.0" license = "GPL-3.0-or-later" description = "Nebula configuration abstractions" homepage = "https://github.com/Kuddev/pebrel" @@ -14,5 +14,5 @@ serde = "1.0.163" toml.workspace = true [dev-dependencies] -nebula_config_derive = { version = "1.9.1", path = "../nebula_config_derive" } +nebula_config_derive = { version = "2.0.0", path = "../nebula_config_derive" } serde = { version = "1.0.163", features = ["derive"] } diff --git a/nebula_config_derive/Cargo.toml b/nebula_config_derive/Cargo.toml index 5f8f56c..76a2af2 100644 --- a/nebula_config_derive/Cargo.toml +++ b/nebula_config_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nebula_config_derive" -version = "1.9.1" +version = "2.0.0" license = "GPL-3.0-or-later" description = "Failure resistant deserialization derive" homepage = "https://github.com/Kuddev/pebrel" @@ -18,7 +18,7 @@ syn = { version = "2.0.16", features = ["derive", "parsing", "proc-macro", "prin [dev-dependencies.nebula_config] path = "../nebula_config" -version = "1.9.1" +version = "2.0.0" [dev-dependencies] log = "0.4.11" diff --git a/nebula_settings/Cargo.toml b/nebula_settings/Cargo.toml index 4065ece..74decd3 100644 --- a/nebula_settings/Cargo.toml +++ b/nebula_settings/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nebula-settings" -version = "1.9.1" +version = "2.0.0" license = "GPL-3.0-or-later" description = "Shared reader for Nebula runtime user settings (nebula_settings.txt) and theme terminal-color tables" publish = false diff --git a/nebula_split/Cargo.toml b/nebula_split/Cargo.toml index c51b80c..3c0f867 100644 --- a/nebula_split/Cargo.toml +++ b/nebula_split/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nebula-split" -version = "1.9.1" +version = "2.0.0" license = "GPL-3.0-or-later" description = "Shared split-pane rules: layout tree, geometry, divider drag and directional focus" publish = false diff --git a/nebula_terminal/Cargo.toml b/nebula_terminal/Cargo.toml index cf482e9..01a3a6a 100644 --- a/nebula_terminal/Cargo.toml +++ b/nebula_terminal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nebula_terminal" -version = "1.9.1" +version = "2.0.0" license = "GPL-3.0-or-later" description = "Nebula terminal emulation core" readme = "../README.md" diff --git a/packaging/AGENTS.md b/packaging/AGENTS.md index af7d031..ca35f90 100644 --- a/packaging/AGENTS.md +++ b/packaging/AGENTS.md @@ -8,3 +8,4 @@ - GitHub 推送使用普通非强制推送。发布后二进制版本标签不可因说明文档修订而移动。 - 结束前核验 Release 标题、标签、正文、真实资产文件名、空资产标签、大小、SHA256、分支与版本标签指向;创建命令成功不等于发布核验完成。 - 构建图、资产集合、更新协议或发布事务的非平凡变化写入 `architecture/notes/packaging/`。 +- Windows MSIX/Scoop 的更新归属及打包边界见 [`安装归属记录`](../architecture/notes/packaging/2026-09-28-windows-distribution-ownership.md);SDK 打包成功不代表商店认证或渠道安装验收完成。 diff --git a/packaging/scoop/README.md b/packaging/scoop/README.md new file mode 100644 index 0000000..7a3517d --- /dev/null +++ b/packaging/scoop/README.md @@ -0,0 +1,68 @@ +# Scoop distribution + +`bucket/pebrel.json` is a publishable Scoop manifest for the existing stable +Windows portable archives. It does not run the Inno installer or relocate user +settings. It registers the `pebrel` command and a `--gpui` Start menu shortcut. + +## Current artifact + +The checked-in manifest targets **v1.9.1**, using the published x64 / ARM64 ZIP +names and SHA256 digests returned by GitHub. Scoop verifies downloaded bytes +against the manifest. This repository's new installation-channel handling is a +source change for the next product release, not part of the old v1.9.1 binaries. + +Test the local manifest from a regular PowerShell session: + +```powershell +scoop install .\packaging\scoop\bucket\pebrel.json +pebrel --version +scoop uninstall pebrel +``` + +These commands change the user's Scoop installation; package generation and +schema checks do not execute them automatically. Application settings remain in +the application's existing user data location. + +## Publish a bucket + +The independent public bucket is [Kuddev/scoop-bucket](https://github.com/Kuddev/scoop-bucket). +This directory retains the source manifest; reviewed updates must also be committed +to that bucket. Users can install with: + +```powershell +scoop bucket add pebrel https://github.com/Kuddev/scoop-bucket +scoop install pebrel/pebrel +scoop update pebrel +``` + +The initial public bucket contains only `README.md` and `bucket/pebrel.json`. +It distributes existing application releases, not plugin packages. Scoop Extras +submission is separate and has not been performed. + +## Update the manifest + +The generator is offline and requires metadata from a published stable release: + +```powershell +$json = gh api repos/Kuddev/pebrel/releases/tags/vVERSION +[IO.File]::WriteAllText('release.json', ($json -join "`n"), [Text.UTF8Encoding]::new($false)) +python scripts/generate-scoop-manifest.py --release-json release.json --output packaging/scoop/bucket/pebrel.json +``` + +Replace `VERSION` with the released version. The generator rejects draft, +prerelease, missing/duplicate assets, unexpected URLs and missing SHA256 digests. +Required architecture assets come from `scripts/stable_release.py`; do not add +unpublished download links. The manifest's `checkver` / `autoupdate` also support +the standard Scoop bucket update tools, which calculate hashes for new downloads. + +The `pre_install` hook writes a UTF-8 `pebrel-distribution` marker containing +`scoop` next to the installed executable, on installation and upgrades. New +product builds use it to stop standalone updater activity. Do not put this +marker in the shared upstream ZIP: manually unpacked copies remain distinct. + +Before publishing a refreshed bucket, verify x64 / ARM64 installation, CLI, +shortcuts, native console helpers, upgrading a running application, and uninstall +without deleting user settings. Manifest/schema validation is not that acceptance. + +References: [manifests](https://github.com/ScoopInstaller/Scoop/wiki/App-Manifests), +[autoupdate](https://github.com/ScoopInstaller/Scoop/wiki/App-Manifest-Autoupdate). diff --git a/packaging/scoop/bucket/pebrel.json b/packaging/scoop/bucket/pebrel.json new file mode 100644 index 0000000..7c6f60a --- /dev/null +++ b/packaging/scoop/bucket/pebrel.json @@ -0,0 +1,36 @@ +{ + "version": "1.9.1", + "description": "GPU-accelerated terminal with native UI and AI-agent integration", + "homepage": "https://github.com/Kuddev/pebrel", + "license": "GPL-3.0-or-later", + "architecture": { + "64bit": { + "url": "https://github.com/Kuddev/pebrel/releases/download/v1.9.1/Pebrel-v1.9.1-windows-x64.zip", + "hash": "2d602adb86347d9d160296b3ba89dce740a4f75f78f681f01ac213b655edbbbb" + }, + "arm64": { + "url": "https://github.com/Kuddev/pebrel/releases/download/v1.9.1/Pebrel-v1.9.1-windows-arm64.zip", + "hash": "5d33dbd411bd6998843c820ce6fe5aefc2897cab793f43faa58ad79da07215c8" + } + }, + "pre_install": "[IO.File]::WriteAllText((Join-Path $dir 'pebrel-distribution'), \"scoop`n\", [Text.UTF8Encoding]::new($false))", + "bin": "pebrel.exe", + "shortcuts": [ + [ + "pebrel.exe", + "Pebrel", + "--gpui" + ] + ], + "checkver": "github", + "autoupdate": { + "architecture": { + "64bit": { + "url": "https://github.com/Kuddev/pebrel/releases/download/v$version/Pebrel-v$version-windows-x64.zip" + }, + "arm64": { + "url": "https://github.com/Kuddev/pebrel/releases/download/v$version/Pebrel-v$version-windows-arm64.zip" + } + } + } +} diff --git a/packaging/windows/AppxManifest.xml b/packaging/windows/AppxManifest.xml new file mode 100644 index 0000000..be81003 --- /dev/null +++ b/packaging/windows/AppxManifest.xml @@ -0,0 +1,34 @@ + + + + + Pebrel + @PUBLISHER_DISPLAY_NAME@ + Assets\StoreLogo.png + GPU-accelerated terminal + + + + + + + + + + + + + + + + + diff --git a/packaging/windows/README.md b/packaging/windows/README.md new file mode 100644 index 0000000..3388030 --- /dev/null +++ b/packaging/windows/README.md @@ -0,0 +1,104 @@ +# Microsoft Store / MSIX packaging + +The existing native Rust/GPUI product is packaged as a full-trust Win32 app. +There is no UWP/WinUI rewrite and no additional application runtime. + +## 1. Obtain the product identity + +Sign in to [Partner Center](https://partner.microsoft.com/dashboard), complete +the Windows developer enrollment if needed, create/reserve the product, and +copy these values from its product identity page: + +- `Package/Identity/Name` +- `Package/Identity/Publisher` +- `PublisherDisplayName` + +These are required build inputs. The script does not guess a publisher, create +an account, submit a package, sign an agreement, or reserve a product name. +Enrollment requirements depend on account type and region. + +## 2. Build a package + +Use the project's pinned Rust toolchain, ordinary product build prerequisites, +and the Windows SDK with `MakeAppx.exe`. From a Windows checkout: + +```powershell +.\scripts\package-msix.ps1 ` + -Version 'VERSION' ` + -IdentityName 'IDENTITY_NAME' ` + -Publisher 'CN=PUBLISHER_ID_FROM_PARTNER_CENTER' ` + -PublisherDisplayName 'PUBLISHER_DISPLAY_NAME' ` + -Architecture x64 ` + -TargetDirectory 'target\store-build' ` + -OutputDirectory 'dist\store' +``` + +Replace every identity placeholder and `VERSION` before execution. `Version` +must match the current stable product version. MSIX uses `major.minor.patch.0`. +Build ARM64 on the existing native ARM64 build environment with `-Architecture +arm64`; the argument validates payload architecture and does not install a +cross-compilation toolchain. A mismatched application or console helper fails. + +`-MakeAppxPath` can select an SDK tool explicitly. `-Configuration debug` is for +local packaging investigation, not Store submission. Release is the default. + +The wrapper invokes `package-release.ps1` without skip/staleness overrides, +thereby reusing its fresh product build, version check, complete resource layout, +and executable-architecture validation. It then: + +1. Extracts its own validated portable package into an isolated staging folder. +2. Generates the XML identity and three logo sizes from the existing product icon. +3. Declares `Windows.FullTrustApplication`, `runFullTrust`, and the `pebrel.exe` + application execution alias with its console subsystem for CLI use. +4. Runs `MakeAppx pack` with validation enabled; `/nv` is not used. +5. Produces `Pebrel-vVERSION-windows-ARCH.msix` and reports its SHA256. + +It does not overwrite an existing MSIX output. Staging cleanup is limited to the +unique directory created by that invocation. + +## 3. Signing and submission + +The generated package is **unsigned**. Microsoft's current requirements state +that Store MSIX/Appx submissions do not require a CA-trusted signing certificate; +the Store signs/re-signs the package in its publishing process. This differs from +EXE/MSI submission, which requires developer-side Authenticode signing. + +For local sideload acceptance, use a separately approved test identity and test +certificate matching its Publisher, sign with the Windows SDK, then install in +an isolated Windows test environment. Certificate trust changes and package +installation are not performed by the packaging script. Keep private keys out +of the repository. Use the real Partner Center identity for the Store candidate. + +Complete the Store listing, screenshots, privacy-policy link, supported languages +and capability/certification notes. Run Windows App Certification Kit and submit +the candidate through Partner Center. A generated package or successful SDK +validation is not a Store certification result. + +## Integration acceptance still required + +- Start-menu launch and `pebrel.exe` alias, CLI exit codes and working directory. +- ConPTY, PowerShell/cmd, WSL, SSH and lookup of packaged console/helper files. +- Runtime discovery and communication with commands started outside the package. +- Existing configuration import, packaged AppData behavior, plugin/config paths + and uninstall retention. Do not assume a packaged path equals the unpackaged path. +- A real old-package to new-package upgrade; UI, input and active sessions. +- Explorer context menus, startup registration, notifications and optional font + installation. The initial manifest does not reproduce Inno registry entries + or claim these integrations have been accepted under MSIX. + +The application recognizes a real Windows package identity and leaves updates +to that package's source. The UI says MSIX rather than asserting that a sideloaded +package came from the Store. It skips the standalone updater's adjacent startup +lock because the MSIX installation directory is read-only. + +MSIX artifacts are currently a separate explicit packaging entry, not additional +required assets in the stable GitHub Release transaction. Enable Store publishing +automation after the real identity and integration acceptance are complete; do +not append an unvalidated MSIX to an existing stable release's exact asset set. + +References: + +- [Build MSIX from native app binaries](https://learn.microsoft.com/en-us/windows/msix/desktop/source-code-overview) +- [MakeAppx](https://learn.microsoft.com/en-us/windows/msix/package/create-app-package-with-makeappx-tool) +- [Store MSIX requirements](https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/msix/app-package-requirements) +- [Packaged desktop behavior](https://learn.microsoft.com/en-us/windows/msix/desktop/desktop-to-uwp-prepare) diff --git a/scripts/android_release.py b/scripts/android_release.py new file mode 100644 index 0000000..66161a9 --- /dev/null +++ b/scripts/android_release.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Verify the existing Preview identity and the APK actually tested for a release.""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import re +import shutil +import subprocess +import sys +import xml.etree.ElementTree as ET + +if __package__: + from scripts.preview_release import sha256, write_atomic + from scripts.stable_release import expected_asset_names +else: + from preview_release import sha256, write_atomic + from stable_release import expected_asset_names + + +ROOT = Path(__file__).resolve().parents[1] +APPLICATION_ID = "io.github.kuddev.pebrel.mobile.preview" +# 固定当前预览包的证书,防止 CI 悄悄换成 debug、旧预览或新生成的签名。 +CERTIFICATE_SHA256 = "93587953edfe9611601768833b5f008ff700d85c2486ddcfb7f7ea82b2de827b" + + +def asset_name(version: str) -> str: + names = [name for name in expected_asset_names(version) if name.endswith(".apk")] + if len(names) != 1: + raise ValueError("This release requires exactly one Android Preview APK") + return names[0] + + +def validate_identity(metadata: dict, badging: str, signature: str, version: str) -> int: + elements = metadata.get("elements", []) + if (metadata.get("applicationId") != APPLICATION_ID or metadata.get("variantName") != "preview" + or len(elements) != 1 or elements[0].get("filters") != [] + or elements[0].get("outputFile") != "app-preview.apk"): + raise ValueError("Expected the universal Preview APK metadata") + element = elements[0] + code = element.get("versionCode") + # 当前安装基线为 17;本轮发布必须向前递增,不能靠更换文件名伪装升级。 + if type(code) is not int or code <= 17 or element.get("versionName") != f"{version}-preview": + raise ValueError("Android version name/code does not advance the Preview release") + package = re.search(r"^package: name='([^']+)' versionCode='([0-9]+)' versionName='([^']+)'", badging, re.M) + if (not package or package.groups() != (APPLICATION_ID, str(code), f"{version}-preview") + or re.search(r"^application-debuggable(?:\s|$)", badging, re.M)): + raise ValueError("Compiled APK identity differs or is debuggable") + certificates = re.findall(r"^Signer #[0-9]+ certificate SHA-256 digest: ([0-9a-fA-F]{64})$", signature, re.M) + if [value.lower() for value in certificates] != [CERTIFICATE_SHA256]: + raise ValueError("APK signer differs from the current Preview certificate") + return code + + +def test_summary(directory: Path) -> dict: + files = sorted(directory.rglob("TEST-*.xml")) + if not files: + raise ValueError(f"Missing Android test results: {directory.name}") + passed = skipped = 0 + for path in files: + root = ET.parse(path).getroot() + if root.findall(".//failure") or root.findall(".//error"): + raise ValueError(f"Android tests failed: {path.name}") + for suite in root.iter("testsuite"): + if int(suite.get("failures", "0")) or int(suite.get("errors", "0")): + raise ValueError(f"Android test suite failed: {path.name}") + for case in root.iter("testcase"): + if case.find("skipped") is not None: + skipped += 1 + else: + passed += 1 + if not passed: + raise ValueError("Android test report contains no executed passing tests") + return {"passed": passed, "skipped": skipped} + + +def run(*command: str | Path) -> str: + return subprocess.check_output([str(part) for part in command], text=True, encoding="utf-8", timeout=180) + + +def package(version: str, commit: str, sdk: Path, output: Path, report: Path) -> None: + if not re.fullmatch(r"[0-9a-f]{40}", commit) or run("git", "rev-parse", "HEAD").strip() != commit: + raise ValueError("Android package source must match the release commit") + app = ROOT / "mobile/android/app/build" + apk = app / "outputs/apk/preview/app-preview.apk" + metadata = json.loads(apk.with_name("output-metadata.json").read_text(encoding="utf-8")) + build_tools = sdk / "build-tools/35.0.0" + badging = run(build_tools / "aapt", "dump", "badging", apk) + signature = run(build_tools / "apksigner", "verify", "--verbose", "--print-certs", apk) + code = validate_identity(metadata, badging, signature, version) + run(build_tools / "zipalign", "-c", "-P", "16", "4", apk) + + # 复用已有 ABI、JNI、许可证及内置中继校验,不另造第二套内容清单。 + sys.path.insert(0, str(ROOT / "mobile/tools")) + from verify_ghostty_apk import verify as verify_native + from verify_native_relay_apk import verify as verify_relay + native = verify_native(apk) + verify_relay(apk, commit) + tests = { + "unit": test_summary(app / "test-results/testDebugUnitTest"), + "instrumented": test_summary(app / "outputs/androidTest-results/connected"), + } + output.mkdir(parents=True, exist_ok=True) + target = output / asset_name(version) + if target.exists(): + raise ValueError("Android release output already exists") + shutil.copyfile(apk, target) + if sha256(target) != native["sha256"]: + raise ValueError("APK changed during release collection") + write_atomic(report, json.dumps({ + "schema_version": 1, "status": "passed", "commit": commit, + "version": version, "application_id": APPLICATION_ID, "version_code": code, + "certificate_sha256": CERTIFICATE_SHA256, "apk": target.name, + "sha256": sha256(target), "tests": tests, "native": native, + }, indent=2) + "\n") + + +def validate_evidence(report: Path, directory: Path, version: str, commit: str) -> None: + data = json.loads(report.read_text(encoding="utf-8")) + name = asset_name(version) + expected = {"schema_version": 1, "status": "passed", "commit": commit, + "version": version, "application_id": APPLICATION_ID, "apk": name, + "certificate_sha256": CERTIFICATE_SHA256, "sha256": sha256(directory / name)} + if any(data.get(key) != value for key, value in expected.items()): + raise ValueError("Android evidence differs from the release APK or source") + code = data.get("version_code") + if type(code) is not int or code <= 17: + raise ValueError("Android evidence does not advance the installed Preview") + for kind in ("unit", "instrumented"): + count = data.get("tests", {}).get(kind, {}).get("passed") + if type(count) is not int or count < 1: + raise ValueError(f"Missing passed Android {kind} tests") + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--version", required=True) + parser.add_argument("--commit", required=True) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--report", type=Path, required=True) + parser.add_argument("--sdk", type=Path) + parser.add_argument("--verify-evidence", action="store_true") + args = parser.parse_args() + if args.verify_evidence: + validate_evidence(args.report, args.output, args.version, args.commit) + elif args.sdk: + package(args.version, args.commit, args.sdk, args.output, args.report) + else: + parser.error("--sdk is required when collecting an APK") + + +if __name__ == "__main__": + main() diff --git a/scripts/build-installer.ps1 b/scripts/build-installer.ps1 index 9263138..344af15 100644 --- a/scripts/build-installer.ps1 +++ b/scripts/build-installer.ps1 @@ -3,7 +3,8 @@ param( [ValidatePattern('^[0-9]+\.[0-9]+\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?$')] [string] $Version, - [ValidateSet('debug', 'release')] + # 与便携包相同,安装器只能收录已内嵌 GPUI 着色器的发布构建。 + [ValidateSet('release')] [string] $Configuration = 'release', [ValidateSet('NebulaTerminal', 'Pebrel')] diff --git a/scripts/check_prohibited_names.py b/scripts/check_prohibited_names.py index a9366b5..7684654 100644 --- a/scripts/check_prohibited_names.py +++ b/scripts/check_prohibited_names.py @@ -98,6 +98,48 @@ PROTOCOL_COMPATIBILITY_PATTERNS = ( re.compile(r"\\x1bPtmux;"), ) +# 移动端的原生依赖路径和远端命令是构建/兼容接口;只剔除这些确切引用, +# 同一行的其他文案仍须检查,不能按目录或整个源码文件豁免。 +MOBILE_BUILD_REFERENCE_PATTERNS = tuple(re.compile(pattern) for pattern in ( + r"[\"']mobile/android/ghostty/(?:UPSTREAM\.json|build/upstream(?:/arm64-v8a/licenses)?)[\"']", + r"[\"']assets/licenses/Ghostty(?:/Ghostty-MIT\.txt|-UPSTREAM\.json)[\"']", + r"[\"'](?:licenses/)?Ghostty-(?:MIT\.txt|UPSTREAM\.json)[\"']", + r"(?<=notices / )[\"']Ghostty[\"']", + r"[\"']include/ghostty/vt\.h[\"']", +)) +REMOTE_SESSION_PATTERNS = tuple(re.compile(pattern) for pattern in ( + r"\bSshSessionMode\.(?:TMUX|HERDR)\b", + r"\b(?:exec )?tmux (?:new-session -A -s|kill-session -t) '[^'\n]+'", + r"\bexec herdr(?=[\"'\s])", + r"\bherdr session (?:attach|stop) '[^'\n]+'", +)) + + +def mobile_reference_remainder(path: str, text: str) -> str: + if path == "mobile/android/third_party/reader-build/package-lock.json": + # 图形算法依赖的完整包名不是同名终端产品;保留锁文件中的真实坐标。 + text = re.sub(r"\bd3-contour\b", "", text) + if path.startswith("mobile/tools/") or path in { + "mobile/android/app/build.gradle.kts", ".github/workflows/android-release.yml", + }: + for pattern in MOBILE_BUILD_REFERENCE_PATTERNS: + text = pattern.sub("", text) + if path in {"mobile/android/settings.gradle.kts", "mobile/android/app/build.gradle.kts"}: + text = text.replace('\":ghostty\"', "") + if path == "mobile/android/ghostty/src/main/cpp/bridge.h": + text = re.sub(r"^\s*#include ", "", text) + if path in {"mobile/android/ghostty/UPSTREAM.json", "mobile/ssh/licenses/SOURCE.json"}: + text = re.sub( + r'"(?:project|source_url|url)":\s*"https://(?:github\.com|codeload\.github\.com|raw\.githubusercontent\.com)/[^"\s]+"', + "", text, + ) + if path.startswith("mobile/android/app/src/") and path.endswith(".kt"): + for pattern in REMOTE_SESSION_PATTERNS: + text = pattern.sub("", text) + if path.endswith("/connection/SshSessionMode.kt"): + text = text.replace('TMUX("tmux")', "").replace('HERDR("herdr")', "") + return text + def git(*args: str) -> bytes: return subprocess.check_output(["git", "-c", "i18n.logOutputEncoding=utf-8", *args], stderr=subprocess.DEVNULL) @@ -117,7 +159,7 @@ def source_without_allowed_occurrences(path: str, text: str) -> str: text = pattern.sub("", text) for pattern in PROTOCOL_COMPATIBILITY_PATTERNS: text = pattern.sub("", text) - return text + return mobile_reference_remainder(path, text) def prohibited_source_line(path: str, text: str) -> bool: diff --git a/scripts/generate-scoop-manifest.py b/scripts/generate-scoop-manifest.py new file mode 100644 index 0000000..b2a32d3 --- /dev/null +++ b/scripts/generate-scoop-manifest.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""Generate an offline Scoop manifest from a published GitHub Release response.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import re + +if __package__: + from scripts.stable_release import expected_asset_names, validate_version +else: + from stable_release import expected_asset_names, validate_version + + +REPOSITORY = "https://github.com/Kuddev/pebrel" + + +def manifest_from_release(release: dict) -> dict: + if release.get("draft") is not False or release.get("prerelease") is not False: + raise ValueError("Scoop requires a published stable release") + tag = release.get("tag_name", "") + if not isinstance(tag, str) or not tag.startswith("v"): + raise ValueError("Release must have a v-prefixed stable tag") + version = tag[1:] + validate_version(version) + required = expected_asset_names(version) + architecture = {} + autoupdate = {} + assets = release.get("assets", []) + if not isinstance(assets, list): + raise ValueError("Release assets must be an array") + for scoop_arch, asset_arch in (("64bit", "x64"), ("arm64", "arm64")): + name = f"Pebrel-v{version}-windows-{asset_arch}.zip" + if name not in required: + continue + matches = [asset for asset in assets if isinstance(asset, dict) and asset.get("name") == name] + if len(matches) != 1: + raise ValueError(f"Expected exactly one published asset: {name}") + asset = matches[0] + url = f"{REPOSITORY}/releases/download/{tag}/{name}" + digest = asset.get("digest", "") + if asset.get("browser_download_url") != url: + raise ValueError(f"Unexpected download URL for {name}") + if asset.get("state") != "uploaded" or not isinstance(asset.get("size"), int) or asset["size"] <= 0: + raise ValueError(f"Incomplete release asset: {name}") + if not isinstance(digest, str) or not re.fullmatch(r"sha256:[a-fA-F0-9]{64}", digest): + raise ValueError(f"Missing SHA256 digest for {name}") + architecture[scoop_arch] = {"url": url, "hash": digest[7:].lower()} + # Scoop 的自动更新器下载最终 URL 并重算哈希;这里不沿用旧版本摘要。 + autoupdate[scoop_arch] = { + "url": f"{REPOSITORY}/releases/download/v$version/Pebrel-v$version-windows-{asset_arch}.zip" + } + return { + "version": version, + "description": "GPU-accelerated terminal with native UI and AI-agent integration", + "homepage": REPOSITORY, + "license": "GPL-3.0-or-later", + "architecture": architecture, + "pre_install": ( + "[IO.File]::WriteAllText((Join-Path $dir 'pebrel-distribution'), " + '"scoop`n", [Text.UTF8Encoding]::new($false))' + ), + "bin": "pebrel.exe", + "shortcuts": [["pebrel.exe", "Pebrel", "--gpui"]], + "checkver": "github", + "autoupdate": {"architecture": autoupdate}, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--release-json", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + release = json.loads(args.release_json.read_text(encoding="utf-8-sig")) + if not isinstance(release, dict): + parser.error("Release response must be an object") + try: + manifest = manifest_from_release(release) + except ValueError as error: + parser.error(str(error)) + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(json.dumps(manifest, ensure_ascii=False, indent=4) + "\n", encoding="utf-8") + + +if __name__ == "__main__": + main() diff --git a/scripts/installer.iss b/scripts/installer.iss index 2f4eb37..2f5281d 100644 --- a/scripts/installer.iss +++ b/scripts/installer.iss @@ -1,9 +1,9 @@ #ifndef AppVersion - #define AppVersion "1.9.1" + #define AppVersion "2.0.0" #endif #ifndef NumericVersion - #define NumericVersion "1.9.1.0" + #define NumericVersion "2.0.0.0" #endif #ifndef Configuration diff --git a/scripts/package-msix.ps1 b/scripts/package-msix.ps1 new file mode 100644 index 0000000..b1bbc5f --- /dev/null +++ b/scripts/package-msix.ps1 @@ -0,0 +1,64 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)][string] $Version, + [Parameter(Mandatory = $true)][string] $IdentityName, + [Parameter(Mandatory = $true)][string] $Publisher, + [Parameter(Mandatory = $true)][string] $PublisherDisplayName, + [ValidateSet('x64', 'arm64')][string] $Architecture = 'x64', + [ValidateSet('debug', 'release')][string] $Configuration = 'release', + [string] $TargetDirectory, + [string] $OutputDirectory, + [string] $MakeAppxPath +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest +[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false) +. (Join-Path $PSScriptRoot 'windows-msix.ps1') +Assert-PebrelMsixIdentity $IdentityName $Publisher $PublisherDisplayName $Version +if (-not $MakeAppxPath) { $MakeAppxPath = Find-PebrelMakeAppx } +$MakeAppxPath = (Resolve-Path -LiteralPath $MakeAppxPath -ErrorAction Stop).Path +$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..')) +if (-not $OutputDirectory) { $OutputDirectory = Join-Path $repo 'dist' } +if (-not [IO.Path]::IsPathRooted($OutputDirectory)) { $OutputDirectory = Join-Path $repo $OutputDirectory } +$outputRoot = [IO.Path]::GetFullPath($OutputDirectory) +New-Item -ItemType Directory -Path $outputRoot -Force | Out-Null +$output = Join-Path $outputRoot "Pebrel-v$Version-windows-$Architecture.msix" +if (Test-Path -LiteralPath $output) { throw "MSIX already exists: $output" } +$stage = Join-Path $outputRoot ('.msix-stage-' + [guid]::NewGuid().ToString('N')) +New-Item -ItemType Directory -Path $stage | Out-Null +try { + # 复用产品构建、新鲜度、版本、架构和完整资源清单,避免维护第二套 payload。 + $zipOutput = Join-Path $stage 'portable' + & (Join-Path $PSScriptRoot 'package-release.ps1') -Version $Version -Configuration $Configuration ` + -Architecture $Architecture -TargetDirectory $TargetDirectory -OutputDirectory $zipOutput + $zip = Join-Path $zipOutput "Pebrel-v$Version-windows-$Architecture.zip" + $layout = Join-Path $stage 'layout' + Expand-Archive -LiteralPath $zip -DestinationPath $layout + New-PebrelMsixLayout -Root $layout -IdentityName $IdentityName -Publisher $Publisher ` + -PublisherDisplayName $PublisherDisplayName -Version $Version -Architecture $Architecture + $temporary = Join-Path $stage 'package.msix' + & $MakeAppxPath pack /h SHA256 /d $layout /p $temporary + if ($LASTEXITCODE -ne 0) { throw "MakeAppx validation/packaging failed: $LASTEXITCODE" } + # 不使用 /nv;只有 SDK 完成语义校验后才发布本地输出文件。 + Move-Item -LiteralPath $temporary -Destination $output + [PSCustomObject]@{ + Path = $output + SHA256 = (Get-FileHash -LiteralPath $output -Algorithm SHA256).Hash.ToLowerInvariant() + IdentityName = $IdentityName + Publisher = $Publisher + Version = "$Version.0" + Architecture = $Architecture + Signed = $false + Configuration = $Configuration + } +} finally { + # 仅清理此次创建的直接子目录,不根据归档或身份字段拼接递归删除目标。 + $resolved = (Resolve-Path -LiteralPath $stage).Path + if ([IO.Path]::GetDirectoryName($resolved) -ne $outputRoot -or + -not [IO.Path]::GetFileName($resolved).StartsWith('.msix-stage-') -or + ((Get-Item -LiteralPath $resolved).Attributes -band [IO.FileAttributes]::ReparsePoint)) { + throw "Unexpected MSIX staging location: $resolved" + } + Remove-Item -LiteralPath $resolved -Recurse -Force +} diff --git a/scripts/package-release.ps1 b/scripts/package-release.ps1 index 378d670..d5c7bd2 100644 --- a/scripts/package-release.ps1 +++ b/scripts/package-release.ps1 @@ -3,7 +3,8 @@ param( [ValidatePattern('^[0-9A-Za-z][0-9A-Za-z.-]*$')] [string] $Version = 'unreleased', - [ValidateSet('debug', 'release')] + # GPUI 调试构建会从编译机源码目录加载 HLSL,不能作为可分发的便携包。 + [ValidateSet('release')] [string] $Configuration = 'release', [ValidateSet('NebulaTerminal', 'Pebrel')] diff --git a/scripts/platform_cfg_budget.txt b/scripts/platform_cfg_budget.txt index ff5670f..1023289 100644 --- a/scripts/platform_cfg_budget.txt +++ b/scripts/platform_cfg_budget.txt @@ -1 +1 @@ -481 +468 diff --git a/scripts/stable_release.py b/scripts/stable_release.py index 0b728f8..6664447 100644 --- a/scripts/stable_release.py +++ b/scripts/stable_release.py @@ -9,6 +9,7 @@ import os from pathlib import Path import re import sys +import zipfile if __package__: from scripts.preview_release import MIN_ASSET_SIZE, VERSION_PATTERN, sha256, write_atomic @@ -49,6 +50,8 @@ def expected_asset_names(version: str) -> tuple[str, ...]: # 1.9.0 和 1.9.1 已公开发布且仅含 ARM64 ZIP,不能追溯要求不存在的安装器。 if version_parts >= (1, 9, 2): names += (f"Pebrel-v{version}-windows-arm64-setup.exe",) + if version_parts >= (2, 0, 0): + names += (f"Pebrel-v{version}-android-universal-preview.apk",) # The old-name installer was retired from 1.7.0; retain historical manifests. if version_parts < (1, 7, 0): names += (f"NebulaTerminal-{version}-windows-x64-setup.exe",) @@ -81,6 +84,15 @@ def _check_magic(path: Path) -> None: raise StableReleaseError(f"Windows installer has no PE header: {path.name}") if path.suffix == ".zip" and not header.startswith((b"PK\x03\x04", b"PK\x05\x06")): raise StableReleaseError(f"Windows ZIP has an invalid ZIP header: {path.name}") + if path.suffix == ".apk": + try: + with zipfile.ZipFile(path) as apk: + if not {"AndroidManifest.xml", "classes.dex"}.issubset(apk.namelist()): + raise StableReleaseError(f"Android APK is missing its manifest or DEX: {path.name}") + if apk.testzip() is not None: + raise StableReleaseError(f"Android APK has damaged ZIP entries: {path.name}") + except zipfile.BadZipFile as error: + raise StableReleaseError(f"Android APK has an invalid ZIP structure: {path.name}") from error if path.name.endswith(".AppImage") and not header.startswith(b"\x7fELF"): raise StableReleaseError(f"AppImage is not an ELF executable: {path.name}") if path.suffix == ".deb" and header != b"!\n": diff --git a/scripts/tests/build-windows-product.tests.ps1 b/scripts/tests/build-windows-product.tests.ps1 index d236a97..a14f52d 100644 --- a/scripts/tests/build-windows-product.tests.ps1 +++ b/scripts/tests/build-windows-product.tests.ps1 @@ -1,4 +1,4 @@ -$ErrorActionPreference = 'Stop' +$ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest $builder = Join-Path $PSScriptRoot '../build-windows-product.ps1' $originalTarget = $env:CARGO_TARGET_DIR @@ -36,6 +36,15 @@ try { if (-not $failed) { throw 'Cargo failure must fail packaging' } if ($env:CARGO_TARGET_DIR -ne 'preserve-caller-target') { throw 'Failure changed caller target' } if ((Get-Location).Path -ne $originalDirectory) { throw 'Failure changed caller directory' } + # 本地调试仍允许构建;分发入口必须在执行构建或收集文件之前拒绝调试包。 + foreach ($name in @('package-release.ps1', 'build-installer.ps1')) { + $packager = Join-Path $PSScriptRoot "../$name" + $rejected = $false + try { & $packager -Configuration debug } catch { + $rejected = $_.FullyQualifiedErrorId -like 'ParameterArgumentValidationError*' + } + if (-not $rejected) { throw "$name must reject non-portable debug builds" } + } Write-Output 'build-windows-product.tests.ps1: PASS' } finally { $env:CARGO_TARGET_DIR = $originalTarget diff --git a/scripts/tests/test_android_release.py b/scripts/tests/test_android_release.py new file mode 100644 index 0000000..2631d8f --- /dev/null +++ b/scripts/tests/test_android_release.py @@ -0,0 +1,114 @@ +from __future__ import annotations + +import copy +import json +from pathlib import Path +import tempfile +import unittest + +from scripts import android_release as android +from scripts.preview_release import sha256 + + +class AndroidReleaseTests(unittest.TestCase): + def identity(self): + metadata = {"applicationId": android.APPLICATION_ID, "variantName": "preview", "elements": [ + {"filters": [], "outputFile": "app-preview.apk", "versionCode": 18, "versionName": "2.0.0-preview"}, + ]} + badging = f"package: name='{android.APPLICATION_ID}' versionCode='18' versionName='2.0.0-preview' platformBuildVersionName='15'\n" + signature = f"Signer #1 certificate SHA-256 digest: {android.CERTIFICATE_SHA256}\n" + return metadata, badging, signature + + def test_accepts_current_preview_and_incremented_version(self): + self.assertEqual(android.validate_identity(*self.identity(), "2.0.0"), 18) + self.assertEqual(android.asset_name("2.0.0"), "Pebrel-v2.0.0-android-universal-preview.apk") + + def test_rejects_production_debug_and_split_variants(self): + metadata, badging, signature = self.identity() + for field, value in (("applicationId", android.APPLICATION_ID.removesuffix(".preview")), + ("variantName", "debug")): + changed = copy.deepcopy(metadata) + changed[field] = value + with self.subTest(field=field), self.assertRaises(ValueError): + android.validate_identity(changed, badging, signature, "2.0.0") + for value in ([{"filterType": "ABI", "value": "arm64-v8a"}], None): + changed = copy.deepcopy(metadata) + changed["elements"][0]["filters"] = value + with self.assertRaises(ValueError): + android.validate_identity(changed, badging, signature, "2.0.0") + with self.assertRaisesRegex(ValueError, "debuggable"): + android.validate_identity(metadata, badging + "application-debuggable\n", signature, "2.0.0") + + def test_rejects_wrong_signer_version_and_compiled_package(self): + metadata, badging, signature = self.identity() + for changed in ("", signature.replace(android.CERTIFICATE_SHA256, "a" * 64), + signature + signature.replace("#1", "#2")): + with self.assertRaisesRegex(ValueError, "signer"): + android.validate_identity(metadata, badging, changed, "2.0.0") + for code in (0, 17, True, "18"): + changed = copy.deepcopy(metadata) + changed["elements"][0]["versionCode"] = code + with self.assertRaisesRegex(ValueError, "version"): + android.validate_identity(changed, badging, signature, "2.0.0") + with self.assertRaisesRegex(ValueError, "version"): + android.validate_identity(metadata, badging, signature, "2.0.1") + with self.assertRaisesRegex(ValueError, "Compiled"): + android.validate_identity(metadata, badging.replace("versionCode='18'", "versionCode='19'"), signature, "2.0.0") + + def test_reports_require_executed_tests_and_reject_failures(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + with self.assertRaisesRegex(ValueError, "Missing"): + android.test_summary(root) + report = root / "TEST-unit.xml" + report.write_text('', encoding="utf-8") + self.assertEqual(android.test_summary(root), {"passed": 1, "skipped": 1}) + invalid = [ + '', + '', + '', + '', + ] + for text in invalid: + report.write_text(text, encoding="utf-8") + with self.subTest(text=text), self.assertRaises(ValueError): + android.test_summary(root) + + def test_aggregate_binds_evidence_to_source_apk_and_both_test_suites(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + name = android.asset_name("2.0.0") + apk = root / name + apk.write_bytes(b"APK byte identity fixture, not an installable package") + data = {"schema_version": 1, "status": "passed", "commit": "a" * 40, "version": "2.0.0", + "application_id": android.APPLICATION_ID, "version_code": 18, + "certificate_sha256": android.CERTIFICATE_SHA256, "apk": name, "sha256": sha256(apk), + "tests": {"unit": {"passed": 1}, "instrumented": {"passed": 1}}} + report = root / "report.json" + report.write_text(json.dumps(data), encoding="utf-8") + android.validate_evidence(report, root, "2.0.0", "a" * 40) + for field, value in (("commit", "b" * 40), ("sha256", "b" * 64), ("version_code", 17), + ("certificate_sha256", "b" * 64), ("tests", {"unit": {"passed": 1}})): + invalid = {**data, field: value} + report.write_text(json.dumps(invalid), encoding="utf-8") + with self.subTest(field=field), self.assertRaises(ValueError): + android.validate_evidence(report, root, "2.0.0", "a" * 40) + + def test_release_workflow_requires_android_and_reuses_preview_signing(self): + root = Path(__file__).resolve().parents[2] + workflow = (root / ".github/workflows/release.yml").read_text(encoding="utf-8") + android_workflow = (root / ".github/workflows/android-release.yml").read_text(encoding="utf-8") + self.assertIn("uses: ./.github/workflows/android-release.yml", workflow) + self.assertIn("needs: [prepare, linux, macos, windows, windows-arm64, android]", workflow) + self.assertIn("release-dist/*.apk", workflow) + for required in (":app:testDebugUnitTest", ":app:lintPreview", ":app:assemblePreview", + ":app:connectedPreviewAndroidTest", "--commit", "stable-package-android", + "stable-evidence-android", "aarch64-unknown-linux-musl", "x86_64-unknown-linux-musl"): + self.assertIn(required, android_workflow) + self.assertNotIn("continue-on-error", android_workflow) + self.assertNotIn("assembleDebug", android_workflow) + self.assertNotIn("keytool -genkey", android_workflow) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/tests/test_prohibited_names.py b/scripts/tests/test_prohibited_names.py index fae08ae..27a60b4 100644 --- a/scripts/tests/test_prohibited_names.py +++ b/scripts/tests/test_prohibited_names.py @@ -129,6 +129,42 @@ class ProhibitedNamesTests(unittest.TestCase): self.assertFalse(check_prohibited_names.prohibited_source_line("Cargo.toml", dependency)) self.assertTrue(check_prohibited_names.prohibited_source_line("README.md", dependency)) + def test_mobile_build_and_session_references_preserve_their_interfaces(self) -> None: + cases = ( + ("mobile/android/third_party/reader-build/package-lock.json", '"node_modules/@types/d3-contour": {'), + ("mobile/android/app/build.gradle.kts", 'implementation(project(":ghostty"))'), + ("mobile/android/settings.gradle.kts", 'include(":app", ":ghostty")'), + ("mobile/tools/generate_assets.py", 'root / "mobile/android/ghostty/UPSTREAM.json"'), + (".github/workflows/android-release.yml", "path: 'mobile/android/ghostty/build/upstream'"), + (".github/workflows/android-release.yml", "key: native-${{ hashFiles('mobile/android/ghostty/UPSTREAM.json') }}"), + ("mobile/tools/generate_assets.py", 'notices / "Ghostty"'), + ("mobile/tools/test_apk_audit.py", '"assets/licenses/Ghostty/Ghostty-MIT.txt": b"fixture"'), + ("mobile/android/ghostty/src/main/cpp/bridge.h", '#include '), + ("mobile/android/ghostty/UPSTREAM.json", '"project": "https://github.com/ghostty-org/ghostty",'), + ("mobile/ssh/licenses/SOURCE.json", '"url": "https://raw.githubusercontent.com/warp-tech/russh/revision/LICENSE.txt",'), + ("mobile/android/app/src/main/java/connection/SshSessionMode.kt", 'TMUX("tmux"), HERDR("herdr")'), + ("mobile/android/app/src/main/java/connection/SshSessionMode.kt", 'SshSessionMode.TMUX -> "exec tmux new-session -A -s \'pebrel\'"'), + ("mobile/android/app/src/main/java/connection/SshSessionMode.kt", 'SshSessionMode.HERDR -> "exec herdr"'), + ("mobile/android/app/src/androidTest/SshIntegrationTest.kt", '"tmux kill-session -t \'session\'" else "herdr session stop \'session\'"'), + ) + for path, text in cases: + with self.subTest(path=path, text=text): + self.assertFalse(check_prohibited_names.prohibited_source_line(path, text)) + # 合法引用仅保护自身,不能让同行追加的比较文案一起逃逸。 + self.assertTrue(check_prohibited_names.prohibited_source_line(path, text + '; compare with Ghostty')) + self.assertTrue(check_prohibited_names.prohibited_source_line("README.md", text)) + + def test_mobile_reference_paths_do_not_exempt_ordinary_mentions(self) -> None: + for path in ( + "mobile/tools/generate_assets.py", "mobile/android/app/build.gradle.kts", + ".github/workflows/android-release.yml", + "mobile/android/ghostty/UPSTREAM.json", "mobile/ssh/licenses/SOURCE.json", + "mobile/android/app/src/main/java/connection/SshSessionMode.kt", + ): + with self.subTest(path=path): + self.assertTrue(check_prohibited_names.prohibited_source_line(path, '"description": "Inspired by Ghostty"')) + self.assertTrue(check_prohibited_names.prohibited_source_line(path, '"url": "https://example.invalid/ghostty"')) + def test_staged_hook_configuration_is_not_exempt(self) -> None: with tempfile.TemporaryDirectory() as directory: repository = Path(directory) diff --git a/scripts/tests/test_stable_release.py b/scripts/tests/test_stable_release.py index 58ad70c..27b218b 100644 --- a/scripts/tests/test_stable_release.py +++ b/scripts/tests/test_stable_release.py @@ -3,6 +3,7 @@ from __future__ import annotations from pathlib import Path import tempfile import unittest +import zipfile from scripts.stable_release import ( STABLE_SHA256_PLACEHOLDER, @@ -23,6 +24,11 @@ VERSION = "1.6.0" def write_fake_asset(path: Path) -> None: + if path.suffix == ".apk": + with zipfile.ZipFile(path, "w") as apk: + apk.writestr("AndroidManifest.xml", "fixture") + apk.writestr("classes.dex", b"fixture" + b"\0" * MIN_ASSET_SIZE) + return if path.suffix == ".exe": header = b"MZ" + b"\0" * 6 elif path.suffix == ".zip": @@ -101,7 +107,8 @@ class StableReleaseTests(unittest.TestCase): self.assertIn("tools/i18n-contract/Cargo.toml", shared) self.assertNotIn("continue-on-error", shared) aggregate = workflow.split("\n aggregate:\n", 1)[1].split("\n publish:\n", 1)[0] - self.assertIn("needs: [prepare, linux, macos, windows, windows-arm64]", aggregate) + self.assertIn("needs: [prepare, linux, macos, windows, windows-arm64, android]", aggregate) + self.assertIn("--verify-evidence", aggregate) self.assertIn("windows_arm64=True", aggregate) arm = workflow.split("\n windows-arm64:\n", 1)[1].split("\n aggregate:\n", 1)[0] for required in ("runs-on: windows-11-arm", "host: aarch64-pc-windows-msvc", @@ -142,7 +149,7 @@ class StableReleaseTests(unittest.TestCase): with self.subTest(version=version): names = expected_asset_names(version) parsed = tuple(map(int, version.split('.'))) - self.assertEqual(len(names), 9 if parsed >= (1, 9, 2) else 8 if parsed >= (1, 9, 0) else 7) + self.assertEqual(len(names), 10 if parsed >= (2, 0, 0) else 9 if parsed >= (1, 9, 2) else 8 if parsed >= (1, 9, 0) else 7) self.assertIn(f"Pebrel-v{version}-windows-x64-setup.exe", names) self.assertNotIn(f"NebulaTerminal-{version}-windows-x64-setup.exe", names) @@ -158,6 +165,27 @@ class StableReleaseTests(unittest.TestCase): with self.assertRaisesRegex(StableReleaseError, "missing: Pebrel-v1.9.0-windows-arm64.zip"): validate_assets(root, version) + def test_20_requires_valid_android_apk_without_changing_historical_assets(self) -> None: + for version in ("1.9.0", "1.9.1", "1.9.2", "1.10.0"): + self.assertFalse(any(name.endswith(".apk") for name in expected_asset_names(version))) + version = "2.0.0" + name = "Pebrel-v2.0.0-android-universal-preview.apk" + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + for asset in expected_asset_names(version): + write_fake_asset(root / asset) + self.assertEqual(len(validate_assets(root, version)), 10) + (root / name).unlink() + with self.assertRaisesRegex(StableReleaseError, "missing: " + name): + validate_assets(root, version) + (root / name).write_bytes(b"PK\x03\x04" + b"x" * MIN_ASSET_SIZE) + with self.assertRaisesRegex(StableReleaseError, "invalid ZIP structure"): + validate_assets(root, version) + with zipfile.ZipFile(root / name, "w") as apk: + apk.writestr("not-an-app", b"x" * MIN_ASSET_SIZE) + with self.assertRaisesRegex(StableReleaseError, "manifest or DEX"): + validate_assets(root, version) + def test_post_191_requires_native_windows_arm64_installer(self) -> None: for version in ("1.9.0", "1.9.1"): with self.subTest(historical_version=version): diff --git a/scripts/windows-msix.ps1 b/scripts/windows-msix.ps1 new file mode 100644 index 0000000..d15aba4 --- /dev/null +++ b/scripts/windows-msix.ps1 @@ -0,0 +1,84 @@ +# MSIX 清单与静态资源准备;构建和发布仍由调用方持有,不加载或执行插件。 +function Assert-PebrelMsixIdentity { + param([string] $IdentityName, [string] $Publisher, [string] $PublisherDisplayName, [string] $Version) + if ($IdentityName -cnotmatch '^[A-Za-z0-9.-]{3,50}$' -or $IdentityName -match '^(Microsoft|Windows)\.') { + throw 'Use the exact Package/Identity/Name reserved in Partner Center (3-50 ASCII letters, numbers, dots or hyphens).' + } + if ([string]::IsNullOrWhiteSpace($Publisher) -or $Publisher -notmatch '(^|,)\s*CN=') { + throw 'Publisher must be the X.500 identity from Partner Center, including CN=.' + } + $null = [System.Security.Cryptography.X509Certificates.X500DistinguishedName]::new($Publisher) + if ([string]::IsNullOrWhiteSpace($PublisherDisplayName) -or $PublisherDisplayName.Length -gt 256) { + throw 'PublisherDisplayName must contain 1-256 characters.' + } + if ($Version -notmatch '^[0-9]+\.[0-9]+\.[0-9]+$') { throw 'MSIX requires a stable major.minor.patch version.' } + $parts = @($Version.Split('.') | ForEach-Object { [int] $_ }) + if ($parts[0] -lt 1 -or @($parts | Where-Object { $_ -gt 65535 }).Count -gt 0) { + throw 'MSIX version components must fit UInt16, with a nonzero major version.' + } +} + +function New-PebrelMsixLayout { + param( + [Parameter(Mandatory = $true)][string] $Root, + [Parameter(Mandatory = $true)][string] $IdentityName, + [Parameter(Mandatory = $true)][string] $Publisher, + [Parameter(Mandatory = $true)][string] $PublisherDisplayName, + [Parameter(Mandatory = $true)][string] $Version, + [ValidateSet('x64', 'arm64')][string] $Architecture = 'x64' + ) + Assert-PebrelMsixIdentity $IdentityName $Publisher $PublisherDisplayName $Version + $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..')) + if (-not (Test-Path -LiteralPath $Root -PathType Container)) { throw "Missing MSIX layout: $Root" } + . (Join-Path $PSScriptRoot 'windows-package-architecture.ps1') + Assert-WindowsPackageArchitecture -Root $Root -RuntimeDirectory (Join-Path $Root 'runtime') -Architecture $Architecture + $template = [IO.File]::ReadAllText((Join-Path $repo 'packaging/windows/AppxManifest.xml'), [Text.Encoding]::UTF8) + $values = @{ + IDENTITY_NAME = $IdentityName + PUBLISHER = $Publisher + PUBLISHER_DISPLAY_NAME = $PublisherDisplayName + VERSION = "$Version.0" + ARCHITECTURE = $Architecture + } + # MatchEvaluator 防止用户字段中的 @TOKEN@ 被第二次替换;XML 值始终转义。 + $manifest = [regex]::Replace($template, '@([A-Z_]+)@', [Text.RegularExpressions.MatchEvaluator] { + param($match) + $key = $match.Groups[1].Value + if (-not $values.ContainsKey($key)) { throw "Unknown manifest token: $key" } + [Security.SecurityElement]::Escape($values[$key]) + }) + $null = [xml] $manifest + $manifestPath = Join-Path $Root 'AppxManifest.xml' + [IO.File]::WriteAllText($manifestPath, $manifest, [Text.UTF8Encoding]::new($false)) + + # 缩放现有产品图标,不另造品牌素材;System.Drawing 仅在 Windows 打包进程内使用。 + Add-Type -AssemblyName System.Drawing + $assets = Join-Path $Root 'Assets' + New-Item -ItemType Directory -Path $assets -Force | Out-Null + $source = [Drawing.Image]::FromFile((Join-Path $repo 'extra/logo/nebula-titanium.png')) + try { + foreach ($entry in @{'StoreLogo.png'=50; 'Square44x44Logo.png'=44; 'Square150x150Logo.png'=150}.GetEnumerator()) { + $bitmap = [Drawing.Bitmap]::new([int]$entry.Value, [int]$entry.Value) + $graphics = [Drawing.Graphics]::FromImage($bitmap) + try { + $graphics.InterpolationMode = [Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic + $graphics.DrawImage($source, 0, 0, [int]$entry.Value, [int]$entry.Value) + $bitmap.Save((Join-Path $assets $entry.Key), [Drawing.Imaging.ImageFormat]::Png) + } finally { $graphics.Dispose(); $bitmap.Dispose() } + } + } finally { $source.Dispose() } +} + +function Find-PebrelMakeAppx { + $command = Get-Command makeappx.exe -ErrorAction SilentlyContinue + if ($command) { return $command.Source } + $kits = Join-Path ${env:ProgramFiles(x86)} 'Windows Kits/10/bin' + $sdk = Get-ChildItem -LiteralPath $kits -Directory -ErrorAction Stop | + Where-Object { $_.Name -match '^10\.0\.[0-9]+\.0$' } | + Sort-Object { [version] $_.Name } -Descending + foreach ($version in $sdk) { + $candidate = Join-Path $version.FullName 'x64/makeappx.exe' + if (Test-Path -LiteralPath $candidate -PathType Leaf) { return $candidate } + } + throw 'Install the Windows 10/11 SDK packaging tools, or supply -MakeAppxPath.' +} diff --git a/scripts/windows-package-architecture.ps1 b/scripts/windows-package-architecture.ps1 index a4f401d..cb75072 100644 --- a/scripts/windows-package-architecture.ps1 +++ b/scripts/windows-package-architecture.ps1 @@ -2,11 +2,13 @@ function Assert-WindowsPackageArchitecture { param( [Parameter(Mandatory = $true)][string] $Root, - [ValidateSet('x64', 'arm64')][string] $Architecture = 'x64' + [ValidateSet('x64', 'arm64')][string] $Architecture = 'x64', + [string] $RuntimeDirectory = $Root ) $machine = if ($Architecture -eq 'arm64') { 0xAA64 } else { 0x8664 } foreach ($name in @('pebrel.exe', 'pebrel-hook.exe', 'conpty.dll', 'OpenConsole.exe')) { - $path = Join-Path $Root $name + $directory = if ($name -eq 'pebrel.exe') { $Root } else { $RuntimeDirectory } + $path = Join-Path $directory $name $stream = [IO.File]::OpenRead($path) $reader = [IO.BinaryReader]::new($stream) try { diff --git a/tools/i18n-contract/Cargo.lock b/tools/i18n-contract/Cargo.lock index b55f5ec..be237b2 100644 --- a/tools/i18n-contract/Cargo.lock +++ b/tools/i18n-contract/Cargo.lock @@ -16,7 +16,7 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "nebula-settings" -version = "1.9.1" +version = "2.0.0" [[package]] name = "pebrel-i18n-contract"