commit aa535db5116bdcdf0b41c7348e2b8c387dfbd796 Author: ma-zhongxu Date: Wed Feb 19 17:55:47 2025 +0800 first commit diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 0000000..0156438 --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,73 @@ +# AnythingLLM Development Container Setup + +Welcome to the AnythingLLM development container configuration, designed to create a seamless and feature-rich development environment for this project. + +

PLEASE READ THIS

+ +## Prerequisites + +- [Docker](https://www.docker.com/get-started) +- [Visual Studio Code](https://code.visualstudio.com/) +- [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) VS Code extension + +## Features + +- **Base Image**: Built on `mcr.microsoft.com/devcontainers/javascript-node:1-18-bookworm`, thus Node.JS LTS v18. +- **Additional Tools**: Includes `hadolint`, and essential apt-packages such as `curl`, `gnupg`, and more. +- **Ports**: Configured to auto-forward ports `3000` (Frontend) and `3001` (Backend). +- **Environment Variables**: Sets `NODE_ENV` to `development` and `ESLINT_USE_FLAT_CONFIG` to `true`. +- **VS Code Extensions**: A suite of extensions such as `Prettier`, `Docker`, `ESLint`, and more are automatically installed. Please revise if you do not agree with any of these extensions. AI-powered extensions and time trackers are (for now) not included to avoid any privacy concerns, but you can install them later in your own environment. + +## Getting Started + +1. Using GitHub Codespaces. Just select to create a new workspace, and the devcontainer will be created for you. + +2. Using your Local VSCode (Release or Insiders). We suggest you first make a fork of the repo and then clone it to your local machine using VSCode tools. Then open the project folder in VSCode, which will prompt you to open the project in a devcontainer. Select yes, and the devcontainer will be created for you. If this does not happen, you can open the command palette and select "Remote-Containers: Reopen in Container". + +## On Creation: + +When the container is built for the first time, it will automatically run `yarn setup` to ensure everything is in place for the Collector, Server and Frontend. This command is expected to be automatically re-run if there is a content change on next reboot. + +## Work in the Container: + +Once the container is up, be patient. Some extensions may complain because dependencies are still being installed, and in the Extensions tab, some may ask you to "Reload" the project. Don't do that yet. First, wait until all settle down for the first time. We suggest you create a new VSCode profile for this devcontainer, so any configuration and extensions you change, won't affect your default profile. + +Checklist: + +- [ ] The usual message asking you to start the Server and Frontend in different windows are now "hidden" in the building process of the devcontainer. Don't forget to do as suggested. +- [ ] Open a JavaScript file, for example "server/index.js" and check if `eslint` is working. It will complain that `'err' is defined but never used.`. This means it is working. +- [ ] Open a React File, for example, "frontend/src/main.jsx," and check if `eslint` complains about `Fast refresh only works when a file has exports. Move your component(s) to a separate file.`. Again, it means `eslint` is working. Now check at the status bar if the `Prettier` has a double checkmark :heavy_check_mark: (double). It means Prettier is working. You will see a nice extension `Formatting:`:heavy_check_mark: that can be used to disable the `Format on Save` feature temporarily. +- [ ] Check if, on the left pane, you have the NPM Scripts (this may be disabled; look at the "Explorer" tree-dots up-right). There will be scripts inside the `package.json` files. You will basically need to run the `dev:collector`, `dev:server` and the `dev:frontend` in this order. When the frontend finishes starting, a window browser will open **inside** the VSCode. Still, you can open it outside. + +:warning: **Important for all developers** :warning: + +- [ ] When you are using the `NODE_ENV=development` the server will not store the configurations you set for security reasons. Please set the proper config on file `.env.development`. The side-effect if you don't, everytime you restart the server, you will be sent to the "Onboarding" page again. + +**Note when using GitHub Codespaces** + +- [ ] When running the "Server" for the first time, it will automatically configure its port to be publicly accessible by default, as this is required for the front end to reach the server backend. To know more, read the content of the `.env` file on the frontend folder about this, and if any issues occur, make sure to manually set the port "Visibility" of the "Server" is set to "Public" if needed. Again, this is only needed for developing on GitHub Codespaces. + + +**For the Collector:** + +- [x] In the past, the Collector dwelled within the Python domain, but now it has journeyed to the splendid realm of Node.JS. Consequently, the configuration complexities of bygone versions are no longer a concern. + +### Now it is ready to start + +In the status bar you will see three shortcuts names `Collector`, `Server` and `Frontend`. Just click-and-wait on that order (don't forget to set the Server port 3001 to Public if you are using GH Codespaces **_before_** starting the Frontend). + +Now you can enjoy your time developing instead of reconfiguring everything. + +## Debugging with the devcontainers + +### For debugging the collector, server and frontend + +First, make sure the built-in extension (ms-vscode.js-debug) is active (I don't know why it would not be, but just in case). If you want, you can install the nightly version (ms-vscode.js-debug-nightly) + +Then, in the "Run and Debug" tab (Ctrl+shift+D), you can select on the menu: + +- Collector debug. This will start the collector in debug mode and attach the debugger. Works very well. +- Server debug. This will start the server in debug mode and attach the debugger. Works very well. +- Frontend debug. This will start the frontend in debug mode and attach the debugger. I am still struggling with this one. I don't know if VSCode can handle the .jsx files seamlessly as the pure .js on the server. Maybe there is a need for a particular configuration for Vite or React. Anyway, it starts. Another two configurations launch Chrome and Edge, and I think we could add breakpoints on .jsx files somehow. The best scenario would be always to use the embedded browser. WIP. + +Please leave comments on the Issues tab or the [![](https://img.shields.io/discord/1114740394715004990?logo=Discord&logoColor=white&label=Discord&labelColor=%235568ee&color=%2355A2DD&link=https%3A%2F%2Fdiscord.gg%2F6UyHPeGZAC)]("https://discord.gg/6UyHPeGZAC") diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..254ae02 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,211 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node +{ + "name": "Node.js", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + // "build": { + // "args": { + // "ARG_UID": "1000", + // "ARG_GID": "1000" + // }, + // "dockerfile": "Dockerfile" + // }, + // "containerUser": "anythingllm", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/javascript-node:1-18-bookworm", + // Features to add to the dev container. More info: https://containers.dev/features. + "features": { + // Docker very useful linter + "ghcr.io/dhoeric/features/hadolint:1": { + "version": "latest" + }, + // Terraform support + "ghcr.io/devcontainers/features/terraform:1": {}, + // Just a wrap to install needed packages + "ghcr.io/devcontainers-contrib/features/apt-packages:1": { + // Dependencies copied from ../docker/Dockerfile plus some dev stuff + "packages": [ + "build-essential", + "ca-certificates", + "curl", + "ffmpeg", + "fonts-liberation", + "git", + "gnupg", + "htop", + "less", + "libappindicator1", + "libasound2", + "libatk-bridge2.0-0", + "libatk1.0-0", + "libc6", + "libcairo2", + "libcups2", + "libdbus-1-3", + "libexpat1", + "libfontconfig1", + "libgbm1", + "libgcc1", + "libgfortran5", + "libglib2.0-0", + "libgtk-3-0", + "libnspr4", + "libnss3", + "libpango-1.0-0", + "libpangocairo-1.0-0", + "libstdc++6", + "libx11-6", + "libx11-xcb1", + "libxcb1", + "libxcomposite1", + "libxcursor1", + "libxdamage1", + "libxext6", + "libxfixes3", + "libxi6", + "libxrandr2", + "libxrender1", + "libxss1", + "libxtst6", + "locales", + "lsb-release", + "procps", + "tzdata", + "wget", + "xdg-utils" + ] + } + }, + "updateContentCommand": "cd server && yarn && cd ../collector && PUPPETEER_DOWNLOAD_BASE_URL=https://storage.googleapis.com/chrome-for-testing-public yarn && cd ../frontend && yarn && cd .. && yarn setup:envs && yarn prisma:setup && echo \"Please run yarn dev:server, yarn dev:collector, and yarn dev:frontend in separate terminal tabs.\"", + // Use 'postCreateCommand' to run commands after the container is created. + // This configures VITE for github codespaces and installs gh cli + "postCreateCommand": "if [ \"${CODESPACES}\" = \"true\" ]; then echo 'VITE_API_BASE=\"https://$CODESPACE_NAME-3001.$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN/api\"' > ./frontend/.env && (type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) && sudo mkdir -p -m 755 /etc/apt/keyrings && wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg && echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null && sudo apt update && sudo apt install gh -y; fi", + "portsAttributes": { + "3001": { + "label": "Backend", + "onAutoForward": "notify" + }, + "3000": { + "label": "Frontend", + "onAutoForward": "openPreview" + } + }, + "capAdd": [ + "SYS_ADMIN" // needed for puppeteer using headless chrome in sandbox + ], + "remoteEnv": { + "NODE_ENV": "development", + "ESLINT_USE_FLAT_CONFIG": "true", + "ANYTHING_LLM_RUNTIME": "docker" + }, + // "initializeCommand": "echo Initialize....", + "shutdownAction": "stopContainer", + // Configure tool-specific properties. + "customizations": { + "codespaces": { + "openFiles": [ + "README.md", + ".devcontainer/README.md" + ] + }, + "vscode": { + "openFiles": [ + "README.md", + ".devcontainer/README.md" + ], + "extensions": [ + "bierner.github-markdown-preview", + "bradlc.vscode-tailwindcss", + "dbaeumer.vscode-eslint", + "editorconfig.editorconfig", + "esbenp.prettier-vscode", + "exiasr.hadolint", + "flowtype.flow-for-vscode", + "gamunu.vscode-yarn", + "hashicorp.terraform", + "mariusschulz.yarn-lock-syntax", + "ms-azuretools.vscode-docker", + "streetsidesoftware.code-spell-checker", + "actboy168.tasks", + "tombonnike.vscode-status-bar-format-toggle", + "ms-vscode.js-debug" + ], + "settings": { + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[dockercompose]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[dockerfile]": { + "editor.defaultFormatter": "ms-azuretools.vscode-docker" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[postcss]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[toml]": { + "editor.defaultFormatter": "tamasfe.even-better-toml" + }, + "eslint.debug": true, + "eslint.enable": true, + "eslint.experimental.useFlatConfig": true, + "eslint.run": "onSave", + "files.associations": { + ".*ignore": "ignore", + ".editorconfig": "editorconfig", + ".env*": "properties", + ".flowconfig": "ini", + ".prettierrc": "json", + "*.css": "tailwindcss", + "*.md": "markdown", + "*.sh": "shellscript", + "docker-compose.*": "dockercompose", + "Dockerfile*": "dockerfile", + "yarn.lock": "yarnlock" + }, + "javascript.format.enable": false, + "javascript.inlayHints.enumMemberValues.enabled": true, + "javascript.inlayHints.functionLikeReturnTypes.enabled": true, + "javascript.inlayHints.parameterTypes.enabled": true, + "javascript.inlayHints.variableTypes.enabled": true, + "js/ts.implicitProjectConfig.module": "CommonJS", + "json.format.enable": false, + "json.schemaDownload.enable": true, + "npm.autoDetect": "on", + "npm.packageManager": "yarn", + "prettier.useEditorConfig": false, + "tailwindCSS.files.exclude": [ + "**/.git/**", + "**/node_modules/**", + "**/.hg/**", + "**/.svn/**", + "**/dist/**" + ], + "typescript.validate.enable": false, + "workbench.editorAssociations": { + "*.md": "vscode.markdown.preview.editor" + } + } + } + } + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cca33a8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +**/server/utils/agents/aibitat/example/** +**/server/storage/documents/** +**/server/storage/vector-cache/** +**/server/storage/*.db +**/server/storage/lancedb +**/collector/hotdir/** +**/collector/outputs/** +**/node_modules/ +**/dist/ +**/v-env/ +**/__pycache__/ +**/.env +**/.env.* +**/bundleinspector.html +**/tmp/** +**/.log +!docker/.env.example +!frontend/.env.production \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e175db5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +# Non-configurable Prettier behaviors +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true + +# Configurable Prettier behaviors +# (change these if your Prettier config differs) +end_of_line = lf +indent_style = space +indent_size = 2 +max_line_length = 80 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..94f480d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..411f850 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: Mintplex-Labs \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/01_bug.yml b/.github/ISSUE_TEMPLATE/01_bug.yml new file mode 100644 index 0000000..582670a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01_bug.yml @@ -0,0 +1,42 @@ +name: 🐛 Bug Report +description: File a bug report for AnythingLLM +title: "[BUG]: " +labels: [possible bug] +body: + - type: markdown + attributes: + value: | + Use this template to file a bug report for AnythingLLM. Please be as descriptive as possible to allow everyone to replicate and solve your issue. + - type: dropdown + id: runtime + attributes: + label: How are you running AnythingLLM? + description: AnythingLLM can be run in many environments, pick the one that best represents where you encounter the bug. + options: + - Docker (local) + - Docker (remote machine) + - Local development + - AnythingLLM desktop app + - All versions + - Not listed + default: 0 + validations: + required: true + + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Also tell us, what did you expect to happen? + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Are there known steps to reproduce? + description: | + Let us know how to reproduce the bug and we may be able to fix it more + quickly. This is not required, but it is helpful. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/02_feature.yml b/.github/ISSUE_TEMPLATE/02_feature.yml new file mode 100644 index 0000000..89238b5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02_feature.yml @@ -0,0 +1,19 @@ +name: ✨ New Feature suggestion +description: Suggest a new feature for AnythingLLM! +title: "[FEAT]: " +labels: [enhancement, feature request] +body: + - type: markdown + attributes: + value: | + Share a new idea for a feature or improvement. Be sure to search existing + issues first to avoid duplicates. + + - type: textarea + id: description + attributes: + label: What would you like to see? + description: | + Describe the feature and why it would be useful to your use-case as well as others. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/03_documentation.yml b/.github/ISSUE_TEMPLATE/03_documentation.yml new file mode 100644 index 0000000..5580085 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03_documentation.yml @@ -0,0 +1,13 @@ +name: 📚 Documentation improvement +title: "[DOCS]: " +description: Report an issue or problem with the documentation. +labels: [documentation] + +body: + - type: textarea + id: description + attributes: + label: Description + description: Describe the issue with the documentation that is giving you trouble or causing confusion. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..d5485e6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: 🧑‍🤝‍🧑 Community Discord + url: https://discord.gg/6UyHPeGZAC + about: Interact with the Mintplex Labs community here by asking for help, discussing and more! diff --git a/.github/workflows/build-and-push-image-semver.yaml b/.github/workflows/build-and-push-image-semver.yaml new file mode 100644 index 0000000..8fb6d35 --- /dev/null +++ b/.github/workflows/build-and-push-image-semver.yaml @@ -0,0 +1,115 @@ +name: Publish AnythingLLM Docker image on Release (amd64 & arm64) + +concurrency: + group: build-${{ github.ref }} + cancel-in-progress: true + +on: + release: + types: [published] + +jobs: + push_multi_platform_to_registries: + name: Push Docker multi-platform image to multiple registries + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Check if DockerHub build needed + shell: bash + run: | + # Check if the secret for USERNAME is set (don't even check for the password) + if [[ -z "${{ secrets.DOCKER_USERNAME }}" ]]; then + echo "DockerHub build not needed" + echo "enabled=false" >> $GITHUB_OUTPUT + else + echo "DockerHub build needed" + echo "enabled=true" >> $GITHUB_OUTPUT + fi + id: dockerhub + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + # Only login to the Docker Hub if the repo is mintplex/anythingllm, to allow for forks to build on GHCR + if: steps.dockerhub.outputs.enabled == 'true' + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: | + ${{ steps.dockerhub.outputs.enabled == 'true' && 'mintplexlabs/anythingllm' || '' }} + ghcr.io/${{ github.repository }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + + - name: Build and push multi-platform Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./docker/Dockerfile + push: true + sbom: true + provenance: mode=max + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # For Docker scout there are some intermediary reported CVEs which exists outside + # of execution content or are unreachable by an attacker but exist in image. + # We create VEX files for these so they don't show in scout summary. + - name: Collect known and verified CVE exceptions + id: cve-list + run: | + # Collect CVEs from filenames in vex folder + CVE_NAMES="" + for file in ./docker/vex/*.vex.json; do + [ -e "$file" ] || continue + filename=$(basename "$file") + stripped_filename=${filename%.vex.json} + CVE_NAMES+=" $stripped_filename" + done + echo "CVE_EXCEPTIONS=$CVE_NAMES" >> $GITHUB_OUTPUT + shell: bash + + # About VEX attestations https://docs.docker.com/scout/explore/exceptions/ + # Justifications https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md#status-justifications + - name: Add VEX attestations + env: + CVE_EXCEPTIONS: ${{ steps.cve-list.outputs.CVE_EXCEPTIONS }} + run: | + echo $CVE_EXCEPTIONS + curl -sSfL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh | sh -s -- + for cve in $CVE_EXCEPTIONS; do + for tag in "${{ join(fromJSON(steps.meta.outputs.json).tags, ' ') }}"; do + echo "Attaching VEX exception $cve to $tag" + docker scout attestation add \ + --file "./docker/vex/$cve.vex.json" \ + --predicate-type https://openvex.dev/ns/v0.2.0 \ + $tag + done + done + shell: bash diff --git a/.github/workflows/build-and-push-image.yaml b/.github/workflows/build-and-push-image.yaml new file mode 100644 index 0000000..fe903f8 --- /dev/null +++ b/.github/workflows/build-and-push-image.yaml @@ -0,0 +1,134 @@ +# This GitHub action is for publishing of the primary image for AnythingLLM +# It will publish a linux/amd64 and linux/arm64 image at the same time +# This file should ONLY BY USED FOR `master` BRANCH. +# TODO: GitHub now has an ubuntu-24.04-arm64 runner, but we still need +# to use QEMU to build the arm64 image because Chromium is not available for Linux arm64 +# so builds will still fail, or fail much more often. Its inconsistent and frustrating. +name: Publish AnythingLLM Primary Docker image (amd64/arm64) + +concurrency: + group: build-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: ['master'] # master branch only. Do not modify. + paths-ignore: + - '**.md' + - 'cloud-deployments/**/*' + - 'images/**/*' + - '.vscode/**/*' + - '**/.env.example' + - '.github/ISSUE_TEMPLATE/**/*' + - '.devcontainer/**/*' + - 'embed/**/*' # Embed is submodule + - 'browser-extension/**/*' # Chrome extension is submodule + - 'server/utils/agents/aibitat/example/**/*' # Do not push new image for local dev testing of new aibitat images. + +jobs: + push_multi_platform_to_registries: + name: Push Docker multi-platform image to multiple registries + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Check if DockerHub build needed + shell: bash + run: | + # Check if the secret for USERNAME is set (don't even check for the password) + if [[ -z "${{ secrets.DOCKER_USERNAME }}" ]]; then + echo "DockerHub build not needed" + echo "enabled=false" >> $GITHUB_OUTPUT + else + echo "DockerHub build needed" + echo "enabled=true" >> $GITHUB_OUTPUT + fi + id: dockerhub + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + # Only login to the Docker Hub if the repo is mintplex/anythingllm, to allow for forks to build on GHCR + if: steps.dockerhub.outputs.enabled == 'true' + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: | + ${{ steps.dockerhub.outputs.enabled == 'true' && 'mintplexlabs/anythingllm' || '' }} + ghcr.io/${{ github.repository }} + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=ref,event=branch + type=ref,event=tag + type=ref,event=pr + + - name: Build and push multi-platform Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./docker/Dockerfile + push: true + sbom: true + provenance: mode=max + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # For Docker scout there are some intermediary reported CVEs which exists outside + # of execution content or are unreachable by an attacker but exist in image. + # We create VEX files for these so they don't show in scout summary. + - name: Collect known and verified CVE exceptions + id: cve-list + run: | + # Collect CVEs from filenames in vex folder + CVE_NAMES="" + for file in ./docker/vex/*.vex.json; do + [ -e "$file" ] || continue + filename=$(basename "$file") + stripped_filename=${filename%.vex.json} + CVE_NAMES+=" $stripped_filename" + done + echo "CVE_EXCEPTIONS=$CVE_NAMES" >> $GITHUB_OUTPUT + shell: bash + + # About VEX attestations https://docs.docker.com/scout/explore/exceptions/ + # Justifications https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md#status-justifications + - name: Add VEX attestations + env: + CVE_EXCEPTIONS: ${{ steps.cve-list.outputs.CVE_EXCEPTIONS }} + run: | + echo $CVE_EXCEPTIONS + curl -sSfL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh | sh -s -- + for cve in $CVE_EXCEPTIONS; do + for tag in "${{ join(fromJSON(steps.meta.outputs.json).tags, ' ') }}"; do + echo "Attaching VEX exception $cve to $tag" + docker scout attestation add \ + --file "./docker/vex/$cve.vex.json" \ + --predicate-type https://openvex.dev/ns/v0.2.0 \ + $tag + done + done + shell: bash \ No newline at end of file diff --git a/.github/workflows/check-translations.yaml b/.github/workflows/check-translations.yaml new file mode 100644 index 0000000..ad1de0c --- /dev/null +++ b/.github/workflows/check-translations.yaml @@ -0,0 +1,37 @@ +# This GitHub action is for validation of all languages which translations are offered for +# in the locales folder in `frontend/src`. All languages are compared to the EN translation +# schema since that is the fallback language setting. This workflow will run on all PRs that +# modify any files in the translation directory +name: Verify translations files + +concurrency: + group: build-${{ github.ref }} + cancel-in-progress: true + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - "frontend/src/locales/**.js" + +jobs: + run-script: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Run verifyTranslations.mjs script + run: | + cd frontend/src/locales + node verifyTranslations.mjs + + - name: Fail job on error + if: failure() + run: exit 1 diff --git a/.github/workflows/dev-build.yaml b/.github/workflows/dev-build.yaml new file mode 100644 index 0000000..7873053 --- /dev/null +++ b/.github/workflows/dev-build.yaml @@ -0,0 +1,114 @@ +name: AnythingLLM Development Docker image (amd64) + +concurrency: + group: build-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: ['sharp-pdf-image-converter'] # put your current branch to create a build. Core team only. + paths-ignore: + - '**.md' + - 'cloud-deployments/*' + - 'images/**/*' + - '.vscode/**/*' + - '**/.env.example' + - '.github/ISSUE_TEMPLATE/**/*' + - 'embed/**/*' # Embed should be published to frontend (yarn build:publish) if any changes are introduced + - 'server/utils/agents/aibitat/example/**/*' # Do not push new image for local dev testing of new aibitat images. + +jobs: + push_multi_platform_to_registries: + name: Push Docker multi-platform image to multiple registries + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Check if DockerHub build needed + shell: bash + run: | + # Check if the secret for USERNAME is set (don't even check for the password) + if [[ -z "${{ secrets.DOCKER_USERNAME }}" ]]; then + echo "DockerHub build not needed" + echo "enabled=false" >> $GITHUB_OUTPUT + else + echo "DockerHub build needed" + echo "enabled=true" >> $GITHUB_OUTPUT + fi + id: dockerhub + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + # Only login to the Docker Hub if the repo is mintplex/anythingllm, to allow for forks to build on GHCR + if: steps.dockerhub.outputs.enabled == 'true' + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: | + ${{ steps.dockerhub.outputs.enabled == 'true' && 'mintplexlabs/anythingllm' || '' }} + tags: | + type=raw,value=dev + + - name: Build and push multi-platform Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./docker/Dockerfile + push: true + sbom: true + provenance: mode=max + platforms: linux/amd64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # For Docker scout there are some intermediary reported CVEs which exists outside + # of execution content or are unreachable by an attacker but exist in image. + # We create VEX files for these so they don't show in scout summary. + - name: Collect known and verified CVE exceptions + id: cve-list + run: | + # Collect CVEs from filenames in vex folder + CVE_NAMES="" + for file in ./docker/vex/*.vex.json; do + [ -e "$file" ] || continue + filename=$(basename "$file") + stripped_filename=${filename%.vex.json} + CVE_NAMES+=" $stripped_filename" + done + echo "CVE_EXCEPTIONS=$CVE_NAMES" >> $GITHUB_OUTPUT + shell: bash + + # About VEX attestations https://docs.docker.com/scout/explore/exceptions/ + # Justifications https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md#status-justifications + # Fixed to use v1.15.1 of scout-cli as v1.16.0 install script is broken + # https://github.com/docker/scout-cli + - name: Add VEX attestations + env: + CVE_EXCEPTIONS: ${{ steps.cve-list.outputs.CVE_EXCEPTIONS }} + run: | + echo $CVE_EXCEPTIONS + curl -sSfL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh | sh -s -- + for cve in $CVE_EXCEPTIONS; do + for tag in "${{ join(fromJSON(steps.meta.outputs.json).tags, ' ') }}"; do + echo "Attaching VEX exception $cve to $tag" + docker scout attestation add \ + --file "./docker/vex/$cve.vex.json" \ + --predicate-type https://openvex.dev/ns/v0.2.0 \ + $tag + done + done + shell: bash \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6a7e55 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +v-env +.env +!.env.example + +node_modules +__pycache__ +v-env +.DS_Store +aws_cf_deploy_anything_llm.json +yarn.lock +*.bak diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..526045b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "browser-extension"] + path = browser-extension + url = git@github.com:Mintplex-Labs/anythingllm-extension.git +[submodule "embed"] + path = embed + url = git@github.com:Mintplex-Labs/anythingllm-embed.git + branch = main diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000..b76a510 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,8 @@ +failure-threshold: warning +ignored: + - DL3008 + - DL3013 +format: tty +trustedRegistries: + - docker.io + - gcr.io diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/anything-llm-master.iml b/.idea/anything-llm-master.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/anything-llm-master.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..54767ec --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,58 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..47478b9 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..10fce6b --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..6aab9b4 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v18.18.0 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..e3b0c14 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,16 @@ +# defaults +**/.git +**/.svn +**/.hg +**/node_modules + +#frontend +frontend/bundleinspector.html +**/dist + +#server +server/swagger/openapi.json + +#embed +**/static/** +embed/src/utils/chat/hljs.js diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..5e2bccf --- /dev/null +++ b/.prettierrc @@ -0,0 +1,38 @@ +{ + "tabWidth": 2, + "useTabs": false, + "endOfLine": "lf", + "semi": true, + "singleQuote": false, + "printWidth": 80, + "trailingComma": "es5", + "bracketSpacing": true, + "bracketSameLine": false, + "overrides": [ + { + "files": ["*.js", "*.mjs", "*.jsx"], + "options": { + "parser": "flow", + "arrowParens": "always" + } + }, + { + "files": ["*.config.js"], + "options": { + "semi": false, + "parser": "flow", + "trailingComma": "none" + } + }, + { + "files": "*.html", + "options": { + "bracketSameLine": true + } + }, + { + "files": ".prettierrc", + "options": { "parser": "json" } + } + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..911b0a7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,74 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Collector debug", + "request": "launch", + "cwd": "${workspaceFolder}/collector", + "env": { + "NODE_ENV": "development" + }, + "runtimeArgs": [ + "index.js" + ], + // not using yarn/nodemon because it doesn't work with breakpoints + // "runtimeExecutable": "yarn", + "skipFiles": [ + "/**" + ], + "type": "node" + }, + { + "name": "Server debug", + "request": "launch", + "cwd": "${workspaceFolder}/server", + "env": { + "NODE_ENV": "development" + }, + "runtimeArgs": [ + "index.js" + ], + // not using yarn/nodemon because it doesn't work with breakpoints + // "runtimeExecutable": "yarn", + "skipFiles": [ + "/**" + ], + "type": "node" + }, + { + "name": "Frontend debug", + "request": "launch", + "cwd": "${workspaceFolder}/frontend", + "env": { + "NODE_ENV": "development", + }, + "runtimeExecutable": "${workspaceFolder}/frontend/node_modules/.bin/vite", + "runtimeArgs": [ + "--debug", + "--host=0.0.0.0" + ], + // "runtimeExecutable": "yarn", + "skipFiles": [ + "/**" + ], + "type": "node" + }, + { + "name": "Launch Edge", + "request": "launch", + "type": "msedge", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}" + }, + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..307bbe6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,62 @@ +{ + "cSpell.words": [ + "adoc", + "aibitat", + "AIbitat", + "allm", + "anythingllm", + "Apipie", + "Astra", + "Chartable", + "cleancss", + "comkey", + "cooldown", + "cooldowns", + "datafile", + "Deduplicator", + "Dockerized", + "docpath", + "elevenlabs", + "Embeddable", + "epub", + "fireworksai", + "GROQ", + "hljs", + "huggingface", + "inferencing", + "koboldcpp", + "Langchain", + "lmstudio", + "localai", + "mbox", + "Milvus", + "Mintplex", + "mixtral", + "moderations", + "novita", + "numpages", + "Ollama", + "Oobabooga", + "openai", + "opendocument", + "openrouter", + "pagerender", + "Qdrant", + "royalblue", + "SearchApi", + "searxng", + "Serper", + "Serply", + "streamable", + "textgenwebui", + "togetherai", + "Unembed", + "uuidv", + "vectordbs", + "Weaviate", + "XAILLM", + "Zilliz" + ], + "eslint.experimental.useFlatConfig": true, + "docker.languageserver.formatter.ignoreMultilineInstructions": true +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..6783e17 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,94 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "options": { + "cwd": "${workspaceFolder}/collector", + "statusbar": { + "color": "#ffea00", + "detail": "Runs the collector", + "label": "Collector: $(play) run", + "running": { + "color": "#ffea00", + "label": "Collector: $(gear~spin) running" + } + } + }, + "command": "cd ${workspaceFolder}/collector/ && yarn dev", + "runOptions": { + "instanceLimit": 1, + "reevaluateOnRerun": true + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": false + }, + "label": "Collector: run" + }, + { + "type": "shell", + "options": { + "cwd": "${workspaceFolder}/server", + "statusbar": { + "color": "#ffea00", + "detail": "Runs the server", + "label": "Server: $(play) run", + "running": { + "color": "#ffea00", + "label": "Server: $(gear~spin) running" + } + } + }, + "command": "if [ \"${CODESPACES}\" = \"true\" ]; then while ! gh codespace ports -c $CODESPACE_NAME | grep 3001; do sleep 1; done; gh codespace ports visibility 3001:public -c $CODESPACE_NAME; fi & cd ${workspaceFolder}/server/ && yarn dev", + "runOptions": { + "instanceLimit": 1, + "reevaluateOnRerun": true + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": false + }, + "label": "Server: run" + }, + { + "type": "shell", + "options": { + "cwd": "${workspaceFolder}/frontend", + "statusbar": { + "color": "#ffea00", + "detail": "Runs the frontend", + "label": "Frontend: $(play) run", + "running": { + "color": "#ffea00", + "label": "Frontend: $(gear~spin) running" + } + } + }, + "command": "cd ${workspaceFolder}/frontend/ && yarn dev", + "runOptions": { + "instanceLimit": 1, + "reevaluateOnRerun": true + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": false + }, + "label": "Frontend: run" + } + ] +} diff --git a/BARE_METAL.md b/BARE_METAL.md new file mode 100644 index 0000000..205cebb --- /dev/null +++ b/BARE_METAL.md @@ -0,0 +1,115 @@ +# Run AnythingLLM in production without Docker + +> [!WARNING] +> This method of deployment is **not supported** by the core-team and is to be used as a reference for your deployment. +> You are fully responsible for securing your deployment and data in this mode. +> **Any issues** experienced from bare-metal or non-containerized deployments will be **not** answered or supported. + +Here you can find the scripts and known working process to run AnythingLLM outside of a Docker container. + +### Minimum Requirements +> [!TIP] +> You should aim for at least 2GB of RAM. Disk storage is proportional to however much data +> you will be storing (documents, vectors, models, etc). Minimum 10GB recommended. + +- NodeJS v18 +- Yarn + + +## Getting started + +1. Clone the repo into your server as the user who the application will run as. +`git clone git@github.com:Mintplex-Labs/anything-llm.git` + +2. `cd anything-llm` and run `yarn setup`. This will install all dependencies to run in production as well as debug the application. + +3. `cp server/.env.example server/.env` to create the basic ENV file for where instance settings will be read from on service start. + +4. Ensure that the `server/.env` file has _at least_ these keys to start. These values will persist and this file will be automatically written and managed after your first successful boot. +``` +STORAGE_DIR="/your/absolute/path/to/server/storage" +``` + +5. Edit the `frontend/.env` file for the `VITE_BASE_API` to now be set to `/api`. This is documented in the .env for which one you should use. +``` +# VITE_API_BASE='http://localhost:3001/api' # Use this URL when developing locally +# VITE_API_BASE="https://$CODESPACE_NAME-3001.$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN/api" # for GitHub Codespaces +VITE_API_BASE='/api' # Use this URL deploying on non-localhost address OR in docker. +``` + +## To start the application + +AnythingLLM is comprised of three main sections. The `frontend`, `server`, and `collector`. When running in production you will be running `server` and `collector` on two different processes, with a build step for compilation of the frontend. + +1. Build the frontend application. +`cd frontend && yarn build` - this will produce a `frontend/dist` folder that will be used later. + +2. Copy `frontend/dist` to `server/public` - `cp -R frontend/dist server/public`. +This should create a folder in `server` named `public` which contains a top level `index.html` file and various other files/folders. + +3. Migrate and prepare your database file. +``` +cd server && npx prisma generate --schema=./prisma/schema.prisma +cd server && npx prisma migrate deploy --schema=./prisma/schema.prisma +``` + +4. Boot the server in production +`cd server && NODE_ENV=production node index.js &` + +5. Boot the collection in another process +`cd collector && NODE_ENV=production node index.js &` + +AnythingLLM should now be running on `http://localhost:3001`! + +## Updating AnythingLLM + +To update AnythingLLM with future updates you can `git pull origin master` to pull in the latest code and then repeat steps 2 - 5 to deploy with all changes fully. + +_note_ You should ensure that each folder runs `yarn` again to ensure packages are up to date in case any dependencies were added, changed, or removed. + +_note_ You should `pkill node` before running an update so that you are not running multiple AnythingLLM processes on the same instance as this can cause conflicts. + + +### Example update script + +```shell +#!/bin/bash + +cd $HOME/anything-llm &&\ +git checkout . &&\ +git pull origin master &&\ +echo "HEAD pulled to commit $(git log -1 --pretty=format:"%h" | tail -n 1)" + +echo "Freezing current ENVs" +curl -I "http://localhost:3001/api/env-dump" | head -n 1|cut -d$' ' -f2 + +echo "Rebuilding Frontend" +cd $HOME/anything-llm/frontend && yarn && yarn build && cd $HOME/anything-llm + +echo "Copying to Sever Public" +rm -rf server/public +cp -r frontend/dist server/public + +echo "Killing node processes" +pkill node + +echo "Installing collector dependencies" +cd $HOME/anything-llm/collector && yarn + +echo "Installing server dependencies & running migrations" +cd $HOME/anything-llm/server && yarn +cd $HOME/anything-llm/server && npx prisma migrate deploy --schema=./prisma/schema.prisma +cd $HOME/anything-llm/server && npx prisma generate + +echo "Booting up services." +truncate -s 0 /logs/server.log # Or any other log file location. +truncate -s 0 /logs/collector.log + +cd $HOME/anything-llm/server +(NODE_ENV=production node index.js) &> /logs/server.log & + +cd $HOME/anything-llm/collector +(NODE_ENV=production node index.js) &> /logs/collector.log & +``` + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cc42d1d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) Mintplex Labs Inc. + +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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f578293 --- /dev/null +++ b/README.md @@ -0,0 +1,268 @@ + + +

+ AnythingLLM logo +

+ +
+Mintplex-Labs%2Fanything-llm | Trendshift +
+ +

+ AnythingLLM: The all-in-one AI app you were looking for.
+ Chat with your docs, use AI Agents, hyper-configurable, multi-user, & no frustrating set up required. +

+ +

+ + Discord + | + + License + | + + Docs + | + + Hosted Instance + +

+ +

+ English · 简体中文 · 日本語 +

+ +

+👉 AnythingLLM for desktop (Mac, Windows, & Linux)! Download Now +

+ +A full-stack application that enables you to turn any document, resource, or piece of content into context that any LLM can use as references during chatting. This application allows you to pick and choose which LLM or Vector Database you want to use as well as supporting multi-user management and permissions. + +![Chatting](https://github.com/Mintplex-Labs/anything-llm/assets/16845892/cfc5f47c-bd91-4067-986c-f3f49621a859) + +
+Watch the demo! + +[![Watch the video](/images/youtube.png)](https://youtu.be/f95rGD9trL0) + +
+ +### Product Overview + +AnythingLLM is a full-stack application where you can use commercial off-the-shelf LLMs or popular open source LLMs and vectorDB solutions to build a private ChatGPT with no compromises that you can run locally as well as host remotely and be able to chat intelligently with any documents you provide it. + +AnythingLLM divides your documents into objects called `workspaces`. A Workspace functions a lot like a thread, but with the addition of containerization of your documents. Workspaces can share documents, but they do not talk to each other so you can keep your context for each workspace clean. + +## Cool features of AnythingLLM + +- 🆕 [**Custom AI Agents**](https://docs.anythingllm.com/agent/custom/introduction) +- 🆕 [**No-code AI Agent builder**](https://docs.anythingllm.com/agent-flows/overview) +- 🖼️ **Multi-modal support (both closed and open-source LLMs!)** +- 👤 Multi-user instance support and permissioning _Docker version only_ +- 🦾 Agents inside your workspace (browse the web, etc) +- 💬 [Custom Embeddable Chat widget for your website](https://github.com/Mintplex-Labs/anythingllm-embed/blob/main/README.md) _Docker version only_ +- 📖 Multiple document type support (PDF, TXT, DOCX, etc) +- Simple chat UI with Drag-n-Drop funcitonality and clear citations. +- 100% Cloud deployment ready. +- Works with all popular [closed and open-source LLM providers](#supported-llms-embedder-models-speech-models-and-vector-databases). +- Built-in cost & time-saving measures for managing very large documents compared to any other chat UI. +- Full Developer API for custom integrations! +- Much more...install and find out! + +### Supported LLMs, Embedder Models, Speech models, and Vector Databases + +**Large Language Models (LLMs):** + +- [Any open-source llama.cpp compatible model](/server/storage/models/README.md#text-generation-llm-selection) +- [OpenAI](https://openai.com) +- [OpenAI (Generic)](https://openai.com) +- [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service) +- [AWS Bedrock](https://aws.amazon.com/bedrock/) +- [Anthropic](https://www.anthropic.com/) +- [NVIDIA NIM (chat models)](https://build.nvidia.com/explore/discover) +- [Google Gemini Pro](https://ai.google.dev/) +- [Hugging Face (chat models)](https://huggingface.co/) +- [Ollama (chat models)](https://ollama.ai/) +- [LM Studio (all models)](https://lmstudio.ai) +- [LocalAi (all models)](https://localai.io/) +- [Together AI (chat models)](https://www.together.ai/) +- [Fireworks AI (chat models)](https://fireworks.ai/) +- [Perplexity (chat models)](https://www.perplexity.ai/) +- [OpenRouter (chat models)](https://openrouter.ai/) +- [DeepSeek (chat models)](https://deepseek.com/) +- [Mistral](https://mistral.ai/) +- [Groq](https://groq.com/) +- [Cohere](https://cohere.com/) +- [KoboldCPP](https://github.com/LostRuins/koboldcpp) +- [LiteLLM](https://github.com/BerriAI/litellm) +- [Text Generation Web UI](https://github.com/oobabooga/text-generation-webui) +- [Apipie](https://apipie.ai/) +- [xAI](https://x.ai/) +- [Novita AI (chat models)](https://novita.ai/model-api/product/llm-api?utm_source=github_anything-llm&utm_medium=github_readme&utm_campaign=link) + +**Embedder models:** + +- [AnythingLLM Native Embedder](/server/storage/models/README.md) (default) +- [OpenAI](https://openai.com) +- [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service) +- [LocalAi (all)](https://localai.io/) +- [Ollama (all)](https://ollama.ai/) +- [LM Studio (all)](https://lmstudio.ai) +- [Cohere](https://cohere.com/) + +**Audio Transcription models:** + +- [AnythingLLM Built-in](https://github.com/Mintplex-Labs/anything-llm/tree/master/server/storage/models#audiovideo-transcription) (default) +- [OpenAI](https://openai.com/) + +**TTS (text-to-speech) support:** + +- Native Browser Built-in (default) +- [PiperTTSLocal - runs in browser](https://github.com/rhasspy/piper) +- [OpenAI TTS](https://platform.openai.com/docs/guides/text-to-speech/voice-options) +- [ElevenLabs](https://elevenlabs.io/) +- Any OpenAI Compatible TTS service. + +**STT (speech-to-text) support:** + +- Native Browser Built-in (default) + +**Vector Databases:** + +- [LanceDB](https://github.com/lancedb/lancedb) (default) +- [Astra DB](https://www.datastax.com/products/datastax-astra) +- [Pinecone](https://pinecone.io) +- [Chroma](https://trychroma.com) +- [Weaviate](https://weaviate.io) +- [Qdrant](https://qdrant.tech) +- [Milvus](https://milvus.io) +- [Zilliz](https://zilliz.com) + +### Technical Overview + +This monorepo consists of three main sections: + +- `frontend`: A viteJS + React frontend that you can run to easily create and manage all your content the LLM can use. +- `server`: A NodeJS express server to handle all the interactions and do all the vectorDB management and LLM interactions. +- `collector`: NodeJS express server that process and parses documents from the UI. +- `docker`: Docker instructions and build process + information for building from source. +- `embed`: Submodule for generation & creation of the [web embed widget](https://github.com/Mintplex-Labs/anythingllm-embed). +- `browser-extension`: Submodule for the [chrome browser extension](https://github.com/Mintplex-Labs/anythingllm-extension). + +## 🛳 Self Hosting + +Mintplex Labs & the community maintain a number of deployment methods, scripts, and templates that you can use to run AnythingLLM locally. Refer to the table below to read how to deploy on your preferred environment or to automatically deploy. +| Docker | AWS | GCP | Digital Ocean | Render.com | +|----------------------------------------|----|-----|---------------|------------| +| [![Deploy on Docker][docker-btn]][docker-deploy] | [![Deploy on AWS][aws-btn]][aws-deploy] | [![Deploy on GCP][gcp-btn]][gcp-deploy] | [![Deploy on DigitalOcean][do-btn]][do-deploy] | [![Deploy on Render.com][render-btn]][render-deploy] | + +| Railway | RepoCloud | Elestio | +| --- | --- | --- | +| [![Deploy on Railway][railway-btn]][railway-deploy] | [![Deploy on RepoCloud][repocloud-btn]][repocloud-deploy] | [![Deploy on Elestio][elestio-btn]][elestio-deploy] | + +[or set up a production AnythingLLM instance without Docker →](./BARE_METAL.md) + +## How to setup for development + +- `yarn setup` To fill in the required `.env` files you'll need in each of the application sections (from root of repo). + - Go fill those out before proceeding. Ensure `server/.env.development` is filled or else things won't work right. +- `yarn dev:server` To boot the server locally (from root of repo). +- `yarn dev:frontend` To boot the frontend locally (from root of repo). +- `yarn dev:collector` To then run the document collector (from root of repo). + +[Learn about documents](./server/storage/documents/DOCUMENTS.md) + +[Learn about vector caching](./server/storage/vector-cache/VECTOR_CACHE.md) + +## External Apps & Integrations + +_These are apps that are not maintained by Mintplex Labs, but are compatible with AnythingLLM. A listing here is not an endorsement._ + +- [Midori AI Subsystem Manager](https://io.midori-ai.xyz/subsystem/anythingllm/) - A streamlined and efficient way to deploy AI systems using Docker container technology. +- [Coolify](https://coolify.io/docs/services/anythingllm/) - Deploy AnythingLLM with a single click. +- [GPTLocalhost for Microsoft Word](https://gptlocalhost.com/demo/) - A local Word Add-in for you to use AnythingLLM in Microsoft Word. + +## Telemetry & Privacy + +AnythingLLM by Mintplex Labs Inc contains a telemetry feature that collects anonymous usage information. + +
+More about Telemetry & Privacy for AnythingLLM + +### Why? + +We use this information to help us understand how AnythingLLM is used, to help us prioritize work on new features and bug fixes, and to help us improve AnythingLLM's performance and stability. + +### Opting out + +Set `DISABLE_TELEMETRY` in your server or docker .env settings to "true" to opt out of telemetry. You can also do this in-app by going to the sidebar > `Privacy` and disabling telemetry. + +### What do you explicitly track? + +We will only track usage details that help us make product and roadmap decisions, specifically: + +- Type of your installation (Docker or Desktop) +- When a document is added or removed. No information _about_ the document. Just that the event occurred. This gives us an idea of use. +- Type of vector database in use. Let's us know which vector database provider is the most used to prioritize changes when updates arrive for that provider. +- Type of LLM in use. Let's us know the most popular choice and prioritize changes when updates arrive for that provider. +- Chat is sent. This is the most regular "event" and gives us an idea of the daily-activity of this project across all installations. Again, only the event is sent - we have no information on the nature or content of the chat itself. + +You can verify these claims by finding all locations `Telemetry.sendTelemetry` is called. Additionally these events are written to the output log so you can also see the specific data which was sent - if enabled. No IP or other identifying information is collected. The Telemetry provider is [PostHog](https://posthog.com/) - an open-source telemetry collection service. + +[View all telemetry events in source code](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry\(&type=code) + +
+ + +## 👋 Contributing + +- create issue +- create PR with branch name format of `-` +- LGTM from core-team + +## 🌟 Contributors + +[![anythingllm contributors](https://contrib.rocks/image?repo=mintplex-labs/anything-llm)](https://github.com/mintplex-labs/anything-llm/graphs/contributors) + +[![Star History Chart](https://api.star-history.com/svg?repos=mintplex-labs/anything-llm&type=Timeline)](https://star-history.com/#mintplex-labs/anything-llm&Date) + +## 🔗 More Products + +- **[VectorAdmin][vector-admin]:** An all-in-one GUI & tool-suite for managing vector databases. +- **[OpenAI Assistant Swarm][assistant-swarm]:** Turn your entire library of OpenAI assistants into one single army commanded from a single agent. + +
+ +[![][back-to-top]](#readme-top) + +
+ +--- + +Copyright © 2025 [Mintplex Labs][profile-link].
+This project is [MIT](./LICENSE) licensed. + + + +[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-222628?style=flat-square +[profile-link]: https://github.com/mintplex-labs +[vector-admin]: https://github.com/mintplex-labs/vector-admin +[assistant-swarm]: https://github.com/Mintplex-Labs/openai-assistant-swarm +[docker-btn]: ./images/deployBtns/docker.png +[docker-deploy]: ./docker/HOW_TO_USE_DOCKER.md +[aws-btn]: ./images/deployBtns/aws.png +[aws-deploy]: ./cloud-deployments/aws/cloudformation/DEPLOY.md +[gcp-btn]: https://deploy.cloud.run/button.svg +[gcp-deploy]: ./cloud-deployments/gcp/deployment/DEPLOY.md +[do-btn]: https://www.deploytodo.com/do-btn-blue.svg +[do-deploy]: ./cloud-deployments/digitalocean/terraform/DEPLOY.md +[render-btn]: https://render.com/images/deploy-to-render-button.svg +[render-deploy]: https://render.com/deploy?repo=https://github.com/Mintplex-Labs/anything-llm&branch=render +[render-btn]: https://render.com/images/deploy-to-render-button.svg +[render-deploy]: https://render.com/deploy?repo=https://github.com/Mintplex-Labs/anything-llm&branch=render +[railway-btn]: https://railway.app/button.svg +[railway-deploy]: https://railway.app/template/HNSCS1?referralCode=WFgJkn +[repocloud-btn]: https://d16t0pc4846x52.cloudfront.net/deploylobe.svg +[repocloud-deploy]: https://repocloud.io/details/?app_id=276 +[elestio-btn]: https://elest.io/images/logos/deploy-to-elestio-btn.png +[elestio-deploy]: https://elest.io/open-source/anythingllm diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..963fe71 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 0.1.x | :white_check_mark: | + + +## Reporting a Vulnerability + +If a security concern is found that you would like to disclose you can create a PR for it or if you would like to clear this issue before posting you can email [Core Mintplex Labs Team](mailto:team@mintplexlabs.com). diff --git a/cloud-deployments/aws/cloudformation/DEPLOY.md b/cloud-deployments/aws/cloudformation/DEPLOY.md new file mode 100644 index 0000000..a06026c --- /dev/null +++ b/cloud-deployments/aws/cloudformation/DEPLOY.md @@ -0,0 +1,49 @@ +# How to deploy a private AnythingLLM instance on AWS + +With an AWS account you can easily deploy a private AnythingLLM instance on AWS. This will create a url that you can access from any browser over HTTP (HTTPS not supported). This single instance will run on your own keys and they will not be exposed - however if you want your instance to be protected it is highly recommend that you set a password one setup is complete. + +**Quick Launch (EASY)** +1. Log in to your AWS account +2. Open [CloudFormation](https://us-west-1.console.aws.amazon.com/cloudformation/home) +3. Ensure you are deploying in a geographic zone that is nearest to your physical location to reduce latency. +4. Click `Create Stack` + +![Create Stack](../../../images/screenshots/create_stack.png) + +5. Use the file `cloudformation_create_anythingllm.json` as your JSON template. + +![Upload Stack](../../../images/screenshots/upload.png) + +6. Click Deploy. +7. Wait for stack events to finish and be marked as `Completed` +8. View `Outputs` tab. + +![Stack Output](../../../images/screenshots/cf_outputs.png) + +9. Wait for all resources to be built. Now wait until instance is available on `[InstanceIP]:3001`. +This process may take up to 10 minutes. See **Note** below on how to visualize this process. + +The output of this cloudformation stack will be: +- 1 EC2 Instance +- 1 Security Group with 0.0.0.0/0 access on port 3001 +- 1 EC2 Instance Volume `gb2` of 10Gib minimum - customizable pre-deploy. + +**Requirements** +- An AWS account with billing information. + +## Please read this notice before submitting issues about your deployment + +**Note:** +Your instance will not be available instantly. Depending on the instance size you launched with it can take 5-10 minutes to fully boot up. + +If you want to check the instance's progress, navigate to [your deployed EC2 instances](https://us-west-1.console.aws.amazon.com/ec2/home) and connect to your instance via SSH in browser. + +Once connected run `sudo tail -f /var/log/cloud-init-output.log` and wait for the file to conclude deployment of the docker image. +You should see an output like this +``` +[+] Running 2/2 + ⠿ Network docker_anything-llm Created + ⠿ Container anything-llm Started +``` + +Additionally, your use of this deployment process means you are responsible for any costs of these AWS resources fully. \ No newline at end of file diff --git a/cloud-deployments/aws/cloudformation/aws_https_instructions.md b/cloud-deployments/aws/cloudformation/aws_https_instructions.md new file mode 100644 index 0000000..26b0a6b --- /dev/null +++ b/cloud-deployments/aws/cloudformation/aws_https_instructions.md @@ -0,0 +1,118 @@ +# How to Configure HTTPS for Anything LLM AWS private deployment +Instructions for manual https configuration after generating and running the aws cloudformation template (aws_build_from_source_no_credentials.json). Tested on following browsers: Firefox version 119, Chrome version 118, Edge 118. + +**Requirements** +- Successful deployment of Amazon Linux 2023 EC2 instance with Docker container running Anything LLM +- Admin priv to configure Elastic IP for EC2 instance via AWS Management Console UI +- Admin priv to configure DNS services (i.e. AWS Route 53) via AWS Management Console UI +- Admin priv to configure EC2 Security Group rules via AWS Management Console UI + +## Step 1: Allocate and assign Elastic IP Address to your deployed EC2 instance +1. Follow AWS instructions on allocating EIP here: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html#using-instance-addressing-eips-allocating +2. Follow AWS instructions on assigning EIP to EC2 instance here: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html#using-instance-addressing-eips-associating + +## Step 2: Configure DNS A record to resolve to the previously assigned EC2 instance via EIP +These instructions assume that you already have a top-level domain configured and are using a subdomain +to access AnythingLLM. +1. Follow AWS instructions on routing traffic to EC2 instance here: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-ec2-instance.html + +## Step 3: Install and enable nginx +These instructions are for CLI configuration and assume you are logged in to EC2 instance as the ec2-user. +1. $sudo yum install nginx -y +2. $sudo systemctl enable nginx && sudo systemctl start nginx + +## Step 4: Install certbot +These instructions are for CLI configuration and assume you are logged in to EC2 instance as the ec2-user. +1. $sudo yum install -y augeas-libs +2. $sudo python3 -m venv /opt/certbot/ +3. $sudo /opt/certbot/bin/pip install --upgrade pip +4. $sudo /opt/certbot/bin/pip install certbot certbot-nginx +5. $sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot + +## Step 5: Configure temporary Inbound Traffic Rule for Security Group to certbot DNS verification +1. Follow AWS instructions on creating inbound rule (http port 80 0.0.0.0/0) for EC2 security group here: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule + +## Step 6: Comment out default http NGINX proxy configuration +These instructions are for CLI configuration and assume you are logged in to EC2 instance as the ec2-user. +1. $sudo vi /etc/nginx/nginx.conf +2. In the nginx.conf file, comment out the default server block configuration for http/port 80. It should look something like the following: +``` +# server { +# listen 80; +# listen [::]:80; +# server_name _; +# root /usr/share/nginx/html; +# +# # Load configuration files for the default server block. +# include /etc/nginx/default.d/*.conf; +# +# error_page 404 /404.html; +# location = /404.html { +# } +# +# error_page 500 502 503 504 /50x.html; +# location = /50x.html { +# } +# } +``` +3. Enter ':wq' to save the changes to the nginx default config + +## Step 7: Create simple http proxy configuration for AnythingLLM +These instructions are for CLI configuration and assume you are logged in to EC2 instance as the ec2-user. +1. $sudo vi /etc/nginx/conf.d/anything.conf +2. Add the following configuration ensuring that you add your FQDN:. + +``` +server { + # Enable websocket connections for agent protocol. + location ~* ^/api/agent-invocation/(.*) { + proxy_pass http://0.0.0.0:3001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + } + + listen 80; + server_name [insert FQDN here]; + location / { + # Prevent timeouts on long-running requests. + proxy_connect_timeout 605; + proxy_send_timeout 605; + proxy_read_timeout 605; + send_timeout 605; + keepalive_timeout 605; + + # Enable readable HTTP Streaming for LLM streamed responses + proxy_buffering off; + proxy_cache off; + + # Proxy your locally running service + proxy_pass http://0.0.0.0:3001; + } +} +``` +3. Enter ':wq' to save the changes to the anything config file + +## Step 8: Test nginx http proxy config and restart nginx service +These instructions are for CLI configuration and assume you are logged in to EC2 instance as the ec2-user. +1. $sudo nginx -t +2. $sudo systemctl restart nginx +3. Navigate to http://FQDN in a browser and you should be proxied to the AnythingLLM web UI. + +## Step 9: Generate/install cert +These instructions are for CLI configuration and assume you are logged in to EC2 instance as the ec2-user. +1. $sudo certbot --nginx -d [Insert FQDN here] + Example command: $sudo certbot --nginx -d anythingllm.exampleorganization.org + This command will generate the appropriate certificate files, write the files to /etc/letsencrypt/live/yourFQDN, and make updates to the nginx + configuration file for anythingllm located at /etc/nginx/conf.d/anything.llm +3. Enter the email address you would like to use for updates. +4. Accept the terms of service. +5. Accept or decline to receive communication from LetsEncrypt. + +## Step 10: Test Cert installation +1. $sudo cat /etc/nginx/conf.d/anything.conf +Your should see a completely updated configuration that includes https/443 and a redirect configuration for http/80. +2. Navigate to https://FQDN in a browser and you should be proxied to the AnythingLLM web UI. + +## Step 11: (Optional) Remove temporary Inbound Traffic Rule for Security Group to certbot DNS verification +1. Follow AWS instructions on deleting inbound rule (http port 80 0.0.0.0/0) for EC2 security group here: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-security-groups.html#deleting-security-group-rule diff --git a/cloud-deployments/aws/cloudformation/cloudformation_create_anythingllm.json b/cloud-deployments/aws/cloudformation/cloudformation_create_anythingllm.json new file mode 100644 index 0000000..b10bbc2 --- /dev/null +++ b/cloud-deployments/aws/cloudformation/cloudformation_create_anythingllm.json @@ -0,0 +1,234 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Create a stack that runs AnythingLLM on a single instance", + "Parameters": { + "InstanceType": { + "Description": "EC2 instance type", + "Type": "String", + "Default": "t3.small" + }, + "InstanceVolume": { + "Description": "Storage size of disk on Instance in GB", + "Type": "Number", + "Default": 10, + "MinValue": 4 + } + }, + "Resources": { + "AnythingLLMInstance": { + "Type": "AWS::EC2::Instance", + "Properties": { + "ImageId": { + "Fn::FindInMap": [ + "Region2AMI", + { + "Ref": "AWS::Region" + }, + "AMI" + ] + }, + "InstanceType": { + "Ref": "InstanceType" + }, + "SecurityGroupIds": [ + { + "Ref": "AnythingLLMInstanceSecurityGroup" + } + ], + "BlockDeviceMappings": [ + { + "DeviceName": { + "Fn::FindInMap": [ + "Region2AMI", + { + "Ref": "AWS::Region" + }, + "RootDeviceName" + ] + }, + "Ebs": { + "VolumeSize": { + "Ref": "InstanceVolume" + } + } + } + ], + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "Content-Type: multipart/mixed; boundary=\"//\"\n", + "MIME-Version: 1.0\n", + "\n", + "--//\n", + "Content-Type: text/cloud-config; charset=\"us-ascii\"\n", + "MIME-Version: 1.0\n", + "Content-Transfer-Encoding: 7bit\n", + "Content-Disposition: attachment; filename=\"cloud-config.txt\"\n", + "\n", + "\n", + "#cloud-config\n", + "cloud_final_modules:\n", + "- [scripts-user, once-per-instance]\n", + "\n", + "\n", + "--//\n", + "Content-Type: text/x-shellscript; charset=\"us-ascii\"\n", + "MIME-Version: 1.0\n", + "Content-Transfer-Encoding: 7bit\n", + "Content-Disposition: attachment; filename=\"userdata.txt\"\n", + "\n", + "\n", + "#!/bin/bash\n", + "# check output of userdata script with sudo tail -f /var/log/cloud-init-output.log\n", + "sudo yum install docker iptables -y\n", + "sudo iptables -A OUTPUT -m owner ! --uid-owner root -d 169.254.169.254 -j DROP\n", + "sudo systemctl enable docker\n", + "sudo systemctl start docker\n", + "mkdir -p /home/ec2-user/anythingllm\n", + "touch /home/ec2-user/anythingllm/.env\n", + "sudo chown ec2-user:ec2-user -R /home/ec2-user/anythingllm\n", + "docker pull mintplexlabs/anythingllm\n", + "docker run -d -p 3001:3001 --cap-add SYS_ADMIN -v /home/ec2-user/anythingllm:/app/server/storage -v /home/ec2-user/anythingllm/.env:/app/server/.env -e STORAGE_DIR=\"/app/server/storage\" mintplexlabs/anythingllm\n", + "echo \"Container ID: $(sudo docker ps --latest --quiet)\"\n", + "export ONLINE=$(curl -Is http://localhost:3001/api/ping | head -n 1|cut -d$' ' -f2)\n", + "echo \"Health check: $ONLINE\"\n", + "echo \"Setup complete! AnythingLLM instance is now online!\"\n", + "\n", + "--//--\n" + ] + ] + } + } + } + }, + "AnythingLLMInstanceSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "AnythingLLM Instance Security Group", + "SecurityGroupIngress": [ + { + "IpProtocol": "tcp", + "FromPort": "22", + "ToPort": "22", + "CidrIp": "0.0.0.0/0" + }, + { + "IpProtocol": "tcp", + "FromPort": "3001", + "ToPort": "3001", + "CidrIp": "0.0.0.0/0" + }, + { + "IpProtocol": "tcp", + "FromPort": "3001", + "ToPort": "3001", + "CidrIpv6": "::/0" + } + ] + } + } + }, + "Outputs": { + "ServerIp": { + "Description": "IP address of the AnythingLLM instance", + "Value": { + "Fn::GetAtt": [ + "AnythingLLMInstance", + "PublicIp" + ] + } + }, + "ServerURL": { + "Description": "URL of the AnythingLLM server", + "Value": { + "Fn::Join": [ + "", + [ + "http://", + { + "Fn::GetAtt": [ + "AnythingLLMInstance", + "PublicIp" + ] + }, + ":3001" + ] + ] + } + } + }, + "Mappings": { + "Region2AMI": { + "ap-south-1": { + "AMI": "ami-0e6329e222e662a52", + "RootDeviceName": "/dev/xvda" + }, + "eu-north-1": { + "AMI": "ami-08c308b1bb265e927", + "RootDeviceName": "/dev/xvda" + }, + "eu-west-3": { + "AMI": "ami-069d1ea6bc64443f0", + "RootDeviceName": "/dev/xvda" + }, + "eu-west-2": { + "AMI": "ami-06a566ca43e14780d", + "RootDeviceName": "/dev/xvda" + }, + "eu-west-1": { + "AMI": "ami-0a8dc52684ee2fee2", + "RootDeviceName": "/dev/xvda" + }, + "ap-northeast-3": { + "AMI": "ami-0c8a89b455fae8513", + "RootDeviceName": "/dev/xvda" + }, + "ap-northeast-2": { + "AMI": "ami-0ff56409a6e8ea2a0", + "RootDeviceName": "/dev/xvda" + }, + "ap-northeast-1": { + "AMI": "ami-0ab0bbbd329f565e6", + "RootDeviceName": "/dev/xvda" + }, + "ca-central-1": { + "AMI": "ami-033c256a10931f206", + "RootDeviceName": "/dev/xvda" + }, + "sa-east-1": { + "AMI": "ami-0dabf4dab6b183eef", + "RootDeviceName": "/dev/xvda" + }, + "ap-southeast-1": { + "AMI": "ami-0dc5785603ad4ff54", + "RootDeviceName": "/dev/xvda" + }, + "ap-southeast-2": { + "AMI": "ami-0c5d61202c3b9c33e", + "RootDeviceName": "/dev/xvda" + }, + "eu-central-1": { + "AMI": "ami-004359656ecac6a95", + "RootDeviceName": "/dev/xvda" + }, + "us-east-1": { + "AMI": "ami-0cff7528ff583bf9a", + "RootDeviceName": "/dev/xvda" + }, + "us-east-2": { + "AMI": "ami-02238ac43d6385ab3", + "RootDeviceName": "/dev/xvda" + }, + "us-west-1": { + "AMI": "ami-01163e76c844a2129", + "RootDeviceName": "/dev/xvda" + }, + "us-west-2": { + "AMI": "ami-0ceecbb0f30a902a6", + "RootDeviceName": "/dev/xvda" + } + } + } +} \ No newline at end of file diff --git a/cloud-deployments/digitalocean/terraform/DEPLOY.md b/cloud-deployments/digitalocean/terraform/DEPLOY.md new file mode 100644 index 0000000..7baf5d4 --- /dev/null +++ b/cloud-deployments/digitalocean/terraform/DEPLOY.md @@ -0,0 +1,44 @@ +# How to deploy a private AnythingLLM instance on DigitalOcean using Terraform + +With a DigitalOcean account, you can easily deploy a private AnythingLLM instance using Terraform. This will create a URL that you can access from any browser over HTTP (HTTPS not supported). This single instance will run on your own keys, and they will not be exposed. However, if you want your instance to be protected, it is highly recommended that you set a password one setup is complete. + +The output of this Terraform configuration will be: +- 1 DigitalOcean Droplet +- An IP address to access your application + +**Requirements** +- An DigitalOcean account with billing information +- Terraform installed on your local machine + - Follow the instructions in the [official Terraform documentation](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli) for your operating system. + +## How to deploy on DigitalOcean +Open your terminal and navigate to the `docker` folder +1. Create a `.env` file by cloning the `.env.example`. +2. Navigate to `digitalocean/terraform` folder. +3. Replace the token value in the provider "digitalocean" block in main.tf with your DigitalOcean API token. +4. Run the following commands to initialize Terraform, review the infrastructure changes, and apply them: + ``` + terraform init + terraform plan + terraform apply + ``` +Confirm the changes by typing yes when prompted. +5. Once the deployment is complete, Terraform will output the public IP address of your droplet. You can access your application using this IP address. + +## How to deploy on DigitalOcean +To delete the resources created by Terraform, run the following command in the terminal: +` +terraform destroy +` + +## Please read this notice before submitting issues about your deployment + +**Note:** +Your instance will not be available instantly. Depending on the instance size you launched with it can take anywhere from 5-10 minutes to fully boot up. + +If you want to check the instances progress, navigate to [your deployed instances](https://cloud.digitalocean.com/droplets) and connect to your instance via SSH in browser. + +Once connected run `sudo tail -f /var/log/cloud-init-output.log` and wait for the file to conclude deployment of the docker image. + + +Additionally, your use of this deployment process means you are responsible for any costs of these Digital Ocean resources fully. diff --git a/cloud-deployments/digitalocean/terraform/main.tf b/cloud-deployments/digitalocean/terraform/main.tf new file mode 100644 index 0000000..7a76f57 --- /dev/null +++ b/cloud-deployments/digitalocean/terraform/main.tf @@ -0,0 +1,52 @@ +terraform { + required_version = ">= 1.0.0" + + required_providers { + digitalocean = { + source = "digitalocean/digitalocean" + version = "~> 2.0" + } + } +} + +provider "digitalocean" { + # Add your DigitalOcean API token here + token = "DigitalOcean API token" +} + + +resource "digitalocean_droplet" "anything_llm_instance" { + image = "ubuntu-24-04-x64" + name = "anything-llm-instance" + region = "nyc3" + size = "s-2vcpu-2gb" + + user_data = templatefile("user_data.tp1", { + env_content = local.formatted_env_content + }) +} + +locals { + env_content = file("../../../docker/.env") + formatted_env_content = join("\n", [ + for line in split("\n", local.env_content) : + line + if !( + ( + substr(line, 0, 1) == "#" + ) || + ( + substr(line, 0, 3) == "UID" + ) || + ( + substr(line, 0, 3) == "GID" + ) || + ( + substr(line, 0, 11) == "CLOUD_BUILD" + ) || + ( + line == "" + ) + ) + ]) +} \ No newline at end of file diff --git a/cloud-deployments/digitalocean/terraform/outputs.tf b/cloud-deployments/digitalocean/terraform/outputs.tf new file mode 100644 index 0000000..2b6d559 --- /dev/null +++ b/cloud-deployments/digitalocean/terraform/outputs.tf @@ -0,0 +1,4 @@ +output "ip_address" { + value = digitalocean_droplet.anything_llm_instance.ipv4_address + description = "The public IP address of your droplet application." +} \ No newline at end of file diff --git a/cloud-deployments/digitalocean/terraform/user_data.tp1 b/cloud-deployments/digitalocean/terraform/user_data.tp1 new file mode 100644 index 0000000..1853f69 --- /dev/null +++ b/cloud-deployments/digitalocean/terraform/user_data.tp1 @@ -0,0 +1,22 @@ +#!/bin/bash +# check output of userdata script with sudo tail -f /var/log/cloud-init-output.log + +sudo apt-get update +sudo apt-get install -y docker.io +sudo usermod -a -G docker ubuntu + +sudo systemctl enable docker +sudo systemctl start docker + +mkdir -p /home/anythingllm +cat </home/anythingllm/.env +${env_content} +EOF + +sudo docker pull mintplexlabs/anythingllm +sudo docker run -d -p 3001:3001 --cap-add SYS_ADMIN -v /home/anythingllm:/app/server/storage -v /home/anythingllm/.env:/app/server/.env -e STORAGE_DIR="/app/server/storage" mintplexlabs/anythingllm +echo "Container ID: $(sudo docker ps --latest --quiet)" + +export ONLINE=$(curl -Is http://localhost:3001/api/ping | head -n 1|cut -d$' ' -f2) +echo "Health check: $ONLINE" +echo "Setup complete! AnythingLLM instance is now online!" diff --git a/cloud-deployments/gcp/deployment/DEPLOY.md b/cloud-deployments/gcp/deployment/DEPLOY.md new file mode 100644 index 0000000..7e1ed73 --- /dev/null +++ b/cloud-deployments/gcp/deployment/DEPLOY.md @@ -0,0 +1,54 @@ +# How to deploy a private AnythingLLM instance on GCP + +With a GCP account you can easily deploy a private AnythingLLM instance on GCP. This will create a url that you can access from any browser over HTTP (HTTPS not supported). This single instance will run on your own keys and they will not be exposed - however if you want your instance to be protected it is highly recommend that you set a password one setup is complete. + +The output of this cloudformation stack will be: +- 1 GCP VM +- 1 Security Group with 0.0.0.0/0 access on Ports 22 & 3001 +- 1 GCP VM Volume `gb2` of 10Gib minimum + +**Requirements** +- An GCP account with billing information. + +## How to deploy on GCP +Open your terminal +1. Log in to your GCP account using the following command: + ``` + gcloud auth login + ``` + +2. After successful login, Run the following command to create a deployment using the Deployment Manager CLI: + + ``` + + gcloud deployment-manager deployments create anything-llm-deployment --config gcp/deployment/gcp_deploy_anything_llm.yaml + + ``` + +Once you execute these steps, the CLI will initiate the deployment process on GCP based on your configuration file. You can monitor the deployment status and view the outputs using the Google Cloud Console or the Deployment Manager CLI commands. + +``` +gcloud compute instances get-serial-port-output anything-llm-instance +``` + +ssh into the instance + +``` +gcloud compute ssh anything-llm-instance +``` + +Delete the deployment +``` +gcloud deployment-manager deployments delete anything-llm-deployment +``` + +## Please read this notice before submitting issues about your deployment + +**Note:** +Your instance will not be available instantly. Depending on the instance size you launched with it can take anywhere from 5-10 minutes to fully boot up. + +If you want to check the instances progress, navigate to [your deployed instances](https://console.cloud.google.com/compute/instances) and connect to your instance via SSH in browser. + +Once connected run `sudo tail -f /var/log/cloud-init-output.log` and wait for the file to conclude deployment of the docker image. + +Additionally, your use of this deployment process means you are responsible for any costs of these GCP resources fully. diff --git a/cloud-deployments/gcp/deployment/gcp_deploy_anything_llm.yaml b/cloud-deployments/gcp/deployment/gcp_deploy_anything_llm.yaml new file mode 100644 index 0000000..3f44d5a --- /dev/null +++ b/cloud-deployments/gcp/deployment/gcp_deploy_anything_llm.yaml @@ -0,0 +1,45 @@ +resources: + - name: anything-llm-instance + type: compute.v1.instance + properties: + zone: us-central1-a + machineType: zones/us-central1-a/machineTypes/n1-standard-1 + disks: + - deviceName: boot + type: PERSISTENT + boot: true + autoDelete: true + initializeParams: + sourceImage: projects/ubuntu-os-cloud/global/images/family/ubuntu-2004-lts + diskSizeGb: 10 + networkInterfaces: + - network: global/networks/default + accessConfigs: + - name: External NAT + type: ONE_TO_ONE_NAT + metadata: + items: + - key: startup-script + value: | + #!/bin/bash + # check output of userdata script with sudo tail -f /var/log/cloud-init-output.log + + sudo apt-get update + sudo apt-get install -y docker.io + sudo usermod -a -G docker ubuntu + sudo systemctl enable docker + sudo systemctl start docker + + mkdir -p /home/anythingllm + touch /home/anythingllm/.env + sudo chown -R ubuntu:ubuntu /home/anythingllm + + sudo docker pull mintplexlabs/anythingllm + sudo docker run -d -p 3001:3001 --cap-add SYS_ADMIN -v /home/anythingllm:/app/server/storage -v /home/anythingllm/.env:/app/server/.env -e STORAGE_DIR="/app/server/storage" mintplexlabs/anythingllm + echo "Container ID: $(sudo docker ps --latest --quiet)" + + export ONLINE=$(curl -Is http://localhost:3001/api/ping | head -n 1|cut -d$' ' -f2) + echo "Health check: $ONLINE" + + echo "Setup complete! AnythingLLM instance is now online!" + diff --git a/cloud-deployments/huggingface-spaces/Dockerfile b/cloud-deployments/huggingface-spaces/Dockerfile new file mode 100644 index 0000000..def87bf --- /dev/null +++ b/cloud-deployments/huggingface-spaces/Dockerfile @@ -0,0 +1,31 @@ +# With this dockerfile in a Huggingface space you will get an entire AnythingLLM instance running +# in your space with all features you would normally get from the docker based version of AnythingLLM. +# +# How to use +# - Login to https://huggingface.co/spaces +# - Click on "Create new Space" +# - Name the space and select "Docker" as the SDK w/ a blank template +# - The default 2vCPU/16GB machine is OK. The more the merrier. +# - Decide if you want your AnythingLLM Space public or private. +# **You might want to stay private until you at least set a password or enable multi-user mode** +# - Click "Create Space" +# - Click on "Settings" on top of page (https://huggingface.co/spaces///settings) +# - Scroll to "Persistent Storage" and select the lowest tier of now - you can upgrade if you run out. +# - Confirm and continue storage upgrade +# - Go to "Files" Tab (https://huggingface.co/spaces///tree/main) +# - Click "Add Files" +# - Upload this file or create a file named `Dockerfile` and copy-paste this content into it. "Commit to main" and save. +# - Your container will build and boot. You now have AnythingLLM on HuggingFace. Your data is stored in the persistent storage attached. +# Have Fun 🤗 +# Have issues? Check the logs on HuggingFace for clues. +FROM mintplexlabs/anythingllm:render + +USER root +RUN mkdir -p /data/storage +RUN ln -s /data/storage /storage +USER anythingllm + +ENV STORAGE_DIR="/data/storage" +ENV SERVER_PORT=7860 + +ENTRYPOINT ["/bin/bash", "/usr/local/bin/render-entrypoint.sh"] \ No newline at end of file diff --git a/cloud-deployments/k8/manifest.yaml b/cloud-deployments/k8/manifest.yaml new file mode 100644 index 0000000..9aeef6a --- /dev/null +++ b/cloud-deployments/k8/manifest.yaml @@ -0,0 +1,214 @@ +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: anything-llm-volume + annotations: + pv.beta.kubernetes.io/uid: "1000" + pv.beta.kubernetes.io/gid: "1000" +spec: + storageClassName: gp2 + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + awsElasticBlockStore: + # This is the volume UUID from AWS EC2 EBS Volumes list. + volumeID: "{{ anythingllm_awsElasticBlockStore_volumeID }}" + fsType: ext4 + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: topology.kubernetes.io/zone + operator: In + values: + - us-east-1c +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: anything-llm-volume-claim + namespace: "{{ namespace }}" +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: anything-llm + namespace: "{{ namespace }}" + labels: + anything-llm: "true" +spec: + selector: + matchLabels: + k8s-app: anything-llm + replicas: 1 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 0% + maxUnavailable: 100% + template: + metadata: + labels: + anything-llm: "true" + k8s-app: anything-llm + app.kubernetes.io/name: anything-llm + app.kubernetes.io/part-of: anything-llm + annotations: + prometheus.io/scrape: "true" + prometheus.io/path: /metrics + prometheus.io/port: "9090" + spec: + serviceAccountName: "default" + terminationGracePeriodSeconds: 10 + securityContext: + fsGroup: 1000 + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 1000 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: topology.kubernetes.io/zone + operator: In + values: + - us-east-1c + containers: + - name: anything-llm + resources: + limits: + memory: "1Gi" + cpu: "500m" + requests: + memory: "512Mi" + cpu: "250m" + imagePullPolicy: IfNotPresent + image: "mintplexlabs/anythingllm:render" + securityContext: + allowPrivilegeEscalation: true + capabilities: + add: + - SYS_ADMIN + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 1000 + command: + # Specify a command to override the Dockerfile's ENTRYPOINT. + - /bin/bash + - -c + - | + set -x -e + sleep 3 + echo "AWS_REGION: $AWS_REGION" + echo "SERVER_PORT: $SERVER_PORT" + echo "NODE_ENV: $NODE_ENV" + echo "STORAGE_DIR: $STORAGE_DIR" + { + cd /app/server/ && + npx prisma generate --schema=./prisma/schema.prisma && + npx prisma migrate deploy --schema=./prisma/schema.prisma && + node /app/server/index.js + echo "Server process exited with status $?" + } & + { + node /app/collector/index.js + echo "Collector process exited with status $?" + } & + wait -n + exit $? + readinessProbe: + httpGet: + path: /v1/api/health + port: 8888 + initialDelaySeconds: 15 + periodSeconds: 5 + successThreshold: 2 + livenessProbe: + httpGet: + path: /v1/api/health + port: 8888 + initialDelaySeconds: 15 + periodSeconds: 5 + failureThreshold: 3 + env: + - name: AWS_REGION + value: "{{ aws_region }}" + - name: AWS_ACCESS_KEY_ID + value: "{{ aws_access_id }}" + - name: AWS_SECRET_ACCESS_KEY + value: "{{ aws_access_secret }}" + - name: SERVER_PORT + value: "3001" + - name: JWT_SECRET + value: "my-random-string-for-seeding" # Please generate random string at least 12 chars long. + - name: STORAGE_DIR + value: "/storage" + - name: NODE_ENV + value: "production" + - name: UID + value: "1000" + - name: GID + value: "1000" + volumeMounts: + - name: anything-llm-server-storage-volume-mount + mountPath: /storage + volumes: + - name: anything-llm-server-storage-volume-mount + persistentVolumeClaim: + claimName: anything-llm-volume-claim +--- +# This serves the UI and the backend. +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: anything-llm-ingress + namespace: "{{ namespace }}" + annotations: + external-dns.alpha.kubernetes.io/hostname: "{{ namespace }}-chat.{{ base_domain }}" + kubernetes.io/ingress.class: "internal-ingress" + nginx.ingress.kubernetes.io/rewrite-target: / + ingress.kubernetes.io/ssl-redirect: "false" +spec: + rules: + - host: "{{ namespace }}-chat.{{ base_domain }}" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: anything-llm-svc + port: + number: 3001 + tls: # < placing a host in the TLS config will indicate a cert should be created + - hosts: + - "{{ namespace }}-chat.{{ base_domain }}" + secretName: letsencrypt-prod +--- +apiVersion: v1 +kind: Service +metadata: + labels: + kubernetes.io/name: anything-llm + name: anything-llm-svc + namespace: "{{ namespace }}" +spec: + ports: + # "port" is external port, and "targetPort" is internal. + - port: 3301 + targetPort: 3001 + name: traffic + - port: 9090 + targetPort: 9090 + name: metrics + selector: + k8s-app: anything-llm \ No newline at end of file diff --git a/collector/.env.example b/collector/.env.example new file mode 100644 index 0000000..ea4aba3 --- /dev/null +++ b/collector/.env.example @@ -0,0 +1 @@ +# Placeholder .env file for collector runtime diff --git a/collector/.gitignore b/collector/.gitignore new file mode 100644 index 0000000..57436ce --- /dev/null +++ b/collector/.gitignore @@ -0,0 +1,6 @@ +hotdir/* +!hotdir/__HOTDIR__.md +yarn-error.log +!yarn.lock +outputs +scripts diff --git a/collector/.nvmrc b/collector/.nvmrc new file mode 100644 index 0000000..59f4a2f --- /dev/null +++ b/collector/.nvmrc @@ -0,0 +1 @@ +v18.13.0 \ No newline at end of file diff --git a/collector/extensions/index.js b/collector/extensions/index.js new file mode 100644 index 0000000..81a3a3d --- /dev/null +++ b/collector/extensions/index.js @@ -0,0 +1,159 @@ +const { setDataSigner } = require("../middleware/setDataSigner"); +const { verifyPayloadIntegrity } = require("../middleware/verifyIntegrity"); +const { resolveRepoLoader, resolveRepoLoaderFunction } = require("../utils/extensions/RepoLoader"); +const { reqBody } = require("../utils/http"); +const { validURL } = require("../utils/url"); +const RESYNC_METHODS = require("./resync"); + +function extensions(app) { + if (!app) return; + + app.post( + "/ext/resync-source-document", + [verifyPayloadIntegrity, setDataSigner], + async function (request, response) { + try { + const { type, options } = reqBody(request); + if (!RESYNC_METHODS.hasOwnProperty(type)) throw new Error(`Type "${type}" is not a valid type to sync.`); + return await RESYNC_METHODS[type](options, response); + } catch (e) { + console.error(e); + response.status(200).json({ + success: false, + content: null, + reason: e.message || "A processing error occurred.", + }); + } + return; + } + ) + + app.post( + "/ext/:repo_platform-repo", + [verifyPayloadIntegrity, setDataSigner], + async function (request, response) { + try { + const loadRepo = resolveRepoLoaderFunction(request.params.repo_platform); + const { success, reason, data } = await loadRepo( + reqBody(request), + response, + ); + response.status(200).json({ + success, + reason, + data, + }); + } catch (e) { + console.error(e); + response.status(200).json({ + success: false, + reason: e.message || "A processing error occurred.", + data: {}, + }); + } + return; + } + ); + + // gets all branches for a specific repo + app.post( + "/ext/:repo_platform-repo/branches", + [verifyPayloadIntegrity], + async function (request, response) { + try { + const RepoLoader = resolveRepoLoader(request.params.repo_platform); + const allBranches = await new RepoLoader( + reqBody(request) + ).getRepoBranches(); + response.status(200).json({ + success: true, + reason: null, + data: { + branches: allBranches, + }, + }); + } catch (e) { + console.error(e); + response.status(400).json({ + success: false, + reason: e.message, + data: { + branches: [], + }, + }); + } + return; + } + ); + + app.post( + "/ext/youtube-transcript", + [verifyPayloadIntegrity], + async function (request, response) { + try { + const { loadYouTubeTranscript } = require("../utils/extensions/YoutubeTranscript"); + const { success, reason, data } = await loadYouTubeTranscript( + reqBody(request) + ); + response.status(200).json({ success, reason, data }); + } catch (e) { + console.error(e); + response.status(400).json({ + success: false, + reason: e.message, + data: { + title: null, + author: null, + }, + }); + } + return; + } + ); + + app.post( + "/ext/website-depth", + [verifyPayloadIntegrity], + async function (request, response) { + try { + const websiteDepth = require("../utils/extensions/WebsiteDepth"); + const { url, depth = 1, maxLinks = 20 } = reqBody(request); + if (!validURL(url)) throw new Error("Not a valid URL."); + const scrapedData = await websiteDepth(url, depth, maxLinks); + response.status(200).json({ success: true, data: scrapedData }); + } catch (e) { + console.error(e); + response.status(400).json({ success: false, reason: e.message }); + } + return; + } + ); + + app.post( + "/ext/confluence", + [verifyPayloadIntegrity, setDataSigner], + async function (request, response) { + try { + const { loadConfluence } = require("../utils/extensions/Confluence"); + const { success, reason, data } = await loadConfluence( + reqBody(request), + response + ); + response.status(200).json({ success, reason, data }); + } catch (e) { + console.error(e); + response.status(400).json({ + success: false, + reason: e.message, + data: { + title: null, + author: null, + }, + }); + } + return; + } + ); +} + +module.exports = extensions; diff --git a/collector/extensions/resync/index.js b/collector/extensions/resync/index.js new file mode 100644 index 0000000..cb25958 --- /dev/null +++ b/collector/extensions/resync/index.js @@ -0,0 +1,114 @@ +const { getLinkText } = require("../../processLink"); + +/** + * Fetches the content of a raw link. Returns the content as a text string of the link in question. + * @param {object} data - metadata from document (eg: link) + * @param {import("../../middleware/setDataSigner").ResponseWithSigner} response + */ +async function resyncLink({ link }, response) { + if (!link) throw new Error('Invalid link provided'); + try { + const { success, content = null } = await getLinkText(link); + if (!success) throw new Error(`Failed to sync link content. ${reason}`); + response.status(200).json({ success, content }); + } catch (e) { + console.error(e); + response.status(200).json({ + success: false, + content: null, + }); + } +} + +/** + * Fetches the content of a YouTube link. Returns the content as a text string of the video in question. + * We offer this as there may be some videos where a transcription could be manually edited after initial scraping + * but in general - transcriptions often never change. + * @param {object} data - metadata from document (eg: link) + * @param {import("../../middleware/setDataSigner").ResponseWithSigner} response + */ +async function resyncYouTube({ link }, response) { + if (!link) throw new Error('Invalid link provided'); + try { + const { fetchVideoTranscriptContent } = require("../../utils/extensions/YoutubeTranscript"); + const { success, reason, content } = await fetchVideoTranscriptContent({ url: link }); + if (!success) throw new Error(`Failed to sync YouTube video transcript. ${reason}`); + response.status(200).json({ success, content }); + } catch (e) { + console.error(e); + response.status(200).json({ + success: false, + content: null, + }); + } +} + +/** + * Fetches the content of a specific confluence page via its chunkSource. + * Returns the content as a text string of the page in question and only that page. + * @param {object} data - metadata from document (eg: chunkSource) + * @param {import("../../middleware/setDataSigner").ResponseWithSigner} response + */ +async function resyncConfluence({ chunkSource }, response) { + if (!chunkSource) throw new Error('Invalid source property provided'); + try { + // Confluence data is `payload` encrypted. So we need to expand its + // encrypted payload back into query params so we can reFetch the page with same access token/params. + const source = response.locals.encryptionWorker.expandPayload(chunkSource); + const { fetchConfluencePage } = require("../../utils/extensions/Confluence"); + const { success, reason, content } = await fetchConfluencePage({ + pageUrl: `https:${source.pathname}`, // need to add back the real protocol + baseUrl: source.searchParams.get('baseUrl'), + spaceKey: source.searchParams.get('spaceKey'), + accessToken: source.searchParams.get('token'), + username: source.searchParams.get('username'), + }); + + if (!success) throw new Error(`Failed to sync Confluence page content. ${reason}`); + response.status(200).json({ success, content }); + } catch (e) { + console.error(e); + response.status(200).json({ + success: false, + content: null, + }); + } +} + +/** + * Fetches the content of a specific confluence page via its chunkSource. + * Returns the content as a text string of the page in question and only that page. + * @param {object} data - metadata from document (eg: chunkSource) + * @param {import("../../middleware/setDataSigner").ResponseWithSigner} response + */ +async function resyncGithub({ chunkSource }, response) { + if (!chunkSource) throw new Error('Invalid source property provided'); + try { + // Github file data is `payload` encrypted (might contain PAT). So we need to expand its + // encrypted payload back into query params so we can reFetch the page with same access token/params. + const source = response.locals.encryptionWorker.expandPayload(chunkSource); + const { fetchGithubFile } = require("../../utils/extensions/RepoLoader/GithubRepo"); + const { success, reason, content } = await fetchGithubFile({ + repoUrl: `https:${source.pathname}`, // need to add back the real protocol + branch: source.searchParams.get('branch'), + accessToken: source.searchParams.get('pat'), + sourceFilePath: source.searchParams.get('path'), + }); + + if (!success) throw new Error(`Failed to sync GitHub file content. ${reason}`); + response.status(200).json({ success, content }); + } catch (e) { + console.error(e); + response.status(200).json({ + success: false, + content: null, + }); + } +} + +module.exports = { + link: resyncLink, + youtube: resyncYouTube, + confluence: resyncConfluence, + github: resyncGithub, +} \ No newline at end of file diff --git a/collector/hotdir/__HOTDIR__.md b/collector/hotdir/__HOTDIR__.md new file mode 100644 index 0000000..8f20efc --- /dev/null +++ b/collector/hotdir/__HOTDIR__.md @@ -0,0 +1,3 @@ +### What is the "Hot directory" + +This is a pre-set file location that documents will be written to when uploaded by AnythingLLM. There is really no need to touch it. \ No newline at end of file diff --git a/collector/index.js b/collector/index.js new file mode 100644 index 0000000..b307b58 --- /dev/null +++ b/collector/index.js @@ -0,0 +1,151 @@ +process.env.NODE_ENV === "development" + ? require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` }) + : require("dotenv").config(); + +require("./utils/logger")(); +const express = require("express"); +const bodyParser = require("body-parser"); +const cors = require("cors"); +const path = require("path"); +const { ACCEPTED_MIMES } = require("./utils/constants"); +const { reqBody } = require("./utils/http"); +const { processSingleFile } = require("./processSingleFile"); +const { processLink, getLinkText } = require("./processLink"); +const { wipeCollectorStorage } = require("./utils/files"); +const extensions = require("./extensions"); +const { processRawText } = require("./processRawText"); +const { verifyPayloadIntegrity } = require("./middleware/verifyIntegrity"); +const app = express(); +const FILE_LIMIT = "3GB"; + +app.use(cors({ origin: true })); +app.use( + bodyParser.text({ limit: FILE_LIMIT }), + bodyParser.json({ limit: FILE_LIMIT }), + bodyParser.urlencoded({ + limit: FILE_LIMIT, + extended: true, + }) +); + +app.post( + "/process", + [verifyPayloadIntegrity], + async function (request, response) { + const { filename, options = {} } = reqBody(request); + try { + const targetFilename = path + .normalize(filename) + .replace(/^(\.\.(\/|\\|$))+/, ""); + const { + success, + reason, + documents = [], + } = await processSingleFile(targetFilename, options); + response + .status(200) + .json({ filename: targetFilename, success, reason, documents }); + } catch (e) { + console.error(e); + response.status(200).json({ + filename: filename, + success: false, + reason: "A processing error occurred.", + documents: [], + }); + } + return; + } +); + +app.post( + "/process-link", + [verifyPayloadIntegrity], + async function (request, response) { + const { link } = reqBody(request); + try { + const { success, reason, documents = [] } = await processLink(link); + response.status(200).json({ url: link, success, reason, documents }); + } catch (e) { + console.error(e); + response.status(200).json({ + url: link, + success: false, + reason: "A processing error occurred.", + documents: [], + }); + } + return; + } +); + +app.post( + "/util/get-link", + [verifyPayloadIntegrity], + async function (request, response) { + const { link, captureAs = "text" } = reqBody(request); + try { + const { success, content = null } = await getLinkText(link, captureAs); + response.status(200).json({ url: link, success, content }); + } catch (e) { + console.error(e); + response.status(200).json({ + url: link, + success: false, + content: null, + }); + } + return; + } +); + +app.post( + "/process-raw-text", + [verifyPayloadIntegrity], + async function (request, response) { + const { textContent, metadata } = reqBody(request); + try { + const { + success, + reason, + documents = [], + } = await processRawText(textContent, metadata); + response + .status(200) + .json({ filename: metadata.title, success, reason, documents }); + } catch (e) { + console.error(e); + response.status(200).json({ + filename: metadata?.title || "Unknown-doc.txt", + success: false, + reason: "A processing error occurred.", + documents: [], + }); + } + return; + } +); + +extensions(app); + +app.get("/accepts", function (_, response) { + response.status(200).json(ACCEPTED_MIMES); +}); + +app.all("*", function (_, response) { + response.sendStatus(200); +}); + +app + .listen(8888, async () => { + await wipeCollectorStorage(); + console.log(`Document processor app listening on port 8888`); + }) + .on("error", function (_) { + process.once("SIGUSR2", function () { + process.kill(process.pid, "SIGUSR2"); + }); + process.on("SIGINT", function () { + process.kill(process.pid, "SIGINT"); + }); + }); diff --git a/collector/middleware/setDataSigner.js b/collector/middleware/setDataSigner.js new file mode 100644 index 0000000..3ea3b2f --- /dev/null +++ b/collector/middleware/setDataSigner.js @@ -0,0 +1,41 @@ +const { EncryptionWorker } = require("../utils/EncryptionWorker"); +const { CommunicationKey } = require("../utils/comKey"); + +/** + * Express Response Object interface with defined encryptionWorker attached to locals property. + * @typedef {import("express").Response & import("express").Response['locals'] & {encryptionWorker: EncryptionWorker} } ResponseWithSigner +*/ + +// You can use this middleware to assign the EncryptionWorker to the response locals +// property so that if can be used to encrypt/decrypt arbitrary data via response object. +// eg: Encrypting API keys in chunk sources. + +// The way this functions is that the rolling RSA Communication Key is used server-side to private-key encrypt the raw +// key of the persistent EncryptionManager credentials. Since EncryptionManager credentials do _not_ roll, we should not send them +// even between server<>collector in plaintext because if the user configured the server/collector to be public they could technically +// be exposing the key in transit via the X-Payload-Signer header. Even if this risk is minimal we should not do this. + +// This middleware uses the CommunicationKey public key to first decrypt the base64 representation of the EncryptionManager credentials +// and then loads that in to the EncryptionWorker as a buffer so we can use the same credentials across the system. Should we ever break the +// collector out into its own service this would still work without SSL/TLS. + +/** + * + * @param {import("express").Request} request + * @param {import("express").Response} response + * @param {import("express").NextFunction} next + */ +function setDataSigner(request, response, next) { + const comKey = new CommunicationKey(); + const encryptedPayloadSigner = request.header("X-Payload-Signer"); + if (!encryptedPayloadSigner) console.log('Failed to find signed-payload to set encryption worker! Encryption calls will fail.'); + + const decryptedPayloadSignerKey = comKey.decrypt(encryptedPayloadSigner); + const encryptionWorker = new EncryptionWorker(decryptedPayloadSignerKey); + response.locals.encryptionWorker = encryptionWorker; + next(); +} + +module.exports = { + setDataSigner +} \ No newline at end of file diff --git a/collector/middleware/verifyIntegrity.js b/collector/middleware/verifyIntegrity.js new file mode 100644 index 0000000..0dcb3f7 --- /dev/null +++ b/collector/middleware/verifyIntegrity.js @@ -0,0 +1,21 @@ +const { CommunicationKey } = require("../utils/comKey"); + +function verifyPayloadIntegrity(request, response, next) { + const comKey = new CommunicationKey(); + if (process.env.NODE_ENV === "development") { + comKey.log('verifyPayloadIntegrity is skipped in development.') + next(); + return; + } + + const signature = request.header("X-Integrity"); + if (!signature) return response.status(400).json({ msg: 'Failed integrity signature check.' }) + + const validSignedPayload = comKey.verify(signature, request.body); + if (!validSignedPayload) return response.status(400).json({ msg: 'Failed integrity signature check.' }) + next(); +} + +module.exports = { + verifyPayloadIntegrity +} \ No newline at end of file diff --git a/collector/nodemon.json b/collector/nodemon.json new file mode 100644 index 0000000..23e9d55 --- /dev/null +++ b/collector/nodemon.json @@ -0,0 +1,3 @@ +{ + "events": {} +} \ No newline at end of file diff --git a/collector/package.json b/collector/package.json new file mode 100644 index 0000000..dd7c843 --- /dev/null +++ b/collector/package.json @@ -0,0 +1,54 @@ +{ + "name": "anything-llm-document-collector", + "version": "0.2.0", + "description": "Document collector server endpoints", + "main": "index.js", + "author": "Timothy Carambat (Mintplex Labs)", + "license": "MIT", + "private": false, + "engines": { + "node": ">=18.12.1" + }, + "scripts": { + "dev": "cross-env NODE_ENV=development nodemon --ignore hotdir --ignore storage --trace-warnings index.js", + "start": "cross-env NODE_ENV=production node index.js", + "lint": "yarn prettier --ignore-path ../.prettierignore --write ./processSingleFile ./processLink ./utils index.js" + }, + "dependencies": { + "@langchain/community": "^0.2.23", + "@xenova/transformers": "^2.11.0", + "bcrypt": "^5.1.0", + "body-parser": "^1.20.2", + "cors": "^2.8.5", + "dotenv": "^16.0.3", + "epub2": "^3.0.2", + "express": "^4.18.2", + "fluent-ffmpeg": "^2.1.2", + "html-to-text": "^9.0.5", + "ignore": "^5.3.0", + "js-tiktoken": "^1.0.8", + "langchain": "0.1.36", + "mammoth": "^1.6.0", + "mbox-parser": "^1.0.1", + "mime": "^3.0.0", + "moment": "^2.29.4", + "node-html-parser": "^6.1.13", + "node-xlsx": "^0.24.0", + "officeparser": "^4.0.5", + "openai": "4.38.5", + "pdf-parse": "^1.1.1", + "puppeteer": "~21.5.2", + "sharp": "^0.33.5", + "slugify": "^1.6.6", + "tesseract.js": "^6.0.0", + "url-pattern": "^1.0.3", + "uuid": "^9.0.0", + "wavefile": "^11.0.0", + "winston": "^3.13.0", + "youtubei.js": "^9.1.0" + }, + "devDependencies": { + "nodemon": "^2.0.22", + "prettier": "^2.4.1" + } +} diff --git a/collector/processLink/convert/generic.js b/collector/processLink/convert/generic.js new file mode 100644 index 0000000..a22166d --- /dev/null +++ b/collector/processLink/convert/generic.js @@ -0,0 +1,127 @@ +const { v4 } = require("uuid"); +const { + PuppeteerWebBaseLoader, +} = require("langchain/document_loaders/web/puppeteer"); +const { writeToServerDocuments } = require("../../utils/files"); +const { tokenizeString } = require("../../utils/tokenizer"); +const { default: slugify } = require("slugify"); + +/** + * Scrape a generic URL and return the content in the specified format + * @param {string} link - The URL to scrape + * @param {('html' | 'text')} captureAs - The format to capture the page content as + * @param {boolean} processAsDocument - Whether to process the content as a document or return the content directly + * @returns {Promise} - The content of the page + */ +async function scrapeGenericUrl( + link, + captureAs = "text", + processAsDocument = true +) { + console.log(`-- Working URL ${link} => (${captureAs}) --`); + const content = await getPageContent(link, captureAs); + + if (!content.length) { + console.error(`Resulting URL content was empty at ${link}.`); + return { + success: false, + reason: `No URL content found at ${link}.`, + documents: [], + }; + } + + if (!processAsDocument) { + return { + success: true, + content, + }; + } + + const url = new URL(link); + const decodedPathname = decodeURIComponent(url.pathname); + const filename = `${url.hostname}${decodedPathname.replace(/\//g, "_")}`; + + const data = { + id: v4(), + url: "file://" + slugify(filename) + ".html", + title: slugify(filename) + ".html", + docAuthor: "no author found", + description: "No description found.", + docSource: "URL link uploaded by the user.", + chunkSource: `link://${link}`, + published: new Date().toLocaleString(), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + const document = writeToServerDocuments( + data, + `url-${slugify(filename)}-${data.id}` + ); + console.log(`[SUCCESS]: URL ${link} converted & ready for embedding.\n`); + return { success: true, reason: null, documents: [document] }; +} + +/** + * Get the content of a page + * @param {string} link - The URL to get the content of + * @param {('html' | 'text')} captureAs - The format to capture the page content as + * @returns {Promise} - The content of the page + */ +async function getPageContent(link, captureAs = "text") { + try { + let pageContents = []; + const loader = new PuppeteerWebBaseLoader(link, { + launchOptions: { + headless: "new", + ignoreHTTPSErrors: true, + }, + gotoOptions: { + waitUntil: "networkidle2", + }, + async evaluate(page, browser) { + const result = await page.evaluate((captureAs) => { + if (captureAs === "text") return document.body.innerText; + if (captureAs === "html") return document.documentElement.innerHTML; + return document.body.innerText; + }, captureAs); + await browser.close(); + return result; + }, + }); + + const docs = await loader.load(); + + for (const doc of docs) { + pageContents.push(doc.pageContent); + } + + return pageContents.join(" "); + } catch (error) { + console.error( + "getPageContent failed to be fetched by puppeteer - falling back to fetch!", + error + ); + } + + try { + const pageText = await fetch(link, { + method: "GET", + headers: { + "Content-Type": "text/plain", + "User-Agent": + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36,gzip(gfe)", + }, + }).then((res) => res.text()); + return pageText; + } catch (error) { + console.error("getPageContent failed to be fetched by any method.", error); + } + + return null; +} + +module.exports = { + scrapeGenericUrl, +}; diff --git a/collector/processLink/index.js b/collector/processLink/index.js new file mode 100644 index 0000000..ac0c591 --- /dev/null +++ b/collector/processLink/index.js @@ -0,0 +1,23 @@ +const { validURL } = require("../utils/url"); +const { scrapeGenericUrl } = require("./convert/generic"); + +async function processLink(link) { + if (!validURL(link)) return { success: false, reason: "Not a valid URL." }; + return await scrapeGenericUrl(link); +} + +/** + * Get the text content of a link + * @param {string} link - The link to get the text content of + * @param {('html' | 'text' | 'json')} captureAs - The format to capture the page content as + * @returns {Promise<{success: boolean, content: string}>} - Response from collector + */ +async function getLinkText(link, captureAs = "text") { + if (!validURL(link)) return { success: false, reason: "Not a valid URL." }; + return await scrapeGenericUrl(link, captureAs, false); +} + +module.exports = { + processLink, + getLinkText, +}; diff --git a/collector/processRawText/index.js b/collector/processRawText/index.js new file mode 100644 index 0000000..a29eb63 --- /dev/null +++ b/collector/processRawText/index.js @@ -0,0 +1,69 @@ +const { v4 } = require("uuid"); +const { writeToServerDocuments } = require("../utils/files"); +const { tokenizeString } = require("../utils/tokenizer"); +const { default: slugify } = require("slugify"); + +// Will remove the last .extension from the input +// and stringify the input + move to lowercase. +function stripAndSlug(input) { + if (!input.includes('.')) return slugify(input, { lower: true }); + return slugify(input.split('.').slice(0, -1).join('-'), { lower: true }) +} + +const METADATA_KEYS = { + possible: { + url: ({ url, title }) => { + let validUrl; + try { + const u = new URL(url); + validUrl = ["https:", "http:"].includes(u.protocol); + } catch { } + + if (validUrl) return `web://${url.toLowerCase()}.website`; + return `file://${stripAndSlug(title)}.txt`; + }, + title: ({ title }) => `${stripAndSlug(title)}.txt`, + docAuthor: ({ docAuthor }) => { return typeof docAuthor === 'string' ? docAuthor : 'no author specified' }, + description: ({ description }) => { return typeof description === 'string' ? description : 'no description found' }, + docSource: ({ docSource }) => { return typeof docSource === 'string' ? docSource : 'no source set' }, + chunkSource: ({ chunkSource, title }) => { return typeof chunkSource === 'string' ? chunkSource : `${stripAndSlug(title)}.txt` }, + published: ({ published }) => { + if (isNaN(Number(published))) return new Date().toLocaleString(); + return new Date(Number(published)).toLocaleString() + }, + } +} + +async function processRawText(textContent, metadata) { + console.log(`-- Working Raw Text doc ${metadata.title} --`); + if (!textContent || textContent.length === 0) { + return { + success: false, + reason: "textContent was empty - nothing to process.", + documents: [], + }; + } + + const data = { + id: v4(), + url: METADATA_KEYS.possible.url(metadata), + title: METADATA_KEYS.possible.title(metadata), + docAuthor: METADATA_KEYS.possible.docAuthor(metadata), + description: METADATA_KEYS.possible.description(metadata), + docSource: METADATA_KEYS.possible.docSource(metadata), + chunkSource: METADATA_KEYS.possible.chunkSource(metadata), + published: METADATA_KEYS.possible.published(metadata), + wordCount: textContent.split(" ").length, + pageContent: textContent, + token_count_estimate: tokenizeString(textContent), + }; + + const document = writeToServerDocuments( + data, + `raw-${stripAndSlug(metadata.title)}-${data.id}` + ); + console.log(`[SUCCESS]: Raw text and metadata saved & ready for embedding.\n`); + return { success: true, reason: null, documents: [document] }; +} + +module.exports = { processRawText } \ No newline at end of file diff --git a/collector/processSingleFile/convert/asAudio.js b/collector/processSingleFile/convert/asAudio.js new file mode 100644 index 0000000..5f033af --- /dev/null +++ b/collector/processSingleFile/convert/asAudio.js @@ -0,0 +1,73 @@ +const { v4 } = require("uuid"); +const { + createdDate, + trashFile, + writeToServerDocuments, +} = require("../../utils/files"); +const { tokenizeString } = require("../../utils/tokenizer"); +const { default: slugify } = require("slugify"); +const { LocalWhisper } = require("../../utils/WhisperProviders/localWhisper"); +const { OpenAiWhisper } = require("../../utils/WhisperProviders/OpenAiWhisper"); + +const WHISPER_PROVIDERS = { + openai: OpenAiWhisper, + local: LocalWhisper, +}; + +async function asAudio({ fullFilePath = "", filename = "", options = {} }) { + const WhisperProvider = WHISPER_PROVIDERS.hasOwnProperty( + options?.whisperProvider + ) + ? WHISPER_PROVIDERS[options?.whisperProvider] + : WHISPER_PROVIDERS.local; + + console.log(`-- Working ${filename} --`); + const whisper = new WhisperProvider({ options }); + const { content, error } = await whisper.processFile(fullFilePath, filename); + + if (!!error) { + console.error(`Error encountered for parsing of ${filename}.`); + trashFile(fullFilePath); + return { + success: false, + reason: error, + documents: [], + }; + } + + if (!content?.length) { + console.error(`Resulting text content was empty for ${filename}.`); + trashFile(fullFilePath); + return { + success: false, + reason: `No text content found in ${filename}.`, + documents: [], + }; + } + + const data = { + id: v4(), + url: "file://" + fullFilePath, + title: filename, + docAuthor: "no author found", + description: "No description found.", + docSource: "pdf file uploaded by the user.", + chunkSource: "", + published: createdDate(fullFilePath), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + const document = writeToServerDocuments( + data, + `${slugify(filename)}-${data.id}` + ); + trashFile(fullFilePath); + console.log( + `[SUCCESS]: ${filename} transcribed, converted & ready for embedding.\n` + ); + return { success: true, reason: null, documents: [document] }; +} + +module.exports = asAudio; diff --git a/collector/processSingleFile/convert/asDocx.js b/collector/processSingleFile/convert/asDocx.js new file mode 100644 index 0000000..d33a46b --- /dev/null +++ b/collector/processSingleFile/convert/asDocx.js @@ -0,0 +1,57 @@ +const { v4 } = require("uuid"); +const { DocxLoader } = require("langchain/document_loaders/fs/docx"); +const { + createdDate, + trashFile, + writeToServerDocuments, +} = require("../../utils/files"); +const { tokenizeString } = require("../../utils/tokenizer"); +const { default: slugify } = require("slugify"); + +async function asDocX({ fullFilePath = "", filename = "" }) { + const loader = new DocxLoader(fullFilePath); + + console.log(`-- Working ${filename} --`); + let pageContent = []; + const docs = await loader.load(); + for (const doc of docs) { + console.log(`-- Parsing content from docx page --`); + if (!doc.pageContent.length) continue; + pageContent.push(doc.pageContent); + } + + if (!pageContent.length) { + console.error(`Resulting text content was empty for ${filename}.`); + trashFile(fullFilePath); + return { + success: false, + reason: `No text content found in ${filename}.`, + documents: [], + }; + } + + const content = pageContent.join(""); + const data = { + id: v4(), + url: "file://" + fullFilePath, + title: filename, + docAuthor: "no author found", + description: "No description found.", + docSource: "pdf file uploaded by the user.", + chunkSource: "", + published: createdDate(fullFilePath), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + const document = writeToServerDocuments( + data, + `${slugify(filename)}-${data.id}` + ); + trashFile(fullFilePath); + console.log(`[SUCCESS]: ${filename} converted & ready for embedding.\n`); + return { success: true, reason: null, documents: [document] }; +} + +module.exports = asDocX; diff --git a/collector/processSingleFile/convert/asEPub.js b/collector/processSingleFile/convert/asEPub.js new file mode 100644 index 0000000..51bb20c --- /dev/null +++ b/collector/processSingleFile/convert/asEPub.js @@ -0,0 +1,55 @@ +const { v4 } = require("uuid"); +const { EPubLoader } = require("langchain/document_loaders/fs/epub"); +const { tokenizeString } = require("../../utils/tokenizer"); +const { + createdDate, + trashFile, + writeToServerDocuments, +} = require("../../utils/files"); +const { default: slugify } = require("slugify"); + +async function asEPub({ fullFilePath = "", filename = "" }) { + let content = ""; + try { + const loader = new EPubLoader(fullFilePath, { splitChapters: false }); + const docs = await loader.load(); + docs.forEach((doc) => (content += doc.pageContent)); + } catch (err) { + console.error("Could not read epub file!", err); + } + + if (!content?.length) { + console.error(`Resulting text content was empty for ${filename}.`); + trashFile(fullFilePath); + return { + success: false, + reason: `No text content found in ${filename}.`, + documents: [], + }; + } + + console.log(`-- Working ${filename} --`); + const data = { + id: v4(), + url: "file://" + fullFilePath, + title: filename, + docAuthor: "Unknown", // TODO: Find a better author + description: "Unknown", // TODO: Find a better description + docSource: "a epub file uploaded by the user.", + chunkSource: "", + published: createdDate(fullFilePath), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + const document = writeToServerDocuments( + data, + `${slugify(filename)}-${data.id}` + ); + trashFile(fullFilePath); + console.log(`[SUCCESS]: ${filename} converted & ready for embedding.\n`); + return { success: true, reason: null, documents: [document] }; +} + +module.exports = asEPub; diff --git a/collector/processSingleFile/convert/asImage.js b/collector/processSingleFile/convert/asImage.js new file mode 100644 index 0000000..57b6b7e --- /dev/null +++ b/collector/processSingleFile/convert/asImage.js @@ -0,0 +1,48 @@ +const { v4 } = require("uuid"); +const { tokenizeString } = require("../../utils/tokenizer"); +const { + createdDate, + trashFile, + writeToServerDocuments, +} = require("../../utils/files"); +const OCRLoader = require("../../utils/OCRLoader"); +const { default: slugify } = require("slugify"); + +async function asImage({ fullFilePath = "", filename = "" }) { + let content = await new OCRLoader().ocrImage(fullFilePath); + + if (!content?.length) { + console.error(`Resulting text content was empty for ${filename}.`); + trashFile(fullFilePath); + return { + success: false, + reason: `No text content found in ${filename}.`, + documents: [], + }; + } + + console.log(`-- Working ${filename} --`); + const data = { + id: v4(), + url: "file://" + fullFilePath, + title: filename, + docAuthor: "Unknown", // TODO: Find a better author + description: "Unknown", // TODO: Find a better description + docSource: "a text file uploaded by the user.", + chunkSource: "", + published: createdDate(fullFilePath), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + const document = writeToServerDocuments( + data, + `${slugify(filename)}-${data.id}` + ); + trashFile(fullFilePath); + console.log(`[SUCCESS]: ${filename} converted & ready for embedding.\n`); + return { success: true, reason: null, documents: [document] }; +} + +module.exports = asImage; diff --git a/collector/processSingleFile/convert/asMbox.js b/collector/processSingleFile/convert/asMbox.js new file mode 100644 index 0000000..48de60f --- /dev/null +++ b/collector/processSingleFile/convert/asMbox.js @@ -0,0 +1,74 @@ +const { v4 } = require("uuid"); +const fs = require("fs"); +const { mboxParser } = require("mbox-parser"); +const { + createdDate, + trashFile, + writeToServerDocuments, +} = require("../../utils/files"); +const { tokenizeString } = require("../../utils/tokenizer"); +const { default: slugify } = require("slugify"); + +async function asMbox({ fullFilePath = "", filename = "" }) { + console.log(`-- Working ${filename} --`); + + const mails = await mboxParser(fs.createReadStream(fullFilePath)) + .then((mails) => mails) + .catch((error) => { + console.log(`Could not parse mail items`, error); + return []; + }); + + if (!mails.length) { + console.error(`Resulting mail items was empty for ${filename}.`); + trashFile(fullFilePath); + return { + success: false, + reason: `No mail items found in ${filename}.`, + documents: [], + }; + } + + let item = 1; + const documents = []; + for (const mail of mails) { + if (!mail.hasOwnProperty("text")) continue; + + const content = mail.text; + if (!content) continue; + console.log( + `-- Working on message "${mail.subject || "Unknown subject"}" --` + ); + + const data = { + id: v4(), + url: "file://" + fullFilePath, + title: mail?.subject + ? slugify(mail?.subject?.replace(".", "")) + ".mbox" + : `msg_${item}-${filename}`, + docAuthor: mail?.from?.text, + description: "No description found.", + docSource: "Mbox message file uploaded by the user.", + chunkSource: "", + published: createdDate(fullFilePath), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + item++; + const document = writeToServerDocuments( + data, + `${slugify(filename)}-${data.id}-msg-${item}` + ); + documents.push(document); + } + + trashFile(fullFilePath); + console.log( + `[SUCCESS]: ${filename} messages converted & ready for embedding.\n` + ); + return { success: true, reason: null, documents }; +} + +module.exports = asMbox; diff --git a/collector/processSingleFile/convert/asOfficeMime.js b/collector/processSingleFile/convert/asOfficeMime.js new file mode 100644 index 0000000..09e320d --- /dev/null +++ b/collector/processSingleFile/convert/asOfficeMime.js @@ -0,0 +1,53 @@ +const { v4 } = require("uuid"); +const officeParser = require("officeparser"); +const { + createdDate, + trashFile, + writeToServerDocuments, +} = require("../../utils/files"); +const { tokenizeString } = require("../../utils/tokenizer"); +const { default: slugify } = require("slugify"); + +async function asOfficeMime({ fullFilePath = "", filename = "" }) { + console.log(`-- Working ${filename} --`); + let content = ""; + try { + content = await officeParser.parseOfficeAsync(fullFilePath); + } catch (error) { + console.error(`Could not parse office or office-like file`, error); + } + + if (!content.length) { + console.error(`Resulting text content was empty for ${filename}.`); + trashFile(fullFilePath); + return { + success: false, + reason: `No text content found in ${filename}.`, + documents: [], + }; + } + + const data = { + id: v4(), + url: "file://" + fullFilePath, + title: filename, + docAuthor: "no author found", + description: "No description found.", + docSource: "Office file uploaded by the user.", + chunkSource: "", + published: createdDate(fullFilePath), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + const document = writeToServerDocuments( + data, + `${slugify(filename)}-${data.id}` + ); + trashFile(fullFilePath); + console.log(`[SUCCESS]: ${filename} converted & ready for embedding.\n`); + return { success: true, reason: null, documents: [document] }; +} + +module.exports = asOfficeMime; diff --git a/collector/processSingleFile/convert/asPDF/PDFLoader/index.js b/collector/processSingleFile/convert/asPDF/PDFLoader/index.js new file mode 100644 index 0000000..26bcf2b --- /dev/null +++ b/collector/processSingleFile/convert/asPDF/PDFLoader/index.js @@ -0,0 +1,97 @@ +const fs = require("fs").promises; + +class PDFLoader { + constructor(filePath, { splitPages = true } = {}) { + this.filePath = filePath; + this.splitPages = splitPages; + } + + async load() { + const buffer = await fs.readFile(this.filePath); + const { getDocument, version } = await this.getPdfJS(); + + const pdf = await getDocument({ + data: new Uint8Array(buffer), + useWorkerFetch: false, + isEvalSupported: false, + useSystemFonts: true, + }).promise; + + const meta = await pdf.getMetadata().catch(() => null); + const documents = []; + + for (let i = 1; i <= pdf.numPages; i += 1) { + const page = await pdf.getPage(i); + const content = await page.getTextContent(); + + if (content.items.length === 0) { + continue; + } + + let lastY; + const textItems = []; + for (const item of content.items) { + if ("str" in item) { + if (lastY === item.transform[5] || !lastY) { + textItems.push(item.str); + } else { + textItems.push(`\n${item.str}`); + } + lastY = item.transform[5]; + } + } + + const text = textItems.join(""); + documents.push({ + pageContent: text.trim(), + metadata: { + source: this.filePath, + pdf: { + version, + info: meta?.info, + metadata: meta?.metadata, + totalPages: pdf.numPages, + }, + loc: { pageNumber: i }, + }, + }); + } + + if (this.splitPages) { + return documents; + } + + if (documents.length === 0) { + return []; + } + + return [ + { + pageContent: documents.map((doc) => doc.pageContent).join("\n\n"), + metadata: { + source: this.filePath, + pdf: { + version, + info: meta?.info, + metadata: meta?.metadata, + totalPages: pdf.numPages, + }, + }, + }, + ]; + } + + async getPdfJS() { + try { + const pdfjs = await import("pdf-parse/lib/pdf.js/v1.10.100/build/pdf.js"); + return { getDocument: pdfjs.getDocument, version: pdfjs.version }; + } catch (e) { + console.error(e); + throw new Error( + "Failed to load pdf-parse. Please install it with eg. `npm install pdf-parse`." + ); + } + } +} + +module.exports = PDFLoader; diff --git a/collector/processSingleFile/convert/asPDF/index.js b/collector/processSingleFile/convert/asPDF/index.js new file mode 100644 index 0000000..350f217 --- /dev/null +++ b/collector/processSingleFile/convert/asPDF/index.js @@ -0,0 +1,72 @@ +const { v4 } = require("uuid"); +const { + createdDate, + trashFile, + writeToServerDocuments, +} = require("../../../utils/files"); +const { tokenizeString } = require("../../../utils/tokenizer"); +const { default: slugify } = require("slugify"); +const PDFLoader = require("./PDFLoader"); +const OCRLoader = require("../../../utils/OCRLoader"); + +async function asPdf({ fullFilePath = "", filename = "" }) { + const pdfLoader = new PDFLoader(fullFilePath, { + splitPages: true, + }); + + console.log(`-- Working ${filename} --`); + const pageContent = []; + let docs = await pdfLoader.load(); + + if (docs.length === 0) { + console.log( + `[asPDF] No text content found for ${filename}. Will attempt OCR parse.` + ); + docs = await new OCRLoader().ocrPDF(fullFilePath); + } + + for (const doc of docs) { + console.log( + `-- Parsing content from pg ${ + doc.metadata?.loc?.pageNumber || "unknown" + } --` + ); + if (!doc.pageContent || !doc.pageContent.length) continue; + pageContent.push(doc.pageContent); + } + + if (!pageContent.length) { + console.error(`[asPDF] Resulting text content was empty for ${filename}.`); + trashFile(fullFilePath); + return { + success: false, + reason: `No text content found in ${filename}.`, + documents: [], + }; + } + + const content = pageContent.join(""); + const data = { + id: v4(), + url: "file://" + fullFilePath, + title: filename, + docAuthor: docs[0]?.metadata?.pdf?.info?.Creator || "no author found", + description: docs[0]?.metadata?.pdf?.info?.Title || "No description found.", + docSource: "pdf file uploaded by the user.", + chunkSource: "", + published: createdDate(fullFilePath), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + const document = writeToServerDocuments( + data, + `${slugify(filename)}-${data.id}` + ); + trashFile(fullFilePath); + console.log(`[SUCCESS]: ${filename} converted & ready for embedding.\n`); + return { success: true, reason: null, documents: [document] }; +} + +module.exports = asPdf; diff --git a/collector/processSingleFile/convert/asTxt.js b/collector/processSingleFile/convert/asTxt.js new file mode 100644 index 0000000..bc95969 --- /dev/null +++ b/collector/processSingleFile/convert/asTxt.js @@ -0,0 +1,53 @@ +const { v4 } = require("uuid"); +const fs = require("fs"); +const { tokenizeString } = require("../../utils/tokenizer"); +const { + createdDate, + trashFile, + writeToServerDocuments, +} = require("../../utils/files"); +const { default: slugify } = require("slugify"); + +async function asTxt({ fullFilePath = "", filename = "" }) { + let content = ""; + try { + content = fs.readFileSync(fullFilePath, "utf8"); + } catch (err) { + console.error("Could not read file!", err); + } + + if (!content?.length) { + console.error(`Resulting text content was empty for ${filename}.`); + trashFile(fullFilePath); + return { + success: false, + reason: `No text content found in ${filename}.`, + documents: [], + }; + } + + console.log(`-- Working ${filename} --`); + const data = { + id: v4(), + url: "file://" + fullFilePath, + title: filename, + docAuthor: "Unknown", // TODO: Find a better author + description: "Unknown", // TODO: Find a better description + docSource: "a text file uploaded by the user.", + chunkSource: "", + published: createdDate(fullFilePath), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + const document = writeToServerDocuments( + data, + `${slugify(filename)}-${data.id}` + ); + trashFile(fullFilePath); + console.log(`[SUCCESS]: ${filename} converted & ready for embedding.\n`); + return { success: true, reason: null, documents: [document] }; +} + +module.exports = asTxt; diff --git a/collector/processSingleFile/convert/asXlsx.js b/collector/processSingleFile/convert/asXlsx.js new file mode 100644 index 0000000..ca9b8eb --- /dev/null +++ b/collector/processSingleFile/convert/asXlsx.js @@ -0,0 +1,113 @@ +const { v4 } = require("uuid"); +const xlsx = require("node-xlsx").default; +const path = require("path"); +const fs = require("fs"); +const { + createdDate, + trashFile, + writeToServerDocuments, +} = require("../../utils/files"); +const { tokenizeString } = require("../../utils/tokenizer"); +const { default: slugify } = require("slugify"); + +function convertToCSV(data) { + return data + .map((row) => + row + .map((cell) => { + if (cell === null || cell === undefined) return ""; + if (typeof cell === "string" && cell.includes(",")) + return `"${cell}"`; + return cell; + }) + .join(",") + ) + .join("\n"); +} + +async function asXlsx({ fullFilePath = "", filename = "" }) { + const documents = []; + const folderName = slugify(`${path.basename(filename)}-${v4().slice(0, 4)}`, { + lower: true, + trim: true, + }); + + const outFolderPath = + process.env.NODE_ENV === "development" + ? path.resolve( + __dirname, + `../../../server/storage/documents/${folderName}` + ) + : path.resolve(process.env.STORAGE_DIR, `documents/${folderName}`); + + try { + const workSheetsFromFile = xlsx.parse(fullFilePath); + if (!fs.existsSync(outFolderPath)) + fs.mkdirSync(outFolderPath, { recursive: true }); + + for (const sheet of workSheetsFromFile) { + try { + const { name, data } = sheet; + const content = convertToCSV(data); + + if (!content?.length) { + console.warn(`Sheet "${name}" is empty. Skipping.`); + continue; + } + + console.log(`-- Processing sheet: ${name} --`); + const sheetData = { + id: v4(), + url: `file://${path.join(outFolderPath, `${slugify(name)}.csv`)}`, + title: `${filename} - Sheet:${name}`, + docAuthor: "Unknown", + description: `Spreadsheet data from sheet: ${name}`, + docSource: "an xlsx file uploaded by the user.", + chunkSource: "", + published: createdDate(fullFilePath), + wordCount: content.split(/\s+/).length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + const document = writeToServerDocuments( + sheetData, + `sheet-${slugify(name)}`, + outFolderPath + ); + documents.push(document); + console.log( + `[SUCCESS]: Sheet "${name}" converted & ready for embedding.` + ); + } catch (err) { + console.error(`Error processing sheet "${name}":`, err); + continue; + } + } + } catch (err) { + console.error("Could not process xlsx file!", err); + return { + success: false, + reason: `Error processing ${filename}: ${err.message}`, + documents: [], + }; + } finally { + trashFile(fullFilePath); + } + + if (documents.length === 0) { + console.error(`No valid sheets found in ${filename}.`); + return { + success: false, + reason: `No valid sheets found in ${filename}.`, + documents: [], + }; + } + + console.log( + `[SUCCESS]: ${filename} fully processed. Created ${documents.length} document(s).\n` + ); + return { success: true, reason: null, documents }; +} + +module.exports = asXlsx; diff --git a/collector/processSingleFile/index.js b/collector/processSingleFile/index.js new file mode 100644 index 0000000..a00b139 --- /dev/null +++ b/collector/processSingleFile/index.js @@ -0,0 +1,78 @@ +const path = require("path"); +const fs = require("fs"); +const { + WATCH_DIRECTORY, + SUPPORTED_FILETYPE_CONVERTERS, +} = require("../utils/constants"); +const { + trashFile, + isTextType, + normalizePath, + isWithin, +} = require("../utils/files"); +const RESERVED_FILES = ["__HOTDIR__.md"]; + +async function processSingleFile(targetFilename, options = {}) { + const fullFilePath = path.resolve( + WATCH_DIRECTORY, + normalizePath(targetFilename) + ); + if (!isWithin(path.resolve(WATCH_DIRECTORY), fullFilePath)) + return { + success: false, + reason: "Filename is a not a valid path to process.", + documents: [], + }; + + if (RESERVED_FILES.includes(targetFilename)) + return { + success: false, + reason: "Filename is a reserved filename and cannot be processed.", + documents: [], + }; + if (!fs.existsSync(fullFilePath)) + return { + success: false, + reason: "File does not exist in upload directory.", + documents: [], + }; + + const fileExtension = path.extname(fullFilePath).toLowerCase(); + if (fullFilePath.includes(".") && !fileExtension) { + return { + success: false, + reason: `No file extension found. This file cannot be processed.`, + documents: [], + }; + } + + let processFileAs = fileExtension; + if (!SUPPORTED_FILETYPE_CONVERTERS.hasOwnProperty(fileExtension)) { + if (isTextType(fullFilePath)) { + console.log( + `\x1b[33m[Collector]\x1b[0m The provided filetype of ${fileExtension} does not have a preset and will be processed as .txt.` + ); + processFileAs = ".txt"; + } else { + trashFile(fullFilePath); + return { + success: false, + reason: `File extension ${fileExtension} not supported for parsing and cannot be assumed as text file type.`, + documents: [], + }; + } + } + + const FileTypeProcessor = require(SUPPORTED_FILETYPE_CONVERTERS[ + processFileAs + ]); + return await FileTypeProcessor({ + fullFilePath, + filename: targetFilename, + options, + }); +} + +module.exports = { + processSingleFile, +}; diff --git a/collector/storage/.gitignore b/collector/storage/.gitignore new file mode 100644 index 0000000..845482b --- /dev/null +++ b/collector/storage/.gitignore @@ -0,0 +1,2 @@ +tmp/* +!tmp/.placeholder \ No newline at end of file diff --git a/collector/storage/tmp/.placeholder b/collector/storage/tmp/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/collector/utils/EncryptionWorker/index.js b/collector/utils/EncryptionWorker/index.js new file mode 100644 index 0000000..ddc2773 --- /dev/null +++ b/collector/utils/EncryptionWorker/index.js @@ -0,0 +1,77 @@ +const crypto = require("crypto"); + +// Differs from EncryptionManager in that is does not set or define the keys that will be used +// to encrypt or read data and it must be told the key (as base64 string) explicitly that will be used and is provided to +// the class on creation. This key should be the same `key` that is used by the EncryptionManager class. +class EncryptionWorker { + constructor(presetKeyBase64 = "") { + this.key = Buffer.from(presetKeyBase64, "base64"); + this.algorithm = "aes-256-cbc"; + this.separator = ":"; + } + + log(text, ...args) { + console.log(`\x1b[36m[EncryptionManager]\x1b[0m ${text}`, ...args); + } + + /** + * Give a chunk source, parse its payload query param and expand that object back into the URL + * as additional query params + * @param {string} chunkSource + * @returns {URL} Javascript URL object with query params decrypted from payload query param. + */ + expandPayload(chunkSource = "") { + try { + const url = new URL(chunkSource); + if (!url.searchParams.has("payload")) return url; + + const decryptedPayload = this.decrypt(url.searchParams.get("payload")); + const encodedParams = JSON.parse(decryptedPayload); + url.searchParams.delete("payload"); // remove payload prop + + // Add all query params needed to replay as query params + Object.entries(encodedParams).forEach(([key, value]) => + url.searchParams.append(key, value) + ); + return url; + } catch (e) { + console.error(e); + } + return new URL(chunkSource); + } + + encrypt(plainTextString = null) { + try { + if (!plainTextString) + throw new Error("Empty string is not valid for this method."); + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv(this.algorithm, this.key, iv); + const encrypted = cipher.update(plainTextString, "utf8", "hex"); + return [ + encrypted + cipher.final("hex"), + Buffer.from(iv).toString("hex"), + ].join(this.separator); + } catch (e) { + this.log(e); + return null; + } + } + + decrypt(encryptedString) { + try { + const [encrypted, iv] = encryptedString.split(this.separator); + if (!iv) throw new Error("IV not found"); + const decipher = crypto.createDecipheriv( + this.algorithm, + this.key, + Buffer.from(iv, "hex") + ); + return decipher.update(encrypted, "hex", "utf8") + decipher.final("utf8"); + } catch (e) { + this.log(e); + return null; + } + } +} + +module.exports = { EncryptionWorker }; diff --git a/collector/utils/OCRLoader/index.js b/collector/utils/OCRLoader/index.js new file mode 100644 index 0000000..45f7650 --- /dev/null +++ b/collector/utils/OCRLoader/index.js @@ -0,0 +1,307 @@ +const fs = require("fs"); +const os = require("os"); +const path = require("path"); + +class OCRLoader { + constructor() { + this.cacheDir = path.resolve( + process.env.STORAGE_DIR + ? path.resolve(process.env.STORAGE_DIR, `models`, `tesseract`) + : path.resolve(__dirname, `../../../server/storage/models/tesseract`) + ); + } + + log(text, ...args) { + console.log(`\x1b[36m[OCRLoader]\x1b[0m ${text}`, ...args); + } + + /** + * Loads a PDF file and returns an array of documents. + * This function is reserved to parsing for SCANNED documents - digital documents are not supported in this function + * @returns {Promise<{pageContent: string, metadata: object}[]>} An array of documents with page content and metadata. + */ + async ocrPDF( + filePath, + { maxExecutionTime = 300_000, batchSize = 10, maxWorkers = null } = {} + ) { + if ( + !filePath || + !fs.existsSync(filePath) || + !fs.statSync(filePath).isFile() + ) { + this.log(`File ${filePath} does not exist. Skipping OCR.`); + return []; + } + + const documentTitle = path.basename(filePath); + this.log(`Starting OCR of ${documentTitle}`); + const pdfjs = await import("pdf-parse/lib/pdf.js/v2.0.550/build/pdf.js"); + let buffer = fs.readFileSync(filePath); + + const pdfDocument = await pdfjs.getDocument({ data: buffer }); + + const documents = []; + const meta = await pdfDocument.getMetadata().catch(() => null); + const metadata = { + source: filePath, + pdf: { + version: "v2.0.550", + info: meta?.info, + metadata: meta?.metadata, + totalPages: pdfDocument.numPages, + }, + }; + + const pdfSharp = new PDFSharp({ + validOps: [ + pdfjs.OPS.paintJpegXObject, + pdfjs.OPS.paintImageXObject, + pdfjs.OPS.paintInlineImageXObject, + ], + }); + await pdfSharp.init(); + + const { createWorker, OEM } = require("tesseract.js"); + const BATCH_SIZE = batchSize; + const MAX_EXECUTION_TIME = maxExecutionTime; + const NUM_WORKERS = maxWorkers ?? Math.min(os.cpus().length, 4); + const totalPages = pdfDocument.numPages; + const workerPool = await Promise.all( + Array(NUM_WORKERS) + .fill(0) + .map(() => + createWorker("eng", OEM.LSTM_ONLY, { + cachePath: this.cacheDir, + }) + ) + ); + + const startTime = Date.now(); + try { + this.log("Bootstrapping OCR completed successfully!", { + MAX_EXECUTION_TIME_MS: MAX_EXECUTION_TIME, + BATCH_SIZE, + MAX_CONCURRENT_WORKERS: NUM_WORKERS, + TOTAL_PAGES: totalPages, + }); + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => { + reject( + new Error( + `OCR job took too long to complete (${ + MAX_EXECUTION_TIME / 1000 + } seconds)` + ) + ); + }, MAX_EXECUTION_TIME); + }); + + const processPages = async () => { + for ( + let startPage = 1; + startPage <= totalPages; + startPage += BATCH_SIZE + ) { + const endPage = Math.min(startPage + BATCH_SIZE - 1, totalPages); + const pageNumbers = Array.from( + { length: endPage - startPage + 1 }, + (_, i) => startPage + i + ); + this.log(`Working on pages ${startPage} - ${endPage}`); + + const pageQueue = [...pageNumbers]; + const results = []; + const workerPromises = workerPool.map(async (worker, workerIndex) => { + while (pageQueue.length > 0) { + const pageNum = pageQueue.shift(); + this.log( + `\x1b[34m[Worker ${ + workerIndex + 1 + }]\x1b[0m assigned pg${pageNum}` + ); + const page = await pdfDocument.getPage(pageNum); + const imageBuffer = await pdfSharp.pageToBuffer({ page }); + if (!imageBuffer) continue; + const { data } = await worker.recognize(imageBuffer, {}, "text"); + this.log( + `✅ \x1b[34m[Worker ${ + workerIndex + 1 + }]\x1b[0m completed pg${pageNum}` + ); + results.push({ + pageContent: data.text, + metadata: { + ...metadata, + loc: { pageNumber: pageNum }, + }, + }); + } + }); + + await Promise.all(workerPromises); + documents.push( + ...results.sort( + (a, b) => a.metadata.loc.pageNumber - b.metadata.loc.pageNumber + ) + ); + } + return documents; + }; + + await Promise.race([timeoutPromise, processPages()]); + } catch (e) { + this.log(`Error: ${e.message}`, e.stack); + } finally { + global.Image = undefined; + await Promise.all(workerPool.map((worker) => worker.terminate())); + } + + this.log(`Completed OCR of ${documentTitle}!`, { + documentsParsed: documents.length, + totalPages: totalPages, + executionTime: `${((Date.now() - startTime) / 1000).toFixed(2)}s`, + }); + return documents; + } + + /** + * Loads an image file and returns the OCRed text. + * @param {string} filePath - The path to the image file. + * @param {Object} options - The options for the OCR. + * @param {number} options.maxExecutionTime - The maximum execution time of the OCR in milliseconds. + * @returns {Promise} The OCRed text. + */ + async ocrImage(filePath, { maxExecutionTime = 300_000 } = {}) { + let content = ""; + let worker = null; + if ( + !filePath || + !fs.existsSync(filePath) || + !fs.statSync(filePath).isFile() + ) { + this.log(`File ${filePath} does not exist. Skipping OCR.`); + return null; + } + + const documentTitle = path.basename(filePath); + try { + this.log(`Starting OCR of ${documentTitle}`); + const startTime = Date.now(); + const { createWorker, OEM } = require("tesseract.js"); + worker = await createWorker("eng", OEM.LSTM_ONLY, { + cachePath: this.cacheDir, + }); + + // Race the timeout with the OCR + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => { + reject( + new Error( + `OCR job took too long to complete (${ + maxExecutionTime / 1000 + } seconds)` + ) + ); + }, maxExecutionTime); + }); + + const processImage = async () => { + const { data } = await worker.recognize(filePath, {}, "text"); + content = data.text; + }; + + await Promise.race([timeoutPromise, processImage()]); + this.log(`Completed OCR of ${documentTitle}!`, { + executionTime: `${((Date.now() - startTime) / 1000).toFixed(2)}s`, + }); + + return content; + } catch (e) { + this.log(`Error: ${e.message}`); + return null; + } finally { + if (!worker) return; + await worker.terminate(); + } + } +} + +/** + * Converts a PDF page to a buffer using Sharp. + * @param {Object} options - The options for the Sharp PDF page object. + * @param {Object} options.page - The PDFJS page proxy object. + * @returns {Promise} The buffer of the page. + */ +class PDFSharp { + constructor({ validOps = [] } = {}) { + this.sharp = null; + this.validOps = validOps; + } + + log(text, ...args) { + console.log(`\x1b[36m[PDFSharp]\x1b[0m ${text}`, ...args); + } + + async init() { + this.sharp = (await import("sharp")).default; + } + + /** + * Converts a PDF page to a buffer. + * @param {Object} options - The options for the Sharp PDF page object. + * @param {Object} options.page - The PDFJS page proxy object. + * @returns {Promise} The buffer of the page. + */ + async pageToBuffer({ page }) { + if (!this.sharp) await this.init(); + try { + this.log(`Converting page ${page.pageNumber} to image...`); + const ops = await page.getOperatorList(); + const pageImages = ops.fnArray.length; + + for (let i = 0; i < pageImages; i++) { + try { + if (!this.validOps.includes(ops.fnArray[i])) continue; + + const name = ops.argsArray[i][0]; + const img = await page.objs.get(name); + const { width, height } = img; + const size = img.data.length; + const channels = size / width / height; + const targetDPI = 70; + const targetWidth = Math.floor(width * (targetDPI / 72)); + const targetHeight = Math.floor(height * (targetDPI / 72)); + + const image = this.sharp(img.data, { + raw: { width, height, channels }, + density: targetDPI, + }) + .resize({ + width: targetWidth, + height: targetHeight, + fit: "fill", + }) + .withMetadata({ + density: targetDPI, + resolution: targetDPI, + }) + .png(); + + // For debugging purposes + // await image.toFile(path.resolve(__dirname, `../../storage/`, `pg${page.pageNumber}.png`)); + return await image.toBuffer(); + } catch (error) { + this.log(`Iteration error: ${error.message}`, error.stack); + continue; + } + } + this.log(`No valid images found on page ${page.pageNumber}`); + return null; + } catch (error) { + this.log(`Error: ${error.message}`, error.stack); + return null; + } + } +} + +module.exports = OCRLoader; diff --git a/collector/utils/WhisperProviders/OpenAiWhisper.js b/collector/utils/WhisperProviders/OpenAiWhisper.js new file mode 100644 index 0000000..e2405b6 --- /dev/null +++ b/collector/utils/WhisperProviders/OpenAiWhisper.js @@ -0,0 +1,49 @@ +const fs = require("fs"); + +class OpenAiWhisper { + constructor({ options }) { + const { OpenAI: OpenAIApi } = require("openai"); + if (!options.openAiKey) throw new Error("No OpenAI API key was set."); + + this.openai = new OpenAIApi({ + apiKey: options.openAiKey, + }); + this.model = "whisper-1"; + this.temperature = 0; + this.#log("Initialized."); + } + + #log(text, ...args) { + console.log(`\x1b[32m[OpenAiWhisper]\x1b[0m ${text}`, ...args); + } + + async processFile(fullFilePath) { + return await this.openai.audio.transcriptions + .create({ + file: fs.createReadStream(fullFilePath), + model: this.model, + temperature: this.temperature, + }) + .then((response) => { + if (!response) { + return { + content: "", + error: "No content was able to be transcribed.", + }; + } + + return { content: response.text, error: null }; + }) + .catch((error) => { + this.#log( + `Could not get any response from openai whisper`, + error.message + ); + return { content: "", error: error.message }; + }); + } +} + +module.exports = { + OpenAiWhisper, +}; diff --git a/collector/utils/WhisperProviders/localWhisper.js b/collector/utils/WhisperProviders/localWhisper.js new file mode 100644 index 0000000..05ebab2 --- /dev/null +++ b/collector/utils/WhisperProviders/localWhisper.js @@ -0,0 +1,219 @@ +const fs = require("fs"); +const path = require("path"); +const { v4 } = require("uuid"); +const defaultWhisper = "Xenova/whisper-small"; // Model Card: https://huggingface.co/Xenova/whisper-small +const fileSize = { + "Xenova/whisper-small": "250mb", + "Xenova/whisper-large": "1.56GB", +}; + +class LocalWhisper { + constructor({ options }) { + this.model = options?.WhisperModelPref ?? defaultWhisper; + this.fileSize = fileSize[this.model]; + this.cacheDir = path.resolve( + process.env.STORAGE_DIR + ? path.resolve(process.env.STORAGE_DIR, `models`) + : path.resolve(__dirname, `../../../server/storage/models`) + ); + + this.modelPath = path.resolve(this.cacheDir, ...this.model.split("/")); + // Make directory when it does not exist in existing installations + if (!fs.existsSync(this.cacheDir)) + fs.mkdirSync(this.cacheDir, { recursive: true }); + + this.#log("Initialized."); + } + + #log(text, ...args) { + console.log(`\x1b[32m[LocalWhisper]\x1b[0m ${text}`, ...args); + } + + #validateAudioFile(wavFile) { + const sampleRate = wavFile.fmt.sampleRate; + const duration = wavFile.data.samples / sampleRate; + + // Most speech recognition systems expect minimum 8kHz + // But we'll set it lower to be safe + if (sampleRate < 4000) { + // 4kHz minimum + throw new Error( + "Audio file sample rate is too low for accurate transcription. Minimum required is 4kHz." + ); + } + + // Typical audio file duration limits + const MAX_DURATION_SECONDS = 4 * 60 * 60; // 4 hours + if (duration > MAX_DURATION_SECONDS) { + throw new Error("Audio file duration exceeds maximum limit of 4 hours."); + } + + // Check final sample count after upsampling to prevent memory issues + const targetSampleRate = 16000; + const upsampledSamples = duration * targetSampleRate; + const MAX_SAMPLES = 230_400_000; // ~4 hours at 16kHz + + if (upsampledSamples > MAX_SAMPLES) { + throw new Error("Audio file exceeds maximum allowed length."); + } + + return true; + } + + async #convertToWavAudioData(sourcePath) { + try { + let buffer; + const wavefile = require("wavefile"); + const ffmpeg = require("fluent-ffmpeg"); + const outFolder = path.resolve(__dirname, `../../storage/tmp`); + if (!fs.existsSync(outFolder)) + fs.mkdirSync(outFolder, { recursive: true }); + + const fileExtension = path.extname(sourcePath).toLowerCase(); + if (fileExtension !== ".wav") { + this.#log( + `File conversion required! ${fileExtension} file detected - converting to .wav` + ); + const outputFile = path.resolve(outFolder, `${v4()}.wav`); + const convert = new Promise((resolve) => { + ffmpeg(sourcePath) + .toFormat("wav") + .on("error", (error) => { + this.#log(`Conversion Error! ${error.message}`); + resolve(false); + }) + .on("progress", (progress) => + this.#log( + `Conversion Processing! ${progress.targetSize}KB converted` + ) + ) + .on("end", () => { + this.#log(`Conversion Complete! File converted to .wav!`); + resolve(true); + }) + .save(outputFile); + }); + const success = await convert; + if (!success) + throw new Error( + "[Conversion Failed]: Could not convert file to .wav format!" + ); + + const chunks = []; + const stream = fs.createReadStream(outputFile); + for await (let chunk of stream) chunks.push(chunk); + buffer = Buffer.concat(chunks); + fs.rmSync(outputFile); + } else { + const chunks = []; + const stream = fs.createReadStream(sourcePath); + for await (let chunk of stream) chunks.push(chunk); + buffer = Buffer.concat(chunks); + } + + const wavFile = new wavefile.WaveFile(buffer); + try { + this.#validateAudioFile(wavFile); + } catch (error) { + this.#log(`Audio validation failed: ${error.message}`); + throw new Error(`Invalid audio file: ${error.message}`); + } + + wavFile.toBitDepth("32f"); + wavFile.toSampleRate(16000); + + let audioData = wavFile.getSamples(); + if (Array.isArray(audioData)) { + if (audioData.length > 1) { + const SCALING_FACTOR = Math.sqrt(2); + + // Merge channels into first channel to save memory + for (let i = 0; i < audioData[0].length; ++i) { + audioData[0][i] = + (SCALING_FACTOR * (audioData[0][i] + audioData[1][i])) / 2; + } + } + audioData = audioData[0]; + } + + return audioData; + } catch (error) { + console.error(`convertToWavAudioData`, error); + return null; + } + } + + async client() { + if (!fs.existsSync(this.modelPath)) { + this.#log( + `The native whisper model has never been run and will be downloaded right now. Subsequent runs will be faster. (~${this.fileSize})` + ); + } + + try { + // Convert ESM to CommonJS via import so we can load this library. + const pipeline = (...args) => + import("@xenova/transformers").then(({ pipeline }) => + pipeline(...args) + ); + return await pipeline("automatic-speech-recognition", this.model, { + cache_dir: this.cacheDir, + ...(!fs.existsSync(this.modelPath) + ? { + // Show download progress if we need to download any files + progress_callback: (data) => { + if (!data.hasOwnProperty("progress")) return; + console.log( + `\x1b[34m[Embedding - Downloading Model Files]\x1b[0m ${ + data.file + } ${~~data?.progress}%` + ); + }, + } + : {}), + }); + } catch (error) { + this.#log("Failed to load the native whisper model:", error); + throw error; + } + } + + async processFile(fullFilePath, filename) { + try { + const transcriberPromise = new Promise((resolve) => + this.client().then((client) => resolve(client)) + ); + const audioDataPromise = new Promise((resolve) => + this.#convertToWavAudioData(fullFilePath).then((audioData) => + resolve(audioData) + ) + ); + const [audioData, transcriber] = await Promise.all([ + audioDataPromise, + transcriberPromise, + ]); + + if (!audioData) { + this.#log(`Failed to parse content from ${filename}.`); + return { + content: null, + error: `Failed to parse content from ${filename}.`, + }; + } + + this.#log(`Transcribing audio data to text...`); + const { text } = await transcriber(audioData, { + chunk_length_s: 30, + stride_length_s: 5, + }); + + return { content: text, error: null }; + } catch (error) { + return { content: null, error: error.message }; + } + } +} + +module.exports = { + LocalWhisper, +}; diff --git a/collector/utils/comKey/index.js b/collector/utils/comKey/index.js new file mode 100644 index 0000000..a2e2f52 --- /dev/null +++ b/collector/utils/comKey/index.js @@ -0,0 +1,54 @@ +const crypto = require("crypto"); +const fs = require("fs"); +const path = require("path"); +const keyPath = + process.env.NODE_ENV === "development" + ? path.resolve(__dirname, `../../../server/storage/comkey`) + : path.resolve( + process.env.STORAGE_DIR ?? + path.resolve(__dirname, `../../../server/storage`), + `comkey` + ); + +class CommunicationKey { + #pubKeyName = "ipc-pub.pem"; + #storageLoc = keyPath; + + constructor() {} + + log(text, ...args) { + console.log(`\x1b[36m[CommunicationKeyVerify]\x1b[0m ${text}`, ...args); + } + + #readPublicKey() { + return fs.readFileSync(path.resolve(this.#storageLoc, this.#pubKeyName)); + } + + // Given a signed payload from private key from /app/server/ this signature should + // decode to match the textData provided. This class does verification only in collector. + // Note: The textData is typically the JSON stringified body sent to the document processor API. + verify(signature = "", textData = "") { + try { + let data = textData; + if (typeof textData !== "string") data = JSON.stringify(data); + return crypto.verify( + "RSA-SHA256", + Buffer.from(data), + this.#readPublicKey(), + Buffer.from(signature, "hex") + ); + } catch {} + return false; + } + + // Use the rolling public-key to decrypt arbitrary data that was encrypted via the private key on the server side CommunicationKey class + // that we know was done with the same key-pair and the given input is in base64 format already. + // Returns plaintext string of the data that was encrypted. + decrypt(base64String = "") { + return crypto + .publicDecrypt(this.#readPublicKey(), Buffer.from(base64String, "base64")) + .toString(); + } +} + +module.exports = { CommunicationKey }; diff --git a/collector/utils/constants.js b/collector/utils/constants.js new file mode 100644 index 0000000..236fc2f --- /dev/null +++ b/collector/utils/constants.js @@ -0,0 +1,71 @@ +const WATCH_DIRECTORY = require("path").resolve(__dirname, "../hotdir"); + +const ACCEPTED_MIMES = { + "text/plain": [".txt", ".md", ".org", ".adoc", ".rst"], + "text/html": [".html"], + + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": [ + ".docx", + ], + "application/vnd.openxmlformats-officedocument.presentationml.presentation": [ + ".pptx", + ], + + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [ + ".xlsx", + ], + + "application/vnd.oasis.opendocument.text": [".odt"], + "application/vnd.oasis.opendocument.presentation": [".odp"], + + "application/pdf": [".pdf"], + "application/mbox": [".mbox"], + + "audio/wav": [".wav"], + "audio/mpeg": [".mp3"], + + "video/mp4": [".mp4"], + "video/mpeg": [".mpeg"], + "application/epub+zip": [".epub"], + "image/png": [".png"], + "image/jpeg": [".jpg"], + "image/jpg": [".jpg"], +}; + +const SUPPORTED_FILETYPE_CONVERTERS = { + ".txt": "./convert/asTxt.js", + ".md": "./convert/asTxt.js", + ".org": "./convert/asTxt.js", + ".adoc": "./convert/asTxt.js", + ".rst": "./convert/asTxt.js", + + ".html": "./convert/asTxt.js", + ".pdf": "./convert/asPDF/index.js", + + ".docx": "./convert/asDocx.js", + ".pptx": "./convert/asOfficeMime.js", + + ".odt": "./convert/asOfficeMime.js", + ".odp": "./convert/asOfficeMime.js", + + ".xlsx": "./convert/asXlsx.js", + + ".mbox": "./convert/asMbox.js", + + ".epub": "./convert/asEPub.js", + + ".mp3": "./convert/asAudio.js", + ".wav": "./convert/asAudio.js", + ".mp4": "./convert/asAudio.js", + ".mpeg": "./convert/asAudio.js", + + ".png": "./convert/asImage.js", + ".jpg": "./convert/asImage.js", + ".jpeg": "./convert/asImage.js", +}; + +module.exports = { + SUPPORTED_FILETYPE_CONVERTERS, + WATCH_DIRECTORY, + ACCEPTED_MIMES, +}; diff --git a/collector/utils/extensions/Confluence/ConfluenceLoader/index.js b/collector/utils/extensions/Confluence/ConfluenceLoader/index.js new file mode 100644 index 0000000..d84a64f --- /dev/null +++ b/collector/utils/extensions/Confluence/ConfluenceLoader/index.js @@ -0,0 +1,141 @@ +/* + * This is a custom implementation of the Confluence langchain loader. There was an issue where + * code blocks were not being extracted. This is a temporary fix until this issue is resolved.*/ + +const { htmlToText } = require("html-to-text"); + +class ConfluencePagesLoader { + constructor({ + baseUrl, + spaceKey, + username, + accessToken, + limit = 25, + expand = "body.storage,version", + personalAccessToken, + cloud = true, + }) { + this.baseUrl = baseUrl; + this.spaceKey = spaceKey; + this.username = username; + this.accessToken = accessToken; + this.limit = limit; + this.expand = expand; + this.personalAccessToken = personalAccessToken; + this.cloud = cloud; + } + + get authorizationHeader() { + if (this.personalAccessToken) { + return `Bearer ${this.personalAccessToken}`; + } else if (this.username && this.accessToken) { + const authToken = Buffer.from( + `${this.username}:${this.accessToken}` + ).toString("base64"); + return `Basic ${authToken}`; + } + return undefined; + } + + async load(options) { + try { + const pages = await this.fetchAllPagesInSpace( + options?.start, + options?.limit + ); + return pages.map((page) => this.createDocumentFromPage(page)); + } catch (error) { + console.error("Error:", error); + return []; + } + } + + async fetchConfluenceData(url) { + try { + const initialHeaders = { + "Content-Type": "application/json", + Accept: "application/json", + }; + const authHeader = this.authorizationHeader; + if (authHeader) { + initialHeaders.Authorization = authHeader; + } + const response = await fetch(url, { + headers: initialHeaders, + }); + if (!response.ok) { + throw new Error( + `Failed to fetch ${url} from Confluence: ${response.status}` + ); + } + return await response.json(); + } catch (error) { + throw new Error(`Failed to fetch ${url} from Confluence: ${error}`); + } + } + + // https://developer.atlassian.com/cloud/confluence/rest/v2/intro/#auth + async fetchAllPagesInSpace(start = 0, limit = this.limit) { + const url = `${this.baseUrl}${ + this.cloud ? "/wiki" : "" + }/rest/api/content?spaceKey=${ + this.spaceKey + }&limit=${limit}&start=${start}&expand=${this.expand}`; + const data = await this.fetchConfluenceData(url); + if (data.size === 0) { + return []; + } + const nextPageStart = start + data.size; + const nextPageResults = await this.fetchAllPagesInSpace( + nextPageStart, + limit + ); + return data.results.concat(nextPageResults); + } + + createDocumentFromPage(page) { + // Function to extract code blocks + const extractCodeBlocks = (content) => { + const codeBlockRegex = + /]*>[\s\S]*?<\/ac:plain-text-body>[\s\S]*?<\/ac:structured-macro>/g; + const languageRegex = + /(.*?)<\/ac:parameter>/; + + return content.replace(codeBlockRegex, (match) => { + const language = match.match(languageRegex)?.[1] || ""; + const code = + match.match( + /<\/ac:plain-text-body>/ + )?.[1] || ""; + return `\n\`\`\`${language}\n${code.trim()}\n\`\`\`\n`; + }); + }; + + const contentWithCodeBlocks = extractCodeBlocks(page.body.storage.value); + const plainTextContent = htmlToText(contentWithCodeBlocks, { + wordwrap: false, + preserveNewlines: true, + }); + const textWithPreservedStructure = plainTextContent.replace( + /\n{3,}/g, + "\n\n" + ); + const pageUrl = `${this.baseUrl}/spaces/${this.spaceKey}/pages/${page.id}`; + + return { + pageContent: textWithPreservedStructure, + metadata: { + id: page.id, + status: page.status, + title: page.title, + type: page.type, + url: pageUrl, + version: page.version?.number, + updated_by: page.version?.by?.displayName, + updated_at: page.version?.when, + }, + }; + } +} + +module.exports = { ConfluencePagesLoader }; diff --git a/collector/utils/extensions/Confluence/index.js b/collector/utils/extensions/Confluence/index.js new file mode 100644 index 0000000..4d17d56 --- /dev/null +++ b/collector/utils/extensions/Confluence/index.js @@ -0,0 +1,257 @@ +const fs = require("fs"); +const path = require("path"); +const { default: slugify } = require("slugify"); +const { v4 } = require("uuid"); +const { writeToServerDocuments, sanitizeFileName } = require("../../files"); +const { tokenizeString } = require("../../tokenizer"); +const { ConfluencePagesLoader } = require("./ConfluenceLoader"); + +/** + * Load Confluence documents from a spaceID and Confluence credentials + * @param {object} args - forwarded request body params + * @param {import("../../../middleware/setDataSigner").ResponseWithSigner} response - Express response object with encryptionWorker + * @returns + */ +async function loadConfluence( + { + baseUrl = null, + spaceKey = null, + username = null, + accessToken = null, + cloud = true, + personalAccessToken = null, + }, + response +) { + if (!personalAccessToken && (!username || !accessToken)) { + return { + success: false, + reason: + "You need either a personal access token (PAT), or a username and access token to use the Confluence connector.", + }; + } + + if (!baseUrl || !validBaseUrl(baseUrl)) { + return { + success: false, + reason: "Provided base URL is not a valid URL.", + }; + } + + if (!spaceKey) { + return { + success: false, + reason: "You need to provide a Confluence space key.", + }; + } + + const { origin, hostname } = new URL(baseUrl); + console.log(`-- Working Confluence ${origin} --`); + const loader = new ConfluencePagesLoader({ + baseUrl: origin, // Use the origin to avoid issues with subdomains, ports, protocols, etc. + spaceKey, + username, + accessToken, + cloud, + personalAccessToken, + }); + + const { docs, error } = await loader + .load() + .then((docs) => { + return { docs, error: null }; + }) + .catch((e) => { + return { + docs: [], + error: e.message?.split("Error:")?.[1] || e.message, + }; + }); + + if (!docs.length || !!error) { + return { + success: false, + reason: error ?? "No pages found for that Confluence space.", + }; + } + const outFolder = slugify( + `confluence-${hostname}-${v4().slice(0, 4)}` + ).toLowerCase(); + + const outFolderPath = + process.env.NODE_ENV === "development" + ? path.resolve( + __dirname, + `../../../../server/storage/documents/${outFolder}` + ) + : path.resolve(process.env.STORAGE_DIR, `documents/${outFolder}`); + + if (!fs.existsSync(outFolderPath)) + fs.mkdirSync(outFolderPath, { recursive: true }); + + docs.forEach((doc) => { + if (!doc.pageContent) return; + + const data = { + id: v4(), + url: doc.metadata.url + ".page", + title: doc.metadata.title || doc.metadata.source, + docAuthor: origin, + description: doc.metadata.title, + docSource: `${origin} Confluence`, + chunkSource: generateChunkSource( + { doc, baseUrl: origin, spaceKey, accessToken, username, cloud }, + response.locals.encryptionWorker + ), + published: new Date().toLocaleString(), + wordCount: doc.pageContent.split(" ").length, + pageContent: doc.pageContent, + token_count_estimate: tokenizeString(doc.pageContent), + }; + + console.log( + `[Confluence Loader]: Saving ${doc.metadata.title} to ${outFolder}` + ); + + const fileName = sanitizeFileName( + `${slugify(doc.metadata.title)}-${data.id}` + ); + writeToServerDocuments(data, fileName, outFolderPath); + }); + + return { + success: true, + reason: null, + data: { + spaceKey, + destination: outFolder, + }, + }; +} + +/** + * Gets the page content from a specific Confluence page, not all pages in a workspace. + * @returns + */ +async function fetchConfluencePage({ + pageUrl, + baseUrl, + spaceKey, + username, + accessToken, + cloud = true, +}) { + if (!pageUrl || !baseUrl || !spaceKey || !username || !accessToken) { + return { + success: false, + content: null, + reason: + "You need either a username and access token, or a personal access token (PAT), to use the Confluence connector.", + }; + } + + if (!validBaseUrl(baseUrl)) { + return { + success: false, + content: null, + reason: "Provided base URL is not a valid URL.", + }; + } + + if (!spaceKey) { + return { + success: false, + content: null, + reason: "You need to provide a Confluence space key.", + }; + } + + console.log(`-- Working Confluence Page ${pageUrl} --`); + const loader = new ConfluencePagesLoader({ + baseUrl, // Should be the origin of the baseUrl + spaceKey, + username, + accessToken, + cloud, + }); + + const { docs, error } = await loader + .load() + .then((docs) => { + return { docs, error: null }; + }) + .catch((e) => { + return { + docs: [], + error: e.message?.split("Error:")?.[1] || e.message, + }; + }); + + if (!docs.length || !!error) { + return { + success: false, + reason: error ?? "No pages found for that Confluence space.", + content: null, + }; + } + + const targetDocument = docs.find( + (doc) => doc.pageContent && doc.metadata.url === pageUrl + ); + if (!targetDocument) { + return { + success: false, + reason: "Target page could not be found in Confluence space.", + content: null, + }; + } + + return { + success: true, + reason: null, + content: targetDocument.pageContent, + }; +} + +/** + * Validates if the provided baseUrl is a valid URL at all. + * @param {string} baseUrl + * @returns {boolean} + */ +function validBaseUrl(baseUrl) { + try { + new URL(baseUrl); + return true; + } catch (e) { + return false; + } +} + +/** + * Generate the full chunkSource for a specific Confluence page so that we can resync it later. + * This data is encrypted into a single `payload` query param so we can replay credentials later + * since this was encrypted with the systems persistent password and salt. + * @param {object} chunkSourceInformation + * @param {import("../../EncryptionWorker").EncryptionWorker} encryptionWorker + * @returns {string} + */ +function generateChunkSource( + { doc, baseUrl, spaceKey, accessToken, username, cloud }, + encryptionWorker +) { + const payload = { + baseUrl, + spaceKey, + token: accessToken, + username, + cloud, + }; + return `confluence://${doc.metadata.url}?payload=${encryptionWorker.encrypt( + JSON.stringify(payload) + )}`; +} + +module.exports = { + loadConfluence, + fetchConfluencePage, +}; diff --git a/collector/utils/extensions/RepoLoader/GithubRepo/RepoLoader/index.js b/collector/utils/extensions/RepoLoader/GithubRepo/RepoLoader/index.js new file mode 100644 index 0000000..26d3e17 --- /dev/null +++ b/collector/utils/extensions/RepoLoader/GithubRepo/RepoLoader/index.js @@ -0,0 +1,235 @@ +/** + * @typedef {Object} RepoLoaderArgs + * @property {string} repo - The GitHub repository URL. + * @property {string} [branch] - The branch to load from (optional). + * @property {string} [accessToken] - GitHub access token for authentication (optional). + * @property {string[]} [ignorePaths] - Array of paths to ignore when loading (optional). + */ + +/** + * @class + * @classdesc Loads and manages GitHub repository content. + */ +class GitHubRepoLoader { + /** + * Creates an instance of RepoLoader. + * @param {RepoLoaderArgs} [args] - The configuration options. + * @returns {GitHubRepoLoader} + */ + constructor(args = {}) { + this.ready = false; + this.repo = args?.repo; + this.branch = args?.branch; + this.accessToken = args?.accessToken || null; + this.ignorePaths = args?.ignorePaths || []; + + this.author = null; + this.project = null; + this.branches = []; + } + + #validGithubUrl() { + try { + const url = new URL(this.repo); + + // Not a github url at all. + if (url.hostname !== "github.com") { + console.log( + `[GitHub Loader]: Invalid GitHub URL provided! Hostname must be 'github.com'. Got ${url.hostname}` + ); + return false; + } + + // Assume the url is in the format of github.com/{author}/{project} + // Remove the first slash from the pathname so we can split it properly. + const [author, project, ..._rest] = url.pathname.slice(1).split("/"); + if (!author || !project) { + console.log( + `[GitHub Loader]: Invalid GitHub URL provided! URL must be in the format of 'github.com/{author}/{project}'. Got ${url.pathname}` + ); + return false; + } + + this.author = author; + this.project = project; + return true; + } catch (e) { + console.log( + `[GitHub Loader]: Invalid GitHub URL provided! Error: ${e.message}` + ); + return false; + } + } + + // Ensure the branch provided actually exists + // and if it does not or has not been set auto-assign to primary branch. + async #validBranch() { + await this.getRepoBranches(); + if (!!this.branch && this.branches.includes(this.branch)) return; + + console.log( + "[GitHub Loader]: Branch not set! Auto-assigning to a default branch." + ); + this.branch = this.branches.includes("main") ? "main" : "master"; + console.log(`[GitHub Loader]: Branch auto-assigned to ${this.branch}.`); + return; + } + + async #validateAccessToken() { + if (!this.accessToken) return; + const valid = await fetch("https://api.github.com/octocat", { + method: "GET", + headers: { + Authorization: `Bearer ${this.accessToken}`, + "X-GitHub-Api-Version": "2022-11-28", + }, + }) + .then((res) => { + if (!res.ok) throw new Error(res.statusText); + return res.ok; + }) + .catch((e) => { + console.error( + "Invalid GitHub Access Token provided! Access token will not be used", + e.message + ); + return false; + }); + + if (!valid) this.accessToken = null; + return; + } + + /** + * Initializes the RepoLoader instance. + * @returns {Promise} The initialized RepoLoader instance. + */ + async init() { + if (!this.#validGithubUrl()) return; + await this.#validBranch(); + await this.#validateAccessToken(); + this.ready = true; + return this; + } + + /** + * Recursively loads the repository content. + * @returns {Promise>} An array of loaded documents. + * @throws {Error} If the RepoLoader is not in a ready state. + */ + async recursiveLoader() { + if (!this.ready) throw new Error("[GitHub Loader]: not in ready state!"); + const { + GithubRepoLoader: LCGithubLoader, + } = require("@langchain/community/document_loaders/web/github"); + + if (this.accessToken) + console.log( + `[GitHub Loader]: Access token set! Recursive loading enabled!` + ); + + const loader = new LCGithubLoader(this.repo, { + branch: this.branch, + recursive: !!this.accessToken, // Recursive will hit rate limits. + maxConcurrency: 5, + unknown: "warn", + accessToken: this.accessToken, + ignorePaths: this.ignorePaths, + verbose: true, + }); + + const docs = await loader.load(); + return docs; + } + + // Sort branches to always show either main or master at the top of the result. + #branchPrefSort(branches = []) { + const preferredSort = ["main", "master"]; + return branches.reduce((acc, branch) => { + if (preferredSort.includes(branch)) return [branch, ...acc]; + return [...acc, branch]; + }, []); + } + + /** + * Retrieves all branches for the repository. + * @returns {Promise} An array of branch names. + */ + async getRepoBranches() { + if (!this.#validGithubUrl() || !this.author || !this.project) return []; + await this.#validateAccessToken(); // Ensure API access token is valid for pre-flight + + let page = 0; + let polling = true; + const branches = []; + + while (polling) { + console.log(`Fetching page ${page} of branches for ${this.project}`); + await fetch( + `https://api.github.com/repos/${this.author}/${this.project}/branches?per_page=100&page=${page}`, + { + method: "GET", + headers: { + ...(this.accessToken + ? { Authorization: `Bearer ${this.accessToken}` } + : {}), + "X-GitHub-Api-Version": "2022-11-28", + }, + } + ) + .then((res) => { + if (res.ok) return res.json(); + throw new Error(`Invalid request to Github API: ${res.statusText}`); + }) + .then((branchObjects) => { + polling = branchObjects.length > 0; + branches.push(branchObjects.map((branch) => branch.name)); + page++; + }) + .catch((err) => { + polling = false; + console.log(`RepoLoader.branches`, err); + }); + } + + this.branches = [...new Set(branches.flat())]; + return this.#branchPrefSort(this.branches); + } + + /** + * Fetches the content of a single file from the repository. + * @param {string} sourceFilePath - The path to the file in the repository. + * @returns {Promise} The content of the file, or null if fetching fails. + */ + async fetchSingleFile(sourceFilePath) { + try { + return fetch( + `https://api.github.com/repos/${this.author}/${this.project}/contents/${sourceFilePath}?ref=${this.branch}`, + { + method: "GET", + headers: { + Accept: "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + ...(!!this.accessToken + ? { Authorization: `Bearer ${this.accessToken}` } + : {}), + }, + } + ) + .then((res) => { + if (res.ok) return res.json(); + throw new Error(`Failed to fetch from Github API: ${res.statusText}`); + }) + .then((json) => { + if (json.hasOwnProperty("status") || !json.hasOwnProperty("content")) + throw new Error(json?.message || "missing content"); + return atob(json.content); + }); + } catch (e) { + console.error(`RepoLoader.fetchSingleFile`, e); + return null; + } + } +} + +module.exports = GitHubRepoLoader; diff --git a/collector/utils/extensions/RepoLoader/GithubRepo/index.js b/collector/utils/extensions/RepoLoader/GithubRepo/index.js new file mode 100644 index 0000000..ae3396c --- /dev/null +++ b/collector/utils/extensions/RepoLoader/GithubRepo/index.js @@ -0,0 +1,159 @@ +const RepoLoader = require("./RepoLoader"); +const fs = require("fs"); +const path = require("path"); +const { default: slugify } = require("slugify"); +const { v4 } = require("uuid"); +const { writeToServerDocuments } = require("../../../files"); +const { tokenizeString } = require("../../../tokenizer"); + +/** + * Load in a GitHub Repo recursively or just the top level if no PAT is provided + * @param {object} args - forwarded request body params + * @param {import("../../../middleware/setDataSigner").ResponseWithSigner} response - Express response object with encryptionWorker + * @returns + */ +async function loadGithubRepo(args, response) { + const repo = new RepoLoader(args); + await repo.init(); + + if (!repo.ready) + return { + success: false, + reason: "Could not prepare GitHub repo for loading! Check URL", + }; + + console.log( + `-- Working GitHub ${repo.author}/${repo.project}:${repo.branch} --` + ); + const docs = await repo.recursiveLoader(); + if (!docs.length) { + return { + success: false, + reason: "No files were found for those settings.", + }; + } + + console.log(`[GitHub Loader]: Found ${docs.length} source files. Saving...`); + const outFolder = slugify( + `${repo.author}-${repo.project}-${repo.branch}-${v4().slice(0, 4)}` + ).toLowerCase(); + + const outFolderPath = + process.env.NODE_ENV === "development" + ? path.resolve( + __dirname, + `../../../../../server/storage/documents/${outFolder}` + ) + : path.resolve(process.env.STORAGE_DIR, `documents/${outFolder}`); + + if (!fs.existsSync(outFolderPath)) + fs.mkdirSync(outFolderPath, { recursive: true }); + + for (const doc of docs) { + if (!doc.pageContent) continue; + const data = { + id: v4(), + url: "github://" + doc.metadata.source, + title: doc.metadata.source, + docAuthor: repo.author, + description: "No description found.", + docSource: doc.metadata.source, + chunkSource: generateChunkSource( + repo, + doc, + response.locals.encryptionWorker + ), + published: new Date().toLocaleString(), + wordCount: doc.pageContent.split(" ").length, + pageContent: doc.pageContent, + token_count_estimate: tokenizeString(doc.pageContent), + }; + console.log( + `[GitHub Loader]: Saving ${doc.metadata.source} to ${outFolder}` + ); + writeToServerDocuments( + data, + `${slugify(doc.metadata.source)}-${data.id}`, + outFolderPath + ); + } + + return { + success: true, + reason: null, + data: { + author: repo.author, + repo: repo.project, + branch: repo.branch, + files: docs.length, + destination: outFolder, + }, + }; +} + +/** + * Gets the page content from a specific source file in a give GitHub Repo, not all items in a repo. + * @returns + */ +async function fetchGithubFile({ + repoUrl, + branch, + accessToken = null, + sourceFilePath, +}) { + const repo = new RepoLoader({ + repo: repoUrl, + branch, + accessToken, + }); + await repo.init(); + + if (!repo.ready) + return { + success: false, + content: null, + reason: "Could not prepare GitHub repo for loading! Check URL or PAT.", + }; + + console.log( + `-- Working GitHub ${repo.author}/${repo.project}:${repo.branch} file:${sourceFilePath} --` + ); + const fileContent = await repo.fetchSingleFile(sourceFilePath); + if (!fileContent) { + return { + success: false, + reason: "Target file returned a null content response.", + content: null, + }; + } + + return { + success: true, + reason: null, + content: fileContent, + }; +} + +/** + * Generate the full chunkSource for a specific file so that we can resync it later. + * This data is encrypted into a single `payload` query param so we can replay credentials later + * since this was encrypted with the systems persistent password and salt. + * @param {RepoLoader} repo + * @param {import("@langchain/core/documents").Document} doc + * @param {import("../../EncryptionWorker").EncryptionWorker} encryptionWorker + * @returns {string} + */ +function generateChunkSource(repo, doc, encryptionWorker) { + const payload = { + owner: repo.author, + project: repo.project, + branch: repo.branch, + path: doc.metadata.source, + pat: !!repo.accessToken ? repo.accessToken : null, + }; + return `github://${repo.repo}?payload=${encryptionWorker.encrypt( + JSON.stringify(payload) + )}`; +} + +module.exports = { loadGithubRepo, fetchGithubFile }; diff --git a/collector/utils/extensions/RepoLoader/GitlabRepo/RepoLoader/index.js b/collector/utils/extensions/RepoLoader/GitlabRepo/RepoLoader/index.js new file mode 100644 index 0000000..79afd5a --- /dev/null +++ b/collector/utils/extensions/RepoLoader/GitlabRepo/RepoLoader/index.js @@ -0,0 +1,376 @@ +const ignore = require("ignore"); + +/** + * @typedef {Object} RepoLoaderArgs + * @property {string} repo - The GitLab repository URL. + * @property {string} [branch] - The branch to load from (optional). + * @property {string} [accessToken] - GitLab access token for authentication (optional). + * @property {string[]} [ignorePaths] - Array of paths to ignore when loading (optional). + * @property {boolean} [fetchIssues] - Should issues be fetched (optional). + */ + +/** + * @typedef {Object} FileTreeObject + * @property {string} id - The file object ID. + * @property {string} name - name of file. + * @property {('blob'|'tree')} type - type of file object. + * @property {string} path - path + name of file. + * @property {string} mode - Linux permission code. + */ + +/** + * @class + * @classdesc Loads and manages GitLab repository content. + */ +class GitLabRepoLoader { + /** + * Creates an instance of RepoLoader. + * @param {RepoLoaderArgs} [args] - The configuration options. + * @returns {GitLabRepoLoader} + */ + constructor(args = {}) { + this.ready = false; + this.repo = args?.repo; + this.branch = args?.branch; + this.accessToken = args?.accessToken || null; + this.ignorePaths = args?.ignorePaths || []; + this.ignoreFilter = ignore().add(this.ignorePaths); + this.withIssues = args?.fetchIssues || false; + + this.projectId = null; + this.apiBase = "https://gitlab.com"; + this.author = null; + this.project = null; + this.branches = []; + } + + #validGitlabUrl() { + const UrlPattern = require("url-pattern"); + const validPatterns = [ + new UrlPattern("https\\://gitlab.com/(:author*)/(:project(*))", { + segmentValueCharset: "a-zA-Z0-9-._~%+", + }), + // This should even match the regular hosted URL, but we may want to know + // if this was a hosted GitLab (above) or a self-hosted (below) instance + // since the API interface could be different. + new UrlPattern( + "(:protocol(http|https))\\://(:hostname*)/(:author*)/(:project(*))", + { + segmentValueCharset: "a-zA-Z0-9-._~%+", + } + ), + ]; + + let match = null; + for (const pattern of validPatterns) { + if (match !== null) continue; + match = pattern.match(this.repo); + } + if (!match) return false; + const { author, project } = match; + + this.projectId = encodeURIComponent(`${author}/${project}`); + this.apiBase = new URL(this.repo).origin; + this.author = author; + this.project = project; + return true; + } + + async #validBranch() { + await this.getRepoBranches(); + if (!!this.branch && this.branches.includes(this.branch)) return; + + console.log( + "[Gitlab Loader]: Branch not set! Auto-assigning to a default branch." + ); + this.branch = this.branches.includes("main") ? "main" : "master"; + console.log(`[Gitlab Loader]: Branch auto-assigned to ${this.branch}.`); + return; + } + + async #validateAccessToken() { + if (!this.accessToken) return; + try { + await fetch(`${this.apiBase}/api/v4/user`, { + method: "GET", + headers: this.accessToken ? { "PRIVATE-TOKEN": this.accessToken } : {}, + }).then((res) => res.ok); + } catch (e) { + console.error( + "Invalid Gitlab Access Token provided! Access token will not be used", + e.message + ); + this.accessToken = null; + } + } + + /** + * Initializes the RepoLoader instance. + * @returns {Promise} The initialized RepoLoader instance. + */ + async init() { + if (!this.#validGitlabUrl()) return; + await this.#validBranch(); + await this.#validateAccessToken(); + this.ready = true; + return this; + } + + /** + * Recursively loads the repository content. + * @returns {Promise>} An array of loaded documents. + * @throws {Error} If the RepoLoader is not in a ready state. + */ + async recursiveLoader() { + if (!this.ready) throw new Error("[Gitlab Loader]: not in ready state!"); + + if (this.accessToken) + console.log( + `[Gitlab Loader]: Access token set! Recursive loading enabled for ${this.repo}!` + ); + + const docs = []; + + console.log(`[Gitlab Loader]: Fetching files.`); + + const files = await this.fetchFilesRecursive(); + + console.log(`[Gitlab Loader]: Fetched ${files.length} files.`); + + for (const file of files) { + if (this.ignoreFilter.ignores(file.path)) continue; + + docs.push({ + pageContent: file.content, + metadata: { + source: file.path, + url: `${this.repo}/-/blob/${this.branch}/${file.path}`, + }, + }); + } + + if (this.withIssues) { + console.log(`[Gitlab Loader]: Fetching issues.`); + const issues = await this.fetchIssues(); + console.log( + `[Gitlab Loader]: Fetched ${issues.length} issues with discussions.` + ); + docs.push( + ...issues.map((issue) => ({ + issue, + metadata: { + source: `issue-${this.repo}-${issue.iid}`, + url: issue.web_url, + }, + })) + ); + } + + return docs; + } + + #branchPrefSort(branches = []) { + const preferredSort = ["main", "master"]; + return branches.reduce((acc, branch) => { + if (preferredSort.includes(branch)) return [branch, ...acc]; + return [...acc, branch]; + }, []); + } + + /** + * Retrieves all branches for the repository. + * @returns {Promise} An array of branch names. + */ + async getRepoBranches() { + if (!this.#validGitlabUrl() || !this.projectId) return []; + await this.#validateAccessToken(); + this.branches = []; + + const branchesRequestData = { + endpoint: `/api/v4/projects/${this.projectId}/repository/branches`, + }; + + let branchesPage = []; + while ((branchesPage = await this.fetchNextPage(branchesRequestData))) { + this.branches.push(...branchesPage.map((branch) => branch.name)); + } + return this.#branchPrefSort(this.branches); + } + + /** + * Returns list of all file objects from tree API for GitLab + * @returns {Promise} + */ + async fetchFilesRecursive() { + const files = []; + const filesRequestData = { + endpoint: `/api/v4/projects/${this.projectId}/repository/tree`, + queryParams: { + ref: this.branch, + recursive: true, + }, + }; + + let filesPage = null; + let pagePromises = []; + while ((filesPage = await this.fetchNextPage(filesRequestData))) { + // Fetch all the files that are not ignored in parallel. + pagePromises = filesPage + .filter((file) => { + if (file.type !== "blob") return false; + return !this.ignoreFilter.ignores(file.path); + }) + .map(async (file) => { + const content = await this.fetchSingleFileContents(file.path); + if (!content) return null; + return { + path: file.path, + content, + }; + }); + + const pageFiles = await Promise.all(pagePromises); + + files.push(...pageFiles.filter((item) => item !== null)); + console.log(`Fetched ${files.length} files.`); + } + console.log(`Total files fetched: ${files.length}`); + return files; + } + + /** + * Fetches all issues from the repository. + * @returns {Promise} An array of issue objects. + */ + async fetchIssues() { + const issues = []; + const issuesRequestData = { + endpoint: `/api/v4/projects/${this.projectId}/issues`, + }; + + let issuesPage = null; + let pagePromises = []; + while ((issuesPage = await this.fetchNextPage(issuesRequestData))) { + // Fetch all the issues in parallel. + pagePromises = issuesPage.map(async (issue) => { + const discussionsRequestData = { + endpoint: `/api/v4/projects/${this.projectId}/issues/${issue.iid}/discussions`, + }; + let discussionPage = null; + const discussions = []; + + while ( + (discussionPage = await this.fetchNextPage(discussionsRequestData)) + ) { + discussions.push( + ...discussionPage.map(({ notes }) => + notes.map( + ({ body, author, created_at }) => + `${author.username} at ${created_at}: +${body}` + ) + ) + ); + } + const result = { + ...issue, + discussions, + }; + return result; + }); + + const pageIssues = await Promise.all(pagePromises); + + issues.push(...pageIssues); + console.log(`Fetched ${issues.length} issues.`); + } + console.log(`Total issues fetched: ${issues.length}`); + return issues; + } + + /** + * Fetches the content of a single file from the repository. + * @param {string} sourceFilePath - The path to the file in the repository. + * @returns {Promise} The content of the file, or null if fetching fails. + */ + async fetchSingleFileContents(sourceFilePath) { + try { + const data = await fetch( + `${this.apiBase}/api/v4/projects/${ + this.projectId + }/repository/files/${encodeURIComponent(sourceFilePath)}/raw?ref=${ + this.branch + }`, + { + method: "GET", + headers: this.accessToken + ? { "PRIVATE-TOKEN": this.accessToken } + : {}, + } + ).then((res) => { + if (res.ok) return res.text(); + throw new Error(`Failed to fetch single file ${sourceFilePath}`); + }); + + return data; + } catch (e) { + console.error(`RepoLoader.fetchSingleFileContents`, e); + return null; + } + } + + /** + * Fetches the next page of data from the API. + * @param {Object} requestData - The request data. + * @returns {Promise|null>} The next page of data, or null if no more pages. + */ + async fetchNextPage(requestData) { + try { + if (requestData.page === -1) return null; + if (!requestData.page) requestData.page = 1; + + const { endpoint, perPage = 100, queryParams = {} } = requestData; + const params = new URLSearchParams({ + ...queryParams, + per_page: perPage, + page: requestData.page, + }); + const url = `${this.apiBase}${endpoint}?${params.toString()}`; + + const response = await fetch(url, { + method: "GET", + headers: this.accessToken ? { "PRIVATE-TOKEN": this.accessToken } : {}, + }); + + // Rate limits get hit very often if no PAT is provided + if (response.status === 401) { + console.warn(`Rate limit hit for ${endpoint}. Skipping.`); + return null; + } + + const totalPages = Number(response.headers.get("x-total-pages")); + const data = await response.json(); + if (!Array.isArray(data)) { + console.warn(`Unexpected response format for ${endpoint}:`, data); + return []; + } + + console.log( + `Gitlab RepoLoader: fetched ${endpoint} page ${requestData.page}/${totalPages} with ${data.length} records.` + ); + + if (totalPages === requestData.page) { + requestData.page = -1; + } else { + requestData.page = Number(response.headers.get("x-next-page")); + } + + return data; + } catch (e) { + console.error(`RepoLoader.fetchNextPage`, e); + return null; + } + } +} + +module.exports = GitLabRepoLoader; diff --git a/collector/utils/extensions/RepoLoader/GitlabRepo/index.js b/collector/utils/extensions/RepoLoader/GitlabRepo/index.js new file mode 100644 index 0000000..cd74fb3 --- /dev/null +++ b/collector/utils/extensions/RepoLoader/GitlabRepo/index.js @@ -0,0 +1,252 @@ +const RepoLoader = require("./RepoLoader"); +const fs = require("fs"); +const path = require("path"); +const { default: slugify } = require("slugify"); +const { v4 } = require("uuid"); +const { writeToServerDocuments } = require("../../../files"); +const { tokenizeString } = require("../../../tokenizer"); + +/** + * Load in a Gitlab Repo recursively or just the top level if no PAT is provided + * @param {object} args - forwarded request body params + * @param {import("../../../middleware/setDataSigner").ResponseWithSigner} response - Express response object with encryptionWorker + * @returns + */ +async function loadGitlabRepo(args, response) { + const repo = new RepoLoader(args); + await repo.init(); + + if (!repo.ready) + return { + success: false, + reason: "Could not prepare Gitlab repo for loading! Check URL", + }; + + console.log( + `-- Working GitLab ${repo.author}/${repo.project}:${repo.branch} --` + ); + const docs = await repo.recursiveLoader(); + if (!docs.length) { + return { + success: false, + reason: "No files were found for those settings.", + }; + } + + console.log(`[GitLab Loader]: Found ${docs.length} source files. Saving...`); + const outFolder = slugify( + `${repo.author}-${repo.project}-${repo.branch}-${v4().slice(0, 4)}` + ).toLowerCase(); + + const outFolderPath = + process.env.NODE_ENV === "development" + ? path.resolve( + __dirname, + `../../../../../server/storage/documents/${outFolder}` + ) + : path.resolve(process.env.STORAGE_DIR, `documents/${outFolder}`); + + if (!fs.existsSync(outFolderPath)) + fs.mkdirSync(outFolderPath, { recursive: true }); + + for (const doc of docs) { + if (!doc.metadata || (!doc.pageContent && !doc.issue)) continue; + let pageContent = null; + + const data = { + id: v4(), + url: "gitlab://" + doc.metadata.source, + docSource: doc.metadata.source, + chunkSource: generateChunkSource( + repo, + doc, + response.locals.encryptionWorker + ), + published: new Date().toLocaleString(), + }; + + if (doc.pageContent) { + pageContent = doc.pageContent; + + data.title = doc.metadata.source; + data.docAuthor = repo.author; + data.description = "No description found."; + } else if (doc.issue) { + pageContent = issueToMarkdown(doc.issue); + + data.title = `Issue ${doc.issue.iid}: ${doc.issue.title}`; + data.docAuthor = doc.issue.author.username; + data.description = doc.issue.description; + } else { + continue; + } + + data.wordCount = pageContent.split(" ").length; + data.token_count_estimate = tokenizeString(pageContent); + data.pageContent = pageContent; + + console.log( + `[GitLab Loader]: Saving ${doc.metadata.source} to ${outFolder}` + ); + + writeToServerDocuments( + data, + `${slugify(doc.metadata.source)}-${data.id}`, + outFolderPath + ); + } + + return { + success: true, + reason: null, + data: { + author: repo.author, + repo: repo.project, + projectId: repo.projectId, + branch: repo.branch, + files: docs.length, + destination: outFolder, + }, + }; +} + +async function fetchGitlabFile({ + repoUrl, + branch, + accessToken = null, + sourceFilePath, +}) { + const repo = new RepoLoader({ + repo: repoUrl, + branch, + accessToken, + }); + await repo.init(); + + if (!repo.ready) + return { + success: false, + content: null, + reason: "Could not prepare GitLab repo for loading! Check URL or PAT.", + }; + console.log( + `-- Working GitLab ${repo.author}/${repo.project}:${repo.branch} file:${sourceFilePath} --` + ); + const fileContent = await repo.fetchSingleFile(sourceFilePath); + if (!fileContent) { + return { + success: false, + reason: "Target file returned a null content response.", + content: null, + }; + } + + return { + success: true, + reason: null, + content: fileContent, + }; +} + +function generateChunkSource(repo, doc, encryptionWorker) { + const payload = { + projectId: decodeURIComponent(repo.projectId), + branch: repo.branch, + path: doc.metadata.source, + pat: !!repo.accessToken ? repo.accessToken : null, + }; + return `gitlab://${repo.repo}?payload=${encryptionWorker.encrypt( + JSON.stringify(payload) + )}`; +} + +function issueToMarkdown(issue) { + const metadata = {}; + + const userFields = ["author", "assignees", "closed_by"]; + const userToUsername = ({ username }) => username; + for (const userField of userFields) { + if (issue[userField]) { + if (Array.isArray(issue[userField])) { + metadata[userField] = issue[userField].map(userToUsername); + } else { + metadata[userField] = userToUsername(issue[userField]); + } + } + } + + const singleValueFields = [ + "web_url", + "state", + "created_at", + "updated_at", + "closed_at", + "due_date", + "type", + "merge_request_count", + "upvotes", + "downvotes", + "labels", + "has_tasks", + "task_status", + "confidential", + "severity", + ]; + + for (const singleValueField of singleValueFields) { + metadata[singleValueField] = issue[singleValueField]; + } + + if (issue.milestone) { + metadata.milestone = `${issue.milestone.title} (${issue.milestone.id})`; + } + + if (issue.time_stats) { + const timeFields = ["time_estimate", "total_time_spent"]; + for (const timeField of timeFields) { + const fieldName = `human_${timeField}`; + if (issue?.time_stats[fieldName]) { + metadata[timeField] = issue.time_stats[fieldName]; + } + } + } + + const metadataString = Object.entries(metadata) + .map(([name, value]) => { + if (!value || value?.length < 1) { + return null; + } + let result = `- ${name.replace("_", " ")}:`; + + if (!Array.isArray(value)) { + result += ` ${value}`; + } else { + result += "\n" + value.map((s) => ` - ${s}`).join("\n"); + } + + return result; + }) + .filter((item) => item != null) + .join("\n"); + + let markdown = `# ${issue.title} (${issue.iid}) + +${issue.description} + +## Metadata + +${metadataString}`; + + if (issue.discussions.length > 0) { + markdown += ` + +## Activity + +${issue.discussions.join("\n\n")} +`; + } + + return markdown; +} + +module.exports = { loadGitlabRepo, fetchGitlabFile }; diff --git a/collector/utils/extensions/RepoLoader/index.js b/collector/utils/extensions/RepoLoader/index.js new file mode 100644 index 0000000..28e8780 --- /dev/null +++ b/collector/utils/extensions/RepoLoader/index.js @@ -0,0 +1,41 @@ +/** + * Dynamically load the correct repository loader from a specific platform + * by default will return GitHub. + * @param {('github'|'gitlab')} platform + * @returns {import("./GithubRepo/RepoLoader")|import("./GitlabRepo/RepoLoader")} the repo loader class for provider + */ +function resolveRepoLoader(platform = "github") { + switch (platform) { + case "github": + console.log(`Loading GitHub RepoLoader...`); + return require("./GithubRepo/RepoLoader"); + case "gitlab": + console.log(`Loading GitLab RepoLoader...`); + return require("./GitlabRepo/RepoLoader"); + default: + console.log(`Loading GitHub RepoLoader...`); + return require("./GithubRepo/RepoLoader"); + } +} + +/** + * Dynamically load the correct repository loader function from a specific platform + * by default will return Github. + * @param {('github'|'gitlab')} platform + * @returns {import("./GithubRepo")['fetchGithubFile'] | import("./GitlabRepo")['fetchGitlabFile']} the repo loader class for provider + */ +function resolveRepoLoaderFunction(platform = "github") { + switch (platform) { + case "github": + console.log(`Loading GitHub loader function...`); + return require("./GithubRepo").loadGithubRepo; + case "gitlab": + console.log(`Loading GitLab loader function...`); + return require("./GitlabRepo").loadGitlabRepo; + default: + console.log(`Loading GitHub loader function...`); + return require("./GithubRepo").loadGithubRepo; + } +} + +module.exports = { resolveRepoLoader, resolveRepoLoaderFunction }; diff --git a/collector/utils/extensions/WebsiteDepth/index.js b/collector/utils/extensions/WebsiteDepth/index.js new file mode 100644 index 0000000..4801a45 --- /dev/null +++ b/collector/utils/extensions/WebsiteDepth/index.js @@ -0,0 +1,166 @@ +const { v4 } = require("uuid"); +const { + PuppeteerWebBaseLoader, +} = require("langchain/document_loaders/web/puppeteer"); +const { default: slugify } = require("slugify"); +const { parse } = require("node-html-parser"); +const { writeToServerDocuments } = require("../../files"); +const { tokenizeString } = require("../../tokenizer"); +const path = require("path"); +const fs = require("fs"); + +async function discoverLinks(startUrl, maxDepth = 1, maxLinks = 20) { + const baseUrl = new URL(startUrl); + const discoveredLinks = new Set([startUrl]); + let queue = [[startUrl, 0]]; // [url, currentDepth] + const scrapedUrls = new Set(); + + for (let currentDepth = 0; currentDepth < maxDepth; currentDepth++) { + const levelSize = queue.length; + const nextQueue = []; + + for (let i = 0; i < levelSize && discoveredLinks.size < maxLinks; i++) { + const [currentUrl, urlDepth] = queue[i]; + + if (!scrapedUrls.has(currentUrl)) { + scrapedUrls.add(currentUrl); + const newLinks = await getPageLinks(currentUrl, baseUrl); + + for (const link of newLinks) { + if (!discoveredLinks.has(link) && discoveredLinks.size < maxLinks) { + discoveredLinks.add(link); + if (urlDepth + 1 < maxDepth) { + nextQueue.push([link, urlDepth + 1]); + } + } + } + } + } + + queue = nextQueue; + if (queue.length === 0 || discoveredLinks.size >= maxLinks) break; + } + + return Array.from(discoveredLinks); +} + +async function getPageLinks(url, baseUrl) { + try { + const loader = new PuppeteerWebBaseLoader(url, { + launchOptions: { headless: "new" }, + gotoOptions: { waitUntil: "networkidle2" }, + }); + const docs = await loader.load(); + const html = docs[0].pageContent; + const links = extractLinks(html, baseUrl); + return links; + } catch (error) { + console.error(`Failed to get page links from ${url}.`, error); + return []; + } +} + +function extractLinks(html, baseUrl) { + const root = parse(html); + const links = root.querySelectorAll("a"); + const extractedLinks = new Set(); + + for (const link of links) { + const href = link.getAttribute("href"); + if (href) { + const absoluteUrl = new URL(href, baseUrl.href).href; + if ( + absoluteUrl.startsWith( + baseUrl.origin + baseUrl.pathname.split("/").slice(0, -1).join("/") + ) + ) { + extractedLinks.add(absoluteUrl); + } + } + } + + return Array.from(extractedLinks); +} + +async function bulkScrapePages(links, outFolderPath) { + const scrapedData = []; + + for (let i = 0; i < links.length; i++) { + const link = links[i]; + console.log(`Scraping ${i + 1}/${links.length}: ${link}`); + + try { + const loader = new PuppeteerWebBaseLoader(link, { + launchOptions: { headless: "new" }, + gotoOptions: { waitUntil: "networkidle2" }, + async evaluate(page, browser) { + const result = await page.evaluate(() => document.body.innerText); + await browser.close(); + return result; + }, + }); + const docs = await loader.load(); + const content = docs[0].pageContent; + + if (!content.length) { + console.warn(`Empty content for ${link}. Skipping.`); + continue; + } + + const url = new URL(link); + const decodedPathname = decodeURIComponent(url.pathname); + const filename = `${url.hostname}${decodedPathname.replace(/\//g, "_")}`; + + const data = { + id: v4(), + url: "file://" + slugify(filename) + ".html", + title: slugify(filename) + ".html", + docAuthor: "no author found", + description: "No description found.", + docSource: "URL link uploaded by the user.", + chunkSource: `link://${link}`, + published: new Date().toLocaleString(), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + writeToServerDocuments(data, data.title, outFolderPath); + scrapedData.push(data); + + console.log(`Successfully scraped ${link}.`); + } catch (error) { + console.error(`Failed to scrape ${link}.`, error); + } + } + + return scrapedData; +} + +async function websiteScraper(startUrl, depth = 1, maxLinks = 20) { + const websiteName = new URL(startUrl).hostname; + const outFolder = slugify( + `${slugify(websiteName)}-${v4().slice(0, 4)}` + ).toLowerCase(); + const outFolderPath = + process.env.NODE_ENV === "development" + ? path.resolve( + __dirname, + `../../../../server/storage/documents/${outFolder}` + ) + : path.resolve(process.env.STORAGE_DIR, `documents/${outFolder}`); + + console.log("Discovering links..."); + const linksToScrape = await discoverLinks(startUrl, depth, maxLinks); + console.log(`Found ${linksToScrape.length} links to scrape.`); + + if (!fs.existsSync(outFolderPath)) + fs.mkdirSync(outFolderPath, { recursive: true }); + console.log("Starting bulk scraping..."); + const scrapedData = await bulkScrapePages(linksToScrape, outFolderPath); + console.log(`Scraped ${scrapedData.length} pages.`); + + return scrapedData; +} + +module.exports = websiteScraper; diff --git a/collector/utils/extensions/YoutubeTranscript/YoutubeLoader/index.js b/collector/utils/extensions/YoutubeTranscript/YoutubeLoader/index.js new file mode 100644 index 0000000..aac94eb --- /dev/null +++ b/collector/utils/extensions/YoutubeTranscript/YoutubeLoader/index.js @@ -0,0 +1,90 @@ +/* + * This is just a custom implementation of the Langchain JS YouTubeLoader class + * as the dependency for YoutubeTranscript is quite fickle and its a rat race to keep it up + * and instead of waiting for patches we can just bring this simple script in-house and at least + * be able to patch it since its so flaky. When we have more connectors we can kill this because + * it will be a pain to maintain over time. + */ +class YoutubeLoader { + #videoId; + #language; + #addVideoInfo; + + constructor({ videoId = null, language = null, addVideoInfo = false } = {}) { + if (!videoId) throw new Error("Invalid video id!"); + this.#videoId = videoId; + this.#language = language; + this.#addVideoInfo = addVideoInfo; + } + + /** + * Extracts the videoId from a YouTube video URL. + * @param url The URL of the YouTube video. + * @returns The videoId of the YouTube video. + */ + static getVideoID(url) { + const match = url.match( + /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#&?]*).*/ + ); + if (match !== null && match[1].length === 11) { + return match[1]; + } else { + throw new Error("Failed to get youtube video id from the url"); + } + } + + /** + * Creates a new instance of the YoutubeLoader class from a YouTube video + * URL. + * @param url The URL of the YouTube video. + * @param config Optional configuration options for the YoutubeLoader instance, excluding the videoId. + * @returns A new instance of the YoutubeLoader class. + */ + static createFromUrl(url, config = {}) { + const videoId = YoutubeLoader.getVideoID(url); + return new YoutubeLoader({ ...config, videoId }); + } + + /** + * Loads the transcript and video metadata from the specified YouTube + * video. It uses the youtube-transcript library to fetch the transcript + * and the youtubei.js library to fetch the video metadata. + * @returns Langchain like doc that is 1 element with PageContent and + */ + async load() { + let transcript; + const metadata = { + source: this.#videoId, + }; + try { + const { YoutubeTranscript } = require("./youtube-transcript"); + transcript = await YoutubeTranscript.fetchTranscript(this.#videoId, { + lang: this.#language, + }); + if (!transcript) { + throw new Error("Transcription not found"); + } + if (this.#addVideoInfo) { + const { Innertube } = require("youtubei.js"); + const youtube = await Innertube.create(); + const info = (await youtube.getBasicInfo(this.#videoId)).basic_info; + metadata.description = info.short_description; + metadata.title = info.title; + metadata.view_count = info.view_count; + metadata.author = info.author; + } + } catch (e) { + throw new Error( + `Failed to get YouTube video transcription: ${e?.message}` + ); + } + return [ + { + pageContent: transcript, + metadata, + }, + ]; + } +} + +module.exports.YoutubeLoader = YoutubeLoader; diff --git a/collector/utils/extensions/YoutubeTranscript/YoutubeLoader/youtube-transcript.js b/collector/utils/extensions/YoutubeTranscript/YoutubeLoader/youtube-transcript.js new file mode 100644 index 0000000..f868875 --- /dev/null +++ b/collector/utils/extensions/YoutubeTranscript/YoutubeLoader/youtube-transcript.js @@ -0,0 +1,117 @@ +const { parse } = require("node-html-parser"); +const RE_YOUTUBE = + /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/i; +const USER_AGENT = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36,gzip(gfe)"; + +class YoutubeTranscriptError extends Error { + constructor(message) { + super(`[YoutubeTranscript] ${message}`); + } +} + +/** + * Class to retrieve transcript if exist + */ +class YoutubeTranscript { + /** + * Fetch transcript from YTB Video + * @param videoId Video url or video identifier + * @param config Object with lang param (eg: en, es, hk, uk) format. + * Will just the grab first caption if it can find one, so no special lang caption support. + */ + static async fetchTranscript(videoId, config = {}) { + const identifier = this.retrieveVideoId(videoId); + const lang = config?.lang ?? "en"; + try { + const transcriptUrl = await fetch( + `https://www.youtube.com/watch?v=${identifier}`, + { + headers: { + "User-Agent": USER_AGENT, + }, + } + ) + .then((res) => res.text()) + .then((html) => parse(html)) + .then((html) => this.#parseTranscriptEndpoint(html, lang)); + + if (!transcriptUrl) + throw new Error("Failed to locate a transcript for this video!"); + + // Result is hopefully some XML. + const transcriptXML = await fetch(transcriptUrl) + .then((res) => res.text()) + .then((xml) => parse(xml)); + + let transcript = ""; + const chunks = transcriptXML.getElementsByTagName("text"); + for (const chunk of chunks) { + // Add space after each text chunk + transcript += chunk.textContent + " "; + } + + // Trim extra whitespace + return transcript.trim().replace(/\s+/g, " "); + } catch (e) { + throw new YoutubeTranscriptError(e); + } + } + + static #parseTranscriptEndpoint(document, langCode = null) { + try { + // Get all script tags on document page + const scripts = document.getElementsByTagName("script"); + + // find the player data script. + const playerScript = scripts.find((script) => + script.textContent.includes("var ytInitialPlayerResponse = {") + ); + + const dataString = + playerScript.textContent + ?.split("var ytInitialPlayerResponse = ")?.[1] //get the start of the object {.... + ?.split("};")?.[0] + // chunk off any code after object closure. + "}"; // add back that curly brace we just cut. + + const data = JSON.parse(dataString.trim()); // Attempt a JSON parse + const availableCaptions = + data?.captions?.playerCaptionsTracklistRenderer?.captionTracks || []; + + // If languageCode was specified then search for it's code, otherwise get the first. + let captionTrack = availableCaptions?.[0]; + if (langCode) + captionTrack = + availableCaptions.find((track) => + track.languageCode.includes(langCode) + ) ?? availableCaptions?.[0]; + + return captionTrack?.baseUrl; + } catch (e) { + console.error(`YoutubeTranscript.#parseTranscriptEndpoint ${e.message}`); + return null; + } + } + + /** + * Retrieve video id from url or string + * @param videoId video url or video id + */ + static retrieveVideoId(videoId) { + if (videoId.length === 11) { + return videoId; + } + const matchId = videoId.match(RE_YOUTUBE); + if (matchId && matchId.length) { + return matchId[1]; + } + throw new YoutubeTranscriptError( + "Impossible to retrieve Youtube video ID." + ); + } +} + +module.exports = { + YoutubeTranscript, + YoutubeTranscriptError, +}; diff --git a/collector/utils/extensions/YoutubeTranscript/index.js b/collector/utils/extensions/YoutubeTranscript/index.js new file mode 100644 index 0000000..0e1e13f --- /dev/null +++ b/collector/utils/extensions/YoutubeTranscript/index.js @@ -0,0 +1,142 @@ +const fs = require("fs"); +const path = require("path"); +const { default: slugify } = require("slugify"); +const { v4 } = require("uuid"); +const { writeToServerDocuments } = require("../../files"); +const { tokenizeString } = require("../../tokenizer"); +const { YoutubeLoader } = require("./YoutubeLoader"); + +function validYoutubeVideoUrl(link) { + const UrlPattern = require("url-pattern"); + const opts = new URL(link); + const url = `${opts.protocol}//${opts.host}${opts.pathname}${ + opts.searchParams.has("v") ? `?v=${opts.searchParams.get("v")}` : "" + }`; + + const shortPatternMatch = new UrlPattern( + "https\\://(www.)youtu.be/(:videoId)" + ).match(url); + const fullPatternMatch = new UrlPattern( + "https\\://(www.)youtube.com/watch?v=(:videoId)" + ).match(url); + const videoId = + shortPatternMatch?.videoId || fullPatternMatch?.videoId || null; + if (!!videoId) return true; + + return false; +} + +async function fetchVideoTranscriptContent({ url }) { + if (!validYoutubeVideoUrl(url)) { + return { + success: false, + reason: "Invalid URL. Should be youtu.be or youtube.com/watch.", + content: null, + metadata: {}, + }; + } + + console.log(`-- Working YouTube ${url} --`); + const loader = YoutubeLoader.createFromUrl(url, { addVideoInfo: true }); + const { docs, error } = await loader + .load() + .then((docs) => { + return { docs, error: null }; + }) + .catch((e) => { + return { + docs: [], + error: e.message?.split("Error:")?.[1] || e.message, + }; + }); + + if (!docs.length || !!error) { + return { + success: false, + reason: error ?? "No transcript found for that YouTube video.", + content: null, + metadata: {}, + }; + } + + const metadata = docs[0].metadata; + const content = docs[0].pageContent; + if (!content.length) { + return { + success: false, + reason: "No transcript could be parsed for that YouTube video.", + content: null, + metadata: {}, + }; + } + + return { + success: true, + reason: null, + content, + metadata, + }; +} + +async function loadYouTubeTranscript({ url }) { + const transcriptResults = await fetchVideoTranscriptContent({ url }); + if (!transcriptResults.success) { + return { + success: false, + reason: + transcriptResults.reason || + "An unknown error occurred during transcription retrieval", + }; + } + const { content, metadata } = transcriptResults; + const outFolder = slugify( + `${metadata.author} YouTube transcripts` + ).toLowerCase(); + + const outFolderPath = + process.env.NODE_ENV === "development" + ? path.resolve( + __dirname, + `../../../../server/storage/documents/${outFolder}` + ) + : path.resolve(process.env.STORAGE_DIR, `documents/${outFolder}`); + + if (!fs.existsSync(outFolderPath)) + fs.mkdirSync(outFolderPath, { recursive: true }); + + const data = { + id: v4(), + url: url + ".youtube", + title: metadata.title || url, + docAuthor: metadata.author, + description: metadata.description, + docSource: url, + chunkSource: `youtube://${url}`, + published: new Date().toLocaleString(), + wordCount: content.split(" ").length, + pageContent: content, + token_count_estimate: tokenizeString(content), + }; + + console.log(`[YouTube Loader]: Saving ${metadata.title} to ${outFolder}`); + writeToServerDocuments( + data, + `${slugify(metadata.title)}-${data.id}`, + outFolderPath + ); + + return { + success: true, + reason: "test", + data: { + title: metadata.title, + author: metadata.author, + destination: outFolder, + }, + }; +} + +module.exports = { + loadYouTubeTranscript, + fetchVideoTranscriptContent, +}; diff --git a/collector/utils/files/index.js b/collector/utils/files/index.js new file mode 100644 index 0000000..edea280 --- /dev/null +++ b/collector/utils/files/index.js @@ -0,0 +1,192 @@ +const fs = require("fs"); +const path = require("path"); +const { MimeDetector } = require("./mime"); + +/** + * Checks if a file is text by checking the mime type and then falling back to buffer inspection. + * This way we can capture all the cases where the mime type is not known but still parseable as text + * without having to constantly add new mime type overrides. + * @param {string} filepath - The path to the file. + * @returns {boolean} - Returns true if the file is text, false otherwise. + */ +function isTextType(filepath) { + if (!fs.existsSync(filepath)) return false; + const result = isKnownTextMime(filepath); + if (result.valid) return true; // Known text type - return true. + if (result.reason !== "generic") return false; // If any other reason than generic - return false. + return parseableAsText(filepath); // Fallback to parsing as text via buffer inspection. +} + +/** + * Checks if a file is known to be text by checking the mime type. + * @param {string} filepath - The path to the file. + * @returns {boolean} - Returns true if the file is known to be text, false otherwise. + */ +function isKnownTextMime(filepath) { + try { + const mimeLib = new MimeDetector(); + const mime = mimeLib.getType(filepath); + if (mimeLib.badMimes.includes(mime)) + return { valid: false, reason: "bad_mime" }; + + const type = mime.split("/")[0]; + if (mimeLib.nonTextTypes.includes(type)) + return { valid: false, reason: "non_text_mime" }; + return { valid: true, reason: "valid_mime" }; + } catch (e) { + return { valid: false, reason: "generic" }; + } +} + +/** + * Checks if a file is parseable as text by forcing it to be read as text in utf8 encoding. + * If the file looks too much like a binary file, it will return false. + * @param {string} filepath - The path to the file. + * @returns {boolean} - Returns true if the file is parseable as text, false otherwise. + */ +function parseableAsText(filepath) { + try { + const fd = fs.openSync(filepath, "r"); + const buffer = Buffer.alloc(1024); // Read first 1KB of the file synchronously + const bytesRead = fs.readSync(fd, buffer, 0, 1024, 0); + fs.closeSync(fd); + + const content = buffer.subarray(0, bytesRead).toString("utf8"); + const nullCount = (content.match(/\0/g) || []).length; + const controlCount = (content.match(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g) || []) + .length; + + const threshold = bytesRead * 0.1; + return nullCount + controlCount < threshold; + } catch { + return false; + } +} + +function trashFile(filepath) { + if (!fs.existsSync(filepath)) return; + + try { + const isDir = fs.lstatSync(filepath).isDirectory(); + if (isDir) return; + } catch { + return; + } + + fs.rmSync(filepath); + return; +} + +function createdDate(filepath) { + try { + const { birthtimeMs, birthtime } = fs.statSync(filepath); + if (birthtimeMs === 0) throw new Error("Invalid stat for file!"); + return birthtime.toLocaleString(); + } catch { + return "unknown"; + } +} + +function writeToServerDocuments( + data = {}, + filename, + destinationOverride = null +) { + const destination = destinationOverride + ? path.resolve(destinationOverride) + : path.resolve( + __dirname, + "../../../server/storage/documents/custom-documents" + ); + if (!fs.existsSync(destination)) + fs.mkdirSync(destination, { recursive: true }); + const destinationFilePath = path.resolve(destination, filename) + ".json"; + + fs.writeFileSync(destinationFilePath, JSON.stringify(data, null, 4), { + encoding: "utf-8", + }); + + return { + ...data, + // relative location string that can be passed into the /update-embeddings api + // that will work since we know the location exists and since we only allow + // 1-level deep folders this will always work. This still works for integrations like GitHub and YouTube. + location: destinationFilePath.split("/").slice(-2).join("/"), + }; +} + +// When required we can wipe the entire collector hotdir and tmp storage in case +// there were some large file failures that we unable to be removed a reboot will +// force remove them. +async function wipeCollectorStorage() { + const cleanHotDir = new Promise((resolve) => { + const directory = path.resolve(__dirname, "../../hotdir"); + fs.readdir(directory, (err, files) => { + if (err) resolve(); + + for (const file of files) { + if (file === "__HOTDIR__.md") continue; + try { + fs.rmSync(path.join(directory, file)); + } catch {} + } + resolve(); + }); + }); + + const cleanTmpDir = new Promise((resolve) => { + const directory = path.resolve(__dirname, "../../storage/tmp"); + fs.readdir(directory, (err, files) => { + if (err) resolve(); + + for (const file of files) { + if (file === ".placeholder") continue; + try { + fs.rmSync(path.join(directory, file)); + } catch {} + } + resolve(); + }); + }); + + await Promise.all([cleanHotDir, cleanTmpDir]); + console.log(`Collector hot directory and tmp storage wiped!`); + return; +} + +/** + * Checks if a given path is within another path. + * @param {string} outer - The outer path (should be resolved). + * @param {string} inner - The inner path (should be resolved). + * @returns {boolean} - Returns true if the inner path is within the outer path, false otherwise. + */ +function isWithin(outer, inner) { + if (outer === inner) return false; + const rel = path.relative(outer, inner); + return !rel.startsWith("../") && rel !== ".."; +} + +function normalizePath(filepath = "") { + const result = path + .normalize(filepath.trim()) + .replace(/^(\.\.(\/|\\|$))+/, "") + .trim(); + if (["..", ".", "/"].includes(result)) throw new Error("Invalid path."); + return result; +} + +function sanitizeFileName(fileName) { + if (!fileName) return fileName; + return fileName.replace(/[<>:"\/\\|?*]/g, ""); +} + +module.exports = { + trashFile, + isTextType, + createdDate, + writeToServerDocuments, + wipeCollectorStorage, + normalizePath, + isWithin, + sanitizeFileName, +}; diff --git a/collector/utils/files/mime.js b/collector/utils/files/mime.js new file mode 100644 index 0000000..bd95496 --- /dev/null +++ b/collector/utils/files/mime.js @@ -0,0 +1,64 @@ +const MimeLib = require("mime"); +class MimeDetector { + nonTextTypes = ["multipart", "model", "audio", "video", "font"]; + badMimes = [ + "application/octet-stream", + "application/zip", + "application/pkcs8", + "application/vnd.microsoft.portable-executable", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // XLSX are binaries and need to be handled explicitly. + "application/x-msdownload", + ]; + + constructor() { + this.lib = MimeLib; + this.setOverrides(); + } + + setOverrides() { + // the .ts extension maps to video/mp2t because of https://en.wikipedia.org/wiki/MPEG_transport_stream + // which has had this extension far before TS was invented. So need to force re-map this MIME map. + this.lib.define( + { + "text/plain": [ + "ts", + "tsx", + "py", + "opts", + "lock", + "jsonl", + "qml", + "sh", + "c", + "cs", + "h", + "js", + "lua", + "pas", + "r", + "go", + "ino", + "hpp", + "linq", + "cs", + ], + }, + true + ); + } + + /** + * Returns the MIME type of the file. If the file has no extension found, it will be processed as a text file. + * @param {string} filepath + * @returns {string} + */ + getType(filepath) { + const parsedMime = this.lib.getType(filepath); + if (!!parsedMime) return parsedMime; + return null; + } +} + +module.exports = { + MimeDetector, +}; diff --git a/collector/utils/http/index.js b/collector/utils/http/index.js new file mode 100644 index 0000000..2283c06 --- /dev/null +++ b/collector/utils/http/index.js @@ -0,0 +1,18 @@ +process.env.NODE_ENV === "development" + ? require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` }) + : require("dotenv").config(); + +function reqBody(request) { + return typeof request.body === "string" + ? JSON.parse(request.body) + : request.body; +} + +function queryParams(request) { + return request.query; +} + +module.exports = { + reqBody, + queryParams, +}; diff --git a/collector/utils/logger/index.js b/collector/utils/logger/index.js new file mode 100644 index 0000000..a0eddec --- /dev/null +++ b/collector/utils/logger/index.js @@ -0,0 +1,68 @@ +const winston = require("winston"); + +class Logger { + logger = console; + static _instance; + constructor() { + if (Logger._instance) return Logger._instance; + this.logger = + process.env.NODE_ENV === "production" ? this.getWinstonLogger() : console; + Logger._instance = this; + } + + getWinstonLogger() { + const logger = winston.createLogger({ + level: "info", + defaultMeta: { service: "collector" }, + transports: [ + new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.printf( + ({ level, message, service, origin = "" }) => { + return `\x1b[36m[${service}]\x1b[0m${ + origin ? `\x1b[33m[${origin}]\x1b[0m` : "" + } ${level}: ${message}`; + } + ) + ), + }), + ], + }); + + function formatArgs(args) { + return args + .map((arg) => { + if (arg instanceof Error) { + return arg.stack; // If argument is an Error object, return its stack trace + } else if (typeof arg === "object") { + return JSON.stringify(arg); // Convert objects to JSON string + } else { + return arg; // Otherwise, return as-is + } + }) + .join(" "); + } + + console.log = function (...args) { + logger.info(formatArgs(args)); + }; + console.error = function (...args) { + logger.error(formatArgs(args)); + }; + console.info = function (...args) { + logger.warn(formatArgs(args)); + }; + return logger; + } +} + +/** + * Sets and overrides Console methods for logging when called. + * This is a singleton method and will not create multiple loggers. + * @returns {winston.Logger | console} - instantiated logger interface. + */ +function setLogger() { + return new Logger().logger; +} +module.exports = setLogger; diff --git a/collector/utils/tokenizer/index.js b/collector/utils/tokenizer/index.js new file mode 100644 index 0000000..2086be2 --- /dev/null +++ b/collector/utils/tokenizer/index.js @@ -0,0 +1,66 @@ +const { getEncoding } = require("js-tiktoken"); + +class TikTokenTokenizer { + static MAX_KB_ESTIMATE = 10; + static DIVISOR = 8; + + constructor() { + if (TikTokenTokenizer.instance) { + this.log( + "Singleton instance already exists. Returning existing instance." + ); + return TikTokenTokenizer.instance; + } + + this.encoder = getEncoding("cl100k_base"); + TikTokenTokenizer.instance = this; + this.log("Initialized new TikTokenTokenizer instance."); + } + + log(text, ...args) { + console.log(`\x1b[35m[TikTokenTokenizer]\x1b[0m ${text}`, ...args); + } + + /** + * Check if the input is too long to encode + * this is more of a rough estimate and a sanity check to prevent + * CPU issues from encoding too large of strings + * Assumes 1 character = 2 bytes in JS + * @param {string} input + * @returns {boolean} + */ + #isTooLong(input) { + const bytesEstimate = input.length * 2; + const kbEstimate = Math.floor(bytesEstimate / 1024); + return kbEstimate >= TikTokenTokenizer.MAX_KB_ESTIMATE; + } + + /** + * Encode a string into tokens for rough token count estimation. + * @param {string} input + * @returns {number} + */ + tokenizeString(input = "") { + try { + if (this.#isTooLong(input)) { + this.log("Input will take too long to encode - estimating"); + return Math.ceil(input.length / TikTokenTokenizer.DIVISOR); + } + + return this.encoder.encode(input).length; + } catch (e) { + this.log("Could not tokenize string! Estimating...", e.message, e.stack); + return Math.ceil(input?.length / TikTokenTokenizer.DIVISOR) || 0; + } + } +} + +const tokenizer = new TikTokenTokenizer(); +module.exports = { + /** + * Encode a string into tokens for rough token count estimation. + * @param {string} input + * @returns {number} + */ + tokenizeString: (input) => tokenizer.tokenizeString(input), +}; diff --git a/collector/utils/url/index.js b/collector/utils/url/index.js new file mode 100644 index 0000000..c9d87b2 --- /dev/null +++ b/collector/utils/url/index.js @@ -0,0 +1,55 @@ +/** ATTN: SECURITY RESEARCHERS + * To Security researchers about to submit an SSRF report CVE - please don't. + * We are aware that the code below is does not defend against any of the thousands of ways + * you can map a hostname to another IP via tunneling, hosts editing, etc. The code below does not have intention of blocking this + * and is simply to prevent the user from accidentally putting in non-valid websites, which is all this protects + * since _all urls must be submitted by the user anyway_ and cannot be done with authentication and manager or admin roles. + * If an attacker has those roles then the system is already vulnerable and this is not a primary concern. + * + * We have gotten this report may times, marked them as duplicate or information and continue to get them. We communicate + * already that deployment (and security) of an instance is on the deployer and system admin deploying it. This would include + * isolation, firewalls, and the general security of the instance. + */ + +const VALID_PROTOCOLS = ["https:", "http:"]; +const INVALID_OCTETS = [192, 172, 10, 127]; + +/** + * If an ip address is passed in the user is attempting to collector some internal service running on internal/private IP. + * This is not a security feature and simply just prevents the user from accidentally entering invalid IP addresses. + * @param {URL} param0 + * @param {URL['hostname']} param0.hostname + * @returns {boolean} + */ +function isInvalidIp({ hostname }) { + const IPRegex = new RegExp( + /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/gi + ); + + // Not an IP address at all - passthrough + if (!IPRegex.test(hostname)) return false; + const [octetOne, ..._rest] = hostname.split("."); + + // If fails to validate to number - abort and return as invalid. + if (isNaN(Number(octetOne))) return true; + + // Allow localhost loopback and 0.0.0.0 for scraping convenience + // for locally hosted services or websites + if (["127.0.0.1", "0.0.0.0"].includes(hostname)) return false; + + return INVALID_OCTETS.includes(Number(octetOne)); +} + +function validURL(url) { + try { + const destination = new URL(url); + if (!VALID_PROTOCOLS.includes(destination.protocol)) return false; + if (isInvalidIp(destination)) return false; + return true; + } catch {} + return false; +} + +module.exports = { + validURL, +}; diff --git a/collector/yarn.lock b/collector/yarn.lock new file mode 100644 index 0000000..df7cf31 --- /dev/null +++ b/collector/yarn.lock @@ -0,0 +1,3832 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@anthropic-ai/sdk@^0.9.1": + version "0.9.1" + resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.9.1.tgz#b2d2b7bf05c90dce502c9a2e869066870f69ba88" + integrity sha512-wa1meQ2WSfoY8Uor3EdrJq0jTiZJoKoSii2ZVWRY1oN4Tlr5s59pADg9T79FTbPe1/se5c3pBeZgJL63wmuoBA== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + digest-fetch "^1.3.0" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + web-streams-polyfill "^3.2.1" + +"@babel/code-frame@^7.0.0": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" + integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== + dependencies: + "@babel/highlight" "^7.24.2" + picocolors "^1.0.0" + +"@babel/helper-validator-identifier@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62" + integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA== + +"@babel/highlight@^7.24.2": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.5.tgz#bc0613f98e1dd0720e99b2a9ee3760194a704b6e" + integrity sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.5" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + +"@emnapi/runtime@^1.2.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60" + integrity sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw== + dependencies: + tslib "^2.4.0" + +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + +"@huggingface/jinja@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@huggingface/jinja/-/jinja-0.2.2.tgz#faeb205a9d6995089bef52655ddd8245d3190627" + integrity sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA== + +"@img/sharp-darwin-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz#ef5b5a07862805f1e8145a377c8ba6e98813ca08" + integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ== + optionalDependencies: + "@img/sharp-libvips-darwin-arm64" "1.0.4" + +"@img/sharp-darwin-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61" + integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q== + optionalDependencies: + "@img/sharp-libvips-darwin-x64" "1.0.4" + +"@img/sharp-libvips-darwin-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz#447c5026700c01a993c7804eb8af5f6e9868c07f" + integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg== + +"@img/sharp-libvips-darwin-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062" + integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ== + +"@img/sharp-libvips-linux-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704" + integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA== + +"@img/sharp-libvips-linux-arm@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197" + integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g== + +"@img/sharp-libvips-linux-s390x@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce" + integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA== + +"@img/sharp-libvips-linux-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0" + integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw== + +"@img/sharp-libvips-linuxmusl-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5" + integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA== + +"@img/sharp-libvips-linuxmusl-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff" + integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw== + +"@img/sharp-linux-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22" + integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA== + optionalDependencies: + "@img/sharp-libvips-linux-arm64" "1.0.4" + +"@img/sharp-linux-arm@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff" + integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.0.5" + +"@img/sharp-linux-s390x@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667" + integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q== + optionalDependencies: + "@img/sharp-libvips-linux-s390x" "1.0.4" + +"@img/sharp-linux-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb" + integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA== + optionalDependencies: + "@img/sharp-libvips-linux-x64" "1.0.4" + +"@img/sharp-linuxmusl-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b" + integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" + +"@img/sharp-linuxmusl-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48" + integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64" "1.0.4" + +"@img/sharp-wasm32@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1" + integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg== + dependencies: + "@emnapi/runtime" "^1.2.0" + +"@img/sharp-win32-ia32@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9" + integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ== + +"@img/sharp-win32-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342" + integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== + +"@langchain/community@^0.2.23": + version "0.2.23" + resolved "https://registry.yarnpkg.com/@langchain/community/-/community-0.2.23.tgz#20560e107bcc8432c42e499f1b9292d41a3732f2" + integrity sha512-p1n/zZ1F+O5l51RzeoUeJyhpzq6Wp11tkqKOj8oThKOQJgLhO7q6iFIvmKThzL7mZCNNuPM5r1OPnU4wO6iF/A== + dependencies: + "@langchain/core" ">=0.2.16 <0.3.0" + "@langchain/openai" ">=0.1.0 <0.3.0" + binary-extensions "^2.2.0" + expr-eval "^2.0.2" + flat "^5.0.2" + js-yaml "^4.1.0" + langchain "~0.2.3" + langsmith "~0.1.30" + uuid "^10.0.0" + zod "^3.22.3" + zod-to-json-schema "^3.22.5" + +"@langchain/community@~0.0.47": + version "0.0.53" + resolved "https://registry.yarnpkg.com/@langchain/community/-/community-0.0.53.tgz#a9aaedffa0ed2977e8d302d74e9f90a49a6da037" + integrity sha512-iFqZPt4MRssGYsQoKSXWJQaYTZCC7WNuilp2JCCs3wKmJK3l6mR0eV+PDrnT+TaDHUVxt/b0rwgM0sOiy0j2jA== + dependencies: + "@langchain/core" "~0.1.60" + "@langchain/openai" "~0.0.28" + expr-eval "^2.0.2" + flat "^5.0.2" + langsmith "~0.1.1" + uuid "^9.0.0" + zod "^3.22.3" + zod-to-json-schema "^3.22.5" + +"@langchain/core@>=0.2.11 <0.3.0", "@langchain/core@>=0.2.16 <0.3.0": + version "0.2.20" + resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.2.20.tgz#5115781b0a86db3ce4b697e473405892c09621ca" + integrity sha512-WPBjrzOj79/yqjloDUIw1GDhuRQfHis07TyyDj+qS81nHh0svSasetKcqAZ3L5JoPcBmEL7rRBtM+OcyC3mLVg== + dependencies: + ansi-styles "^5.0.0" + camelcase "6" + decamelize "1.2.0" + js-tiktoken "^1.0.12" + langsmith "~0.1.39" + mustache "^4.2.0" + p-queue "^6.6.2" + p-retry "4" + uuid "^10.0.0" + zod "^3.22.4" + zod-to-json-schema "^3.22.3" + +"@langchain/core@~0.1", "@langchain/core@~0.1.56", "@langchain/core@~0.1.60": + version "0.1.61" + resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.1.61.tgz#9313363e04f1c6981a938b2909c44ce6fceb2736" + integrity sha512-C8OkAly+ugvXsL8TACCmFv9WTTcT4gvQaG6NbrXCOzibBCywfxxcTqEMOyg3zIKpxHEmR0DHqh0OiJRHocnsCg== + dependencies: + ansi-styles "^5.0.0" + camelcase "6" + decamelize "1.2.0" + js-tiktoken "^1.0.8" + langsmith "~0.1.7" + ml-distance "^4.0.0" + mustache "^4.2.0" + p-queue "^6.6.2" + p-retry "4" + uuid "^9.0.0" + zod "^3.22.4" + zod-to-json-schema "^3.22.3" + +"@langchain/openai@>=0.1.0 <0.3.0": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.2.5.tgz#e85b983986a7415ea743d4c854bb0674134334d4" + integrity sha512-gQXS5VBFyAco0jgSnUVan6fYVSIxlffmDaeDGpXrAmz2nQPgiN/h24KYOt2NOZ1zRheRzRuO/CfRagMhyVUaFA== + dependencies: + "@langchain/core" ">=0.2.16 <0.3.0" + js-tiktoken "^1.0.12" + openai "^4.49.1" + zod "^3.22.4" + zod-to-json-schema "^3.22.3" + +"@langchain/openai@~0.0.28": + version "0.0.28" + resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.0.28.tgz#afaeec61b44816935db9ae937496c964c81ab571" + integrity sha512-2s1RA3/eAnz4ahdzsMPBna9hfAqpFNlWdHiPxVGZ5yrhXsbLWWoPcF+22LCk9t0HJKtazi2GCIWc0HVXH9Abig== + dependencies: + "@langchain/core" "~0.1.56" + js-tiktoken "^1.0.7" + openai "^4.32.1" + zod "^3.22.4" + zod-to-json-schema "^3.22.3" + +"@langchain/textsplitters@~0.0.0": + version "0.0.0" + resolved "https://registry.yarnpkg.com/@langchain/textsplitters/-/textsplitters-0.0.0.tgz#5fe05fcd86ad7f1b4766afacda482cba98a3d35d" + integrity sha512-3hPesWomnmVeYMppEGYbyv0v/sRUugUdlFBNn9m1ueJYHAIKbvCErkWxNUH3guyKKYgJVrkvZoQxcd9faucSaw== + dependencies: + "@langchain/core" "~0.1" + js-tiktoken "^1.0.11" + +"@mapbox/node-pre-gyp@^1.0.11": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + +"@puppeteer/browsers@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.8.0.tgz#fb6ee61de15e7f0e67737aea9f9bab1512dbd7d8" + integrity sha512-TkRHIV6k2D8OlUe8RtG+5jgOF/H98Myx0M6AOafC8DdNVOFiBSFa5cpRDtpm8LXOa9sVwe0+e6Q3FC56X/DZfg== + dependencies: + debug "4.3.4" + extract-zip "2.0.1" + progress "2.0.3" + proxy-agent "6.3.1" + tar-fs "3.0.4" + unbzip2-stream "1.4.3" + yargs "17.7.2" + +"@selderee/plugin-htmlparser2@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz#d5b5e29a7ba6d3958a1972c7be16f4b2c188c517" + integrity sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ== + dependencies: + domhandler "^5.0.3" + selderee "^0.11.0" + +"@tokenizer/token@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" + integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== + +"@tootallnate/quickjs-emscripten@^0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" + integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== + +"@types/long@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" + integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== + +"@types/mailparser@^3.0.0": + version "3.4.4" + resolved "https://registry.yarnpkg.com/@types/mailparser/-/mailparser-3.4.4.tgz#0bd71e205573b9dd9a445e10a8b8cb0e45420998" + integrity sha512-C6Znp2QVS25JqtuPyxj38Qh+QoFcLycdxsvcc6IZCGekhaMBzbdTXzwGzhGoYb3TfKu8IRCNV0sV1o3Od97cEQ== + dependencies: + "@types/node" "*" + iconv-lite "^0.6.3" + +"@types/node-fetch@^2.6.4": + version "2.6.11" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" + integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g== + dependencies: + "@types/node" "*" + form-data "^4.0.0" + +"@types/node@*", "@types/node@>=13.7.0": + version "20.12.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.7.tgz#04080362fa3dd6c5822061aa3124f5c152cff384" + integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg== + dependencies: + undici-types "~5.26.4" + +"@types/node@^18.11.18": + version "18.19.31" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.31.tgz#b7d4a00f7cb826b60a543cebdbda5d189aaecdcd" + integrity sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA== + dependencies: + undici-types "~5.26.4" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + +"@types/uuid@^9.0.1": + version "9.0.8" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== + +"@types/yauzl@^2.9.1": + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== + dependencies: + "@types/node" "*" + +"@xenova/transformers@^2.11.0": + version "2.17.1" + resolved "https://registry.yarnpkg.com/@xenova/transformers/-/transformers-2.17.1.tgz#712f7a72c76c8aa2075749382f83dc7dd4e5a9a5" + integrity sha512-zo702tQAFZXhzeD2GCYUNUqeqkoueOdiSbQWa4s0q7ZE4z8WBIwIsMMPGobpgdqjQ2u0Qulo08wuqVEUrBXjkQ== + dependencies: + "@huggingface/jinja" "^0.2.2" + onnxruntime-web "1.14.0" + sharp "^0.32.0" + optionalDependencies: + onnxruntime-node "1.14.0" + +"@xmldom/xmldom@^0.8.10", "@xmldom/xmldom@^0.8.6": + version "0.8.10" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" + integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn@^8.8.0: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +adm-zip@^0.5.10: + version "0.5.12" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.12.tgz#87786328e91d54b37358d8a50f954c4cd73ba60b" + integrity sha512-6TVU49mK6KZb4qG6xWaaM4C7sA/sgUMLy/JYMOzkcp3BvVLpW0fXDFQiIzAuxFCt/2+xD7fNIiPFAoLZPhVNLQ== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + +agentkeepalive@^4.2.1: + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== + dependencies: + humanize-ms "^1.2.1" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +argparse@~1.0.3: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-hyper-unique@^2.1.4: + version "2.1.6" + resolved "https://registry.yarnpkg.com/array-hyper-unique/-/array-hyper-unique-2.1.6.tgz#429412fd63b7bd7c920f6cdbf60d1dd292855b2e" + integrity sha512-BdlHRqjKSYs88WFaVNVEc6Kv8ln/FdzCKPbcDPuWs4/EXkQFhnjc8TyR7hnPxRjcjo5LKOhUMGUWpAqRgeJvpA== + dependencies: + deep-eql "= 4.0.0" + lodash "^4.17.21" + +ast-types@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" + integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== + dependencies: + tslib "^2.0.1" + +async@>=0.2.9, async@^3.2.3: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +b4a@^1.6.4: + version "1.6.6" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.6.tgz#a4cc349a3851987c3c4ac2d7785c18744f6da9ba" + integrity sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +bare-events@^2.0.0, bare-events@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.2.2.tgz#a98a41841f98b2efe7ecc5c5468814469b018078" + integrity sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ== + +bare-fs@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-2.3.0.tgz#0872f8e33cf291c9fd527d827154f156a298d402" + integrity sha512-TNFqa1B4N99pds2a5NYHR15o0ZpdNKbAeKTE/+G6ED/UeOavv8RY3dr/Fu99HW3zU3pXpo2kDNO8Sjsm2esfOw== + dependencies: + bare-events "^2.0.0" + bare-path "^2.0.0" + bare-stream "^1.0.0" + +bare-os@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-2.3.0.tgz#718e680b139effff0624a7421c098e7a2c2d63da" + integrity sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg== + +bare-path@^2.0.0, bare-path@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-2.1.2.tgz#7a0940d34ebe65f7e179fa61ed8d49d9dc151d67" + integrity sha512-o7KSt4prEphWUHa3QUwCxUI00R86VdjiuxmJK0iNVDHYPGo+HsDaVCnqCmPbf/MiW1ok8F4p3m8RTHlWk8K2ig== + dependencies: + bare-os "^2.1.0" + +bare-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-1.0.0.tgz#25c3e56198d922187320c3f8c52d75c4051178b4" + integrity sha512-KhNUoDL40iP4gFaLSsoGE479t0jHijfYdIcxRn/XtezA2BaUD0NRf/JGRpsMq6dMNM+SrCrB0YSSo/5wBY4rOQ== + dependencies: + streamx "^2.16.1" + +base-64@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" + integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== + +base64-js@^1.3.1, base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +basic-ftp@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" + integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== + +bcrypt@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2" + integrity sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.11" + node-addon-api "^5.0.0" + +binary-extensions@^2.0.0, binary-extensions@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +binary-search@^1.3.5: + version "1.3.6" + resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.6.tgz#e32426016a0c5092f0f3598836a1c7da3560565c" + integrity sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA== + +bl@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" + integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bluebird@~3.4.0: + version "3.4.7" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== + +bmp-js@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233" + integrity sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw== + +body-parser@1.20.2, body-parser@^1.20.2: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ== + +buffer@^5.2.1, buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@6: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + +chokidar@^3.5.2: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chromium-bidi@0.4.33: + version "0.4.33" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.33.tgz#9a9aba5a5b07118c8e7d6405f8ee79f47418dd1d" + integrity sha512-IxoFM5WGQOIAd95qrSXzJUv4eXIrh+RvU3rwwqIiwYuvfE7U/Llj4fejbsJnjJMUYCuGtVQsY2gv7oGl4aTNSQ== + dependencies: + mitt "3.0.1" + urlpattern-polyfill "9.0.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^1.9.0, color-convert@^1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.6.0, color-string@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +color@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.8.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cosmiconfig@8.3.6: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + +crlf-normalize@^1.0.19: + version "1.0.20" + resolved "https://registry.yarnpkg.com/crlf-normalize/-/crlf-normalize-1.0.20.tgz#0b3105d3de807bce8a7599113235d725fe9361a8" + integrity sha512-h/rBerTd3YHQGfv7tNT25mfhWvRq2BBLCZZ80GFarFxf6HQGbpW6iqDL3N+HBLpjLfAdcBXfWAzVlLfHkRUQBQ== + dependencies: + ts-type ">=2" + +cross-fetch@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== + dependencies: + node-fetch "^2.6.12" + +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +data-uri-to-buffer@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b" + integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@4.3.4, debug@^4.1.1, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@^3.1.0, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +decamelize@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" + integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== + dependencies: + file-type "^5.2.0" + is-stream "^1.1.0" + tar-stream "^1.5.2" + +decompress-tarbz2@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" + integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== + dependencies: + decompress-tar "^4.1.0" + file-type "^6.1.0" + is-stream "^1.1.0" + seek-bzip "^1.0.5" + unbzip2-stream "^1.0.9" + +decompress-targz@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" + integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== + dependencies: + decompress-tar "^4.1.1" + file-type "^5.2.0" + is-stream "^1.1.0" + +decompress-unzip@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" + integrity sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw== + dependencies: + file-type "^3.8.0" + get-stream "^2.2.0" + pify "^2.3.0" + yauzl "^2.4.2" + +decompress@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" + integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== + dependencies: + decompress-tar "^4.0.0" + decompress-tarbz2 "^4.0.0" + decompress-targz "^4.0.0" + decompress-unzip "^4.0.1" + graceful-fs "^4.1.10" + make-dir "^1.0.0" + pify "^2.3.0" + strip-dirs "^2.0.0" + +"deep-eql@= 4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.0.0.tgz#c70af2713a4e18d9c2c1203ff9d11abbd51c8fbd" + integrity sha512-GxJC5MOg2KyQlv6WiUF/VAnMj4MWnYiXo4oLgeptOELVoknyErb4Z8+5F/IM/K4g9/80YzzatxmWcyRwUseH0A== + dependencies: + type-detect "^4.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deepmerge@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +degenerator@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" + integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== + dependencies: + ast-types "^0.13.4" + escodegen "^2.1.0" + esprima "^4.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-libc@^2.0.0, detect-libc@^2.0.2, detect-libc@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + +devtools-protocol@0.0.1203626: + version "0.0.1203626" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1203626.tgz#4366a4c81a7e0d4fd6924e9182c67f1e5941e820" + integrity sha512-nEzHZteIUZfGCZtTiS1fRpC8UZmsfD1SiyPvaUNvS13dvKf666OAm8YTi0+Ca3n1nLEyu49Cy4+dPWpaHFJk9g== + +digest-fetch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/digest-fetch/-/digest-fetch-1.3.0.tgz#898e69264d00012a23cf26e8a3e40320143fc661" + integrity sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA== + dependencies: + base-64 "^0.1.0" + md5 "^2.3.0" + +dingbat-to-unicode@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz#5091dd673241453e6b5865e26e5a4452cdef5c83" + integrity sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w== + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dotenv@^16.0.3: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +duck@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/duck/-/duck-0.1.12.tgz#de7adf758421230b6d7aee799ce42670586b9efa" + integrity sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg== + dependencies: + underscore "^1.13.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encoding-japanese@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encoding-japanese/-/encoding-japanese-2.0.0.tgz#fa0226e5469e7b5b69a04fea7d5481bd1fa56936" + integrity sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ== + +encoding-japanese@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/encoding-japanese/-/encoding-japanese-2.1.0.tgz#5d3c2b652c84ca563783b86907bf5cdfe9a597e2" + integrity sha512-58XySVxUgVlBikBTbQ8WdDxBDHIdXucB16LO5PBHR8t75D54wQrNo4cg+58+R1CtJfKnsVsvt9XlteRaR8xw1w== + +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^4.2.0, entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +epub2@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/epub2/-/epub2-3.0.2.tgz#eee764e2b6b965d36c7713736bd49f6941d8c9c4" + integrity sha512-rhvpt27CV5MZfRetfNtdNwi3XcNg1Am0TwfveJkK8YWeHItHepQ8Js9J06v8XRIjuTrCW/NSGYMTy55Of7BfNQ== + dependencies: + adm-zip "^0.5.10" + array-hyper-unique "^2.1.4" + bluebird "^3.7.2" + crlf-normalize "^1.0.19" + tslib "^2.6.2" + xml2js "^0.6.2" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escodegen@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + +esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter3@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +expr-eval@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expr-eval/-/expr-eval-2.0.2.tgz#fa6f044a7b0c93fde830954eb9c5b0f7fbc7e201" + integrity sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg== + +express@^4.18.2: + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +fast-fifo@^1.1.0, fast-fifo@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + +file-type@^16.5.4: + version "16.5.4" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd" + integrity sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== + dependencies: + readable-web-to-node-stream "^3.0.0" + strtok3 "^6.2.4" + token-types "^4.1.1" + +file-type@^3.8.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" + integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA== + +file-type@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" + integrity sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ== + +file-type@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" + integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatbuffers@^1.12.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/flatbuffers/-/flatbuffers-1.12.0.tgz#72e87d1726cb1b216e839ef02658aa87dcef68aa" + integrity sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ== + +fluent-ffmpeg@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz#c952de2240f812ebda0aa8006d7776ee2acf7d74" + integrity sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q== + dependencies: + async ">=0.2.9" + which "^1.1.1" + +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + +form-data-encoder@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +formdata-node@^4.3.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== + dependencies: + node-domexception "1.0.0" + web-streams-polyfill "4.0.0-beta.3" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-stream@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" + integrity sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA== + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-uri@^6.0.1: + version "6.0.3" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.3.tgz#0d26697bc13cf91092e519aa63aa60ee5b6f385a" + integrity sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw== + dependencies: + basic-ftp "^5.0.2" + data-uri-to-buffer "^6.0.2" + debug "^4.3.4" + fs-extra "^11.2.0" + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.10, graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +guid-typescript@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/guid-typescript/-/guid-typescript-1.0.9.tgz#e35f77003535b0297ea08548f5ace6adb1480ddc" + integrity sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +html-to-text@9.0.5, html-to-text@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-9.0.5.tgz#6149a0f618ae7a0db8085dca9bbf96d32bb8368d" + integrity sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg== + dependencies: + "@selderee/plugin-htmlparser2" "^0.11.0" + deepmerge "^4.3.1" + dom-serializer "^2.0.0" + htmlparser2 "^8.0.2" + selderee "^0.11.0" + +htmlparser2@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" + integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + entities "^4.4.0" + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +https-proxy-agent@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" + integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== + dependencies: + agent-base "^7.0.2" + debug "4" + +humanize-duration@^3.25.1: + version "3.32.0" + resolved "https://registry.yarnpkg.com/humanize-duration/-/humanize-duration-3.32.0.tgz#b25f64ef55d723b049b197b6b4aa3c96c202b6c9" + integrity sha512-6WsXYTHJr7hXKqoqf5zoWza/lANRAqGlbnZnm0cjDykbXuez1JVXOQGmq0EPB45pXYAJyueRA3S3hfhmMbrMEQ== + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== + dependencies: + ms "^2.0.0" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@0.6.3, iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idb-keyval@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.2.1.tgz#94516d625346d16f56f3b33855da11bfded2db33" + integrity sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg== + +ieee754@^1.1.13, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== + +ignore@^5.3.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + +import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-any-array@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-any-array/-/is-any-array-2.0.1.tgz#9233242a9c098220290aa2ec28f82ca7fa79899e" + integrity sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-natural-number@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" + integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jintr@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jintr/-/jintr-1.1.0.tgz#223a3b07f5e03d410cec6e715c537c8ad1e714c3" + integrity sha512-Tu9wk3BpN2v+kb8yT6YBtue+/nbjeLFv4vvVC4PJ7oCidHKbifWhvORrAbQfxVIQZG+67am/mDagpiGSVtvrZg== + dependencies: + acorn "^8.8.0" + +js-tiktoken@^1.0.11, js-tiktoken@^1.0.7, js-tiktoken@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.11.tgz#d7d707b849f703841112660d9d55169424a35344" + integrity sha512-PajXFLq2vx7/8jllQZ43vzNpAai/0MOVdJjW/UrNyJorNQRTjHrqdGJG/mjHVy7h9M6dW6CaG43eNLMYFkTh6w== + dependencies: + base64-js "^1.5.1" + +js-tiktoken@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.12.tgz#af0f5cf58e5e7318240d050c8413234019424211" + integrity sha512-L7wURW1fH9Qaext0VzaUDpFGVQgjkdE3Dgsy9/+yXyGEpBKnylTd0mU0bfbNkKDlXRb6TEsZkwuflu1B8uQbJQ== + dependencies: + base64-js "^1.5.1" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonpointer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" + integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== + +jszip@^3.7.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + +langchain@0.1.36: + version "0.1.36" + resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.1.36.tgz#2f201f681d83fb265716e28e7dfcfe61fbeef2c2" + integrity sha512-NTbnCL/jKWIeEI//Nm1oG8nhW3vkYWvEMr1MPotmTThTfeKfO87eV/OAzAyh6Ruy6GFs/qofRgQZGIe6XvXTNQ== + dependencies: + "@anthropic-ai/sdk" "^0.9.1" + "@langchain/community" "~0.0.47" + "@langchain/core" "~0.1.60" + "@langchain/openai" "~0.0.28" + "@langchain/textsplitters" "~0.0.0" + binary-extensions "^2.2.0" + js-tiktoken "^1.0.7" + js-yaml "^4.1.0" + jsonpointer "^5.0.1" + langchainhub "~0.0.8" + langsmith "~0.1.7" + ml-distance "^4.0.0" + openapi-types "^12.1.3" + p-retry "4" + uuid "^9.0.0" + yaml "^2.2.1" + zod "^3.22.4" + zod-to-json-schema "^3.22.3" + +langchain@~0.2.3: + version "0.2.12" + resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.2.12.tgz#3fac0b9519a070689b6dd679d5854abc57824dcf" + integrity sha512-ZHtJrHUpridZ7IQu7N/wAQ6iMAAO7VLzkupHqKP79S6p+alrPbn1BjRnh+PeGm92YiY5DafTCuvchmujxx7bCQ== + dependencies: + "@langchain/core" ">=0.2.11 <0.3.0" + "@langchain/openai" ">=0.1.0 <0.3.0" + "@langchain/textsplitters" "~0.0.0" + binary-extensions "^2.2.0" + js-tiktoken "^1.0.12" + js-yaml "^4.1.0" + jsonpointer "^5.0.1" + langchainhub "~0.0.8" + langsmith "~0.1.30" + ml-distance "^4.0.0" + openapi-types "^12.1.3" + p-retry "4" + uuid "^10.0.0" + yaml "^2.2.1" + zod "^3.22.4" + zod-to-json-schema "^3.22.3" + +langchainhub@~0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/langchainhub/-/langchainhub-0.0.8.tgz#fd4b96dc795e22e36c1a20bad31b61b0c33d3110" + integrity sha512-Woyb8YDHgqqTOZvWIbm2CaFDGfZ4NTSyXV687AG4vXEfoNo7cGQp7nhl7wL3ehenKWmNEmcxCLgOZzW8jE6lOQ== + +langsmith@~0.1.1, langsmith@~0.1.7: + version "0.1.21" + resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.1.21.tgz#0e6cb361b4dddcc713f9d1364509fea0da3429b6" + integrity sha512-pC9uujQAQFez9IdkPK4LPJLTnHLRKElImcYGxDrOJTZ2KZ/LIsRn6kPLKx1ZdT9h5CbDbT+ivXiS4bsxQGcbXA== + dependencies: + "@types/uuid" "^9.0.1" + commander "^10.0.1" + p-queue "^6.6.2" + p-retry "4" + uuid "^9.0.0" + +langsmith@~0.1.30, langsmith@~0.1.39: + version "0.1.40" + resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.1.40.tgz#9708889386a5b9d0eb43dd3a9eba93513b57101d" + integrity sha512-11E2WLbh/+41+Qc0w8fJJTC/iz91BA+zXRMX/Wz0KSstnfzIPBoiWa++Kp2X8yCIDNywWWLJhy/B8gYzm7VKig== + dependencies: + "@types/uuid" "^9.0.1" + commander "^10.0.1" + p-queue "^6.6.2" + p-retry "4" + semver "^7.6.3" + uuid "^9.0.0" + +leac@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/leac/-/leac-0.6.0.tgz#dcf136e382e666bd2475f44a1096061b70dc0912" + integrity sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg== + +libbase64@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-1.2.1.tgz#fb93bf4cb6d730f29b92155b6408d1bd2176a8c8" + integrity sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew== + +libbase64@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-1.3.0.tgz#053314755a05d2e5f08bbfc48d0290e9322f4406" + integrity sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg== + +libmime@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/libmime/-/libmime-5.2.0.tgz#c4ed5cbd2d9fdd27534543a68bb8d17c658d51d8" + integrity sha512-X2U5Wx0YmK0rXFbk67ASMeqYIkZ6E5vY7pNWRKtnNzqjvdYYG8xtPDpCnuUEnPU9vlgNev+JoSrcaKSUaNvfsw== + dependencies: + encoding-japanese "2.0.0" + iconv-lite "0.6.3" + libbase64 "1.2.1" + libqp "2.0.1" + +libmime@5.3.5: + version "5.3.5" + resolved "https://registry.yarnpkg.com/libmime/-/libmime-5.3.5.tgz#acd95a32f58dab55c8a9d269c5b4509e7ad6ae31" + integrity sha512-nSlR1yRZ43L3cZCiWEw7ali3jY29Hz9CQQ96Oy+sSspYnIP5N54ucOPHqooBsXzwrX1pwn13VUE05q4WmzfaLg== + dependencies: + encoding-japanese "2.1.0" + iconv-lite "0.6.3" + libbase64 "1.3.0" + libqp "2.1.0" + +libqp@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/libqp/-/libqp-2.0.1.tgz#b8fed76cc1ea6c9ceff8888169e4e0de70cd5cf2" + integrity sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg== + +libqp@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/libqp/-/libqp-2.1.0.tgz#ce84bffd86b76029032093bd866d316e12a3d3f5" + integrity sha512-O6O6/fsG5jiUVbvdgT7YX3xY3uIadR6wEZ7+vy9u7PKHAlSEB6blvC1o5pHBjgsi95Uo0aiBBdkyFecj6jtb7A== + +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +linkify-it@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" + integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== + dependencies: + uc.micro "^2.0.0" + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +logform@^2.3.2, logform@^2.4.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" + integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ== + dependencies: + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +lop@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/lop/-/lop-0.4.1.tgz#744f1696ef480e68ce1947fe557b09db5af2a738" + integrity sha512-9xyho9why2A2tzm5aIcMWKvzqKsnxrf9B5I+8O30olh6lQU8PH978LqZoI4++37RBgS1Em5i54v1TFs/3wnmXQ== + dependencies: + duck "^0.1.12" + option "~0.2.1" + underscore "^1.13.1" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +mailparser@^3.0.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-3.7.1.tgz#4d0ea2eeb50a73dd10854a71ef1d4553bdce01cb" + integrity sha512-RCnBhy5q8XtB3mXzxcAfT1huNqN93HTYYyL6XawlIKycfxM/rXPg9tXoZ7D46+SgCS1zxKzw+BayDQSvncSTTw== + dependencies: + encoding-japanese "2.1.0" + he "1.2.0" + html-to-text "9.0.5" + iconv-lite "0.6.3" + libmime "5.3.5" + linkify-it "5.0.0" + mailsplit "5.4.0" + nodemailer "6.9.13" + punycode.js "2.3.1" + tlds "1.252.0" + +mailsplit@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/mailsplit/-/mailsplit-5.4.0.tgz#9f4692fadd9013e9ce632147d996931d2abac6ba" + integrity sha512-wnYxX5D5qymGIPYLwnp6h8n1+6P6vz/MJn5AzGjZ8pwICWssL+CCQjWBIToOVHASmATot4ktvlLo6CyLfOXWYA== + dependencies: + libbase64 "1.2.1" + libmime "5.2.0" + libqp "2.0.1" + +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + +make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +mammoth@^1.6.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/mammoth/-/mammoth-1.8.0.tgz#d8f1b0d3a0355fda129270346e9dc853f223028f" + integrity sha512-pJNfxSk9IEGVpau+tsZFz22ofjUsl2mnA5eT8PjPs2n0BP+rhVte4Nez6FdgEuxv3IGI3afiV46ImKqTGDVlbA== + dependencies: + "@xmldom/xmldom" "^0.8.6" + argparse "~1.0.3" + base64-js "^1.5.1" + bluebird "~3.4.0" + dingbat-to-unicode "^1.0.1" + jszip "^3.7.1" + lop "^0.4.1" + path-is-absolute "^1.0.0" + underscore "^1.13.1" + xmlbuilder "^10.0.0" + +mbox-parser@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mbox-parser/-/mbox-parser-1.0.1.tgz#cba6bb372a1d3c27aff9da356b4519aeee458e1f" + integrity sha512-9PNE026G/SodrYYxZiDZDJ5YQ0piN8QCqO662MupIGwgsvBtJHFPxYc/uUsQyhUysl4kMG0NLYvfELNkM5+HzQ== + dependencies: + "@types/mailparser" "^3.0.0" + humanize-duration "^3.25.1" + mailparser "^3.0.1" + +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.3: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mitt@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +ml-array-mean@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/ml-array-mean/-/ml-array-mean-1.1.6.tgz#d951a700dc8e3a17b3e0a583c2c64abd0c619c56" + integrity sha512-MIdf7Zc8HznwIisyiJGRH9tRigg3Yf4FldW8DxKxpCCv/g5CafTw0RRu51nojVEOXuCQC7DRVVu5c7XXO/5joQ== + dependencies: + ml-array-sum "^1.1.6" + +ml-array-sum@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/ml-array-sum/-/ml-array-sum-1.1.6.tgz#d1d89c20793cd29c37b09d40e85681aa4515a955" + integrity sha512-29mAh2GwH7ZmiRnup4UyibQZB9+ZLyMShvt4cH4eTK+cL2oEMIZFnSyB3SS8MlsTh6q/w/yh48KmqLxmovN4Dw== + dependencies: + is-any-array "^2.0.0" + +ml-distance-euclidean@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ml-distance-euclidean/-/ml-distance-euclidean-2.0.0.tgz#3a668d236649d1b8fec96380b9435c6f42c9a817" + integrity sha512-yC9/2o8QF0A3m/0IXqCTXCzz2pNEzvmcE/9HFKOZGnTjatvBbsn4lWYJkxENkA4Ug2fnYl7PXQxnPi21sgMy/Q== + +ml-distance@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/ml-distance/-/ml-distance-4.0.1.tgz#4741d17a1735888c5388823762271dfe604bd019" + integrity sha512-feZ5ziXs01zhyFUUUeZV5hwc0f5JW0Sh0ckU1koZe/wdVkJdGxcP06KNQuF0WBTj8FttQUzcvQcpcrOp/XrlEw== + dependencies: + ml-array-mean "^1.1.6" + ml-distance-euclidean "^2.0.0" + ml-tree-similarity "^1.0.0" + +ml-tree-similarity@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ml-tree-similarity/-/ml-tree-similarity-1.0.0.tgz#24705a107e32829e24d945e87219e892159c53f0" + integrity sha512-XJUyYqjSuUQkNQHMscr6tcjldsOoAekxADTplt40QKfwW6nd++1wHWV9AArl0Zvw/TIHgNaZZNvr8QGvE8wLRg== + dependencies: + binary-search "^1.3.5" + num-sort "^2.0.0" + +moment@^2.29.4: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.0.0, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mustache@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +netmask@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== + +node-abi@^3.3.0: + version "3.62.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.62.0.tgz#017958ed120f89a3a14a7253da810f5d724e3f36" + integrity sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g== + dependencies: + semver "^7.3.5" + +node-addon-api@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== + +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + +node-domexception@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-ensure@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/node-ensure/-/node-ensure-0.0.0.tgz#ecae764150de99861ec5c810fd5d096b183932a7" + integrity sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw== + +node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.6.9: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-html-parser@^6.1.13: + version "6.1.13" + resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-6.1.13.tgz#a1df799b83df5c6743fcd92740ba14682083b7e4" + integrity sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg== + dependencies: + css-select "^5.1.0" + he "1.2.0" + +node-xlsx@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/node-xlsx/-/node-xlsx-0.24.0.tgz#a6a365acb18ad37c66c2b254b6ebe0c22dc9dc6f" + integrity sha512-1olwK48XK9nXZsyH/FCltvGrQYvXXZuxVitxXXv2GIuRm51aBi1+5KwR4rWM4KeO61sFU+00913WLZTD+AcXEg== + dependencies: + xlsx "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" + +nodemailer@6.9.13: + version "6.9.13" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.13.tgz#5b292bf1e92645f4852ca872c56a6ba6c4a3d3d6" + integrity sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA== + +nodemon@^2.0.22: + version "2.0.22" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.22.tgz#182c45c3a78da486f673d6c1702e00728daf5258" + integrity sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ== + dependencies: + chokidar "^3.5.2" + debug "^3.2.7" + ignore-by-default "^1.0.1" + minimatch "^3.1.2" + pstree.remy "^1.1.8" + semver "^5.7.1" + simple-update-notifier "^1.0.7" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== + dependencies: + abbrev "1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +num-sort@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/num-sort/-/num-sort-2.1.0.tgz#1cbb37aed071329fdf41151258bc011898577a9b" + integrity sha512-1MQz1Ed8z2yckoBeSfkQHHO9K1yDRxxtotKSJ9yvcTUUxSvfvzEq5GwBrjjHEpMlq/k5gvXdmJ1SbYxWtpNoVg== + +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +officeparser@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/officeparser/-/officeparser-4.0.8.tgz#46def4a4664e8e2a36717bc5ae2bd99298eab565" + integrity sha512-dIVwWkKWwFy7qQO+8foRTTDJybs3CEJORDulCQPqirqhtiK2cETojmuekRtBcVebpcAKCXTg4dYs0mjmifwJKA== + dependencies: + "@xmldom/xmldom" "^0.8.10" + decompress "^4.2.0" + file-type "^16.5.4" + pdf-parse "^1.1.1" + rimraf "^2.6.3" + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + +onnx-proto@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/onnx-proto/-/onnx-proto-4.0.4.tgz#2431a25bee25148e915906dda0687aafe3b9e044" + integrity sha512-aldMOB3HRoo6q/phyB6QRQxSt895HNNw82BNyZ2CMh4bjeKv7g/c+VpAFtJuEMVfYLMbRx61hbuqnKceLeDcDA== + dependencies: + protobufjs "^6.8.8" + +onnxruntime-common@~1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/onnxruntime-common/-/onnxruntime-common-1.14.0.tgz#2bb5dac5261269779aa5fb6536ca379657de8bf6" + integrity sha512-3LJpegM2iMNRX2wUmtYfeX/ytfOzNwAWKSq1HbRrKc9+uqG/FsEA0bbKZl1btQeZaXhC26l44NWpNUeXPII7Ew== + +onnxruntime-node@1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/onnxruntime-node/-/onnxruntime-node-1.14.0.tgz#c4ae6c355cfae7d83abaf36dd39a905c4a010217" + integrity sha512-5ba7TWomIV/9b6NH/1x/8QEeowsb+jBEvFzU6z0T4mNsFwdPqXeFUM7uxC6QeSRkEbWu3qEB0VMjrvzN/0S9+w== + dependencies: + onnxruntime-common "~1.14.0" + +onnxruntime-web@1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/onnxruntime-web/-/onnxruntime-web-1.14.0.tgz#c8cee538781b1d4c1c6b043934f4a3e6ddf1466e" + integrity sha512-Kcqf43UMfW8mCydVGcX9OMXI2VN17c0p6XvR7IPSZzBf/6lteBzXHvcEVWDPmCKuGombl997HgLqj91F11DzXw== + dependencies: + flatbuffers "^1.12.0" + guid-typescript "^1.0.9" + long "^4.0.0" + onnx-proto "^4.0.4" + onnxruntime-common "~1.14.0" + platform "^1.3.6" + +openai@4.38.5: + version "4.38.5" + resolved "https://registry.yarnpkg.com/openai/-/openai-4.38.5.tgz#87de78eed9f7e63331fb6b1307d8c9dd986b39d0" + integrity sha512-Ym5GJL98ZhLJJ7enBx53jjG3vwN/fsB+Ozh46nnRZZS9W1NiYqbwkJ+sXd3dkCIiWIgcyyOPL2Zr8SQAzbpj3g== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + web-streams-polyfill "^3.2.1" + +openai@^4.32.1: + version "4.39.1" + resolved "https://registry.yarnpkg.com/openai/-/openai-4.39.1.tgz#619687717e42094927a1977362dd03261a4c4ff3" + integrity sha512-Qe3HXHrCMjwAHXWPywUwzI4PHf9r14uyiRRuMmiJjsfv1tREYMHtgIHXNlb54ZxtYJaIj19RHYMq6UQ2hgrIJQ== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + web-streams-polyfill "^3.2.1" + +openai@^4.49.1: + version "4.54.0" + resolved "https://registry.yarnpkg.com/openai/-/openai-4.54.0.tgz#eeb209c6892b997e524181b6ddb7e27bf4d09389" + integrity sha512-e/12BdtTtj+tXs7iHm+Dm7H7WjEWnw7O52B2wSfCQ6lD5F6cvjzo7cANXy5TJ1Q3/qc8YRPT5wBTTFtP5sBp1g== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + +openapi-types@^12.1.3: + version "12.1.3" + resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" + integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== + +opencollective-postinstall@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + +option@~0.2.1: + version "0.2.4" + resolved "https://registry.yarnpkg.com/option/-/option-0.2.4.tgz#fd475cdf98dcabb3cb397a3ba5284feb45edbfe4" + integrity sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + +p-queue@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== + dependencies: + eventemitter3 "^4.0.4" + p-timeout "^3.2.0" + +p-retry@4: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-timeout@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + +pac-proxy-agent@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz#6b9ddc002ec3ff0ba5fdf4a8a21d363bcc612d75" + integrity sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A== + dependencies: + "@tootallnate/quickjs-emscripten" "^0.23.0" + agent-base "^7.0.2" + debug "^4.3.4" + get-uri "^6.0.1" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" + pac-resolver "^7.0.0" + socks-proxy-agent "^8.0.2" + +pac-resolver@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6" + integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg== + dependencies: + degenerator "^5.0.0" + netmask "^2.0.2" + +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parseley@^0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/parseley/-/parseley-0.12.1.tgz#4afd561d50215ebe259e3e7a853e62f600683aef" + integrity sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw== + dependencies: + leac "^0.6.0" + peberminta "^0.9.0" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pdf-parse@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pdf-parse/-/pdf-parse-1.1.1.tgz#745e07408679548b3995ff896fd38e96e19d14a7" + integrity sha512-v6ZJ/efsBpGrGGknjtq9J/oC8tZWq0KWL5vQrk2GlzLEQPUDB1ex+13Rmidl1neNN358Jn9EHZw5y07FFtaC7A== + dependencies: + debug "^3.1.0" + node-ensure "^0.0.0" + +peberminta@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/peberminta/-/peberminta-0.9.0.tgz#8ec9bc0eb84b7d368126e71ce9033501dca2a352" + integrity sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ== + +peek-readable@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" + integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== + +platform@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" + integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== + +prebuild-install@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.2.tgz#a5fd9986f5a6251fbc47e1e5c65de71e68c0a056" + integrity sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + +prettier@^2.4.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +progress@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +protobufjs@^6.8.8: + version "6.11.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" + integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-agent@6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.1.tgz#40e7b230552cf44fd23ffaf7c59024b692612687" + integrity sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ== + dependencies: + agent-base "^7.0.2" + debug "^4.3.4" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" + lru-cache "^7.14.1" + pac-proxy-agent "^7.0.1" + proxy-from-env "^1.1.0" + socks-proxy-agent "^8.0.2" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode.js@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + +puppeteer-core@21.5.2: + version "21.5.2" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-21.5.2.tgz#6d3de4efb2ae65f1ee072043787b75594e88035f" + integrity sha512-v4T0cWnujSKs+iEfmb8ccd7u4/x8oblEyKqplqKnJ582Kw8PewYAWvkH4qUWhitN3O2q9RF7dzkvjyK5HbzjLA== + dependencies: + "@puppeteer/browsers" "1.8.0" + chromium-bidi "0.4.33" + cross-fetch "4.0.0" + debug "4.3.4" + devtools-protocol "0.0.1203626" + ws "8.14.2" + +puppeteer@~21.5.2: + version "21.5.2" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-21.5.2.tgz#0a4a72175c0fd0944d6486f4734807e1671d527b" + integrity sha512-BaAGJOq8Fl6/cck6obmwaNLksuY0Bg/lIahCLhJPGXBFUD2mCffypa4A592MaWnDcye7eaHmSK9yot0pxctY8A== + dependencies: + "@puppeteer/browsers" "1.8.0" + cosmiconfig "8.3.6" + puppeteer-core "21.5.2" + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-web-to-node-stream@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" + integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== + dependencies: + readable-stream "^3.6.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +regenerator-runtime@^0.13.3: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@>=0.6.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + +seek-bzip@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" + integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== + dependencies: + commander "^2.8.1" + +selderee@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/selderee/-/selderee-0.11.0.tgz#6af0c7983e073ad3e35787ffe20cefd9daf0ec8a" + integrity sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA== + dependencies: + parseley "^0.12.0" + +semver@^5.7.1: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.5, semver@^7.5.4: + version "7.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" + integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== + dependencies: + lru-cache "^6.0.0" + +semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +semver@~7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sharp@^0.32.0: + version "0.32.6" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.32.6.tgz#6ad30c0b7cd910df65d5f355f774aa4fce45732a" + integrity sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w== + dependencies: + color "^4.2.3" + detect-libc "^2.0.2" + node-addon-api "^6.1.0" + prebuild-install "^7.1.1" + semver "^7.5.4" + simple-get "^4.0.1" + tar-fs "^3.0.4" + tunnel-agent "^0.6.0" + +sharp@^0.33.5: + version "0.33.5" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e" + integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== + dependencies: + color "^4.2.3" + detect-libc "^2.0.3" + semver "^7.6.3" + optionalDependencies: + "@img/sharp-darwin-arm64" "0.33.5" + "@img/sharp-darwin-x64" "0.33.5" + "@img/sharp-libvips-darwin-arm64" "1.0.4" + "@img/sharp-libvips-darwin-x64" "1.0.4" + "@img/sharp-libvips-linux-arm" "1.0.5" + "@img/sharp-libvips-linux-arm64" "1.0.4" + "@img/sharp-libvips-linux-s390x" "1.0.4" + "@img/sharp-libvips-linux-x64" "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" + "@img/sharp-libvips-linuxmusl-x64" "1.0.4" + "@img/sharp-linux-arm" "0.33.5" + "@img/sharp-linux-arm64" "0.33.5" + "@img/sharp-linux-s390x" "0.33.5" + "@img/sharp-linux-x64" "0.33.5" + "@img/sharp-linuxmusl-arm64" "0.33.5" + "@img/sharp-linuxmusl-x64" "0.33.5" + "@img/sharp-wasm32" "0.33.5" + "@img/sharp-win32-ia32" "0.33.5" + "@img/sharp-win32-x64" "0.33.5" + +side-channel@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0, simple-get@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +simple-update-notifier@^1.0.7: + version "1.1.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82" + integrity sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg== + dependencies: + semver "~7.0.0" + +slugify@^1.6.6: + version "1.6.6" + resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.6.tgz#2d4ac0eacb47add6af9e04d3be79319cbcc7924b" + integrity sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw== + +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +socks-proxy-agent@^8.0.2: + version "8.0.3" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz#6b2da3d77364fde6292e810b496cb70440b9b89d" + integrity sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A== + dependencies: + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.7.1" + +socks@^2.7.1: + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== + dependencies: + ip-address "^9.0.5" + smart-buffer "^4.2.0" + +source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +streamx@^2.15.0, streamx@^2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.16.1.tgz#2b311bd34832f08aa6bb4d6a80297c9caef89614" + integrity sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ== + dependencies: + fast-fifo "^1.1.0" + queue-tick "^1.0.1" + optionalDependencies: + bare-events "^2.2.0" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-dirs@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" + integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== + dependencies: + is-natural-number "^4.0.1" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +strtok3@^6.2.4: + version "6.3.0" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.3.0.tgz#358b80ffe6d5d5620e19a073aa78ce947a90f9a0" + integrity sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw== + dependencies: + "@tokenizer/token" "^0.3.0" + peek-readable "^4.1.0" + +supports-color@^5.3.0, supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +tar-fs@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.4.tgz#a21dc60a2d5d9f55e0089ccd78124f1d3771dbbf" + integrity sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w== + dependencies: + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^3.1.5" + +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-fs@^3.0.4: + version "3.0.6" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.6.tgz#eaccd3a67d5672f09ca8e8f9c3d2b89fa173f217" + integrity sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w== + dependencies: + pump "^3.0.0" + tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^2.1.1" + bare-path "^2.1.0" + +tar-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== + dependencies: + bl "^1.0.0" + buffer-alloc "^1.2.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.3.0" + to-buffer "^1.1.1" + xtend "^4.0.0" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + +tar@^6.1.11: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +tesseract.js-core@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz#6f25da94f70f8e8f02aff47a43be61d49e6f67c3" + integrity sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA== + +tesseract.js@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/tesseract.js/-/tesseract.js-6.0.0.tgz#62ff7fffc8833b5810430a4067785e49d5ca8e7f" + integrity sha512-tqYCod1HwJzkeZw1l6XWx+ly2hhisGcBtak9MArhYwDAxL0NgeVhLJcUjqPxZMQtpgtVUzWcpZPryi+hnaQGVw== + dependencies: + bmp-js "^0.1.0" + idb-keyval "^6.2.0" + is-url "^1.2.4" + node-fetch "^2.6.9" + opencollective-postinstall "^2.0.3" + regenerator-runtime "^0.13.3" + tesseract.js-core "^6.0.0" + wasm-feature-detect "^1.2.11" + zlibjs "^0.3.1" + +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + +through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tlds@1.252.0: + version "1.252.0" + resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.252.0.tgz#71d9617f4ef4cc7347843bee72428e71b8b0f419" + integrity sha512-GA16+8HXvqtfEnw/DTcwB0UU354QE1n3+wh08oFjr6Znl7ZLAeUgYzCcK+/CCrOyE0vnHR8/pu3XXG3vDijXpQ== + +to-buffer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +token-types@^4.1.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/token-types/-/token-types-4.2.1.tgz#0f897f03665846982806e138977dbe72d44df753" + integrity sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ== + dependencies: + "@tokenizer/token" "^0.3.0" + ieee754 "^1.2.1" + +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +triple-beam@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== + +ts-type@>=2: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ts-type/-/ts-type-3.0.1.tgz#b52e7623065e0beb43c77c426347d85cf81dff84" + integrity sha512-cleRydCkBGBFQ4KAvLH0ARIkciduS745prkGVVxPGvcRGhMMoSJUB7gNR1ByKhFTEYrYRg2CsMRGYnqp+6op+g== + dependencies: + "@types/node" "*" + tslib ">=2" + typedarray-dts "^1.0.0" + +tslib@>=2, tslib@^2.0.1, tslib@^2.5.0, tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tslib@^2.4.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +type-detect@^4.0.0: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray-dts@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typedarray-dts/-/typedarray-dts-1.0.0.tgz#9dec9811386dbfba964c295c2606cf9a6b982d06" + integrity sha512-Ka0DBegjuV9IPYFT1h0Qqk5U4pccebNIJCGl8C5uU7xtOs+jpJvKGAY4fHGK25hTmXZOEUl9Cnsg5cS6K/b5DA== + +uc.micro@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + +unbzip2-stream@1.4.3, unbzip2-stream@^1.0.9: + version "1.4.3" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" + integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + +underscore@^1.13.1: + version "1.13.6" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" + integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +undici@^5.19.1: + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +url-pattern@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/url-pattern/-/url-pattern-1.0.3.tgz#0409292471b24f23c50d65a47931793d2b5acfc1" + integrity sha512-uQcEj/2puA4aq1R3A2+VNVBgaWYR24FdWjl7VNW83rnWftlhyzOZ/tBjezRiC2UkIzuxC8Top3IekN3vUf1WxA== + +urlpattern-polyfill@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-9.0.0.tgz#bc7e386bb12fd7898b58d1509df21d3c29ab3460" + integrity sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +wasm-feature-detect@^1.2.11: + version "1.8.0" + resolved "https://registry.yarnpkg.com/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz#4e9f55b0a64d801f372fbb0324ed11ad3abd0c78" + integrity sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ== + +wavefile@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/wavefile/-/wavefile-11.0.0.tgz#9302165874327ff63a704d00b154c753eaa1b8e7" + integrity sha512-/OBiAALgWU24IG7sC84cDO/KfFuvajWc5Uec0oV2zrpOOZZDgGdOwHwgEzOrwh8jkubBk7PtZfQBIcI1OaE5Ng== + +web-streams-polyfill@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== + +web-streams-polyfill@^3.2.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which@^1.1.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +winston-transport@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.7.0.tgz#e302e6889e6ccb7f383b926df6936a5b781bd1f0" + integrity sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.13.0.tgz#e76c0d722f78e04838158c61adc1287201de7ce3" + integrity sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.7.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@8.14.2: + version "8.14.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + +"xlsx@https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz": + version "0.20.2" + resolved "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz#0f64eeed3f1a46e64724620c3553f2dbd3cd2d7d" + +xml2js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@^10.0.0: + version "10.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-10.1.1.tgz#8cae6688cc9b38d850b7c8d3c0a4161dcaf475b0" + integrity sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg== + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^2.2.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.2.tgz#7a2b30f2243a5fc299e1f14ca58d475ed4bc5362" + integrity sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yauzl@^2.10.0, yauzl@^2.4.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +youtubei.js@^9.1.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-9.4.0.tgz#ccccaf4a295b96e3e17134a66730bbc82461594b" + integrity sha512-8plCOZD2WabqWSEgZU3RjzigIIeR7sF028EERJENYrC9xO/6awpLMZfeoE1gNrNEbKcA+bzbMvonqlvBdxGdKg== + dependencies: + jintr "^1.1.0" + tslib "^2.5.0" + undici "^5.19.1" + +zlibjs@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/zlibjs/-/zlibjs-0.3.1.tgz#50197edb28a1c42ca659cc8b4e6a9ddd6d444554" + integrity sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w== + +zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.22.5: + version "3.23.0" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.23.0.tgz#4fc60e88d3c709eedbfaae3f92f8a7bf786469f2" + integrity sha512-az0uJ243PxsRIa2x1WmNE/pnuA05gUq/JB8Lwe1EDCCL/Fz9MgjYQ0fPlyc2Tcv6aF2ZA7WM5TWaRZVEFaAIag== + +zod@^3.22.3, zod@^3.22.4: + version "3.23.5" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.5.tgz#c7b7617d017d4a2f21852f533258d26a9a5ae09f" + integrity sha512-fkwiq0VIQTksNNA131rDOsVJcns0pfVUjHzLrNBiF/O/Xxb5lQyEXkhZWcJ7npWsYlvs+h0jFWXXy4X46Em1JA== diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 0000000..5f1b0e4 --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,318 @@ +SERVER_PORT=3001 +STORAGE_DIR="/app/server/storage" +UID='1000' +GID='1000' +# SIG_KEY='passphrase' # Please generate random string at least 32 chars long. +# SIG_SALT='salt' # Please generate random string at least 32 chars long. +# JWT_SECRET="my-random-string-for-seeding" # Only needed if AUTH_TOKEN is set. Please generate random string at least 12 chars long. + +########################################### +######## LLM API SElECTION ################ +########################################### +# LLM_PROVIDER='openai' +# OPEN_AI_KEY= +# OPEN_MODEL_PREF='gpt-4o' + +# LLM_PROVIDER='gemini' +# GEMINI_API_KEY= +# GEMINI_LLM_MODEL_PREF='gemini-pro' + +# LLM_PROVIDER='azure' +# AZURE_OPENAI_ENDPOINT= +# AZURE_OPENAI_KEY= +# OPEN_MODEL_PREF='my-gpt35-deployment' # This is the "deployment" on Azure you want to use. Not the base model. +# EMBEDDING_MODEL_PREF='embedder-model' # This is the "deployment" on Azure you want to use for embeddings. Not the base model. Valid base model is text-embedding-ada-002 + +# LLM_PROVIDER='anthropic' +# ANTHROPIC_API_KEY=sk-ant-xxxx +# ANTHROPIC_MODEL_PREF='claude-2' + +# LLM_PROVIDER='lmstudio' +# LMSTUDIO_BASE_PATH='http://your-server:1234/v1' +# LMSTUDIO_MODEL_PREF='Loaded from Chat UI' # this is a bug in LMStudio 0.2.17 +# LMSTUDIO_MODEL_TOKEN_LIMIT=4096 + +# LLM_PROVIDER='localai' +# LOCAL_AI_BASE_PATH='http://host.docker.internal:8080/v1' +# LOCAL_AI_MODEL_PREF='luna-ai-llama2' +# LOCAL_AI_MODEL_TOKEN_LIMIT=4096 +# LOCAL_AI_API_KEY="sk-123abc" + +# LLM_PROVIDER='ollama' +# OLLAMA_BASE_PATH='http://host.docker.internal:11434' +# OLLAMA_MODEL_PREF='llama2' +# OLLAMA_MODEL_TOKEN_LIMIT=4096 + +# LLM_PROVIDER='togetherai' +# TOGETHER_AI_API_KEY='my-together-ai-key' +# TOGETHER_AI_MODEL_PREF='mistralai/Mixtral-8x7B-Instruct-v0.1' + +# LLM_PROVIDER='mistral' +# MISTRAL_API_KEY='example-mistral-ai-api-key' +# MISTRAL_MODEL_PREF='mistral-tiny' + +# LLM_PROVIDER='perplexity' +# PERPLEXITY_API_KEY='my-perplexity-key' +# PERPLEXITY_MODEL_PREF='codellama-34b-instruct' + +# LLM_PROVIDER='openrouter' +# OPENROUTER_API_KEY='my-openrouter-key' +# OPENROUTER_MODEL_PREF='openrouter/auto' + +# LLM_PROVIDER='huggingface' +# HUGGING_FACE_LLM_ENDPOINT=https://uuid-here.us-east-1.aws.endpoints.huggingface.cloud +# HUGGING_FACE_LLM_API_KEY=hf_xxxxxx +# HUGGING_FACE_LLM_TOKEN_LIMIT=8000 + +# LLM_PROVIDER='groq' +# GROQ_API_KEY=gsk_abcxyz +# GROQ_MODEL_PREF=llama3-8b-8192 + +# LLM_PROVIDER='koboldcpp' +# KOBOLD_CPP_BASE_PATH='http://127.0.0.1:5000/v1' +# KOBOLD_CPP_MODEL_PREF='koboldcpp/codellama-7b-instruct.Q4_K_S' +# KOBOLD_CPP_MODEL_TOKEN_LIMIT=4096 + +# LLM_PROVIDER='textgenwebui' +# TEXT_GEN_WEB_UI_BASE_PATH='http://127.0.0.1:5000/v1' +# TEXT_GEN_WEB_UI_TOKEN_LIMIT=4096 +# TEXT_GEN_WEB_UI_API_KEY='sk-123abc' + +# LLM_PROVIDER='generic-openai' +# GENERIC_OPEN_AI_BASE_PATH='http://proxy.url.openai.com/v1' +# GENERIC_OPEN_AI_MODEL_PREF='gpt-3.5-turbo' +# GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT=4096 +# GENERIC_OPEN_AI_API_KEY=sk-123abc + +# LLM_PROVIDER='litellm' +# LITE_LLM_MODEL_PREF='gpt-3.5-turbo' +# LITE_LLM_MODEL_TOKEN_LIMIT=4096 +# LITE_LLM_BASE_PATH='http://127.0.0.1:4000' +# LITE_LLM_API_KEY='sk-123abc' + +# LLM_PROVIDER='novita' +# NOVITA_LLM_API_KEY='your-novita-api-key-here' check on https://novita.ai/settings/key-management +# NOVITA_LLM_MODEL_PREF='deepseek/deepseek-r1' + +# LLM_PROVIDER='cohere' +# COHERE_API_KEY= +# COHERE_MODEL_PREF='command-r' + +# LLM_PROVIDER='bedrock' +# AWS_BEDROCK_LLM_ACCESS_KEY_ID= +# AWS_BEDROCK_LLM_ACCESS_KEY= +# AWS_BEDROCK_LLM_REGION=us-west-2 +# AWS_BEDROCK_LLM_MODEL_PREFERENCE=meta.llama3-1-8b-instruct-v1:0 +# AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT=8191 + +# LLM_PROVIDER='fireworksai' +# FIREWORKS_AI_LLM_API_KEY='my-fireworks-ai-key' +# FIREWORKS_AI_LLM_MODEL_PREF='accounts/fireworks/models/llama-v3p1-8b-instruct' + +# LLM_PROVIDER='apipie' +# APIPIE_LLM_API_KEY='sk-123abc' +# APIPIE_LLM_MODEL_PREF='openrouter/llama-3.1-8b-instruct' + +# LLM_PROVIDER='xai' +# XAI_LLM_API_KEY='xai-your-api-key-here' +# XAI_LLM_MODEL_PREF='grok-beta' + +# LLM_PROVIDER='nvidia-nim' +# NVIDIA_NIM_LLM_BASE_PATH='http://127.0.0.1:8000' +# NVIDIA_NIM_LLM_MODEL_PREF='meta/llama-3.2-3b-instruct' + +# LLM_PROVIDER='deepseek' +# DEEPSEEK_API_KEY='your-deepseek-api-key-here' +# DEEPSEEK_MODEL_PREF='deepseek-chat' + +########################################### +######## Embedding API SElECTION ########## +########################################### +# Only used if you are using an LLM that does not natively support embedding (openai or Azure) +# EMBEDDING_ENGINE='openai' +# OPEN_AI_KEY=sk-xxxx +# EMBEDDING_MODEL_PREF='text-embedding-ada-002' + +# EMBEDDING_ENGINE='azure' +# AZURE_OPENAI_ENDPOINT= +# AZURE_OPENAI_KEY= +# EMBEDDING_MODEL_PREF='my-embedder-model' # This is the "deployment" on Azure you want to use for embeddings. Not the base model. Valid base model is text-embedding-ada-002 + +# EMBEDDING_ENGINE='localai' +# EMBEDDING_BASE_PATH='http://localhost:8080/v1' +# EMBEDDING_MODEL_PREF='text-embedding-ada-002' +# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=1000 # The max chunk size in chars a string to embed can be + +# EMBEDDING_ENGINE='ollama' +# EMBEDDING_BASE_PATH='http://host.docker.internal:11434' +# EMBEDDING_MODEL_PREF='nomic-embed-text:latest' +# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192 + +# EMBEDDING_ENGINE='lmstudio' +# EMBEDDING_BASE_PATH='https://host.docker.internal:1234/v1' +# EMBEDDING_MODEL_PREF='nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.Q4_0.gguf' +# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192 + +# EMBEDDING_ENGINE='cohere' +# COHERE_API_KEY= +# EMBEDDING_MODEL_PREF='embed-english-v3.0' + +# EMBEDDING_ENGINE='voyageai' +# VOYAGEAI_API_KEY= +# EMBEDDING_MODEL_PREF='voyage-large-2-instruct' + +# EMBEDDING_ENGINE='litellm' +# EMBEDDING_MODEL_PREF='text-embedding-ada-002' +# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192 +# LITE_LLM_BASE_PATH='http://127.0.0.1:4000' +# LITE_LLM_API_KEY='sk-123abc' + +# EMBEDDING_ENGINE='generic-openai' +# EMBEDDING_MODEL_PREF='text-embedding-ada-002' +# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192 +# EMBEDDING_BASE_PATH='http://127.0.0.1:4000' +# GENERIC_OPEN_AI_EMBEDDING_API_KEY='sk-123abc' +# GENERIC_OPEN_AI_EMBEDDING_MAX_CONCURRENT_CHUNKS=500 + +# EMBEDDING_ENGINE='gemini' +# GEMINI_EMBEDDING_API_KEY= +# EMBEDDING_MODEL_PREF='text-embedding-004' + +########################################### +######## Vector Database Selection ######## +########################################### +# Enable all below if you are using vector database: Chroma. +# VECTOR_DB="chroma" +# CHROMA_ENDPOINT='http://host.docker.internal:8000' +# CHROMA_API_HEADER="X-Api-Key" +# CHROMA_API_KEY="sk-123abc" + +# Enable all below if you are using vector database: Pinecone. +# VECTOR_DB="pinecone" +# PINECONE_API_KEY= +# PINECONE_INDEX= + +# Enable all below if you are using vector database: LanceDB. +# VECTOR_DB="lancedb" + +# Enable all below if you are using vector database: Weaviate. +# VECTOR_DB="weaviate" +# WEAVIATE_ENDPOINT="http://localhost:8080" +# WEAVIATE_API_KEY= + +# Enable all below if you are using vector database: Qdrant. +# VECTOR_DB="qdrant" +# QDRANT_ENDPOINT="http://localhost:6333" +# QDRANT_API_KEY= + +# Enable all below if you are using vector database: Milvus. +# VECTOR_DB="milvus" +# MILVUS_ADDRESS="http://localhost:19530" +# MILVUS_USERNAME= +# MILVUS_PASSWORD= + +# Enable all below if you are using vector database: Zilliz Cloud. +# VECTOR_DB="zilliz" +# ZILLIZ_ENDPOINT="https://sample.api.gcp-us-west1.zillizcloud.com" +# ZILLIZ_API_TOKEN=api-token-here + +# Enable all below if you are using vector database: Astra DB. +# VECTOR_DB="astra" +# ASTRA_DB_APPLICATION_TOKEN= +# ASTRA_DB_ENDPOINT= + +########################################### +######## Audio Model Selection ############ +########################################### +# (default) use built-in whisper-small model. +# WHISPER_PROVIDER="local" + +# use openai hosted whisper model. +# WHISPER_PROVIDER="openai" +# OPEN_AI_KEY=sk-xxxxxxxx + +########################################### +######## TTS/STT Model Selection ########## +########################################### +# TTS_PROVIDER="native" + +# TTS_PROVIDER="openai" +# TTS_OPEN_AI_KEY=sk-example +# TTS_OPEN_AI_VOICE_MODEL=nova + +# TTS_PROVIDER="generic-openai" +# TTS_OPEN_AI_COMPATIBLE_KEY=sk-example +# TTS_OPEN_AI_COMPATIBLE_VOICE_MODEL=nova +# TTS_OPEN_AI_COMPATIBLE_ENDPOINT="https://api.openai.com/v1" + +# TTS_PROVIDER="elevenlabs" +# TTS_ELEVEN_LABS_KEY= +# TTS_ELEVEN_LABS_VOICE_MODEL=21m00Tcm4TlvDq8ikWAM # Rachel + +# CLOUD DEPLOYMENT VARIRABLES ONLY +# AUTH_TOKEN="hunter2" # This is the password to your application if remote hosting. +# DISABLE_TELEMETRY="false" + +########################################### +######## PASSWORD COMPLEXITY ############## +########################################### +# Enforce a password schema for your organization users. +# Documentation on how to use https://github.com/kamronbatman/joi-password-complexity +# Default is only 8 char minimum +# PASSWORDMINCHAR=8 +# PASSWORDMAXCHAR=250 +# PASSWORDLOWERCASE=1 +# PASSWORDUPPERCASE=1 +# PASSWORDNUMERIC=1 +# PASSWORDSYMBOL=1 +# PASSWORDREQUIREMENTS=4 + +########################################### +######## ENABLE HTTPS SERVER ############## +########################################### +# By enabling this and providing the path/filename for the key and cert, +# the server will use HTTPS instead of HTTP. +#ENABLE_HTTPS="true" +#HTTPS_CERT_PATH="sslcert/cert.pem" +#HTTPS_KEY_PATH="sslcert/key.pem" + +########################################### +######## AGENT SERVICE KEYS ############### +########################################### + +#------ SEARCH ENGINES ------- +#============================= +#------ Google Search -------- https://programmablesearchengine.google.com/controlpanel/create +# AGENT_GSE_KEY= +# AGENT_GSE_CTX= + +#------ SearchApi.io ----------- https://www.searchapi.io/ +# AGENT_SEARCHAPI_API_KEY= +# AGENT_SEARCHAPI_ENGINE=google + +#------ Serper.dev ----------- https://serper.dev/ +# AGENT_SERPER_DEV_KEY= + +#------ Bing Search ----------- https://portal.azure.com/ +# AGENT_BING_SEARCH_API_KEY= + +#------ Serply.io ----------- https://serply.io/ +# AGENT_SERPLY_API_KEY= + +#------ SearXNG ----------- https://github.com/searxng/searxng +# AGENT_SEARXNG_API_URL= + +#------ Tavily ----------- https://www.tavily.com/ +# AGENT_TAVILY_API_KEY= + +########################################### +######## Other Configurations ############ +########################################### + +# Disable viewing chat history from the UI and frontend APIs. +# See https://docs.anythingllm.com/configuration#disable-view-chat-history for more information. +# DISABLE_VIEW_CHAT_HISTORY=1 + +# Enable simple SSO passthrough to pre-authenticate users from a third party service. +# See https://docs.anythingllm.com/configuration#simple-sso-passthrough for more information. +# SIMPLE_SSO_ENABLED=1 diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..70a33e9 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,173 @@ +# Setup base image +FROM ubuntu:jammy-20240627.1 AS base + +# Build arguments +ARG ARG_UID=1000 +ARG ARG_GID=1000 + +FROM base AS build-arm64 +RUN echo "Preparing build of AnythingLLM image for arm64 architecture" + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Install system dependencies +# hadolint ignore=DL3008,DL3013 +RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ + unzip curl gnupg libgfortran5 libgbm1 tzdata netcat \ + libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 \ + libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libx11-6 libx11-xcb1 libxcb1 \ + libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 \ + libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release \ + xdg-utils git build-essential ffmpeg && \ + mkdir -p /etc/apt/keyrings && \ + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ + apt-get update && \ + apt-get install -yq --no-install-recommends nodejs && \ + curl -LO https://github.com/yarnpkg/yarn/releases/download/v1.22.19/yarn_1.22.19_all.deb \ + && dpkg -i yarn_1.22.19_all.deb \ + && rm yarn_1.22.19_all.deb && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Create a group and user with specific UID and GID +RUN groupadd -g "$ARG_GID" anythingllm && \ + useradd -l -u "$ARG_UID" -m -d /app -s /bin/bash -g anythingllm anythingllm && \ + mkdir -p /app/frontend/ /app/server/ /app/collector/ && chown -R anythingllm:anythingllm /app + +# Copy docker helper scripts +COPY ./docker/docker-entrypoint.sh /usr/local/bin/ +COPY ./docker/docker-healthcheck.sh /usr/local/bin/ +COPY --chown=anythingllm:anythingllm ./docker/.env.example /app/server/.env + +# Ensure the scripts are executable +RUN chmod +x /usr/local/bin/docker-entrypoint.sh && \ + chmod +x /usr/local/bin/docker-healthcheck.sh + +USER anythingllm +WORKDIR /app + +# Puppeteer does not ship with an ARM86 compatible build for Chromium +# so web-scraping would be broken in arm docker containers unless we patch it +# by manually installing a compatible chromedriver. +RUN echo "Need to patch Puppeteer x Chromium support for ARM86 - installing dep!" && \ + curl https://playwright.azureedge.net/builds/chromium/1088/chromium-linux-arm64.zip -o chrome-linux.zip && \ + unzip chrome-linux.zip && \ + rm -rf chrome-linux.zip + +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true +ENV CHROME_PATH=/app/chrome-linux/chrome +ENV PUPPETEER_EXECUTABLE_PATH=/app/chrome-linux/chrome + +RUN echo "Done running arm64 specific installation steps" + +############################################# + +# amd64-specific stage +FROM base AS build-amd64 +RUN echo "Preparing build of AnythingLLM image for non-ARM architecture" + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Install system dependencies +# hadolint ignore=DL3008,DL3013 +RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ + curl gnupg libgfortran5 libgbm1 tzdata netcat \ + libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 \ + libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libx11-6 libx11-xcb1 libxcb1 \ + libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 \ + libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release \ + xdg-utils git build-essential ffmpeg && \ + mkdir -p /etc/apt/keyrings && \ + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ + apt-get update && \ + apt-get install -yq --no-install-recommends nodejs && \ + curl -LO https://github.com/yarnpkg/yarn/releases/download/v1.22.19/yarn_1.22.19_all.deb \ + && dpkg -i yarn_1.22.19_all.deb \ + && rm yarn_1.22.19_all.deb && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Create a group and user with specific UID and GID +RUN groupadd -g "$ARG_GID" anythingllm && \ + useradd -l -u "$ARG_UID" -m -d /app -s /bin/bash -g anythingllm anythingllm && \ + mkdir -p /app/frontend/ /app/server/ /app/collector/ && chown -R anythingllm:anythingllm /app + +# Copy docker helper scripts +COPY ./docker/docker-entrypoint.sh /usr/local/bin/ +COPY ./docker/docker-healthcheck.sh /usr/local/bin/ +COPY --chown=anythingllm:anythingllm ./docker/.env.example /app/server/.env + +# Ensure the scripts are executable +RUN chmod +x /usr/local/bin/docker-entrypoint.sh && \ + chmod +x /usr/local/bin/docker-healthcheck.sh + +############################################# +# COMMON BUILD FLOW FOR ALL ARCHS +############################################# + +# hadolint ignore=DL3006 +FROM build-${TARGETARCH} AS build +RUN echo "Running common build flow of AnythingLLM image for all architectures" + +USER anythingllm +WORKDIR /app + +# Install & Build frontend layer +FROM build AS frontend-build +COPY --chown=anythingllm:anythingllm ./frontend /app/frontend/ +WORKDIR /app/frontend +RUN yarn install --network-timeout 100000 && yarn cache clean +RUN yarn build && \ + cp -r dist /tmp/frontend-build && \ + rm -rf * && \ + cp -r /tmp/frontend-build dist && \ + rm -rf /tmp/frontend-build +WORKDIR /app + +# Install server layer +# Also pull and build collector deps (chromium issues prevent bad bindings) +FROM build AS backend-build +COPY ./server /app/server/ +WORKDIR /app/server +RUN yarn install --production --network-timeout 100000 && yarn cache clean +WORKDIR /app + +# Install collector dependencies +COPY ./collector/ ./collector/ +WORKDIR /app/collector +ENV PUPPETEER_DOWNLOAD_BASE_URL=https://storage.googleapis.com/chrome-for-testing-public +RUN yarn install --production --network-timeout 100000 && yarn cache clean + +WORKDIR /app +USER anythingllm + +# Since we are building from backend-build we just need to move built frontend into server/public +FROM backend-build AS production-build +WORKDIR /app +COPY --chown=anythingllm:anythingllm --from=frontend-build /app/frontend/dist /app/server/public +USER root +RUN chown -R anythingllm:anythingllm /app/server && \ + chown -R anythingllm:anythingllm /app/collector +USER anythingllm + +# No longer needed? (deprecated) +# WORKDIR /app/server +# RUN npx prisma generate --schema=./prisma/schema.prisma && \ +# npx prisma migrate deploy --schema=./prisma/schema.prisma +# WORKDIR /app + +# Setup the environment +ENV NODE_ENV=production +ENV ANYTHING_LLM_RUNTIME=docker + +# Setup the healthcheck +HEALTHCHECK --interval=1m --timeout=10s --start-period=1m \ + CMD /bin/bash /usr/local/bin/docker-healthcheck.sh || exit 1 + +# Run the server +# CMD ["sh", "-c", "tail -f /dev/null"] # For development: keep container open +ENTRYPOINT ["/bin/bash", "/usr/local/bin/docker-entrypoint.sh"] diff --git a/docker/HOW_TO_USE_DOCKER.md b/docker/HOW_TO_USE_DOCKER.md new file mode 100644 index 0000000..2eeaee0 --- /dev/null +++ b/docker/HOW_TO_USE_DOCKER.md @@ -0,0 +1,209 @@ +# How to use Dockerized Anything LLM + +Use the Dockerized version of AnythingLLM for a much faster and complete startup of AnythingLLM. + +### Minimum Requirements + +> [!TIP] +> Running AnythingLLM on AWS/GCP/Azure? +> You should aim for at least 2GB of RAM. Disk storage is proportional to however much data +> you will be storing (documents, vectors, models, etc). Minimum 10GB recommended. + +- `docker` installed on your machine +- `yarn` and `node` on your machine +- access to an LLM running locally or remotely + +\*AnythingLLM by default uses a built-in vector database powered by [LanceDB](https://github.com/lancedb/lancedb) + +\*AnythingLLM by default embeds text on instance privately [Learn More](../server/storage/models/README.md) + +## Recommend way to run dockerized AnythingLLM! + +> [!IMPORTANT] +> If you are running another service on localhost like Chroma, LocalAi, or LMStudio +> you will need to use http://host.docker.internal:xxxx to access the service from within +> the docker container using AnythingLLM as `localhost:xxxx` will not resolve for the host system. +> +> **Requires** Docker v18.03+ on Win/Mac and 20.10+ on Linux/Ubuntu for host.docker.internal to resolve! +> +> _Linux_: add `--add-host=host.docker.internal:host-gateway` to docker run command for this to resolve. +> +> eg: Chroma host URL running on localhost:8000 on host machine needs to be http://host.docker.internal:8000 +> when used in AnythingLLM. + +> [!TIP] +> It is best to mount the containers storage volume to a folder on your host machine +> so that you can pull in future updates without deleting your existing data! + +Pull in the latest image from docker. Supports both `amd64` and `arm64` CPU architectures. + +```shell +docker pull mintplexlabs/anythingllm +``` + + + + + + + + + + + + + + + + + +
Mount the storage locally and run AnythingLLM in Docker
+ Linux/MacOs + + +```shell +export STORAGE_LOCATION=$HOME/anythingllm && \ +mkdir -p $STORAGE_LOCATION && \ +touch "$STORAGE_LOCATION/.env" && \ +docker run -d -p 3001:3001 \ +--cap-add SYS_ADMIN \ +-v ${STORAGE_LOCATION}:/app/server/storage \ +-v ${STORAGE_LOCATION}/.env:/app/server/.env \ +-e STORAGE_DIR="/app/server/storage" \ +mintplexlabs/anythingllm +``` + +
+ Windows + + +```powershell +# Run this in powershell terminal +$env:STORAGE_LOCATION="$HOME\Documents\anythingllm"; ` +If(!(Test-Path $env:STORAGE_LOCATION)) {New-Item $env:STORAGE_LOCATION -ItemType Directory}; ` +If(!(Test-Path "$env:STORAGE_LOCATION\.env")) {New-Item "$env:STORAGE_LOCATION\.env" -ItemType File}; ` +docker run -d -p 3001:3001 ` +--cap-add SYS_ADMIN ` +-v "$env:STORAGE_LOCATION`:/app/server/storage" ` +-v "$env:STORAGE_LOCATION\.env:/app/server/.env" ` +-e STORAGE_DIR="/app/server/storage" ` +mintplexlabs/anythingllm; +``` + +
Docker Compose + + +```yaml +version: '3.8' +services: + anythingllm: + image: mintplexlabs/anythingllm + container_name: anythingllm + ports: + - "3001:3001" + cap_add: + - SYS_ADMIN + environment: + # Adjust for your environment + - STORAGE_DIR=/app/server/storage + - JWT_SECRET="make this a large list of random numbers and letters 20+" + - LLM_PROVIDER=ollama + - OLLAMA_BASE_PATH=http://127.0.0.1:11434 + - OLLAMA_MODEL_PREF=llama2 + - OLLAMA_MODEL_TOKEN_LIMIT=4096 + - EMBEDDING_ENGINE=ollama + - EMBEDDING_BASE_PATH=http://127.0.0.1:11434 + - EMBEDDING_MODEL_PREF=nomic-embed-text:latest + - EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192 + - VECTOR_DB=lancedb + - WHISPER_PROVIDER=local + - TTS_PROVIDER=native + - PASSWORDMINCHAR=8 + # Add any other keys here for services or settings + # you can find in the docker/.env.example file + volumes: + - anythingllm_storage:/app/server/storage + restart: always + +volumes: + anythingllm_storage: + driver: local + driver_opts: + type: none + o: bind + device: /path/on/local/disk +``` + +
+ +Go to `http://localhost:3001` and you are now using AnythingLLM! All your data and progress will persist between +container rebuilds or pulls from Docker Hub. + +## How to use the user interface + +- To access the full application, visit `http://localhost:3001` in your browser. + +## About UID and GID in the ENV + +- The UID and GID are set to 1000 by default. This is the default user in the Docker container and on most host operating systems. If there is a mismatch between your host user UID and GID and what is set in the `.env` file, you may experience permission issues. + +## Build locally from source _not recommended for casual use_ + +- `git clone` this repo and `cd anything-llm` to get to the root directory. +- `touch server/storage/anythingllm.db` to create empty SQLite DB file. +- `cd docker/` +- `cp .env.example .env` **you must do this before building** +- `docker-compose up -d --build` to build the image - this will take a few moments. + +Your docker host will show the image as online once the build process is completed. This will build the app to `http://localhost:3001`. + +## Integrations and one-click setups + +The integrations below are templates or tooling built by the community to make running the docker experience of AnythingLLM easier. + +### Use the Midori AI Subsystem to Manage AnythingLLM + +Follow the setup found on [Midori AI Subsystem Site](https://io.midori-ai.xyz/subsystem/manager/) for your host OS +After setting that up install the AnythingLLM docker backend to the Midori AI Subsystem. + +Once that is done, you are all set! + +## Common questions and fixes + +### Cannot connect to service running on localhost! + +If you are in docker and cannot connect to a service running on your host machine running on a local interface or loopback: + +- `localhost` +- `127.0.0.1` +- `0.0.0.0` + +> [!IMPORTANT] +> On linux `http://host.docker.internal:xxxx` does not work. +> Use `http://172.17.0.1:xxxx` instead to emulate this functionality. + +Then in docker you need to replace that localhost part with `host.docker.internal`. For example, if running Ollama on the host machine, bound to http://127.0.0.1:11434 you should put `http://host.docker.internal:11434` into the connection URL in AnythingLLM. + + +### API is not working, cannot login, LLM is "offline"? + +You are likely running the docker container on a remote machine like EC2 or some other instance where the reachable URL +is not `http://localhost:3001` and instead is something like `http://193.xx.xx.xx:3001` - in this case all you need to do is add the following to your `frontend/.env.production` before running `docker-compose up -d --build` + +``` +# frontend/.env.production +GENERATE_SOURCEMAP=false +VITE_API_BASE="http://:3001/api" +``` + +For example, if the docker instance is available on `192.186.1.222` your `VITE_API_BASE` would look like `VITE_API_BASE="http://192.186.1.222:3001/api"` in `frontend/.env.production`. + +### Having issues with Ollama? + +If you are getting errors like `llama:streaming - could not stream chat. Error: connect ECONNREFUSED 172.17.0.1:11434` then visit the README below. + +[Fix common issues with Ollama](../server/utils/AiProviders/ollama/README.md) + +### Still not working? + +[Ask for help on Discord](https://discord.gg/6UyHPeGZAC) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..b5782ee --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,31 @@ +name: anythingllm + +networks: + anything-llm: + driver: bridge + +services: + anything-llm: + container_name: anythingllm + build: + context: ../. + dockerfile: ./docker/Dockerfile + args: + ARG_UID: ${UID:-1000} + ARG_GID: ${GID:-1000} + cap_add: + - SYS_ADMIN + volumes: + - "./.env:/app/server/.env" + - "../server/storage:/app/server/storage" + - "../collector/hotdir/:/app/collector/hotdir" + - "../collector/outputs/:/app/collector/outputs" + user: "${UID:-1000}:${GID:-1000}" + ports: + - "3001:3001" + env_file: + - .env + networks: + - anything-llm + extra_hosts: + - "host.docker.internal:host-gateway" diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh new file mode 100644 index 0000000..1ac69e5 --- /dev/null +++ b/docker/docker-entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/bash +{ + cd /app/server/ && + npx prisma generate --schema=./prisma/schema.prisma && + npx prisma migrate deploy --schema=./prisma/schema.prisma && + node /app/server/index.js +} & +{ node /app/collector/index.js; } & +wait -n +exit $? diff --git a/docker/docker-healthcheck.sh b/docker/docker-healthcheck.sh new file mode 100644 index 0000000..49bee3e --- /dev/null +++ b/docker/docker-healthcheck.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Send a request to the specified URL +response=$(curl --write-out '%{http_code}' --silent --output /dev/null http://localhost:3001/api/ping) + +# If the HTTP response code is 200 (OK), the server is up +if [ "$response" -eq 200 ]; then + echo "Server is up" + exit 0 +else + echo "Server is down" + exit 1 +fi diff --git a/docker/vex/CVE-2019-10790.vex.json b/docker/vex/CVE-2019-10790.vex.json new file mode 100644 index 0000000..4233fd1 --- /dev/null +++ b/docker/vex/CVE-2019-10790.vex.json @@ -0,0 +1,22 @@ +{ + "@context": "https://openvex.dev/ns/v0.2.0", + "@id": "https://openvex.dev/docs/public/vex-6750d79bb005487e11d10f81d0b3ac92c47e6e259292c6b2d02558f9f4bca52d", + "author": "tim@mintplexlabs.com", + "timestamp": "2024-07-22T13:49:12.883675-07:00", + "version": 1, + "statements": [ + { + "vulnerability": { + "name": "CVE-2019-10790" + }, + "timestamp": "2024-07-22T13:49:12.883678-07:00", + "products": [ + { + "@id": "pkg:npm/taffydb@2.6.2" + } + ], + "status": "not_affected", + "justification": "vulnerable_code_not_in_execute_path" + } + ] +} \ No newline at end of file diff --git a/docker/vex/CVE-2024-29415.vex.json b/docker/vex/CVE-2024-29415.vex.json new file mode 100644 index 0000000..dfe5b46 --- /dev/null +++ b/docker/vex/CVE-2024-29415.vex.json @@ -0,0 +1,22 @@ +{ + "@context": "https://openvex.dev/ns/v0.2.0", + "@id": "https://openvex.dev/docs/public/vex-939548c125c5bfebd3fd91e64c1c53bffacbde06b3611b4474ea90fa58045004", + "author": "tim@mintplexlabs.com", + "timestamp": "2024-07-19T16:08:47.147169-07:00", + "version": 1, + "statements": [ + { + "vulnerability": { + "name": "CVE-2024-29415" + }, + "timestamp": "2024-07-19T16:08:47.147172-07:00", + "products": [ + { + "@id": "pkg:npm/ip@2.0.0" + } + ], + "status": "not_affected", + "justification": "vulnerable_code_not_present" + } + ] +} \ No newline at end of file diff --git a/docker/vex/CVE-2024-37890.vex.json b/docker/vex/CVE-2024-37890.vex.json new file mode 100644 index 0000000..13498ec --- /dev/null +++ b/docker/vex/CVE-2024-37890.vex.json @@ -0,0 +1,22 @@ +{ + "@context": "https://openvex.dev/ns/v0.2.0", + "@id": "https://openvex.dev/docs/public/vex-939548c125c5bfebd3fd91e64c1c53bffacbde06b3611b4474ea90fa58045004", + "author": "tim@mintplexlabs.com", + "timestamp": "2024-07-19T16:08:47.147169-07:00", + "version": 1, + "statements": [ + { + "vulnerability": { + "name": "CVE-2024-37890" + }, + "timestamp": "2024-07-19T16:08:47.147172-07:00", + "products": [ + { + "@id": "pkg:npm/ws@8.14.2" + } + ], + "status": "not_affected", + "justification": "vulnerable_code_not_in_execute_path" + } + ] +} \ No newline at end of file diff --git a/docker/vex/CVE-2024-4068.vex.json b/docker/vex/CVE-2024-4068.vex.json new file mode 100644 index 0000000..41f73ed --- /dev/null +++ b/docker/vex/CVE-2024-4068.vex.json @@ -0,0 +1,22 @@ +{ + "@context": "https://openvex.dev/ns/v0.2.0", + "@id": "https://openvex.dev/docs/public/vex-939548c125c5bfebd3fd91e64c1c53bffacbde06b3611b4474ea90fa58045004", + "author": "tim@mintplexlabs.com", + "timestamp": "2024-07-19T16:08:47.147169-07:00", + "version": 1, + "statements": [ + { + "vulnerability": { + "name": "CVE-2024-4068" + }, + "timestamp": "2024-07-19T16:08:47.147172-07:00", + "products": [ + { + "@id": "pkg:npm/braces@3.0.2" + } + ], + "status": "not_affected", + "justification": "vulnerable_code_not_present" + } + ] +} \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..b6ef861 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,90 @@ +import globals from "./server/node_modules/globals/index.js" +import eslintRecommended from "./server/node_modules/@eslint/js/src/index.js" +import eslintConfigPrettier from "./server/node_modules/eslint-config-prettier/index.js" +import prettier from "./server/node_modules/eslint-plugin-prettier/eslint-plugin-prettier.js" +import react from "./server/node_modules/eslint-plugin-react/index.js" +import reactRefresh from "./server/node_modules/eslint-plugin-react-refresh/index.js" +import reactHooks from "./server/node_modules/eslint-plugin-react-hooks/index.js" +import ftFlow from "./server/node_modules/eslint-plugin-ft-flow/dist/index.js" +import hermesParser from "./server/node_modules/hermes-eslint/dist/index.js" + +const reactRecommended = react.configs.recommended +const jsxRuntime = react.configs["jsx-runtime"] + +export default [ + eslintRecommended.configs.recommended, + eslintConfigPrettier, + { + ignores: ["**/*.test.js"], + languageOptions: { + parser: hermesParser, + parserOptions: { + ecmaFeatures: { jsx: true } + }, + ecmaVersion: 2020, + sourceType: "module", + globals: { + ...globals.browser, + ...globals.es2020, + ...globals.node + } + }, + linterOptions: { reportUnusedDisableDirectives: true }, + settings: { react: { version: "18.2" } }, + plugins: { + ftFlow, + react, + "jsx-runtime": jsxRuntime, + "react-hooks": reactHooks, + prettier + }, + rules: { + ...reactRecommended.rules, + ...reactHooks.configs.recommended.rules, + ...ftFlow.recommended, + "no-unused-vars": "warn", + "no-undef": "warn", + "no-empty": "warn", + "no-extra-boolean-cast": "warn", + "prettier/prettier": "warn" + } + }, + { + files: ["frontend/src/**/*.js"], + plugins: { + ftFlow, + prettier + }, + rules: { + "prettier/prettier": "warn" + } + }, + { + files: [ + "server/endpoints/**/*.js", + "server/models/**/*.js", + "server/swagger/**/*.js", + "server/utils/**/*.js", + "server/index.js" + ], + rules: { + "no-undef": "warn" + } + }, + { + files: ["frontend/src/**/*.jsx"], + plugins: { + ftFlow, + react, + "jsx-runtime": jsxRuntime, + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + prettier + }, + rules: { + ...jsxRuntime.rules, + "react/prop-types": "off", // FIXME + "react-refresh/only-export-components": "warn" + } + } +] diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000..05619d2 --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1,3 @@ +VITE_API_BASE='http://localhost:3001/api' # Use this URL when developing locally +# VITE_API_BASE="https://$CODESPACE_NAME-3001.$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN/api" # for GitHub Codespaces +# VITE_API_BASE='/api' # Use this URL deploying on non-localhost address OR in docker. diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..77e294d --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,27 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +bundleinspector.html +.env.production +flow-typed diff --git a/frontend/.nvmrc b/frontend/.nvmrc new file mode 100644 index 0000000..59f4a2f --- /dev/null +++ b/frontend/.nvmrc @@ -0,0 +1 @@ +v18.13.0 \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..22cc5b0 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,38 @@ + + + + + + + + AnythingLLM | Your personal LLM trained on anything + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/frontend/jsconfig.json b/frontend/jsconfig.json new file mode 100644 index 0000000..4cc5167 --- /dev/null +++ b/frontend/jsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "esnext", + "jsx": "react", + "paths": { + "@/*": [ + "./src/*" + ] + } + } +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..e7ae44a --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,73 @@ +{ + "name": "anything-llm-frontend", + "private": false, + "license": "MIT", + "type": "module", + "scripts": { + "start": "vite --open", + "dev": "cross-env NODE_ENV=development vite --debug --host=0.0.0.0", + "build": "vite build && node scripts/postbuild.js", + "lint": "yarn prettier --ignore-path ../.prettierignore --write ./src", + "preview": "vite preview" + }, + "dependencies": { + "@microsoft/fetch-event-source": "^2.0.1", + "@mintplex-labs/piper-tts-web": "^1.0.4", + "@phosphor-icons/react": "^2.1.7", + "@tremor/react": "^3.15.1", + "dompurify": "^3.0.8", + "file-saver": "^2.0.5", + "he": "^1.2.0", + "highlight.js": "^11.9.0", + "i18next": "^23.11.3", + "i18next-browser-languagedetector": "^7.2.1", + "js-levenshtein": "^1.1.6", + "lodash.debounce": "^4.0.8", + "markdown-it": "^13.0.1", + "katex": "^0.6.0", + "moment": "^2.30.1", + "onnxruntime-web": "^1.18.0", + "pluralize": "^8.0.0", + "react": "^18.2.0", + "react-device-detect": "^2.2.2", + "react-dom": "^18.2.0", + "react-dropzone": "^14.2.3", + "react-i18next": "^14.1.1", + "react-loading-skeleton": "^3.1.0", + "react-router-dom": "^6.3.0", + "react-speech-recognition": "^3.10.0", + "react-tag-input-component": "^2.0.2", + "react-toastify": "^9.1.3", + "react-tooltip": "^5.25.2", + "recharts": "^2.12.5", + "recharts-to-png": "^2.3.1", + "text-case": "^1.0.9", + "truncate": "^3.0.0", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@esbuild-plugins/node-globals-polyfill": "^0.1.1", + "@types/react": "^18.2.23", + "@types/react-dom": "^18.2.8", + "@types/react-router-dom": "^5.3.3", + "@vitejs/plugin-react": "^4.0.0-beta.0", + "autoprefixer": "^10.4.14", + "buffer": "^6.0.3", + "eslint": "^8.50.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-ft-flow": "^3.0.0", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "flow-bin": "^0.217.0", + "flow-remove-types": "^2.217.1", + "globals": "^13.21.0", + "hermes-eslint": "^0.15.0", + "postcss": "^8.4.23", + "prettier": "^3.0.3", + "rollup-plugin-visualizer": "^5.9.0", + "tailwindcss": "^3.3.1", + "vite": "^4.3.0" + } +} diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..869bdc1 --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,7 @@ +import tailwind from 'tailwindcss' +import autoprefixer from 'autoprefixer' +import tailwindConfig from './tailwind.config.js' + +export default { + plugins: [tailwind(tailwindConfig), autoprefixer], +} \ No newline at end of file diff --git a/frontend/public/anything-llm-dark.png b/frontend/public/anything-llm-dark.png new file mode 100644 index 0000000..a294843 Binary files /dev/null and b/frontend/public/anything-llm-dark.png differ diff --git a/frontend/public/anything-llm-light.png b/frontend/public/anything-llm-light.png new file mode 100644 index 0000000..341d21b Binary files /dev/null and b/frontend/public/anything-llm-light.png differ diff --git a/frontend/public/embed/anythingllm-chat-widget.min.css b/frontend/public/embed/anythingllm-chat-widget.min.css new file mode 100644 index 0000000..fc96d34 --- /dev/null +++ b/frontend/public/embed/anythingllm-chat-widget.min.css @@ -0,0 +1 @@ +*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.allm-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.allm-fixed{position:fixed}.allm-absolute{position:absolute}.allm-relative{position:relative}.allm-sticky{position:sticky}.allm-inset-0{top:0;right:0;bottom:0;left:0}.allm-bottom-0{bottom:0}.allm-bottom-\[10rem\]{bottom:10rem}.allm-left-0{left:0}.allm-right-0{right:0}.allm-right-\[46px\]{right:46px}.allm-right-\[50px\]{right:50px}.allm-top-0{top:0}.allm-top-\[64px\]{top:64px}.allm-z-10{z-index:10}.allm-z-50{z-index:50}.allm-mx-2{margin-left:.5rem;margin-right:.5rem}.allm-mx-4{margin-left:1rem;margin-right:1rem}.allm-mx-\[20px\]{margin-left:20px;margin-right:20px}.allm-my-1{margin-top:.25rem;margin-bottom:.25rem}.allm-my-3{margin-top:.75rem;margin-bottom:.75rem}.allm-mb-1{margin-bottom:.25rem}.allm-mb-2{margin-bottom:.5rem}.allm-mb-4{margin-bottom:1rem}.allm-ml-2{margin-left:.5rem}.allm-ml-4{margin-left:1rem}.allm-ml-\[54px\]{margin-left:54px}.allm-ml-\[9px\]{margin-left:9px}.allm-mr-4{margin-right:1rem}.allm-mr-6{margin-right:1.5rem}.allm-mr-\[37px\]{margin-right:37px}.allm-mt-2{margin-top:.5rem}.allm-mt-3{margin-top:.75rem}.allm-mt-4{margin-top:1rem}.allm-block{display:block}.allm-inline-block{display:inline-block}.allm-flex{display:flex}.allm-inline-flex{display:inline-flex}.allm-hidden{display:none}.allm-h-4{height:1rem}.allm-h-5{height:1.25rem}.allm-h-9{height:2.25rem}.allm-h-\[76px\]{height:76px}.allm-h-fit{height:-moz-fit-content;height:fit-content}.allm-h-full{height:100%}.allm-max-h-\[100px\]{max-height:100px}.allm-max-h-\[82vh\]{max-height:82vh}.allm-w-4{width:1rem}.allm-w-5{width:1.25rem}.allm-w-9{width:2.25rem}.allm-w-\[75\%\]{width:75%}.allm-w-full{width:100%}.allm-flex-shrink-0{flex-shrink:0}.allm-flex-grow{flex-grow:1}@keyframes allm-pulse{50%{opacity:.5}}.allm-animate-pulse{animation:allm-pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes allm-spin{to{transform:rotate(360deg)}}.allm-animate-spin{animation:allm-spin 1s linear infinite}.allm-cursor-pointer{cursor:pointer}.allm-cursor-text{cursor:text}.allm-resize-none{resize:none}.allm-flex-col{flex-direction:column}.allm-items-start{align-items:flex-start}.allm-items-end{align-items:flex-end}.allm-items-center{align-items:center}.allm-justify-start{justify-content:flex-start}.allm-justify-end{justify-content:flex-end}.allm-justify-center{justify-content:center}.allm-gap-x-1{-moz-column-gap:.25rem;column-gap:.25rem}.allm-gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.allm-gap-x-5{-moz-column-gap:1.25rem;column-gap:1.25rem}.allm-gap-x-\[12px\]{-moz-column-gap:12px;column-gap:12px}.allm-gap-y-1{row-gap:.25rem}.allm-gap-y-2{row-gap:.5rem}.allm-overflow-hidden{overflow:hidden}.allm-overflow-y-auto{overflow-y:auto}.allm-overflow-y-scroll{overflow-y:scroll}.allm-whitespace-pre-line{white-space:pre-line}.allm-rounded-2xl{border-radius:1rem}.allm-rounded-full{border-radius:9999px}.allm-rounded-lg{border-radius:.5rem}.allm-rounded-sm{border-radius:.125rem}.allm-rounded-xl{border-radius:.75rem}.allm-rounded-t-2xl{border-top-left-radius:1rem;border-top-right-radius:1rem}.allm-rounded-t-\[18px\]{border-top-left-radius:18px;border-top-right-radius:18px}.allm-rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.allm-rounded-bl-\[18px\]{border-bottom-left-radius:18px}.allm-rounded-bl-\[4px\]{border-bottom-left-radius:4px}.allm-rounded-br-\[18px\]{border-bottom-right-radius:18px}.allm-rounded-br-\[4px\]{border-bottom-right-radius:4px}.allm-border{border-width:1px}.allm-border-l-2{border-left-width:2px}.allm-border-none{border-style:none}.allm-border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219 / var(--tw-border-opacity))}.allm-border-red-500{--tw-border-opacity:1;border-color:rgb(239 68 68 / var(--tw-border-opacity))}.allm-border-white\/10{border-color:#ffffff1a}.allm-bg-black\/20{background-color:#0003}.allm-bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.allm-bg-red-200{--tw-bg-opacity:1;background-color:rgb(254 202 202 / var(--tw-bg-opacity))}.allm-bg-red-300{--tw-bg-opacity:1;background-color:rgb(252 165 165 / var(--tw-bg-opacity))}.allm-bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242 / var(--tw-bg-opacity))}.allm-bg-transparent{background-color:transparent}.allm-bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.allm-p-1{padding:.25rem}.allm-p-2{padding:.5rem}.allm-p-4{padding:1rem}.allm-px-2{padding-left:.5rem;padding-right:.5rem}.allm-px-4{padding-left:1rem;padding-right:1rem}.allm-px-\[22px\]{padding-left:22px;padding-right:22px}.allm-py-2{padding-top:.5rem;padding-bottom:.5rem}.allm-py-4{padding-top:1rem;padding-bottom:1rem}.allm-py-\[11px\]{padding-top:11px;padding-bottom:11px}.allm-py-\[5px\]{padding-top:5px;padding-bottom:5px}.allm-pb-2{padding-bottom:.5rem}.allm-pb-4{padding-bottom:1rem}.allm-pb-\[100px\]{padding-bottom:100px}.allm-pb-\[30px\]{padding-bottom:30px}.allm-pl-2{padding-left:.5rem}.allm-pt-4{padding-top:1rem}.allm-pt-\[5px\]{padding-top:5px}.allm-text-left{text-align:left}.allm-text-center{text-align:center}.allm-text-right{text-align:right}.allm-font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.allm-font-sans{font-family:plus-jakarta-sans,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.\!allm-text-xs{font-size:.75rem!important;line-height:1rem!important}.allm-text-2xl{font-size:1.5rem;line-height:2rem}.allm-text-\[10px\]{font-size:10px}.allm-text-\[14px\]{font-size:14px}.allm-text-base{font-size:1rem;line-height:1.5rem}.allm-text-sm{font-size:.875rem;line-height:1.25rem}.allm-text-xs{font-size:.75rem;line-height:1rem}.allm-font-bold{font-weight:700}.allm-font-normal{font-weight:400}.allm-leading-\[20px\]{line-height:20px}.allm-text-\[\#22262899\]\/60{color:#22262899}.allm-text-\[\#222628\]{--tw-text-opacity:1;color:rgb(34 38 40 / var(--tw-text-opacity))}.allm-text-\[\#7A7D7E\]{--tw-text-opacity:1;color:rgb(122 125 126 / var(--tw-text-opacity))}.allm-text-black{--tw-text-opacity:1;color:rgb(0 0 0 / var(--tw-text-opacity))}.allm-text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219 / var(--tw-text-opacity))}.allm-text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175 / var(--tw-text-opacity))}.allm-text-green-500{--tw-text-opacity:1;color:rgb(34 197 94 / var(--tw-text-opacity))}.allm-text-red-500{--tw-text-opacity:1;color:rgb(239 68 68 / var(--tw-text-opacity))}.allm-text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184 / var(--tw-text-opacity))}.allm-text-slate-800\/60{color:#1e293b99}.allm-text-white{--tw-text-opacity:1;color:rgb(255 255 255 / var(--tw-text-opacity))}.allm-text-white\/50{color:#ffffff80}.allm-text-zinc-300{--tw-text-opacity:1;color:rgb(212 212 216 / var(--tw-text-opacity))}.allm-no-underline{text-decoration-line:none}.allm-shadow-\[0_4px_14px_rgba\(0\,0\,0\,0\.25\)\]{--tw-shadow:0 4px 14px rgba(0,0,0,.25);--tw-shadow-colored:0 4px 14px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.allm-shadow-lg{--tw-shadow:0 10px 15px -3px rgb(0 0 0 / .1),0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.msg-suggestion{animation-name:fadeIn;animation-duration:.3s;animation-timing-function:linear;animation-fill-mode:forwards}@keyframes fadeIn{0%{opacity:0%}25%{opacity:25%}50%{opacity:50%}75%{opacity:75%}to{opacity:100%}}.placeholder\:allm-text-slate-800\/60::-moz-placeholder{color:#1e293b99}.placeholder\:allm-text-slate-800\/60::placeholder{color:#1e293b99}.hover\:allm-cursor-pointer:hover{cursor:pointer}.hover\:allm-bg-black\/50:hover{background-color:#00000080}.hover\:allm-bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.hover\:allm-underline:hover{text-decoration-line:underline}.hover\:allm-opacity-80:hover{opacity:.8}.hover\:allm-opacity-95:hover{opacity:.95}.hover\:allm-shadow-\[0_4px_14px_rgba\(0\,0\,0\,0\.5\)\]:hover{--tw-shadow:0 4px 14px rgba(0,0,0,.5);--tw-shadow-colored:0 4px 14px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:allm-outline-none:focus{outline:2px solid transparent;outline-offset:2px}.active\:allm-outline-none:active{outline:2px solid transparent;outline-offset:2px}.allm-group:hover .group-hover\:allm-text-\[\#22262899\]\/90{color:#222628e6} \ No newline at end of file diff --git a/frontend/public/embed/anythingllm-chat-widget.min.js b/frontend/public/embed/anythingllm-chat-widget.min.js new file mode 100644 index 0000000..56d59a0 --- /dev/null +++ b/frontend/public/embed/anythingllm-chat-widget.min.js @@ -0,0 +1,37 @@ +!function(Jn,Nt){"object"==typeof exports&&typeof module<"u"?Nt(exports):"function"==typeof define&&define.amd?define(["exports"],Nt):Nt((Jn=typeof globalThis<"u"?globalThis:Jn||self).EmbeddedAnythingLLM={})}(this,(function(Jn){"use strict";var F1,B1,Nt=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function jo(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var ec={exports:{}},Wo={},tc={exports:{}},ee={},zr=Symbol.for("react.element"),J1=Symbol.for("react.portal"),em=Symbol.for("react.fragment"),tm=Symbol.for("react.strict_mode"),nm=Symbol.for("react.profiler"),rm=Symbol.for("react.provider"),om=Symbol.for("react.context"),am=Symbol.for("react.forward_ref"),sm=Symbol.for("react.suspense"),im=Symbol.for("react.memo"),lm=Symbol.for("react.lazy"),nc=Symbol.iterator; +/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var rc={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},oc=Object.assign,ac={};function er(e,t,n){this.props=e,this.context=t,this.refs=ac,this.updater=n||rc}function sc(){}function Vs(e,t,n){this.props=e,this.context=t,this.refs=ac,this.updater=n||rc}er.prototype.isReactComponent={},er.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},er.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},sc.prototype=er.prototype;var zs=Vs.prototype=new sc;zs.constructor=Vs,oc(zs,er.prototype),zs.isPureReactComponent=!0;var ic=Array.isArray,lc=Object.prototype.hasOwnProperty,Gs={current:null},uc={key:!0,ref:!0,__self:!0,__source:!0};function cc(e,t,n){var r,o={},a=null,s=null;if(null!=t)for(r in void 0!==t.ref&&(s=t.ref),void 0!==t.key&&(a=""+t.key),t)lc.call(t,r)&&!uc.hasOwnProperty(r)&&(o[r]=t[r]);var i=arguments.length-2;if(1===i)o.children=n;else if(1>>1,H=T[k];if(!(0>>1;ko(ce,L))seo(le,ce)?(T[k]=le,T[se]=L,k=se):(T[k]=ce,T[me]=L,k=me);else{if(!(seo(le,L)))break e;T[k]=le,T[se]=L,k=se}}}return y}function o(T,y){var L=T.sortIndex-y.sortIndex;return 0!==L?L:T.id-y.id}if("object"==typeof performance&&"function"==typeof performance.now){var a=performance;e.unstable_now=function(){return a.now()}}else{var s=Date,i=s.now();e.unstable_now=function(){return s.now()-i}}var l=[],u=[],c=1,d=null,p=3,m=!1,A=!1,b=!1,C="function"==typeof setTimeout?setTimeout:null,h="function"==typeof clearTimeout?clearTimeout:null,g=typeof setImmediate<"u"?setImmediate:null;function E(T){for(var y=n(u);null!==y;){if(null===y.callback)r(u);else{if(!(y.startTime<=T))break;r(u),y.sortIndex=y.expirationTime,t(l,y)}y=n(u)}}function v(T){if(b=!1,E(T),!A)if(null!==n(l))A=!0,P(N);else{var y=n(u);null!==y&&x(v,y.startTime-T)}}function N(T,y){A=!1,b&&(b=!1,h(R),R=-1),m=!0;var L=p;try{for(E(y),d=n(l);null!==d&&(!(d.expirationTime>y)||T&&!Q());){var k=d.callback;if("function"==typeof k){d.callback=null,p=d.priorityLevel;var H=k(d.expirationTime<=y);y=e.unstable_now(),"function"==typeof H?d.callback=H:d===n(l)&&r(l),E(y)}else r(l);d=n(l)}if(null!==d)var j=!0;else{var me=n(u);null!==me&&x(v,me.startTime-y),j=!1}return j}finally{d=null,p=L,m=!1}}typeof navigator<"u"&&void 0!==navigator.scheduling&&void 0!==navigator.scheduling.isInputPending&&navigator.scheduling.isInputPending.bind(navigator.scheduling);var Y,_=!1,S=null,R=-1,q=5,I=-1;function Q(){return!(e.unstable_now()-IT||125k?(T.sortIndex=L,t(u,T),null===n(l)&&T===n(u)&&(b?(h(R),R=-1):b=!0,x(v,L-k))):(T.sortIndex=H,t(l,T),A||m||(A=!0,P(N))),T},e.unstable_shouldYield=Q,e.unstable_wrapCallback=function(T){var y=p;return function(){var L=p;p=y;try{return T.apply(this,arguments)}finally{p=L}}}})(gc),mc.exports=gc;var _m=mc.exports,hc=Z,lt=_m; +/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */function M(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Ws=Object.prototype.hasOwnProperty,vm=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Ac={},bc={};function Ye(e,t,n,r,o,a,s){this.acceptsBooleans=2===t||3===t||4===t,this.attributeName=r,this.attributeNamespace=o,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=a,this.removeEmptyString=s}var Me={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach((function(e){Me[e]=new Ye(e,0,!1,e,null,!1,!1)})),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach((function(e){var t=e[0];Me[t]=new Ye(t,1,!1,e[1],null,!1,!1)})),["contentEditable","draggable","spellCheck","value"].forEach((function(e){Me[e]=new Ye(e,2,!1,e.toLowerCase(),null,!1,!1)})),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach((function(e){Me[e]=new Ye(e,2,!1,e,null,!1,!1)})),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach((function(e){Me[e]=new Ye(e,3,!1,e.toLowerCase(),null,!1,!1)})),["checked","multiple","muted","selected"].forEach((function(e){Me[e]=new Ye(e,3,!0,e,null,!1,!1)})),["capture","download"].forEach((function(e){Me[e]=new Ye(e,4,!1,e,null,!1,!1)})),["cols","rows","size","span"].forEach((function(e){Me[e]=new Ye(e,6,!1,e,null,!1,!1)})),["rowSpan","start"].forEach((function(e){Me[e]=new Ye(e,5,!1,e.toLowerCase(),null,!1,!1)}));var Ys=/[\-:]([a-z])/g;function Ks(e){return e[1].toUpperCase()}function Xs(e,t,n,r){var o=Me.hasOwnProperty(t)?Me[t]:null;(null!==o?0!==o.type:r||!(2"u"||function(e,t,n,r){if(null!==n&&0===n.type)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return!r&&(null!==n?!n.acceptsBooleans:"data-"!==(e=e.toLowerCase().slice(0,5))&&"aria-"!==e);default:return!1}}(e,t,n,r))return!0;if(r)return!1;if(null!==n)switch(n.type){case 3:return!t;case 4:return!1===t;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}(t,n,o,r)&&(n=null),r||null===o?function(e){return!!Ws.call(bc,e)||!Ws.call(Ac,e)&&(vm.test(e)?bc[e]=!0:(Ac[e]=!0,!1))}(t)&&(null===n?e.removeAttribute(t):e.setAttribute(t,""+n)):o.mustUseProperty?e[o.propertyName]=null===n?3!==o.type&&"":n:(t=o.attributeName,r=o.attributeNamespace,null===n?e.removeAttribute(t):(n=3===(o=o.type)||4===o&&!0===n?"":""+n,r?e.setAttributeNS(r,t,n):e.setAttribute(t,n))))}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach((function(e){var t=e.replace(Ys,Ks);Me[t]=new Ye(t,1,!1,e,null,!1,!1)})),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach((function(e){var t=e.replace(Ys,Ks);Me[t]=new Ye(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)})),["xml:base","xml:lang","xml:space"].forEach((function(e){var t=e.replace(Ys,Ks);Me[t]=new Ye(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)})),["tabIndex","crossOrigin"].forEach((function(e){Me[e]=new Ye(e,1,!1,e.toLowerCase(),null,!1,!1)})),Me.xlinkHref=new Ye("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach((function(e){Me[e]=new Ye(e,1,!1,e.toLowerCase(),null,!0,!0)}));var Yt=hc.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,Qo=Symbol.for("react.element"),nr=Symbol.for("react.portal"),rr=Symbol.for("react.fragment"),Qs=Symbol.for("react.strict_mode"),Js=Symbol.for("react.profiler"),_c=Symbol.for("react.provider"),vc=Symbol.for("react.context"),ei=Symbol.for("react.forward_ref"),ti=Symbol.for("react.suspense"),ni=Symbol.for("react.suspense_list"),ri=Symbol.for("react.memo"),sn=Symbol.for("react.lazy"),Dc=Symbol.for("react.offscreen"),yc=Symbol.iterator;function $r(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=yc&&e[yc]||e["@@iterator"])?e:null}var oi,_e=Object.assign;function Zr(e){if(void 0===oi)try{throw Error()}catch(n){var t=n.stack.trim().match(/\n( *(at )?)/);oi=t&&t[1]||""}return"\n"+oi+e}var ai=!1;function si(e,t){if(!e||ai)return"";ai=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(t)if(t=function(){throw Error()},Object.defineProperty(t.prototype,"props",{set:function(){throw Error()}}),"object"==typeof Reflect&&Reflect.construct){try{Reflect.construct(t,[])}catch(u){var r=u}Reflect.construct(e,[],t)}else{try{t.call()}catch(u){r=u}e.call(t.prototype)}else{try{throw Error()}catch(u){r=u}e()}}catch(u){if(u&&r&&"string"==typeof u.stack){for(var o=u.stack.split("\n"),a=r.stack.split("\n"),s=o.length-1,i=a.length-1;1<=s&&0<=i&&o[s]!==a[i];)i--;for(;1<=s&&0<=i;s--,i--)if(o[s]!==a[i]){if(1!==s||1!==i)do{if(s--,0>--i||o[s]!==a[i]){var l="\n"+o[s].replace(" at new "," at ");return e.displayName&&l.includes("")&&(l=l.replace("",e.displayName)),l}}while(1<=s&&0<=i);break}}}finally{ai=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?Zr(e):""}function Cm(e){switch(e.tag){case 5:return Zr(e.type);case 16:return Zr("Lazy");case 13:return Zr("Suspense");case 19:return Zr("SuspenseList");case 0:case 2:case 15:return e=si(e.type,!1);case 11:return e=si(e.type.render,!1);case 1:return e=si(e.type,!0);default:return""}}function ii(e){if(null==e)return null;if("function"==typeof e)return e.displayName||e.name||null;if("string"==typeof e)return e;switch(e){case rr:return"Fragment";case nr:return"Portal";case Js:return"Profiler";case Qs:return"StrictMode";case ti:return"Suspense";case ni:return"SuspenseList"}if("object"==typeof e)switch(e.$$typeof){case vc:return(e.displayName||"Context")+".Consumer";case _c:return(e._context.displayName||"Context")+".Provider";case ei:var t=e.render;return(e=e.displayName)||(e=""!==(e=t.displayName||t.name||"")?"ForwardRef("+e+")":"ForwardRef"),e;case ri:return null!==(t=e.displayName||null)?t:ii(e.type)||"Memo";case sn:t=e._payload,e=e._init;try{return ii(e(t))}catch{}}return null}function Nm(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=(e=t.render).displayName||e.name||"",t.displayName||(""!==e?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ii(t);case 8:return t===Qs?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if("function"==typeof t)return t.displayName||t.name||null;if("string"==typeof t)return t}return null}function ln(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":case"object":return e;default:return""}}function Tc(e){var t=e.type;return(e=e.nodeName)&&"input"===e.toLowerCase()&&("checkbox"===t||"radio"===t)}function Jo(e){e._valueTracker||(e._valueTracker=function(e){var t=Tc(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&"function"==typeof n.get&&"function"==typeof n.set){var o=n.get,a=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(s){r=""+s,a.call(this,s)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(s){r=""+s},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}(e))}function Cc(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Tc(e)?e.checked?"true":"false":e.value),(e=r)!==n&&(t.setValue(e),!0)}function ea(e){if(typeof(e=e||(typeof document<"u"?document:void 0))>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function li(e,t){var n=t.checked;return _e({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function Nc(e,t){var n=null==t.defaultValue?"":t.defaultValue,r=null!=t.checked?t.checked:t.defaultChecked;n=ln(null!=t.value?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:"checkbox"===t.type||"radio"===t.type?null!=t.checked:null!=t.value}}function Sc(e,t){null!=(t=t.checked)&&Xs(e,"checked",t,!1)}function ui(e,t){Sc(e,t);var n=ln(t.value),r=t.type;if(null!=n)"number"===r?(0===n&&""===e.value||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if("submit"===r||"reset"===r)return void e.removeAttribute("value");t.hasOwnProperty("value")?ci(e,t.type,n):t.hasOwnProperty("defaultValue")&&ci(e,t.type,ln(t.defaultValue)),null==t.checked&&null!=t.defaultChecked&&(e.defaultChecked=!!t.defaultChecked)}function wc(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!("submit"!==r&&"reset"!==r||void 0!==t.value&&null!==t.value))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}""!==(n=e.name)&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,""!==n&&(e.name=n)}function ci(e,t,n){("number"!==t||ea(e.ownerDocument)!==e)&&(null==n?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var jr=Array.isArray;function or(e,t,n,r){if(e=e.options,t){t={};for(var o=0;o"+t.valueOf().toString()+"",t=ta.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}},typeof MSApp<"u"&&MSApp.execUnsafeLocalFunction?function(t,n,r,o){MSApp.execUnsafeLocalFunction((function(){return e(t,n)}))}:e);function Wr(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t}var Yr={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},wm=["Webkit","ms","Moz","O"];function Ic(e,t,n){return null==t||"boolean"==typeof t||""===t?"":n||"number"!=typeof t||0===t||Yr.hasOwnProperty(e)&&Yr[e]?(""+t).trim():t+"px"}function Mc(e,t){for(var n in e=e.style,t)if(t.hasOwnProperty(n)){var r=0===n.indexOf("--"),o=Ic(n,t[n],r);"float"===n&&(n="cssFloat"),r?e.setProperty(n,o):e[n]=o}}Object.keys(Yr).forEach((function(e){wm.forEach((function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Yr[t]=Yr[e]}))}));var xm=_e({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function fi(e,t){if(t){if(xm[e]&&(null!=t.children||null!=t.dangerouslySetInnerHTML))throw Error(M(137,e));if(null!=t.dangerouslySetInnerHTML){if(null!=t.children)throw Error(M(60));if("object"!=typeof t.dangerouslySetInnerHTML||!("__html"in t.dangerouslySetInnerHTML))throw Error(M(61))}if(null!=t.style&&"object"!=typeof t.style)throw Error(M(62))}}function mi(e,t){if(-1===e.indexOf("-"))return"string"==typeof t.is;switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var gi=null;function hi(e){return(e=e.target||e.srcElement||window).correspondingUseElement&&(e=e.correspondingUseElement),3===e.nodeType?e.parentNode:e}var Ei=null,ar=null,sr=null;function Fc(e){if(e=Ao(e)){if("function"!=typeof Ei)throw Error(M(280));var t=e.stateNode;t&&(t=Ta(t),Ei(e.stateNode,e.type,t))}}function Bc(e){ar?sr?sr.push(e):sr=[e]:ar=e}function Pc(){if(ar){var e=ar,t=sr;if(sr=ar=null,Fc(e),t)for(e=0;e>>=0,0===e?32:31-(qm(e)/Hm|0)|0},qm=Math.log,Hm=Math.LN2;var sa=64,ia=4194304;function Jr(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return 4194240&e;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return 130023424&e;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function la(e,t){var n=e.pendingLanes;if(0===n)return 0;var r=0,o=e.suspendedLanes,a=e.pingedLanes,s=268435455&n;if(0!==s){var i=s&~o;0!==i?r=Jr(i):0!==(a&=s)&&(r=Jr(a))}else 0!==(s=n&~o)?r=Jr(s):0!==a&&(r=Jr(a));if(0===r)return 0;if(0!==t&&t!==r&&!(t&o)&&((o=r&-r)>=(a=t&-t)||16===o&&0!=(4194240&a)))return t;if(4&r&&(r|=16&n),0!==(t=e.entangledLanes))for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function eo(e,t,n){e.pendingLanes|=t,536870912!==t&&(e.suspendedLanes=0,e.pingedLanes=0),(e=e.eventTimes)[t=31-St(t)]=n}function Ti(e,t){var n=e.entangledLanes|=t;for(e=e.entanglements;n;){var r=31-St(n),o=1<=lo),p0=" ",f0=!1;function m0(e,t){switch(e){case"keyup":return-1!==bg.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function g0(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var ur=!1;var yg={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function h0(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return"input"===t?!!yg[e.type]:"textarea"===t}function E0(e,t,n,r){Bc(r),0<(t=va(t,"onChange")).length&&(n=new Ri("onChange","change",null,n,r),e.push({event:n,listeners:t}))}var uo=null,co=null;function Tg(e){M0(e,0)}function Ea(e){if(Cc(mr(e)))return e}function Cg(e,t){if("change"===e)return t}var A0=!1;if(Wt){var Fi;if(Wt){var Bi="oninput"in document;if(!Bi){var b0=document.createElement("div");b0.setAttribute("oninput","return;"),Bi="function"==typeof b0.oninput}Fi=Bi}else Fi=!1;A0=Fi&&(!document.documentMode||9=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=D0(n)}}function T0(e,t){return!(!e||!t)&&(e===t||(!e||3!==e.nodeType)&&(t&&3===t.nodeType?T0(e,t.parentNode):"contains"in e?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))))}function C0(){for(var e=window,t=ea();t instanceof e.HTMLIFrameElement;){try{var n="string"==typeof t.contentWindow.location.href}catch{n=!1}if(!n)break;t=ea((e=t.contentWindow).document)}return t}function Pi(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&("text"===e.type||"search"===e.type||"tel"===e.type||"url"===e.type||"password"===e.type)||"textarea"===t||"true"===e.contentEditable)}function Lg(e){var t=C0(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&T0(n.ownerDocument.documentElement,n)){if(null!==r&&Pi(n))if(t=r.start,void 0===(e=r.end)&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if((e=(t=n.ownerDocument||document)&&t.defaultView||window).getSelection){e=e.getSelection();var o=n.textContent.length,a=Math.min(r.start,o);r=void 0===r.end?a:Math.min(r.end,o),!e.extend&&a>r&&(o=r,r=a,a=o),o=y0(n,a);var s=y0(n,r);o&&s&&(1!==e.rangeCount||e.anchorNode!==o.node||e.anchorOffset!==o.offset||e.focusNode!==s.node||e.focusOffset!==s.offset)&&((t=t.createRange()).setStart(o.node,o.offset),e.removeAllRanges(),a>r?(e.addRange(t),e.extend(s.node,s.offset)):(t.setEnd(s.node,s.offset),e.addRange(t)))}for(t=[],e=n;e=e.parentNode;)1===e.nodeType&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for("function"==typeof n.focus&&n.focus(),n=0;n=document.documentMode,cr=null,Ui=null,fo=null,qi=!1;function N0(e,t,n){var r=n.window===n?n.document:9===n.nodeType?n:n.ownerDocument;qi||null==cr||cr!==ea(r)||("selectionStart"in(r=cr)&&Pi(r)?r={start:r.selectionStart,end:r.selectionEnd}:r={anchorNode:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection()).anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset},fo&&po(fo,r)||(fo=r,0<(r=va(Ui,"onSelect")).length&&(t=new Ri("onSelect","select",null,t,n),e.push({event:t,listeners:r}),t.target=cr)))}function Aa(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n}var dr={animationend:Aa("Animation","AnimationEnd"),animationiteration:Aa("Animation","AnimationIteration"),animationstart:Aa("Animation","AnimationStart"),transitionend:Aa("Transition","TransitionEnd")},Hi={},S0={};function ba(e){if(Hi[e])return Hi[e];if(!dr[e])return e;var n,t=dr[e];for(n in t)if(t.hasOwnProperty(n)&&n in S0)return Hi[e]=t[n];return e}Wt&&(S0=document.createElement("div").style,"AnimationEvent"in window||(delete dr.animationend.animation,delete dr.animationiteration.animation,delete dr.animationstart.animation),"TransitionEvent"in window||delete dr.transitionend.transition);var w0=ba("animationend"),x0=ba("animationiteration"),R0=ba("animationstart"),L0=ba("transitionend"),O0=new Map,k0="abort auxClick cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(" ");function mn(e,t){O0.set(e,t),xn(t,[e])}for(var Vi=0;Vigr||(e.current=Qi[gr],Qi[gr]=null,gr--)}function fe(e,t){gr++,Qi[gr]=e.current,e.current=t}var En={},qe=hn(En),Je=hn(!1),On=En;function hr(e,t){var n=e.type.contextTypes;if(!n)return En;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var a,o={};for(a in n)o[a]=t[a];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function et(e){return null!=(e=e.childContextTypes)}function Ca(){Ae(Je),Ae(qe)}function H0(e,t,n){if(qe.current!==En)throw Error(M(168));fe(qe,t),fe(Je,n)}function V0(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,"function"!=typeof r.getChildContext)return n;for(var o in r=r.getChildContext())if(!(o in t))throw Error(M(108,Nm(e)||"Unknown",o));return _e({},n,r)}function Na(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||En,On=qe.current,fe(qe,e),fe(Je,Je.current),!0}function z0(e,t,n){var r=e.stateNode;if(!r)throw Error(M(169));n?(e=V0(e,t,On),r.__reactInternalMemoizedMergedChildContext=e,Ae(Je),Ae(qe),fe(qe,e)):Ae(Je),fe(Je,n)}var Xt=null,Sa=!1,Ji=!1;function G0(e){null===Xt?Xt=[e]:Xt.push(e)}function An(){if(!Ji&&null!==Xt){Ji=!0;var e=0,t=ue;try{var n=Xt;for(ue=1;e>=s,o-=s,Qt=1<<32-St(t)+o|n<R?(q=S,S=null):q=S.sibling;var I=p(h,S,E[R],v);if(null===I){null===S&&(S=q);break}e&&S&&null===I.alternate&&t(h,S),g=a(I,g,R),null===_?N=I:_.sibling=I,_=I,S=q}if(R===E.length)return n(h,S),be&&In(h,R),N;if(null===S){for(;RR?(q=S,S=null):q=S.sibling;var Q=p(h,S,I.value,v);if(null===Q){null===S&&(S=q);break}e&&S&&null===Q.alternate&&t(h,S),g=a(Q,g,R),null===_?N=Q:_.sibling=Q,_=Q,S=q}if(I.done)return n(h,S),be&&In(h,R),N;if(null===S){for(;!I.done;R++,I=E.next())null!==(I=d(h,I.value,v))&&(g=a(I,g,R),null===_?N=I:_.sibling=I,_=I);return be&&In(h,R),N}for(S=r(h,S);!I.done;R++,I=E.next())null!==(I=m(S,h,R,I.value,v))&&(e&&null!==I.alternate&&S.delete(null===I.key?R:I.key),g=a(I,g,R),null===_?N=I:_.sibling=I,_=I);return e&&S.forEach((function(ie){return t(h,ie)})),be&&In(h,R),N}(h,g,E,v);Fa(h,E)}return"string"==typeof E&&""!==E||"number"==typeof E?(E=""+E,null!==g&&6===g.tag?(n(h,g.sibling),(g=o(g,E)).return=h,h=g):(n(h,g),(g=Yl(E,h.mode,v)).return=h,h=g),s(h)):n(h,g)}}var Dr=ad(!0),sd=ad(!1),_o={},qt=hn(_o),vo=hn(_o),Do=hn(_o);function Fn(e){if(e===_o)throw Error(M(174));return e}function fl(e,t){switch(fe(Do,t),fe(vo,e),fe(qt,_o),e=t.nodeType){case 9:case 11:t=(t=t.documentElement)?t.namespaceURI:pi(null,"");break;default:t=pi(t=(e=8===e?t.parentNode:t).namespaceURI||null,e=e.tagName)}Ae(qt),fe(qt,t)}function yr(){Ae(qt),Ae(vo),Ae(Do)}function id(e){Fn(Do.current);var t=Fn(qt.current),n=pi(t,e.type);t!==n&&(fe(vo,e),fe(qt,n))}function ml(e){vo.current===e&&(Ae(qt),Ae(vo))}var ve=hn(0);function Ba(e){for(var t=e;null!==t;){if(13===t.tag){var n=t.memoizedState;if(null!==n&&(null===(n=n.dehydrated)||"$?"===n.data||"$!"===n.data))return t}else if(19===t.tag&&void 0!==t.memoizedProps.revealOrder){if(128&t.flags)return t}else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}var gl=[];function hl(){for(var e=0;en?n:4,e(!0);var r=El.transition;El.transition={};try{e(!1),t()}finally{ue=n,El.transition=r}}function Cd(){return _t().memoizedState}function jg(e,t,n){var r=Tn(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Nd(e))Sd(t,n);else if(null!==(n=K0(e,t,n,r))){kt(n,e,r,Xe()),wd(n,t,r)}}function Wg(e,t,n){var r=Tn(e),o={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Nd(e))Sd(t,o);else{var a=e.alternate;if(0===e.lanes&&(null===a||0===a.lanes)&&null!==(a=t.lastRenderedReducer))try{var s=t.lastRenderedState,i=a(s,n);if(o.hasEagerState=!0,o.eagerState=i,wt(i,s)){var l=t.interleaved;return null===l?(o.next=o,ul(t)):(o.next=l.next,l.next=o),void(t.interleaved=o)}}catch{}null!==(n=K0(e,t,o,r))&&(kt(n,e,r,o=Xe()),wd(n,t,r))}}function Nd(e){var t=e.alternate;return e===De||null!==t&&t===De}function Sd(e,t){yo=Ua=!0;var n=e.pending;null===n?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function wd(e,t,n){if(4194240&n){var r=t.lanes;n|=r&=e.pendingLanes,t.lanes=n,Ti(e,n)}}var Va={readContext:bt,useCallback:He,useContext:He,useEffect:He,useImperativeHandle:He,useInsertionEffect:He,useLayoutEffect:He,useMemo:He,useReducer:He,useRef:He,useState:He,useDebugValue:He,useDeferredValue:He,useTransition:He,useMutableSource:He,useSyncExternalStore:He,useId:He,unstable_isNewReconciler:!1},Yg={readContext:bt,useCallback:function(e,t){return Ht().memoizedState=[e,void 0===t?null:t],e},useContext:bt,useEffect:Ed,useImperativeHandle:function(e,t,n){return n=null!=n?n.concat([e]):null,qa(4194308,4,_d.bind(null,t,e),n)},useLayoutEffect:function(e,t){return qa(4194308,4,e,t)},useInsertionEffect:function(e,t){return qa(4,2,e,t)},useMemo:function(e,t){var n=Ht();return t=void 0===t?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Ht();return t=void 0!==n?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=jg.bind(null,De,e),[r.memoizedState,e]},useRef:function(e){return e={current:e},Ht().memoizedState=e},useState:gd,useDebugValue:Tl,useDeferredValue:function(e){return Ht().memoizedState=e},useTransition:function(){var e=gd(!1),t=e[0];return e=Zg.bind(null,e[1]),Ht().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=De,o=Ht();if(be){if(void 0===n)throw Error(M(407));n=n()}else{if(n=t(),null===Oe)throw Error(M(349));30&Bn||cd(r,t,n)}o.memoizedState=n;var a={value:n,getSnapshot:t};return o.queue=a,Ed(pd.bind(null,r,a,e),[e]),r.flags|=2048,No(9,dd.bind(null,r,a,n,t),void 0,null),n},useId:function(){var e=Ht(),t=Oe.identifierPrefix;if(be){var n=Jt;t=":"+t+"R"+(n=(Qt&~(1<<32-St(Qt)-1)).toString(32)+n),0<(n=To++)&&(t+="H"+n.toString(32)),t+=":"}else t=":"+t+"r"+(n=$g++).toString(32)+":";return e.memoizedState=t},unstable_isNewReconciler:!1},Kg={readContext:bt,useCallback:Dd,useContext:bt,useEffect:yl,useImperativeHandle:vd,useInsertionEffect:Ad,useLayoutEffect:bd,useMemo:yd,useReducer:vl,useRef:hd,useState:function(){return vl(Co)},useDebugValue:Tl,useDeferredValue:function(e){return Td(_t(),we.memoizedState,e)},useTransition:function(){return[vl(Co)[0],_t().memoizedState]},useMutableSource:ld,useSyncExternalStore:ud,useId:Cd,unstable_isNewReconciler:!1},Xg={readContext:bt,useCallback:Dd,useContext:bt,useEffect:yl,useImperativeHandle:vd,useInsertionEffect:Ad,useLayoutEffect:bd,useMemo:yd,useReducer:Dl,useRef:hd,useState:function(){return Dl(Co)},useDebugValue:Tl,useDeferredValue:function(e){var t=_t();return null===we?t.memoizedState=e:Td(t,we.memoizedState,e)},useTransition:function(){return[Dl(Co)[0],_t().memoizedState]},useMutableSource:ld,useSyncExternalStore:ud,useId:Cd,unstable_isNewReconciler:!1};function Tr(e,t){try{var n="",r=t;do{n+=Cm(r),r=r.return}while(r);var o=n}catch(a){o="\nError generating stack: "+a.message+"\n"+a.stack}return{value:e,source:t,stack:o,digest:null}}function Cl(e,t,n){return{value:e,source:null,stack:n??null,digest:t??null}}function Nl(e,t){try{console.error(t.value)}catch(n){setTimeout((function(){throw n}))}}var Qg="function"==typeof WeakMap?WeakMap:Map;function xd(e,t,n){(n=tn(-1,n)).tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){Ya||(Ya=!0,Hl=r),Nl(0,t)},n}function Rd(e,t,n){(n=tn(-1,n)).tag=3;var r=e.type.getDerivedStateFromError;if("function"==typeof r){var o=t.value;n.payload=function(){return r(o)},n.callback=function(){Nl(0,t)}}var a=e.stateNode;return null!==a&&"function"==typeof a.componentDidCatch&&(n.callback=function(){Nl(0,t),"function"!=typeof r&&(null===Dn?Dn=new Set([this]):Dn.add(this));var s=t.stack;this.componentDidCatch(t.value,{componentStack:null!==s?s:""})}),n}function Ld(e,t,n){var r=e.pingCache;if(null===r){r=e.pingCache=new Qg;var o=new Set;r.set(t,o)}else void 0===(o=r.get(t))&&(o=new Set,r.set(t,o));o.has(n)||(o.add(n),e=ph.bind(null,e,t,n),t.then(e,e))}function Od(e){do{var t;if((t=13===e.tag)&&(t=null===(t=e.memoizedState)||null!==t.dehydrated),t)return e;e=e.return}while(null!==e);return null}function kd(e,t,n,r,o){return 1&e.mode?(e.flags|=65536,e.lanes=o,e):(e===t?e.flags|=65536:(e.flags|=128,n.flags|=131072,n.flags&=-52805,1===n.tag&&(null===n.alternate?n.tag=17:((t=tn(-1,1)).tag=2,_n(n,t,1))),n.lanes|=1),e)}var Jg=Yt.ReactCurrentOwner,tt=!1;function Ke(e,t,n,r){t.child=null===e?sd(t,null,n,r):Dr(t,e.child,n,r)}function Id(e,t,n,r,o){n=n.render;var a=t.ref;return vr(t,o),r=bl(e,t,n,r,a,o),n=_l(),null===e||tt?(be&&n&&el(t),t.flags|=1,Ke(e,t,r,o),t.child):(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,nn(e,t,o))}function Md(e,t,n,r,o){if(null===e){var a=n.type;return"function"!=typeof a||Wl(a)||void 0!==a.defaultProps||null!==n.compare||void 0!==n.defaultProps?((e=ts(n.type,null,r,t,t.mode,o)).ref=t.ref,e.return=t,t.child=e):(t.tag=15,t.type=a,Fd(e,t,a,r,o))}if(a=e.child,!(e.lanes&o)){var s=a.memoizedProps;if((n=null!==(n=n.compare)?n:po)(s,r)&&e.ref===t.ref)return nn(e,t,o)}return t.flags|=1,(e=Nn(a,r)).ref=t.ref,e.return=t,t.child=e}function Fd(e,t,n,r,o){if(null!==e){var a=e.memoizedProps;if(po(a,r)&&e.ref===t.ref){if(tt=!1,t.pendingProps=r=a,0==(e.lanes&o))return t.lanes=e.lanes,nn(e,t,o);131072&e.flags&&(tt=!0)}}return Sl(e,t,n,r,o)}function Bd(e,t,n){var r=t.pendingProps,o=r.children,a=null!==e?e.memoizedState:null;if("hidden"===r.mode)if(1&t.mode){if(!(1073741824&n))return e=null!==a?a.baseLanes|n:n,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e,cachePool:null,transitions:null},t.updateQueue=null,fe(Nr,pt),pt|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},r=null!==a?a.baseLanes:n,fe(Nr,pt),pt|=r}else t.memoizedState={baseLanes:0,cachePool:null,transitions:null},fe(Nr,pt),pt|=n;else null!==a?(r=a.baseLanes|n,t.memoizedState=null):r=n,fe(Nr,pt),pt|=r;return Ke(e,t,o,n),t.child}function Pd(e,t){var n=t.ref;(null===e&&null!==n||null!==e&&e.ref!==n)&&(t.flags|=512,t.flags|=2097152)}function Sl(e,t,n,r,o){var a=et(n)?On:qe.current;return a=hr(t,a),vr(t,o),n=bl(e,t,n,r,a,o),r=_l(),null===e||tt?(be&&r&&el(t),t.flags|=1,Ke(e,t,n,o),t.child):(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,nn(e,t,o))}function Ud(e,t,n,r,o){if(et(n)){var a=!0;Na(t)}else a=!1;if(vr(t,o),null===t.stateNode)Ga(e,t),nd(t,n,r),pl(t,n,r,o),r=!0;else if(null===e){var s=t.stateNode,i=t.memoizedProps;s.props=i;var l=s.context,u=n.contextType;"object"==typeof u&&null!==u?u=bt(u):u=hr(t,u=et(n)?On:qe.current);var c=n.getDerivedStateFromProps,d="function"==typeof c||"function"==typeof s.getSnapshotBeforeUpdate;d||"function"!=typeof s.UNSAFE_componentWillReceiveProps&&"function"!=typeof s.componentWillReceiveProps||(i!==r||l!==u)&&rd(t,s,r,u),bn=!1;var p=t.memoizedState;s.state=p,Ia(t,r,s,o),l=t.memoizedState,i!==r||p!==l||Je.current||bn?("function"==typeof c&&(dl(t,n,c,r),l=t.memoizedState),(i=bn||td(t,n,i,r,p,l,u))?(d||"function"!=typeof s.UNSAFE_componentWillMount&&"function"!=typeof s.componentWillMount||("function"==typeof s.componentWillMount&&s.componentWillMount(),"function"==typeof s.UNSAFE_componentWillMount&&s.UNSAFE_componentWillMount()),"function"==typeof s.componentDidMount&&(t.flags|=4194308)):("function"==typeof s.componentDidMount&&(t.flags|=4194308),t.memoizedProps=r,t.memoizedState=l),s.props=r,s.state=l,s.context=u,r=i):("function"==typeof s.componentDidMount&&(t.flags|=4194308),r=!1)}else{s=t.stateNode,X0(e,t),i=t.memoizedProps,u=t.type===t.elementType?i:Rt(t.type,i),s.props=u,d=t.pendingProps,p=s.context,"object"==typeof(l=n.contextType)&&null!==l?l=bt(l):l=hr(t,l=et(n)?On:qe.current);var m=n.getDerivedStateFromProps;(c="function"==typeof m||"function"==typeof s.getSnapshotBeforeUpdate)||"function"!=typeof s.UNSAFE_componentWillReceiveProps&&"function"!=typeof s.componentWillReceiveProps||(i!==d||p!==l)&&rd(t,s,r,l),bn=!1,p=t.memoizedState,s.state=p,Ia(t,r,s,o);var A=t.memoizedState;i!==d||p!==A||Je.current||bn?("function"==typeof m&&(dl(t,n,m,r),A=t.memoizedState),(u=bn||td(t,n,u,r,p,A,l)||!1)?(c||"function"!=typeof s.UNSAFE_componentWillUpdate&&"function"!=typeof s.componentWillUpdate||("function"==typeof s.componentWillUpdate&&s.componentWillUpdate(r,A,l),"function"==typeof s.UNSAFE_componentWillUpdate&&s.UNSAFE_componentWillUpdate(r,A,l)),"function"==typeof s.componentDidUpdate&&(t.flags|=4),"function"==typeof s.getSnapshotBeforeUpdate&&(t.flags|=1024)):("function"!=typeof s.componentDidUpdate||i===e.memoizedProps&&p===e.memoizedState||(t.flags|=4),"function"!=typeof s.getSnapshotBeforeUpdate||i===e.memoizedProps&&p===e.memoizedState||(t.flags|=1024),t.memoizedProps=r,t.memoizedState=A),s.props=r,s.state=A,s.context=l,r=u):("function"!=typeof s.componentDidUpdate||i===e.memoizedProps&&p===e.memoizedState||(t.flags|=4),"function"!=typeof s.getSnapshotBeforeUpdate||i===e.memoizedProps&&p===e.memoizedState||(t.flags|=1024),r=!1)}return wl(e,t,n,r,a,o)}function wl(e,t,n,r,o,a){Pd(e,t);var s=0!=(128&t.flags);if(!r&&!s)return o&&z0(t,n,!1),nn(e,t,a);r=t.stateNode,Jg.current=t;var i=s&&"function"!=typeof n.getDerivedStateFromError?null:r.render();return t.flags|=1,null!==e&&s?(t.child=Dr(t,e.child,null,a),t.child=Dr(t,null,i,a)):Ke(e,t,i,a),t.memoizedState=r.state,o&&z0(t,n,!0),t.child}function qd(e){var t=e.stateNode;t.pendingContext?H0(0,t.pendingContext,t.pendingContext!==t.context):t.context&&H0(0,t.context,!1),fl(e,t.containerInfo)}function Hd(e,t,n,r,o){return br(),ol(o),t.flags|=256,Ke(e,t,n,r),t.child}var $d,kl,Zd,jd,xl={dehydrated:null,treeContext:null,retryLane:0};function Rl(e){return{baseLanes:e,cachePool:null,transitions:null}}function Vd(e,t,n){var i,r=t.pendingProps,o=ve.current,a=!1,s=0!=(128&t.flags);if((i=s)||(i=(null===e||null!==e.memoizedState)&&0!=(2&o)),i?(a=!0,t.flags&=-129):(null===e||null!==e.memoizedState)&&(o|=1),fe(ve,1&o),null===e)return rl(t),null!==(e=t.memoizedState)&&null!==(e=e.dehydrated)?(1&t.mode?"$!"===e.data?t.lanes=8:t.lanes=1073741824:t.lanes=1,null):(s=r.children,e=r.fallback,a?(r=t.mode,a=t.child,s={mode:"hidden",children:s},1&r||null===a?a=ns(s,r,0,null):(a.childLanes=0,a.pendingProps=s),e=Vn(e,r,n,null),a.return=t,e.return=t,a.sibling=e,t.child=a,t.child.memoizedState=Rl(n),t.memoizedState=xl,e):Ll(t,s));if(null!==(o=e.memoizedState)&&null!==(i=o.dehydrated))return function(e,t,n,r,o,a,s){if(n)return 256&t.flags?(t.flags&=-257,r=Cl(Error(M(422))),za(e,t,s,r)):null!==t.memoizedState?(t.child=e.child,t.flags|=128,null):(a=r.fallback,o=t.mode,r=ns({mode:"visible",children:r.children},o,0,null),a=Vn(a,o,s,null),a.flags|=2,r.return=t,a.return=t,r.sibling=a,t.child=r,1&t.mode&&Dr(t,e.child,null,s),t.child.memoizedState=Rl(s),t.memoizedState=xl,a);if(!(1&t.mode))return za(e,t,s,null);if("$!"===o.data){if(r=o.nextSibling&&o.nextSibling.dataset)var i=r.dgst;return r=i,za(e,t,s,r=Cl(a=Error(M(419)),r,void 0))}if(i=0!=(s&e.childLanes),tt||i){if(null!==(r=Oe)){switch(s&-s){case 4:o=2;break;case 16:o=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:o=32;break;case 536870912:o=268435456;break;default:o=0}0!==(o=o&(r.suspendedLanes|s)?0:o)&&o!==a.retryLane&&(a.retryLane=o,en(e,o),kt(r,e,o,-1))}return jl(),za(e,t,s,r=Cl(Error(M(421))))}return"$?"===o.data?(t.flags|=128,t.child=e.child,t=fh.bind(null,e),o._reactRetry=t,null):(e=a.treeContext,dt=gn(o.nextSibling),ct=t,be=!0,xt=null,null!==e&&(Et[At++]=Qt,Et[At++]=Jt,Et[At++]=kn,Qt=e.id,Jt=e.overflow,kn=t),t=Ll(t,r.children),t.flags|=4096,t)}(e,t,s,r,i,o,n);if(a){a=r.fallback,s=t.mode,i=(o=e.child).sibling;var l={mode:"hidden",children:r.children};return 1&s||t.child===o?(r=Nn(o,l)).subtreeFlags=14680064&o.subtreeFlags:((r=t.child).childLanes=0,r.pendingProps=l,t.deletions=null),null!==i?a=Nn(i,a):(a=Vn(a,s,n,null)).flags|=2,a.return=t,r.return=t,r.sibling=a,t.child=r,r=a,a=t.child,s=null===(s=e.child.memoizedState)?Rl(n):{baseLanes:s.baseLanes|n,cachePool:null,transitions:s.transitions},a.memoizedState=s,a.childLanes=e.childLanes&~n,t.memoizedState=xl,r}return e=(a=e.child).sibling,r=Nn(a,{mode:"visible",children:r.children}),!(1&t.mode)&&(r.lanes=n),r.return=t,r.sibling=null,null!==e&&(null===(n=t.deletions)?(t.deletions=[e],t.flags|=16):n.push(e)),t.child=r,t.memoizedState=null,r}function Ll(e,t){return(t=ns({mode:"visible",children:t},e.mode,0,null)).return=e,e.child=t}function za(e,t,n,r){return null!==r&&ol(r),Dr(t,e.child,null,n),(e=Ll(t,t.pendingProps.children)).flags|=2,t.memoizedState=null,e}function zd(e,t,n){e.lanes|=t;var r=e.alternate;null!==r&&(r.lanes|=t),ll(e.return,t,n)}function Ol(e,t,n,r,o){var a=e.memoizedState;null===a?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:r,tail:n,tailMode:o}:(a.isBackwards=t,a.rendering=null,a.renderingStartTime=0,a.last=r,a.tail=n,a.tailMode=o)}function Gd(e,t,n){var r=t.pendingProps,o=r.revealOrder,a=r.tail;if(Ke(e,t,r.children,n),2&(r=ve.current))r=1&r|2,t.flags|=128;else{if(null!==e&&128&e.flags)e:for(e=t.child;null!==e;){if(13===e.tag)null!==e.memoizedState&&zd(e,n,t);else if(19===e.tag)zd(e,n,t);else if(null!==e.child){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;null===e.sibling;){if(null===e.return||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}r&=1}if(fe(ve,r),1&t.mode)switch(o){case"forwards":for(n=t.child,o=null;null!==n;)null!==(e=n.alternate)&&null===Ba(e)&&(o=n),n=n.sibling;null===(n=o)?(o=t.child,t.child=null):(o=n.sibling,n.sibling=null),Ol(t,!1,o,n,a);break;case"backwards":for(n=null,o=t.child,t.child=null;null!==o;){if(null!==(e=o.alternate)&&null===Ba(e)){t.child=o;break}e=o.sibling,o.sibling=n,n=o,o=e}Ol(t,!0,n,null,a);break;case"together":Ol(t,!1,null,null,void 0);break;default:t.memoizedState=null}else t.memoizedState=null;return t.child}function Ga(e,t){!(1&t.mode)&&null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2)}function nn(e,t,n){if(null!==e&&(t.dependencies=e.dependencies),Pn|=t.lanes,!(n&t.childLanes))return null;if(null!==e&&t.child!==e.child)throw Error(M(153));if(null!==t.child){for(n=Nn(e=t.child,e.pendingProps),t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,(n=n.sibling=Nn(e,e.pendingProps)).return=t;n.sibling=null}return t.child}function So(e,t){if(!be)switch(e.tailMode){case"hidden":t=e.tail;for(var n=null;null!==t;)null!==t.alternate&&(n=t),t=t.sibling;null===n?e.tail=null:n.sibling=null;break;case"collapsed":n=e.tail;for(var r=null;null!==n;)null!==n.alternate&&(r=n),n=n.sibling;null===r?t||null===e.tail?e.tail=null:e.tail.sibling=null:r.sibling=null}}function Ve(e){var t=null!==e.alternate&&e.alternate.child===e.child,n=0,r=0;if(t)for(var o=e.child;null!==o;)n|=o.lanes|o.childLanes,r|=14680064&o.subtreeFlags,r|=14680064&o.flags,o.return=e,o=o.sibling;else for(o=e.child;null!==o;)n|=o.lanes|o.childLanes,r|=o.subtreeFlags,r|=o.flags,o.return=e,o=o.sibling;return e.subtreeFlags|=r,e.childLanes=n,t}function nh(e,t,n){var r=t.pendingProps;switch(tl(t),t.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Ve(t),null;case 1:case 17:return et(t.type)&&Ca(),Ve(t),null;case 3:return r=t.stateNode,yr(),Ae(Je),Ae(qe),hl(),r.pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),(null===e||null===e.child)&&(Ra(t)?t.flags|=4:null===e||e.memoizedState.isDehydrated&&!(256&t.flags)||(t.flags|=1024,null!==xt&&(Gl(xt),xt=null))),kl(e,t),Ve(t),null;case 5:ml(t);var o=Fn(Do.current);if(n=t.type,null!==e&&null!=t.stateNode)Zd(e,t,n,r,o),e.ref!==t.ref&&(t.flags|=512,t.flags|=2097152);else{if(!r){if(null===t.stateNode)throw Error(M(166));return Ve(t),null}if(e=Fn(qt.current),Ra(t)){r=t.stateNode,n=t.type;var a=t.memoizedProps;switch(r[Ut]=t,r[Eo]=a,e=0!=(1&t.mode),n){case"dialog":Ee("cancel",r),Ee("close",r);break;case"iframe":case"object":case"embed":Ee("load",r);break;case"video":case"audio":for(o=0;o<\/script>",e=e.removeChild(e.firstChild)):"string"==typeof r.is?e=s.createElement(n,{is:r.is}):(e=s.createElement(n),"select"===n&&(s=e,r.multiple?s.multiple=!0:r.size&&(s.size=r.size))):e=s.createElementNS(e,n),e[Ut]=t,e[Eo]=r,$d(e,t,!1,!1),t.stateNode=e;e:{switch(s=mi(n,r),n){case"dialog":Ee("cancel",e),Ee("close",e),o=r;break;case"iframe":case"object":case"embed":Ee("load",e),o=r;break;case"video":case"audio":for(o=0;oSr&&(t.flags|=128,r=!0,So(a,!1),t.lanes=4194304)}else{if(!r)if(null!==(e=Ba(s))){if(t.flags|=128,r=!0,null!==(n=e.updateQueue)&&(t.updateQueue=n,t.flags|=4),So(a,!0),null===a.tail&&"hidden"===a.tailMode&&!s.alternate&&!be)return Ve(t),null}else 2*Ne()-a.renderingStartTime>Sr&&1073741824!==n&&(t.flags|=128,r=!0,So(a,!1),t.lanes=4194304);a.isBackwards?(s.sibling=t.child,t.child=s):(null!==(n=a.last)?n.sibling=s:t.child=s,a.last=s)}return null!==a.tail?(t=a.tail,a.rendering=t,a.tail=t.sibling,a.renderingStartTime=Ne(),t.sibling=null,n=ve.current,fe(ve,r?1&n|2:1&n),t):(Ve(t),null);case 22:case 23:return Zl(),r=null!==t.memoizedState,null!==e&&null!==e.memoizedState!==r&&(t.flags|=8192),r&&1&t.mode?1073741824&pt&&(Ve(t),6&t.subtreeFlags&&(t.flags|=8192)):Ve(t),null;case 24:case 25:return null}throw Error(M(156,t.tag))}function rh(e,t){switch(tl(t),t.tag){case 1:return et(t.type)&&Ca(),65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 3:return yr(),Ae(Je),Ae(qe),hl(),65536&(e=t.flags)&&!(128&e)?(t.flags=-65537&e|128,t):null;case 5:return ml(t),null;case 13:if(Ae(ve),null!==(e=t.memoizedState)&&null!==e.dehydrated){if(null===t.alternate)throw Error(M(340));br()}return 65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 19:return Ae(ve),null;case 4:return yr(),null;case 10:return il(t.type._context),null;case 22:case 23:return Zl(),null;default:return null}}$d=function(e,t){for(var n=t.child;null!==n;){if(5===n.tag||6===n.tag)e.appendChild(n.stateNode);else if(4!==n.tag&&null!==n.child){n.child.return=n,n=n.child;continue}if(n===t)break;for(;null===n.sibling;){if(null===n.return||n.return===t)return;n=n.return}n.sibling.return=n.return,n=n.sibling}},kl=function(){},Zd=function(e,t,n,r){var o=e.memoizedProps;if(o!==r){e=t.stateNode,Fn(qt.current);var s,a=null;switch(n){case"input":o=li(e,o),r=li(e,r),a=[];break;case"select":o=_e({},o,{value:void 0}),r=_e({},r,{value:void 0}),a=[];break;case"textarea":o=di(e,o),r=di(e,r),a=[];break;default:"function"!=typeof o.onClick&&"function"==typeof r.onClick&&(e.onclick=ya)}for(u in fi(n,r),n=null,o)if(!r.hasOwnProperty(u)&&o.hasOwnProperty(u)&&null!=o[u])if("style"===u){var i=o[u];for(s in i)i.hasOwnProperty(s)&&(n||(n={}),n[s]="")}else"dangerouslySetInnerHTML"!==u&&"children"!==u&&"suppressContentEditableWarning"!==u&&"suppressHydrationWarning"!==u&&"autoFocus"!==u&&(Gr.hasOwnProperty(u)?a||(a=[]):(a=a||[]).push(u,null));for(u in r){var l=r[u];if(i=null!=o?o[u]:void 0,r.hasOwnProperty(u)&&l!==i&&(null!=l||null!=i))if("style"===u)if(i){for(s in i)!i.hasOwnProperty(s)||l&&l.hasOwnProperty(s)||(n||(n={}),n[s]="");for(s in l)l.hasOwnProperty(s)&&i[s]!==l[s]&&(n||(n={}),n[s]=l[s])}else n||(a||(a=[]),a.push(u,n)),n=l;else"dangerouslySetInnerHTML"===u?(l=l?l.__html:void 0,i=i?i.__html:void 0,null!=l&&i!==l&&(a=a||[]).push(u,l)):"children"===u?"string"!=typeof l&&"number"!=typeof l||(a=a||[]).push(u,""+l):"suppressContentEditableWarning"!==u&&"suppressHydrationWarning"!==u&&(Gr.hasOwnProperty(u)?(null!=l&&"onScroll"===u&&Ee("scroll",e),a||i===l||(a=[])):(a=a||[]).push(u,l))}n&&(a=a||[]).push("style",n);var u=a;(t.updateQueue=u)&&(t.flags|=4)}},jd=function(e,t,n,r){n!==r&&(t.flags|=4)};var $a=!1,ze=!1,oh="function"==typeof WeakSet?WeakSet:Set,U=null;function Cr(e,t){var n=e.ref;if(null!==n)if("function"==typeof n)try{n(null)}catch(r){Te(e,t,r)}else n.current=null}function Il(e,t,n){try{n()}catch(r){Te(e,t,r)}}var Wd=!1;function wo(e,t,n){var r=t.updateQueue;if(null!==(r=null!==r?r.lastEffect:null)){var o=r=r.next;do{if((o.tag&e)===e){var a=o.destroy;o.destroy=void 0,void 0!==a&&Il(t,n,a)}o=o.next}while(o!==r)}}function Za(e,t){if(null!==(t=null!==(t=t.updateQueue)?t.lastEffect:null)){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Ml(e){var t=e.ref;if(null!==t){var n=e.stateNode;e.tag,e=n,"function"==typeof t?t(e):t.current=e}}function Yd(e){var t=e.alternate;null!==t&&(e.alternate=null,Yd(t)),e.child=null,e.deletions=null,e.sibling=null,5===e.tag&&(null!==(t=e.stateNode)&&(delete t[Ut],delete t[Eo],delete t[Xi],delete t[Hg],delete t[Vg])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Kd(e){return 5===e.tag||3===e.tag||4===e.tag}function Xd(e){e:for(;;){for(;null===e.sibling;){if(null===e.return||Kd(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;5!==e.tag&&6!==e.tag&&18!==e.tag;){if(2&e.flags||null===e.child||4===e.tag)continue e;e.child.return=e,e=e.child}if(!(2&e.flags))return e.stateNode}}function Fl(e,t,n){var r=e.tag;if(5===r||6===r)e=e.stateNode,t?8===n.nodeType?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(8===n.nodeType?(t=n.parentNode).insertBefore(e,n):(t=n).appendChild(e),null!=(n=n._reactRootContainer)||null!==t.onclick||(t.onclick=ya));else if(4!==r&&null!==(e=e.child))for(Fl(e,t,n),e=e.sibling;null!==e;)Fl(e,t,n),e=e.sibling}function Bl(e,t,n){var r=e.tag;if(5===r||6===r)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(4!==r&&null!==(e=e.child))for(Bl(e,t,n),e=e.sibling;null!==e;)Bl(e,t,n),e=e.sibling}var Fe=null,Lt=!1;function vn(e,t,n){for(n=n.child;null!==n;)Qd(e,t,n),n=n.sibling}function Qd(e,t,n){if(Pt&&"function"==typeof Pt.onCommitFiberUnmount)try{Pt.onCommitFiberUnmount(aa,n)}catch{}switch(n.tag){case 5:ze||Cr(n,t);case 6:var r=Fe,o=Lt;Fe=null,vn(e,t,n),Lt=o,null!==(Fe=r)&&(Lt?(e=Fe,n=n.stateNode,8===e.nodeType?e.parentNode.removeChild(n):e.removeChild(n)):Fe.removeChild(n.stateNode));break;case 18:null!==Fe&&(Lt?(e=Fe,n=n.stateNode,8===e.nodeType?Ki(e.parentNode,n):1===e.nodeType&&Ki(e,n),ao(e)):Ki(Fe,n.stateNode));break;case 4:r=Fe,o=Lt,Fe=n.stateNode.containerInfo,Lt=!0,vn(e,t,n),Fe=r,Lt=o;break;case 0:case 11:case 14:case 15:if(!ze&&(null!==(r=n.updateQueue)&&null!==(r=r.lastEffect))){o=r=r.next;do{var a=o,s=a.destroy;a=a.tag,void 0!==s&&(2&a||4&a)&&Il(n,t,s),o=o.next}while(o!==r)}vn(e,t,n);break;case 1:if(!ze&&(Cr(n,t),"function"==typeof(r=n.stateNode).componentWillUnmount))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(i){Te(n,t,i)}vn(e,t,n);break;case 21:vn(e,t,n);break;case 22:1&n.mode?(ze=(r=ze)||null!==n.memoizedState,vn(e,t,n),ze=r):vn(e,t,n);break;default:vn(e,t,n)}}function Jd(e){var t=e.updateQueue;if(null!==t){e.updateQueue=null;var n=e.stateNode;null===n&&(n=e.stateNode=new oh),t.forEach((function(r){var o=mh.bind(null,e,r);n.has(r)||(n.add(r),r.then(o,o))}))}}function Ot(e,t){var n=t.deletions;if(null!==n)for(var r=0;ro&&(o=s),r&=~a}if(r=o,10<(r=(120>(r=Ne()-r)?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*ih(r/1960))-r)){e.timeoutHandle=Yi(Hn.bind(null,e,nt,rn),r);break}Hn(e,nt,rn);break;default:throw Error(M(329))}}}return rt(e,Ne()),e.callbackNode===n?a2.bind(null,e):null}function zl(e,t){var n=Ro;return e.current.memoizedState.isDehydrated&&(qn(e,t).flags|=256),2!==(e=es(e,t))&&(t=nt,nt=n,null!==t&&Gl(t)),e}function Gl(e){null===nt?nt=e:nt.push.apply(nt,e)}function Cn(e,t){for(t&=~Ul,t&=~Wa,e.suspendedLanes|=t,e.pingedLanes&=~t,e=e.expirationTimes;0e?16:e,null===yn)var r=!1;else{if(e=yn,yn=null,Xa=0,6&re)throw Error(M(331));var o=re;for(re|=4,U=e.current;null!==U;){var a=U,s=a.child;if(16&U.flags){var i=a.deletions;if(null!==i){for(var l=0;lNe()-ql?qn(e,0):Ul|=n),rt(e,t)}function p2(e,t){0===t&&(1&e.mode?(t=ia,!(130023424&(ia<<=1))&&(ia=4194304)):t=1);var n=Xe();null!==(e=en(e,t))&&(eo(e,t,n),rt(e,n))}function fh(e){var t=e.memoizedState,n=0;null!==t&&(n=t.retryLane),p2(e,n)}function mh(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,o=e.memoizedState;null!==o&&(n=o.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(M(314))}null!==r&&r.delete(t),p2(e,n)}function m2(e,t){return Zc(e,t)}function gh(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Dt(e,t,n,r){return new gh(e,t,n,r)}function Wl(e){return!(!(e=e.prototype)||!e.isReactComponent)}function Nn(e,t){var n=e.alternate;return null===n?((n=Dt(e.tag,t,e.key,e.mode)).elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=14680064&e.flags,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=null===t?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function ts(e,t,n,r,o,a){var s=2;if(r=e,"function"==typeof e)Wl(e)&&(s=1);else if("string"==typeof e)s=5;else e:switch(e){case rr:return Vn(n.children,o,a,t);case Qs:s=8,o|=8;break;case Js:return(e=Dt(12,n,t,2|o)).elementType=Js,e.lanes=a,e;case ti:return(e=Dt(13,n,t,o)).elementType=ti,e.lanes=a,e;case ni:return(e=Dt(19,n,t,o)).elementType=ni,e.lanes=a,e;case Dc:return ns(n,o,a,t);default:if("object"==typeof e&&null!==e)switch(e.$$typeof){case _c:s=10;break e;case vc:s=9;break e;case ei:s=11;break e;case ri:s=14;break e;case sn:s=16,r=null;break e}throw Error(M(130,null==e?e:typeof e,""))}return(t=Dt(s,n,t,o)).elementType=e,t.type=r,t.lanes=a,t}function Vn(e,t,n,r){return(e=Dt(7,e,r,t)).lanes=n,e}function ns(e,t,n,r){return(e=Dt(22,e,r,t)).elementType=Dc,e.lanes=n,e.stateNode={isHidden:!1},e}function Yl(e,t,n){return(e=Dt(6,e,null,t)).lanes=n,e}function Kl(e,t,n){return(t=Dt(4,null!==e.children?e.children:[],e.key,t)).lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Eh(e,t,n,r,o){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=yi(0),this.expirationTimes=yi(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=yi(0),this.identifierPrefix=r,this.onRecoverableError=o,this.mutableSourceEagerHydrationData=null}function Xl(e,t,n,r,o,a,s,i,l){return e=new Eh(e,t,n,i,l),1===t?(t=1,!0===a&&(t|=8)):t=0,a=Dt(3,null,null,t),e.current=a,a.stateNode=e,a.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},cl(a),e}function g2(e){if(!e)return En;e:{if(Rn(e=e._reactInternals)!==e||1!==e.tag)throw Error(M(170));var t=e;do{switch(t.tag){case 3:t=t.stateNode.context;break e;case 1:if(et(t.type)){t=t.stateNode.__reactInternalMemoizedMergedChildContext;break e}}t=t.return}while(null!==t);throw Error(M(171))}if(1===e.tag){var n=e.type;if(et(n))return V0(e,n,t)}return t}function h2(e,t,n,r,o,a,s,i,l){return(e=Xl(n,r,!0,e,0,a,0,i,l)).context=g2(null),n=e.current,(a=tn(r=Xe(),o=Tn(n))).callback=t??null,_n(n,a,o),e.current.lanes=o,eo(e,o,r),rt(e,r),e}function rs(e,t,n,r){var o=t.current,a=Xe(),s=Tn(o);return n=g2(n),null===t.context?t.context=n:t.pendingContext=n,(t=tn(a,s)).payload={element:e},null!==(r=void 0===r?null:r)&&(t.callback=r),null!==(e=_n(o,t,s))&&(kt(e,o,s,a),ka(e,o,s)),s}function os(e){return(e=e.current).child?(e.child.tag,e.child.stateNode):null}function E2(e,t){if(null!==(e=e.memoizedState)&&null!==e.dehydrated){var n=e.retryLane;e.retryLane=0!==n&&n"u"||"function"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(_2)}catch(e){console.error(e)}}(),fc.exports=it;var v2=fc.exports;js.createRoot=v2.createRoot,js.hydrateRoot=v2.hydrateRoot;const D2={embedId:null,baseApiUrl:null,prompt:null,model:null,temperature:null,chatIcon:"plus",brandImageUrl:null,greeting:null,buttonColor:"#262626",userBgColor:"#2C2F35",assistantBgColor:"#2563eb",noSponsor:null,sponsorText:"Powered by AnythingLLM",sponsorLink:"https://anythingllm.com",position:"bottom-right",assistantName:"AnythingLLM Chat Assistant",assistantIcon:null,windowHeight:null,windowWidth:null,textSize:null,openOnLoad:"off",supportEmail:null,username:null,defaultMessages:[]};const y2={_fallbacks:{defaultMessages:[]},defaultMessages:function(e=null){if("string"!=typeof e)return this._fallbacks.defaultMessages;try{const t=e.split(",");if(!Array.isArray(t)||0===t.length||!t.every((n=>"string"==typeof n&&n.length>0)))throw new Error("Invalid default-messages attribute value. Must be array of strings");return t.map((n=>n.trim()))}catch(t){return console.error("AnythingLLMEmbed",t),this._fallbacks.defaultMessages}}};function Ch(e={}){const t={};for(let[n,r]of Object.entries(e)){if(!y2.hasOwnProperty(n)){t[n]=r;continue}const o=y2[n](r);t[n]=o}return t}let us;const Nh=new Uint8Array(16);function Sh(){if(!us&&(us=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!us))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return us(Nh)}const Pe=[];for(let e=0;e<256;++e)Pe.push((e+256).toString(16).slice(1));const T2={randomUUID:typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function zn(e,t,n){if(T2.randomUUID&&!t&&!e)return T2.randomUUID();const r=(e=e||{}).random||(e.rng||Sh)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){n=n||0;for(let o=0;o<16;++o)t[n+o]=r[o];return t}return function(e,t=0){return Pe[e[t+0]]+Pe[e[t+1]]+Pe[e[t+2]]+Pe[e[t+3]]+"-"+Pe[e[t+4]]+Pe[e[t+5]]+"-"+Pe[e[t+6]]+Pe[e[t+7]]+"-"+Pe[e[t+8]]+Pe[e[t+9]]+"-"+Pe[e[t+10]]+Pe[e[t+11]]+Pe[e[t+12]]+Pe[e[t+13]]+Pe[e[t+14]]+Pe[e[t+15]]}(r)}function C2(){const[e,t]=Z.useState("");return Z.useEffect((()=>{!function(){var s,i;if(!window||null==(s=null==pe?void 0:pe.settings)||!s.embedId)return;const r=`allm_${null==(i=null==pe?void 0:pe.settings)?void 0:i.embedId}_session_id`,o=window.localStorage.getItem(r);if(o)return console.log("Resuming session id",o),void t(o);const a=zn();console.log("Registering new session id",a),window.localStorage.setItem(r,a),t(a)}()}),[window]),e}const tu="___anythingllm-chat-widget-open___";const Lh="\npre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!\n Theme: GitHub Dark Dimmed\n Description: Dark dimmed theme as seen on github.com\n Author: github.com\n Maintainer: @Hirse\n Updated: 2021-05-15\n\n Colors taken from GitHub's CSS\n*/.hljs{color:#adbac7;background:#22272e}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#f47067}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#dcbdfb}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#6cb6ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#96d0ff}.hljs-built_in,.hljs-symbol{color:#f69d50}.hljs-code,.hljs-comment,.hljs-formula{color:#768390}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#8ddb8c}.hljs-subst{color:#adbac7}.hljs-section{color:#316dca;font-weight:700}.hljs-bullet{color:#eac55f}.hljs-emphasis{color:#adbac7;font-style:italic}.hljs-strong{color:#adbac7;font-weight:700}.hljs-addition{color:#b4f1b4;background-color:#1b4721}.hljs-deletion{color:#ffd8d3;background-color:#78191b}\n",Oh='\n /**\n * ==============================================\n * Dot Falling\n * ==============================================\n */\n .allm-dot-falling {\n position: relative;\n left: -9999px;\n width: 10px;\n height: 10px;\n border-radius: 5px;\n background-color: #000000;\n color: #5fa4fa;\n box-shadow: 9999px 0 0 0 #000000;\n animation: dot-falling 1.5s infinite linear;\n animation-delay: 0.1s;\n }\n\n .allm-dot-falling::before,\n .allm-dot-falling::after {\n content: "";\n display: inline-block;\n position: absolute;\n top: 0;\n }\n\n .allm-dot-falling::before {\n width: 10px;\n height: 10px;\n border-radius: 5px;\n background-color: #000000;\n color: #000000;\n animation: dot-falling-before 1.5s infinite linear;\n animation-delay: 0s;\n }\n\n .allm-dot-falling::after {\n width: 10px;\n height: 10px;\n border-radius: 5px;\n background-color: #000000;\n color: #000000;\n animation: dot-falling-after 1.5s infinite linear;\n animation-delay: 0.2s;\n }\n\n @keyframes dot-falling {\n 0% {\n box-shadow: 9999px -15px 0 0 rgba(152, 128, 255, 0);\n }\n 25%,\n 50%,\n 75% {\n box-shadow: 9999px 0 0 0 #000000;\n }\n 100% {\n box-shadow: 9999px 15px 0 0 rgba(152, 128, 255, 0);\n }\n }\n\n @keyframes dot-falling-before {\n 0% {\n box-shadow: 9984px -15px 0 0 rgba(152, 128, 255, 0);\n }\n 25%,\n 50%,\n 75% {\n box-shadow: 9984px 0 0 0 #000000;\n }\n 100% {\n box-shadow: 9984px 15px 0 0 rgba(152, 128, 255, 0);\n }\n }\n\n @keyframes dot-falling-after {\n 0% {\n box-shadow: 10014px -15px 0 0 rgba(152, 128, 255, 0);\n }\n 25%,\n 50%,\n 75% {\n box-shadow: 10014px 0 0 0 #000000;\n }\n 100% {\n box-shadow: 10014px 15px 0 0 rgba(152, 128, 255, 0);\n }\n }\n\n #chat-history::-webkit-scrollbar,\n #chat-container::-webkit-scrollbar,\n .allm-no-scroll::-webkit-scrollbar {\n display: none !important;\n }\n\n /* Hide scrollbar for IE, Edge and Firefox */\n #chat-history,\n #chat-container,\n .allm-no-scroll {\n -ms-overflow-style: none !important; /* IE and Edge */\n scrollbar-width: none !important; /* Firefox */\n }\n\n span.allm-whitespace-pre-line>p {\n margin: 0px;\n }\n';function kh(){return w.jsxs("head",{children:[w.jsx("style",{children:Lh}),w.jsx("style",{children:Oh}),w.jsx("link",{rel:"stylesheet",href:pe.stylesSrc})]})}const Ih=Z.createContext({color:"currentColor",size:"1em",weight:"regular",mirrored:!1});var Mh=Object.defineProperty,cs=Object.getOwnPropertySymbols,N2=Object.prototype.hasOwnProperty,S2=Object.prototype.propertyIsEnumerable,w2=(e,t,n)=>t in e?Mh(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,x2=(e,t)=>{for(var n in t||(t={}))N2.call(t,n)&&w2(e,n,t[n]);if(cs)for(var n of cs(t))S2.call(t,n)&&w2(e,n,t[n]);return e},R2=(e,t)=>{var n={};for(var r in e)N2.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&cs)for(var r of cs(e))t.indexOf(r)<0&&S2.call(e,r)&&(n[r]=e[r]);return n};const Ue=Z.forwardRef(((e,t)=>{const n=e,{alt:r,color:o,size:a,weight:s,mirrored:i,children:l,weights:u}=n,c=R2(n,["alt","color","size","weight","mirrored","children","weights"]),d=Z.useContext(Ih),{color:p="currentColor",size:m,weight:A="regular",mirrored:b=!1}=d,C=R2(d,["color","size","weight","mirrored"]);return f.createElement("svg",x2(x2({ref:t,xmlns:"http://www.w3.org/2000/svg",width:a??m,height:a??m,fill:o??p,viewBox:"0 0 256 256",transform:i||b?"scale(-1, 1)":void 0},C),c),!!r&&f.createElement("title",null,r),l,u.get(s??A))}));Ue.displayName="IconBase";const Fh=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M228,128a100,100,0,0,1-98.66,100H128a99.39,99.39,0,0,1-68.62-27.29,12,12,0,0,1,16.48-17.45,76,76,0,1,0-1.57-109c-.13.13-.25.25-.39.37L54.89,92H72a12,12,0,0,1,0,24H24a12,12,0,0,1-12-12V56a12,12,0,0,1,24,0V76.72L57.48,57.06A100,100,0,0,1,228,128Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M72,104H24V56Z",opacity:"0.2"}),f.createElement("path",{d:"M195.88,60.08A96.08,96.08,0,0,0,60.25,60L49.31,70,29.66,50.3A8,8,0,0,0,16,56v48a8,8,0,0,0,8,8H72a8,8,0,0,0,5.66-13.66l-17-17,10.54-9.65a3.07,3.07,0,0,0,.26-.25,80,80,0,1,1,1.65,114.78,8,8,0,0,0-11,11.63A95.38,95.38,0,0,0,128,224h1.32A96,96,0,0,0,195.88,60.08ZM32,96V75.28L52.69,96Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,128a96,96,0,0,1-94.71,96H128A95.38,95.38,0,0,1,62.1,197.8a8,8,0,0,1,11-11.63A80,80,0,1,0,71.43,71.39a3.07,3.07,0,0,1-.26.25L60.63,81.29l17,17A8,8,0,0,1,72,112H24a8,8,0,0,1-8-8V56A8,8,0,0,1,29.66,50.3L49.31,70,60.25,60A96,96,0,0,1,224,128Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M222,128a94,94,0,0,1-92.74,94H128a93.43,93.43,0,0,1-64.5-25.65,6,6,0,1,1,8.24-8.72A82,82,0,1,0,70,70l-.19.19L39.44,98H72a6,6,0,0,1,0,12H24a6,6,0,0,1-6-6V56a6,6,0,0,1,12,0V90.34L61.63,61.4A94,94,0,0,1,222,128Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,128a96,96,0,0,1-94.71,96H128A95.38,95.38,0,0,1,62.1,197.8a8,8,0,0,1,11-11.63A80,80,0,1,0,71.43,71.39a3.07,3.07,0,0,1-.26.25L44.59,96H72a8,8,0,0,1,0,16H24a8,8,0,0,1-8-8V56a8,8,0,0,1,16,0V85.8L60.25,60A96,96,0,0,1,224,128Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M220,128a92,92,0,0,1-90.77,92H128a91.47,91.47,0,0,1-63.13-25.1,4,4,0,1,1,5.5-5.82A84,84,0,1,0,68.6,68.57l-.13.12L34.3,100H72a4,4,0,0,1,0,8H24a4,4,0,0,1-4-4V56a4,4,0,0,1,8,0V94.89l35-32A92,92,0,0,1,220,128Z"}))]]);var Bh=Object.defineProperty,Ph=Object.defineProperties,Uh=Object.getOwnPropertyDescriptors,L2=Object.getOwnPropertySymbols,qh=Object.prototype.hasOwnProperty,Hh=Object.prototype.propertyIsEnumerable,O2=(e,t,n)=>t in e?Bh(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const k2=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>Ph(e,Uh(t)))(((e,t)=>{for(var n in t||(t={}))qh.call(t,n)&&O2(e,n,t[n]);if(L2)for(var n of L2(t))Hh.call(t,n)&&O2(e,n,t[n]);return e})({ref:t},e),{weights:Fh}))));k2.displayName="ArrowCounterClockwise";const Gh=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M208.49,152.49l-72,72a12,12,0,0,1-17,0l-72-72a12,12,0,0,1,17-17L116,187V40a12,12,0,0,1,24,0V187l51.51-51.52a12,12,0,0,1,17,17Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M200,144l-72,72L56,144Z",opacity:"0.2"}),f.createElement("path",{d:"M207.39,140.94A8,8,0,0,0,200,136H136V40a8,8,0,0,0-16,0v96H56a8,8,0,0,0-5.66,13.66l72,72a8,8,0,0,0,11.32,0l72-72A8,8,0,0,0,207.39,140.94ZM128,204.69,75.31,152H180.69Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M205.66,149.66l-72,72a8,8,0,0,1-11.32,0l-72-72A8,8,0,0,1,56,136h64V40a8,8,0,0,1,16,0v96h64a8,8,0,0,1,5.66,13.66Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M204.24,148.24l-72,72a6,6,0,0,1-8.48,0l-72-72a6,6,0,0,1,8.48-8.48L122,201.51V40a6,6,0,0,1,12,0V201.51l61.76-61.75a6,6,0,0,1,8.48,8.48Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M205.66,149.66l-72,72a8,8,0,0,1-11.32,0l-72-72a8,8,0,0,1,11.32-11.32L120,196.69V40a8,8,0,0,1,16,0V196.69l58.34-58.35a8,8,0,0,1,11.32,11.32Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M202.83,146.83l-72,72a4,4,0,0,1-5.66,0l-72-72a4,4,0,0,1,5.66-5.66L124,206.34V40a4,4,0,0,1,8,0V206.34l65.17-65.17a4,4,0,0,1,5.66,5.66Z"}))]]);var $h=Object.defineProperty,Zh=Object.defineProperties,jh=Object.getOwnPropertyDescriptors,I2=Object.getOwnPropertySymbols,Wh=Object.prototype.hasOwnProperty,Yh=Object.prototype.propertyIsEnumerable,M2=(e,t,n)=>t in e?$h(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const F2=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>Zh(e,jh(t)))(((e,t)=>{for(var n in t||(t={}))Wh.call(t,n)&&M2(e,n,t[n]);if(I2)for(var n of I2(t))Yh.call(t,n)&&M2(e,n,t[n]);return e})({ref:t},e),{weights:Gh}))));F2.displayName="ArrowDown";const Qh=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M241,150.65s0,0,0-.05a51.33,51.33,0,0,0-2.53-5.9L196.93,50.18a12,12,0,0,0-2.5-3.65,36,36,0,0,0-50.92,0A12,12,0,0,0,140,55V76H116V55a12,12,0,0,0-3.51-8.48,36,36,0,0,0-50.92,0,12,12,0,0,0-2.5,3.65L17.53,144.7A51.33,51.33,0,0,0,15,150.6s0,0,0,.05A52,52,0,1,0,116,168V100h24v68a52,52,0,1,0,101-17.35ZM80,62.28a12,12,0,0,1,12-1.22v63.15a51.9,51.9,0,0,0-35.9-7.62ZM64,196a28,28,0,1,1,28-28A28,28,0,0,1,64,196ZM164,61.06a12.06,12.06,0,0,1,12,1.22l23.87,54.31a51.9,51.9,0,0,0-35.9,7.62ZM192,196a28,28,0,1,1,28-28A28,28,0,0,1,192,196Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M104,168a40,40,0,1,1-40-40A40,40,0,0,1,104,168Zm88-40a40,40,0,1,0,40,40A40,40,0,0,0,192,128Z",opacity:"0.2"}),f.createElement("path",{d:"M237.2,151.87v0a47.1,47.1,0,0,0-2.35-5.45L193.26,51.8a7.82,7.82,0,0,0-1.66-2.44,32,32,0,0,0-45.26,0A8,8,0,0,0,144,55V80H112V55a8,8,0,0,0-2.34-5.66,32,32,0,0,0-45.26,0,7.82,7.82,0,0,0-1.66,2.44L21.15,146.4a47.1,47.1,0,0,0-2.35,5.45v0A48,48,0,1,0,112,168V96h32v72a48,48,0,1,0,93.2-16.13ZM76.71,59.75a16,16,0,0,1,19.29-1v73.51a47.9,47.9,0,0,0-46.79-9.92ZM64,200a32,32,0,1,1,32-32A32,32,0,0,1,64,200ZM160,58.74a16,16,0,0,1,19.29,1l27.5,62.58A47.9,47.9,0,0,0,160,132.25ZM192,200a32,32,0,1,1,32-32A32,32,0,0,1,192,200Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M237.22,151.9l0-.1a1.42,1.42,0,0,0-.07-.22,48.46,48.46,0,0,0-2.31-5.3L193.27,51.8a8,8,0,0,0-1.67-2.44,32,32,0,0,0-45.26,0A8,8,0,0,0,144,55V80H112V55a8,8,0,0,0-2.34-5.66,32,32,0,0,0-45.26,0,8,8,0,0,0-1.67,2.44L21.2,146.28a48.46,48.46,0,0,0-2.31,5.3,1.72,1.72,0,0,0-.07.21s0,.08,0,.11a48,48,0,0,0,90.32,32.51,47.49,47.49,0,0,0,2.9-16.59V96h32v71.83a47.49,47.49,0,0,0,2.9,16.59,48,48,0,0,0,90.32-32.51Zm-143.15,27a32,32,0,0,1-60.2-21.71l1.81-4.13A32,32,0,0,1,96,167.88V168h0A32,32,0,0,1,94.07,178.94ZM203,198.07A32,32,0,0,1,160,168h0v-.11a32,32,0,0,1,60.32-14.78l1.81,4.13A32,32,0,0,1,203,198.07Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M233,147.24,191.43,52.6a6,6,0,0,0-1.25-1.83,30,30,0,0,0-42.42,0A6,6,0,0,0,146,55V82H110V55a6,6,0,0,0-1.76-4.25,30,30,0,0,0-42.42,0,6,6,0,0,0-1.25,1.83L23,147.24A46,46,0,1,0,110,168V94h36v74a46,46,0,1,0,87-20.76ZM64,202a34,34,0,1,1,34-34A34,34,0,0,1,64,202Zm0-80a45.77,45.77,0,0,0-18.55,3.92L75.06,58.54A18,18,0,0,1,98,57.71V137A45.89,45.89,0,0,0,64,122Zm94-64.28a18,18,0,0,1,22.94.83l29.61,67.37A45.9,45.9,0,0,0,158,137ZM192,202a34,34,0,1,1,34-34A34,34,0,0,1,192,202Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M237.2,151.87v0a47.1,47.1,0,0,0-2.35-5.45L193.26,51.8a7.82,7.82,0,0,0-1.66-2.44,32,32,0,0,0-45.26,0A8,8,0,0,0,144,55V80H112V55a8,8,0,0,0-2.34-5.66,32,32,0,0,0-45.26,0,7.82,7.82,0,0,0-1.66,2.44L21.15,146.4a47.1,47.1,0,0,0-2.35,5.45v0A48,48,0,1,0,112,168V96h32v72a48,48,0,1,0,93.2-16.13ZM76.71,59.75a16,16,0,0,1,19.29-1v73.51a47.9,47.9,0,0,0-46.79-9.92ZM64,200a32,32,0,1,1,32-32A32,32,0,0,1,64,200ZM160,58.74a16,16,0,0,1,19.29,1l27.5,62.58A47.9,47.9,0,0,0,160,132.25ZM192,200a32,32,0,1,1,32-32A32,32,0,0,1,192,200Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M231.22,148.09,189.6,53.41a3.94,3.94,0,0,0-.83-1.22,28,28,0,0,0-39.6,0A4,4,0,0,0,148,55V84H108V55a4,4,0,0,0-1.17-2.83,28,28,0,0,0-39.6,0,3.94,3.94,0,0,0-.83,1.22L24.78,148.09A44,44,0,1,0,108,168V92h40v76a44,44,0,1,0,83.22-19.91ZM64,204a36,36,0,1,1,36-36A36,36,0,0,1,64,204Zm0-80a43.78,43.78,0,0,0-22.66,6.3L73.4,57.35a20,20,0,0,1,26.6-.59v86A44,44,0,0,0,64,124Zm92-67.23a20,20,0,0,1,26.6.59l32.06,72.94A43.92,43.92,0,0,0,156,142.74ZM192,204a36,36,0,1,1,36-36A36,36,0,0,1,192,204Z"}))]]);var Jh=Object.defineProperty,eE=Object.defineProperties,tE=Object.getOwnPropertyDescriptors,B2=Object.getOwnPropertySymbols,nE=Object.prototype.hasOwnProperty,rE=Object.prototype.propertyIsEnumerable,P2=(e,t,n)=>t in e?Jh(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const U2=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>eE(e,tE(t)))(((e,t)=>{for(var n in t||(t={}))nE.call(t,n)&&P2(e,n,t[n]);if(B2)for(var n of B2(t))rE.call(t,n)&&P2(e,n,t[n]);return e})({ref:t},e),{weights:Qh}))));U2.displayName="Binoculars";const sE=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M120,128a16,16,0,1,1-16-16A16,16,0,0,1,120,128Zm32-16a16,16,0,1,0,16,16A16,16,0,0,0,152,112Zm84,16A108,108,0,0,1,78.77,224.15L46.34,235A20,20,0,0,1,21,209.66l10.81-32.43A108,108,0,1,1,236,128Zm-24,0A84,84,0,1,0,55.27,170.06a12,12,0,0,1,1,9.81l-9.93,29.79,29.79-9.93a12.1,12.1,0,0,1,3.8-.62,12,12,0,0,1,6,1.62A84,84,0,0,0,212,128Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,128A96,96,0,0,1,79.93,211.11h0L42.54,223.58a8,8,0,0,1-10.12-10.12l12.47-37.39h0A96,96,0,1,1,224,128Z",opacity:"0.2"}),f.createElement("path",{d:"M128,24A104,104,0,0,0,36.18,176.88L24.83,210.93a16,16,0,0,0,20.24,20.24l34.05-11.35A104,104,0,1,0,128,24Zm0,192a87.87,87.87,0,0,1-44.06-11.81,8,8,0,0,0-4-1.08,7.85,7.85,0,0,0-2.53.42L40,216,52.47,178.6a8,8,0,0,0-.66-6.54A88,88,0,1,1,128,216Zm12-88a12,12,0,1,1-12-12A12,12,0,0,1,140,128Zm-44,0a12,12,0,1,1-12-12A12,12,0,0,1,96,128Zm88,0a12,12,0,1,1-12-12A12,12,0,0,1,184,128Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M128,24A104,104,0,0,0,36.18,176.88L24.83,210.93a16,16,0,0,0,20.24,20.24l34.05-11.35A104,104,0,1,0,128,24ZM84,140a12,12,0,1,1,12-12A12,12,0,0,1,84,140Zm44,0a12,12,0,1,1,12-12A12,12,0,0,1,128,140Zm44,0a12,12,0,1,1,12-12A12,12,0,0,1,172,140Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M138,128a10,10,0,1,1-10-10A10,10,0,0,1,138,128ZM84,118a10,10,0,1,0,10,10A10,10,0,0,0,84,118Zm88,0a10,10,0,1,0,10,10A10,10,0,0,0,172,118Zm58,10A102,102,0,0,1,79.31,217.65L44.44,229.27a14,14,0,0,1-17.71-17.71l11.62-34.87A102,102,0,1,1,230,128Zm-12,0A90,90,0,1,0,50.08,173.06a6,6,0,0,1,.5,4.91L38.12,215.35a2,2,0,0,0,2.53,2.53L78,205.42a6.2,6.2,0,0,1,1.9-.31,6.09,6.09,0,0,1,3,.81A90,90,0,0,0,218,128Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128ZM84,116a12,12,0,1,0,12,12A12,12,0,0,0,84,116Zm88,0a12,12,0,1,0,12,12A12,12,0,0,0,172,116Zm60,12A104,104,0,0,1,79.12,219.82L45.07,231.17a16,16,0,0,1-20.24-20.24l11.35-34.05A104,104,0,1,1,232,128Zm-16,0A88,88,0,1,0,51.81,172.06a8,8,0,0,1,.66,6.54L40,216,77.4,203.53a7.85,7.85,0,0,1,2.53-.42,8,8,0,0,1,4,1.08A88,88,0,0,0,216,128Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M136,128a8,8,0,1,1-8-8A8,8,0,0,1,136,128Zm-52-8a8,8,0,1,0,8,8A8,8,0,0,0,84,120Zm88,0a8,8,0,1,0,8,8A8,8,0,0,0,172,120Zm56,8A100,100,0,0,1,79.5,215.47l-35.69,11.9a12,12,0,0,1-15.18-15.18l11.9-35.69A100,100,0,1,1,228,128Zm-8,0A92,92,0,1,0,48.35,174.07a4,4,0,0,1,.33,3.27L36.22,214.72a4,4,0,0,0,5.06,5.06l37.38-12.46a3.93,3.93,0,0,1,1.27-.21,4.05,4.05,0,0,1,2,.54A92,92,0,0,0,220,128Z"}))]]);var iE=Object.defineProperty,lE=Object.defineProperties,uE=Object.getOwnPropertyDescriptors,q2=Object.getOwnPropertySymbols,cE=Object.prototype.hasOwnProperty,dE=Object.prototype.propertyIsEnumerable,H2=(e,t,n)=>t in e?iE(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const V2=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>lE(e,uE(t)))(((e,t)=>{for(var n in t||(t={}))cE.call(t,n)&&H2(e,n,t[n]);if(q2)for(var n of q2(t))dE.call(t,n)&&H2(e,n,t[n]);return e})({ref:t},e),{weights:sE}))));V2.displayName="ChatCircleDots";const mE=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M232,56V200a16,16,0,0,1-16,16H40a16,16,0,0,1-16-16V56A16,16,0,0,1,40,40H216A16,16,0,0,1,232,56Z",opacity:"0.2"}),f.createElement("path",{d:"M205.66,85.66l-96,96a8,8,0,0,1-11.32,0l-40-40a8,8,0,0,1,11.32-11.32L104,164.69l90.34-90.35a8,8,0,0,1,11.32,11.32Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40ZM205.66,85.66l-96,96a8,8,0,0,1-11.32,0l-40-40a8,8,0,0,1,11.32-11.32L104,164.69l90.34-90.35a8,8,0,0,1,11.32,11.32Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M228.24,76.24l-128,128a6,6,0,0,1-8.48,0l-56-56a6,6,0,0,1,8.48-8.48L96,191.51,219.76,67.76a6,6,0,0,1,8.48,8.48Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M226.83,74.83l-128,128a4,4,0,0,1-5.66,0l-56-56a4,4,0,0,1,5.66-5.66L96,194.34,221.17,69.17a4,4,0,1,1,5.66,5.66Z"}))]]);var gE=Object.defineProperty,hE=Object.defineProperties,EE=Object.getOwnPropertyDescriptors,z2=Object.getOwnPropertySymbols,AE=Object.prototype.hasOwnProperty,bE=Object.prototype.propertyIsEnumerable,G2=(e,t,n)=>t in e?gE(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const $2=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>hE(e,EE(t)))(((e,t)=>{for(var n in t||(t={}))AE.call(t,n)&&G2(e,n,t[n]);if(z2)for(var n of z2(t))bE.call(t,n)&&G2(e,n,t[n]);return e})({ref:t},e),{weights:mE}))));$2.displayName="Check";const DE=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M236,128a108,108,0,0,1-216,0c0-42.52,24.73-81.34,63-98.9A12,12,0,1,1,93,50.91C63.24,64.57,44,94.83,44,128a84,84,0,0,0,168,0c0-33.17-19.24-63.43-49-77.09A12,12,0,1,1,173,29.1C211.27,46.66,236,85.48,236,128Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z",opacity:"0.2"}),f.createElement("path",{d:"M232,128a104,104,0,0,1-208,0c0-41,23.81-78.36,60.66-95.27a8,8,0,0,1,6.68,14.54C60.15,61.59,40,93.27,40,128a88,88,0,0,0,176,0c0-34.73-20.15-66.41-51.34-80.73a8,8,0,0,1,6.68-14.54C208.19,49.64,232,87,232,128Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,176A72,72,0,0,1,92,65.64a8,8,0,0,1,8,13.85,56,56,0,1,0,56,0,8,8,0,0,1,8-13.85A72,72,0,0,1,128,200Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M230,128a102,102,0,0,1-204,0c0-40.18,23.35-76.86,59.5-93.45a6,6,0,0,1,5,10.9C58.61,60.09,38,92.49,38,128a90,90,0,0,0,180,0c0-35.51-20.61-67.91-52.5-82.55a6,6,0,0,1,5-10.9C206.65,51.14,230,87.82,230,128Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M232,128a104,104,0,0,1-208,0c0-41,23.81-78.36,60.66-95.27a8,8,0,0,1,6.68,14.54C60.15,61.59,40,93.27,40,128a88,88,0,0,0,176,0c0-34.73-20.15-66.41-51.34-80.73a8,8,0,0,1,6.68-14.54C208.19,49.64,232,87,232,128Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M228,128a100,100,0,0,1-200,0c0-39.4,22.9-75.37,58.33-91.63a4,4,0,1,1,3.34,7.27C57.07,58.6,36,91.71,36,128a92,92,0,0,0,184,0c0-36.29-21.07-69.4-53.67-84.36a4,4,0,1,1,3.34-7.27C205.1,52.63,228,88.6,228,128Z"}))]]);var yE=Object.defineProperty,TE=Object.defineProperties,CE=Object.getOwnPropertyDescriptors,Z2=Object.getOwnPropertySymbols,NE=Object.prototype.hasOwnProperty,SE=Object.prototype.propertyIsEnumerable,j2=(e,t,n)=>t in e?yE(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const nu=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>TE(e,CE(t)))(((e,t)=>{for(var n in t||(t={}))NE.call(t,n)&&j2(e,n,t[n]);if(Z2)for(var n of Z2(t))SE.call(t,n)&&j2(e,n,t[n]);return e})({ref:t},e),{weights:DE}))));nu.displayName="CircleNotch";const RE=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M216,40V168H168V88H88V40Z",opacity:"0.2"}),f.createElement("path",{d:"M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32Zm-8,128H176V88a8,8,0,0,0-8-8H96V48H208Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M216,34H88a6,6,0,0,0-6,6V82H40a6,6,0,0,0-6,6V216a6,6,0,0,0,6,6H168a6,6,0,0,0,6-6V174h42a6,6,0,0,0,6-6V40A6,6,0,0,0,216,34ZM162,210H46V94H162Zm48-48H174V88a6,6,0,0,0-6-6H94V46H210Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M216,36H88a4,4,0,0,0-4,4V84H40a4,4,0,0,0-4,4V216a4,4,0,0,0,4,4H168a4,4,0,0,0,4-4V172h44a4,4,0,0,0,4-4V40A4,4,0,0,0,216,36ZM164,212H44V92H164Zm48-48H172V88a4,4,0,0,0-4-4H92V44H212Z"}))]]);var LE=Object.defineProperty,OE=Object.defineProperties,kE=Object.getOwnPropertyDescriptors,W2=Object.getOwnPropertySymbols,IE=Object.prototype.hasOwnProperty,ME=Object.prototype.propertyIsEnumerable,Y2=(e,t,n)=>t in e?LE(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const K2=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>OE(e,kE(t)))(((e,t)=>{for(var n in t||(t={}))IE.call(t,n)&&Y2(e,n,t[n]);if(W2)for(var n of W2(t))ME.call(t,n)&&Y2(e,n,t[n]);return e})({ref:t},e),{weights:RE}))));K2.displayName="Copy";const PE=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M128,96a32,32,0,1,0,32,32A32,32,0,0,0,128,96Zm0,40a8,8,0,1,1,8-8A8,8,0,0,1,128,136Zm0-56A32,32,0,1,0,96,48,32,32,0,0,0,128,80Zm0-40a8,8,0,1,1-8,8A8,8,0,0,1,128,40Zm0,136a32,32,0,1,0,32,32A32,32,0,0,0,128,176Zm0,40a8,8,0,1,1,8-8A8,8,0,0,1,128,216Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M152,128a24,24,0,1,1-24-24A24,24,0,0,1,152,128ZM128,72a24,24,0,1,0-24-24A24,24,0,0,0,128,72Zm0,112a24,24,0,1,0,24,24A24,24,0,0,0,128,184Z",opacity:"0.2"}),f.createElement("path",{d:"M128,96a32,32,0,1,0,32,32A32,32,0,0,0,128,96Zm0,48a16,16,0,1,1,16-16A16,16,0,0,1,128,144Zm0-64A32,32,0,1,0,96,48,32,32,0,0,0,128,80Zm0-48a16,16,0,1,1-16,16A16,16,0,0,1,128,32Zm0,144a32,32,0,1,0,32,32A32,32,0,0,0,128,176Zm0,48a16,16,0,1,1,16-16A16,16,0,0,1,128,224Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M156,128a28,28,0,1,1-28-28A28,28,0,0,1,156,128ZM128,76a28,28,0,1,0-28-28A28,28,0,0,0,128,76Zm0,104a28,28,0,1,0,28,28A28,28,0,0,0,128,180Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M128,98a30,30,0,1,0,30,30A30,30,0,0,0,128,98Zm0,48a18,18,0,1,1,18-18A18,18,0,0,1,128,146Zm0-68A30,30,0,1,0,98,48,30,30,0,0,0,128,78Zm0-48a18,18,0,1,1-18,18A18,18,0,0,1,128,30Zm0,148a30,30,0,1,0,30,30A30,30,0,0,0,128,178Zm0,48a18,18,0,1,1,18-18A18,18,0,0,1,128,226Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M128,96a32,32,0,1,0,32,32A32,32,0,0,0,128,96Zm0,48a16,16,0,1,1,16-16A16,16,0,0,1,128,144Zm0-64A32,32,0,1,0,96,48,32,32,0,0,0,128,80Zm0-48a16,16,0,1,1-16,16A16,16,0,0,1,128,32Zm0,144a32,32,0,1,0,32,32A32,32,0,0,0,128,176Zm0,48a16,16,0,1,1,16-16A16,16,0,0,1,128,224Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M128,100a28,28,0,1,0,28,28A28,28,0,0,0,128,100Zm0,48a20,20,0,1,1,20-20A20,20,0,0,1,128,148Zm0-72a28,28,0,1,0-28-28A28,28,0,0,0,128,76Zm0-48a20,20,0,1,1-20,20A20,20,0,0,1,128,28Zm0,152a28,28,0,1,0,28,28A28,28,0,0,0,128,180Zm0,48a20,20,0,1,1,20-20A20,20,0,0,1,128,228Z"}))]]);var UE=Object.defineProperty,qE=Object.defineProperties,HE=Object.getOwnPropertyDescriptors,X2=Object.getOwnPropertySymbols,VE=Object.prototype.hasOwnProperty,zE=Object.prototype.propertyIsEnumerable,Q2=(e,t,n)=>t in e?UE(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const J2=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>qE(e,HE(t)))(((e,t)=>{for(var n in t||(t={}))VE.call(t,n)&&Q2(e,n,t[n]);if(X2)for(var n of X2(t))zE.call(t,n)&&Q2(e,n,t[n]);return e})({ref:t},e),{weights:PE}))));J2.displayName="DotsThreeOutlineVertical";const ZE=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,44H32A12,12,0,0,0,20,56V192a20,20,0,0,0,20,20H216a20,20,0,0,0,20-20V56A12,12,0,0,0,224,44Zm-96,83.72L62.85,68h130.3ZM92.79,128,44,172.72V83.28Zm17.76,16.28,9.34,8.57a12,12,0,0,0,16.22,0l9.34-8.57L193.15,188H62.85ZM163.21,128,212,83.28v89.44Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,56l-96,88L32,56Z",opacity:"0.2"}),f.createElement("path",{d:"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48Zm-96,85.15L52.57,64H203.43ZM98.71,128,40,181.81V74.19Zm11.84,10.85,12,11.05a8,8,0,0,0,10.82,0l12-11.05,58,53.15H52.57ZM157.29,128,216,74.18V181.82Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM98.71,128,40,181.81V74.19Zm11.84,10.85,12,11.05a8,8,0,0,0,10.82,0l12-11.05,58,53.15H52.57ZM157.29,128,216,74.18V181.82Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,50H32a6,6,0,0,0-6,6V192a14,14,0,0,0,14,14H216a14,14,0,0,0,14-14V56A6,6,0,0,0,224,50Zm-96,85.86L47.42,62H208.58ZM101.67,128,38,186.36V69.64Zm8.88,8.14L124,148.42a6,6,0,0,0,8.1,0l13.4-12.28L208.58,194H47.43ZM154.33,128,218,69.64V186.36Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48Zm-96,85.15L52.57,64H203.43ZM98.71,128,40,181.81V74.19Zm11.84,10.85,12,11.05a8,8,0,0,0,10.82,0l12-11.05,58,53.15H52.57ZM157.29,128,216,74.18V181.82Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,52H32a4,4,0,0,0-4,4V192a12,12,0,0,0,12,12H216a12,12,0,0,0,12-12V56A4,4,0,0,0,224,52Zm-96,86.57L42.28,60H213.72ZM104.63,128,36,190.91V65.09Zm5.92,5.43L125.3,147a4,4,0,0,0,5.4,0l14.75-13.52L213.72,196H42.28ZM151.37,128,220,65.09V190.91Z"}))]]);var jE=Object.defineProperty,WE=Object.defineProperties,YE=Object.getOwnPropertyDescriptors,ep=Object.getOwnPropertySymbols,KE=Object.prototype.hasOwnProperty,XE=Object.prototype.propertyIsEnumerable,tp=(e,t,n)=>t in e?jE(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const np=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>WE(e,YE(t)))(((e,t)=>{for(var n in t||(t={}))KE.call(t,n)&&tp(e,n,t[n]);if(ep)for(var n of ep(t))XE.call(t,n)&&tp(e,n,t[n]);return e})({ref:t},e),{weights:ZE}))));np.displayName="Envelope";const eA=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M204.73,51.85A108.07,108.07,0,0,0,20,128v56a28,28,0,0,0,28,28H64a28,28,0,0,0,28-28V144a28,28,0,0,0-28-28H44.84A84.05,84.05,0,0,1,128,44h.64a83.7,83.7,0,0,1,82.52,72H192a28,28,0,0,0-28,28v40a28,28,0,0,0,28,28h19.6A20,20,0,0,1,192,228H136a12,12,0,0,0,0,24h56a44.05,44.05,0,0,0,44-44V128A107.34,107.34,0,0,0,204.73,51.85ZM64,140a4,4,0,0,1,4,4v40a4,4,0,0,1-4,4H48a4,4,0,0,1-4-4V140Zm124,44V144a4,4,0,0,1,4-4h20v48H192A4,4,0,0,1,188,184Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M80,144v40a16,16,0,0,1-16,16H48a16,16,0,0,1-16-16V128H64A16,16,0,0,1,80,144Zm112-16a16,16,0,0,0-16,16v40a16,16,0,0,0,16,16h32V128Z",opacity:"0.2"}),f.createElement("path",{d:"M201.89,54.66A104.08,104.08,0,0,0,24,128v56a24,24,0,0,0,24,24H64a24,24,0,0,0,24-24V144a24,24,0,0,0-24-24H40.36A88.12,88.12,0,0,1,190.54,65.93,87.39,87.39,0,0,1,215.65,120H192a24,24,0,0,0-24,24v40a24,24,0,0,0,24,24h24a24,24,0,0,1-24,24H136a8,8,0,0,0,0,16h56a40,40,0,0,0,40-40V128A103.41,103.41,0,0,0,201.89,54.66ZM64,136a8,8,0,0,1,8,8v40a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V136Zm128,56a8,8,0,0,1-8-8V144a8,8,0,0,1,8-8h24v56Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M232,128v80a40,40,0,0,1-40,40H136a8,8,0,0,1,0-16h56a24,24,0,0,0,24-24H192a24,24,0,0,1-24-24V144a24,24,0,0,1,24-24h23.65A88,88,0,0,0,66,65.54,87.29,87.29,0,0,0,40.36,120H64a24,24,0,0,1,24,24v40a24,24,0,0,1-24,24H48a24,24,0,0,1-24-24V128A104.11,104.11,0,0,1,201.89,54.66,103.41,103.41,0,0,1,232,128Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M200.47,56.07A101.37,101.37,0,0,0,128.77,26H128A102,102,0,0,0,26,128v56a22,22,0,0,0,22,22H64a22,22,0,0,0,22-22V144a22,22,0,0,0-22-22H38.2A90,90,0,0,1,128,38h.68a89.71,89.71,0,0,1,89.13,84H192a22,22,0,0,0-22,22v40a22,22,0,0,0,22,22h26v2a26,26,0,0,1-26,26H136a6,6,0,0,0,0,12h56a38,38,0,0,0,38-38V128A101.44,101.44,0,0,0,200.47,56.07ZM64,134a10,10,0,0,1,10,10v40a10,10,0,0,1-10,10H48a10,10,0,0,1-10-10V134Zm118,50V144a10,10,0,0,1,10-10h26v60H192A10,10,0,0,1,182,184Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M201.89,54.66A103.43,103.43,0,0,0,128.79,24H128A104,104,0,0,0,24,128v56a24,24,0,0,0,24,24H64a24,24,0,0,0,24-24V144a24,24,0,0,0-24-24H40.36A88.12,88.12,0,0,1,190.54,65.93,87.39,87.39,0,0,1,215.65,120H192a24,24,0,0,0-24,24v40a24,24,0,0,0,24,24h24a24,24,0,0,1-24,24H136a8,8,0,0,0,0,16h56a40,40,0,0,0,40-40V128A103.41,103.41,0,0,0,201.89,54.66ZM64,136a8,8,0,0,1,8,8v40a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V136Zm128,56a8,8,0,0,1-8-8V144a8,8,0,0,1,8-8h24v56Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M199.05,57.48A100.07,100.07,0,0,0,28,128v56a20,20,0,0,0,20,20H64a20,20,0,0,0,20-20V144a20,20,0,0,0-20-20H36.08A92,92,0,0,1,128,36h.7a91.75,91.75,0,0,1,91.22,88H192a20,20,0,0,0-20,20v40a20,20,0,0,0,20,20h28v4a28,28,0,0,1-28,28H136a4,4,0,0,0,0,8h56a36,36,0,0,0,36-36V128A99.44,99.44,0,0,0,199.05,57.48ZM64,132a12,12,0,0,1,12,12v40a12,12,0,0,1-12,12H48a12,12,0,0,1-12-12V132Zm116,52V144a12,12,0,0,1,12-12h28v64H192A12,12,0,0,1,180,184Z"}))]]);var tA=Object.defineProperty,nA=Object.defineProperties,rA=Object.getOwnPropertyDescriptors,rp=Object.getOwnPropertySymbols,oA=Object.prototype.hasOwnProperty,aA=Object.prototype.propertyIsEnumerable,op=(e,t,n)=>t in e?tA(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const ap=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>nA(e,rA(t)))(((e,t)=>{for(var n in t||(t={}))oA.call(t,n)&&op(e,n,t[n]);if(rp)for(var n of rp(t))aA.call(t,n)&&op(e,n,t[n]);return e})({ref:t},e),{weights:eA}))));ap.displayName="Headset";const lA=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M252,152a12,12,0,0,1-12,12H228v12a12,12,0,0,1-24,0V164H192a12,12,0,0,1,0-24h12V128a12,12,0,0,1,24,0v12h12A12,12,0,0,1,252,152ZM56,76H68V88a12,12,0,0,0,24,0V76h12a12,12,0,1,0,0-24H92V40a12,12,0,0,0-24,0V52H56a12,12,0,0,0,0,24ZM184,188h-4v-4a12,12,0,0,0-24,0v4h-4a12,12,0,0,0,0,24h4v4a12,12,0,0,0,24,0v-4h4a12,12,0,0,0,0-24ZM222.14,82.83,82.82,222.14a20,20,0,0,1-28.28,0L33.85,201.46a20,20,0,0,1,0-28.29L173.17,33.86a20,20,0,0,1,28.28,0l20.69,20.68A20,20,0,0,1,222.14,82.83ZM159,112,144,97,53.65,187.31l15,15Zm43.31-43.31-15-15L161,80l15,15Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M176,112,74.34,213.66a8,8,0,0,1-11.31,0L42.34,193a8,8,0,0,1,0-11.31L144,80Z",opacity:"0.2"}),f.createElement("path",{d:"M248,152a8,8,0,0,1-8,8H224v16a8,8,0,0,1-16,0V160H192a8,8,0,0,1,0-16h16V128a8,8,0,0,1,16,0v16h16A8,8,0,0,1,248,152ZM56,72H72V88a8,8,0,0,0,16,0V72h16a8,8,0,0,0,0-16H88V40a8,8,0,0,0-16,0V56H56a8,8,0,0,0,0,16ZM184,192h-8v-8a8,8,0,0,0-16,0v8h-8a8,8,0,0,0,0,16h8v8a8,8,0,0,0,16,0v-8h8a8,8,0,0,0,0-16ZM219.31,80,80,219.31a16,16,0,0,1-22.62,0L36.68,198.63a16,16,0,0,1,0-22.63L176,36.69a16,16,0,0,1,22.63,0l20.68,20.68A16,16,0,0,1,219.31,80Zm-54.63,32L144,91.31l-96,96L68.68,208ZM208,68.69,187.31,48l-32,32L176,100.69Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M248,152a8,8,0,0,1-8,8H224v16a8,8,0,0,1-16,0V160H192a8,8,0,0,1,0-16h16V128a8,8,0,0,1,16,0v16h16A8,8,0,0,1,248,152ZM56,72H72V88a8,8,0,0,0,16,0V72h16a8,8,0,0,0,0-16H88V40a8,8,0,0,0-16,0V56H56a8,8,0,0,0,0,16ZM184,192h-8v-8a8,8,0,0,0-16,0v8h-8a8,8,0,0,0,0,16h8v8a8,8,0,0,0,16,0v-8h8a8,8,0,0,0,0-16ZM219.31,80,80,219.31a16,16,0,0,1-22.62,0L36.68,198.63a16,16,0,0,1,0-22.63L176,36.69a16,16,0,0,1,22.63,0l20.68,20.68A16,16,0,0,1,219.31,80ZM208,68.69,187.31,48l-32,32L176,100.69Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M246,152a6,6,0,0,1-6,6H222v18a6,6,0,0,1-12,0V158H192a6,6,0,0,1,0-12h18V128a6,6,0,0,1,12,0v18h18A6,6,0,0,1,246,152ZM56,70H74V88a6,6,0,0,0,12,0V70h18a6,6,0,0,0,0-12H86V40a6,6,0,0,0-12,0V58H56a6,6,0,0,0,0,12ZM184,194H174V184a6,6,0,0,0-12,0v10H152a6,6,0,0,0,0,12h10v10a6,6,0,0,0,12,0V206h10a6,6,0,0,0,0-12ZM217.9,78.59,78.58,217.9a14,14,0,0,1-19.8,0L38.09,197.21a14,14,0,0,1,0-19.8L177.41,38.1a14,14,0,0,1,19.8,0L217.9,58.79A14,14,0,0,1,217.9,78.59ZM167.51,112,144,88.49,46.58,185.9a2,2,0,0,0,0,2.83l20.69,20.68a2,2,0,0,0,2.82,0h0Zm41.9-44.73L188.73,46.59a2,2,0,0,0-2.83,0L152.48,80,176,103.52,209.41,70.1A2,2,0,0,0,209.41,67.27Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M48,64a8,8,0,0,1,8-8H72V40a8,8,0,0,1,16,0V56h16a8,8,0,0,1,0,16H88V88a8,8,0,0,1-16,0V72H56A8,8,0,0,1,48,64ZM184,192h-8v-8a8,8,0,0,0-16,0v8h-8a8,8,0,0,0,0,16h8v8a8,8,0,0,0,16,0v-8h8a8,8,0,0,0,0-16Zm56-48H224V128a8,8,0,0,0-16,0v16H192a8,8,0,0,0,0,16h16v16a8,8,0,0,0,16,0V160h16a8,8,0,0,0,0-16ZM219.31,80,80,219.31a16,16,0,0,1-22.62,0L36.68,198.63a16,16,0,0,1,0-22.63L176,36.69a16,16,0,0,1,22.63,0l20.68,20.68A16,16,0,0,1,219.31,80Zm-54.63,32L144,91.31l-96,96L68.68,208ZM208,68.69,187.31,48l-32,32L176,100.69Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M244,152a4,4,0,0,1-4,4H220v20a4,4,0,0,1-8,0V156H192a4,4,0,0,1,0-8h20V128a4,4,0,0,1,8,0v20h20A4,4,0,0,1,244,152ZM56,68H76V88a4,4,0,0,0,8,0V68h20a4,4,0,0,0,0-8H84V40a4,4,0,0,0-8,0V60H56a4,4,0,0,0,0,8ZM184,196H172V184a4,4,0,0,0-8,0v12H152a4,4,0,0,0,0,8h12v12a4,4,0,0,0,8,0V204h12a4,4,0,0,0,0-8ZM216.48,77.17,77.17,216.49a12,12,0,0,1-17,0L39.51,195.8a12,12,0,0,1,0-17L178.83,39.51a12,12,0,0,1,17,0L216.48,60.2A12,12,0,0,1,216.48,77.17ZM170.34,112,144,85.66,45.17,184.49a4,4,0,0,0,0,5.65l20.68,20.69a4,4,0,0,0,5.66,0Zm40.49-46.14L190.14,45.17a4,4,0,0,0-5.66,0L149.65,80,176,106.34l34.83-34.83A4,4,0,0,0,210.83,65.86Z"}))]]);var uA=Object.defineProperty,cA=Object.defineProperties,dA=Object.getOwnPropertyDescriptors,sp=Object.getOwnPropertySymbols,pA=Object.prototype.hasOwnProperty,fA=Object.prototype.propertyIsEnumerable,ip=(e,t,n)=>t in e?uA(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const lp=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>cA(e,dA(t)))(((e,t)=>{for(var n in t||(t={}))pA.call(t,n)&&ip(e,n,t[n]);if(sp)for(var n of sp(t))fA.call(t,n)&&ip(e,n,t[n]);return e})({ref:t},e),{weights:lA}))));lp.displayName="MagicWand";const hA=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M232.49,215.51,185,168a92.12,92.12,0,1,0-17,17l47.53,47.54a12,12,0,0,0,17-17ZM44,112a68,68,0,1,1,68,68A68.07,68.07,0,0,1,44,112Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M192,112a80,80,0,1,1-80-80A80,80,0,0,1,192,112Z",opacity:"0.2"}),f.createElement("path",{d:"M229.66,218.34,179.6,168.28a88.21,88.21,0,1,0-11.32,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M168,112a56,56,0,1,1-56-56A56,56,0,0,1,168,112Zm61.66,117.66a8,8,0,0,1-11.32,0l-50.06-50.07a88,88,0,1,1,11.32-11.31l50.06,50.06A8,8,0,0,1,229.66,229.66ZM112,184a72,72,0,1,0-72-72A72.08,72.08,0,0,0,112,184Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M228.24,219.76l-51.38-51.38a86.15,86.15,0,1,0-8.48,8.48l51.38,51.38a6,6,0,0,0,8.48-8.48ZM38,112a74,74,0,1,1,74,74A74.09,74.09,0,0,1,38,112Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M226.83,221.17l-52.7-52.7a84.1,84.1,0,1,0-5.66,5.66l52.7,52.7a4,4,0,0,0,5.66-5.66ZM36,112a76,76,0,1,1,76,76A76.08,76.08,0,0,1,36,112Z"}))]]);var EA=Object.defineProperty,AA=Object.defineProperties,bA=Object.getOwnPropertyDescriptors,up=Object.getOwnPropertySymbols,_A=Object.prototype.hasOwnProperty,vA=Object.prototype.propertyIsEnumerable,cp=(e,t,n)=>t in e?EA(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const dp=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>AA(e,bA(t)))(((e,t)=>{for(var n in t||(t={}))_A.call(t,n)&&cp(e,n,t[n]);if(up)for(var n of up(t))vA.call(t,n)&&cp(e,n,t[n]);return e})({ref:t},e),{weights:hA}))));dp.displayName="MagnifyingGlass";const TA=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M225.86,110.48,57.8,14.58A20,20,0,0,0,29.16,38.67l30.61,89.21L29.16,217.33A20,20,0,0,0,48,244a20.1,20.1,0,0,0,9.81-2.58l.09-.06,168-96.07a20,20,0,0,0,0-34.81ZM55.24,215.23,81,140h55a12,12,0,0,0,0-24H81.07L55.25,40.76l152.69,87.13Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M219.91,134.86,51.93,231a8,8,0,0,1-11.44-9.67l31-90.71a7.89,7.89,0,0,0,0-5.38l-31-90.47a8,8,0,0,1,11.44-9.67l168,95.85A8,8,0,0,1,219.91,134.86Z",opacity:"0.2"}),f.createElement("path",{d:"M223.87,114l-168-95.89A16,16,0,0,0,32.93,37.32l31,90.47a.42.42,0,0,0,0,.1.3.3,0,0,0,0,.1l-31,90.67A16,16,0,0,0,48,240a16.14,16.14,0,0,0,7.92-2.1l167.91-96.05a16,16,0,0,0,.05-27.89ZM48,224l0-.09L78.14,136H136a8,8,0,0,0,0-16H78.22L48.06,32.12,48,32l168,95.83Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M232,127.89a16,16,0,0,1-8.18,14L55.91,237.9A16.14,16.14,0,0,1,48,240a16,16,0,0,1-15.05-21.34L60.3,138.71A4,4,0,0,1,64.09,136H136a8,8,0,0,0,8-8.53,8.19,8.19,0,0,0-8.26-7.47H64.16a4,4,0,0,1-3.79-2.7l-27.44-80A16,16,0,0,1,55.85,18.07l168,95.89A16,16,0,0,1,232,127.89Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M222.88,115.69l-168-95.88a14,14,0,0,0-20,16.85l31,90.48,0,.07a2.11,2.11,0,0,1,0,1.42l-31,90.64A14,14,0,0,0,48,238a14.11,14.11,0,0,0,6.92-1.83L222.84,140.1a14,14,0,0,0,0-24.41Zm-5.95,14L49,225.73a1.87,1.87,0,0,1-2.27-.22,1.92,1.92,0,0,1-.56-2.28L76.7,134H136a6,6,0,0,0,0-12H76.78L46.14,32.7A2,2,0,0,1,49,30.25l168,95.89a1.93,1.93,0,0,1,1,1.74A2,2,0,0,1,216.93,129.66Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M223.87,114l-168-95.89A16,16,0,0,0,32.93,37.32l31,90.47a.42.42,0,0,0,0,.1.3.3,0,0,0,0,.1l-31,90.67A16,16,0,0,0,48,240a16.14,16.14,0,0,0,7.92-2.1l167.91-96.05a16,16,0,0,0,.05-27.89ZM48,224l0-.09L78.14,136H136a8,8,0,0,0,0-16H78.22L48.06,32.12,48,32l168,95.83Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M221.89,117.43l-168-95.88A12,12,0,0,0,36.7,36l31.05,90.48v.05a4.09,4.09,0,0,1,0,2.74L36.72,220A12,12,0,0,0,48,236a12.13,12.13,0,0,0,5.93-1.57l167.94-96.08a12,12,0,0,0,0-20.92Zm-4,14L50,227.47a4,4,0,0,1-5.7-4.88l31-90.59H136a4,4,0,0,0,0-8H75.35a.65.65,0,0,1,0-.13L44.25,33.37A4,4,0,0,1,50,28.52l168,95.87a4,4,0,0,1,0,7Z"}))]]);var CA=Object.defineProperty,NA=Object.defineProperties,SA=Object.getOwnPropertyDescriptors,pp=Object.getOwnPropertySymbols,wA=Object.prototype.hasOwnProperty,xA=Object.prototype.propertyIsEnumerable,fp=(e,t,n)=>t in e?CA(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const mp=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>NA(e,SA(t)))(((e,t)=>{for(var n in t||(t={}))wA.call(t,n)&&fp(e,n,t[n]);if(pp)for(var n of pp(t))xA.call(t,n)&&fp(e,n,t[n]);return e})({ref:t},e),{weights:TA}))));mp.displayName="PaperPlaneRight";const OA=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M228,128a12,12,0,0,1-12,12H140v76a12,12,0,0,1-24,0V140H40a12,12,0,0,1,0-24h76V40a12,12,0,0,1,24,0v76h76A12,12,0,0,1,228,128Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M216,48V208a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V48a8,8,0,0,1,8-8H208A8,8,0,0,1,216,48Z",opacity:"0.2"}),f.createElement("path",{d:"M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM184,136H136v48a8,8,0,0,1-16,0V136H72a8,8,0,0,1,0-16h48V72a8,8,0,0,1,16,0v48h48a8,8,0,0,1,0,16Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M222,128a6,6,0,0,1-6,6H134v82a6,6,0,0,1-12,0V134H40a6,6,0,0,1,0-12h82V40a6,6,0,0,1,12,0v82h82A6,6,0,0,1,222,128Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M220,128a4,4,0,0,1-4,4H132v84a4,4,0,0,1-8,0V132H40a4,4,0,0,1,0-8h84V40a4,4,0,0,1,8,0v84h84A4,4,0,0,1,220,128Z"}))]]);var kA=Object.defineProperty,IA=Object.defineProperties,MA=Object.getOwnPropertyDescriptors,gp=Object.getOwnPropertySymbols,FA=Object.prototype.hasOwnProperty,BA=Object.prototype.propertyIsEnumerable,hp=(e,t,n)=>t in e?kA(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const Ep=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>IA(e,MA(t)))(((e,t)=>{for(var n in t||(t={}))FA.call(t,n)&&hp(e,n,t[n]);if(gp)for(var n of gp(t))BA.call(t,n)&&hp(e,n,t[n]);return e})({ref:t},e),{weights:OA}))));Ep.displayName="Plus";const qA=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M240.26,186.1,152.81,34.23h0a28.74,28.74,0,0,0-49.62,0L15.74,186.1a27.45,27.45,0,0,0,0,27.71A28.31,28.31,0,0,0,40.55,228h174.9a28.31,28.31,0,0,0,24.79-14.19A27.45,27.45,0,0,0,240.26,186.1Zm-20.8,15.7a4.46,4.46,0,0,1-4,2.2H40.55a4.46,4.46,0,0,1-4-2.2,3.56,3.56,0,0,1,0-3.73L124,46.2a4.77,4.77,0,0,1,8,0l87.44,151.87A3.56,3.56,0,0,1,219.46,201.8ZM116,136V104a12,12,0,0,1,24,0v32a12,12,0,0,1-24,0Zm28,40a16,16,0,1,1-16-16A16,16,0,0,1,144,176Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M215.46,216H40.54C27.92,216,20,202.79,26.13,192.09L113.59,40.22c6.3-11,22.52-11,28.82,0l87.46,151.87C236,202.79,228.08,216,215.46,216Z",opacity:"0.2"}),f.createElement("path",{d:"M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM120,104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm8,88a12,12,0,1,1,12-12A12,12,0,0,1,128,192Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M235.07,189.09,147.61,37.22h0a22.75,22.75,0,0,0-39.22,0L20.93,189.09a21.53,21.53,0,0,0,0,21.72A22.35,22.35,0,0,0,40.55,222h174.9a22.35,22.35,0,0,0,19.6-11.19A21.53,21.53,0,0,0,235.07,189.09ZM224.66,204.8a10.46,10.46,0,0,1-9.21,5.2H40.55a10.46,10.46,0,0,1-9.21-5.2,9.51,9.51,0,0,1,0-9.72L118.79,43.21a10.75,10.75,0,0,1,18.42,0l87.46,151.87A9.51,9.51,0,0,1,224.66,204.8ZM122,144V104a6,6,0,0,1,12,0v40a6,6,0,0,1-12,0Zm16,36a10,10,0,1,1-10-10A10,10,0,0,1,138,180Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M233.34,190.09,145.88,38.22h0a20.75,20.75,0,0,0-35.76,0L22.66,190.09a19.52,19.52,0,0,0,0,19.71A20.36,20.36,0,0,0,40.54,220H215.46a20.36,20.36,0,0,0,17.86-10.2A19.52,19.52,0,0,0,233.34,190.09ZM226.4,205.8a12.47,12.47,0,0,1-10.94,6.2H40.54a12.47,12.47,0,0,1-10.94-6.2,11.45,11.45,0,0,1,0-11.72L117.05,42.21a12.76,12.76,0,0,1,21.9,0L226.4,194.08A11.45,11.45,0,0,1,226.4,205.8ZM124,144V104a4,4,0,0,1,8,0v40a4,4,0,0,1-8,0Zm12,36a8,8,0,1,1-8-8A8,8,0,0,1,136,180Z"}))]]);var HA=Object.defineProperty,VA=Object.defineProperties,zA=Object.getOwnPropertyDescriptors,Ap=Object.getOwnPropertySymbols,GA=Object.prototype.hasOwnProperty,$A=Object.prototype.propertyIsEnumerable,bp=(e,t,n)=>t in e?HA(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const ru=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>VA(e,zA(t)))(((e,t)=>{for(var n in t||(t={}))GA.call(t,n)&&bp(e,n,t[n]);if(Ap)for(var n of Ap(t))$A.call(t,n)&&bp(e,n,t[n]);return e})({ref:t},e),{weights:qA}))));ru.displayName="Warning";const WA=new Map([["bold",f.createElement(f.Fragment,null,f.createElement("path",{d:"M208.49,191.51a12,12,0,0,1-17,17L128,145,64.49,208.49a12,12,0,0,1-17-17L111,128,47.51,64.49a12,12,0,0,1,17-17L128,111l63.51-63.52a12,12,0,0,1,17,17L145,128Z"}))],["duotone",f.createElement(f.Fragment,null,f.createElement("path",{d:"M216,48V208a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V48a8,8,0,0,1,8-8H208A8,8,0,0,1,216,48Z",opacity:"0.2"}),f.createElement("path",{d:"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"}))],["fill",f.createElement(f.Fragment,null,f.createElement("path",{d:"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM181.66,170.34a8,8,0,0,1-11.32,11.32L128,139.31,85.66,181.66a8,8,0,0,1-11.32-11.32L116.69,128,74.34,85.66A8,8,0,0,1,85.66,74.34L128,116.69l42.34-42.35a8,8,0,0,1,11.32,11.32L139.31,128Z"}))],["light",f.createElement(f.Fragment,null,f.createElement("path",{d:"M204.24,195.76a6,6,0,1,1-8.48,8.48L128,136.49,60.24,204.24a6,6,0,0,1-8.48-8.48L119.51,128,51.76,60.24a6,6,0,0,1,8.48-8.48L128,119.51l67.76-67.75a6,6,0,0,1,8.48,8.48L136.49,128Z"}))],["regular",f.createElement(f.Fragment,null,f.createElement("path",{d:"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"}))],["thin",f.createElement(f.Fragment,null,f.createElement("path",{d:"M202.83,197.17a4,4,0,0,1-5.66,5.66L128,133.66,58.83,202.83a4,4,0,0,1-5.66-5.66L122.34,128,53.17,58.83a4,4,0,0,1,5.66-5.66L128,122.34l69.17-69.17a4,4,0,1,1,5.66,5.66L133.66,128Z"}))]]);var YA=Object.defineProperty,KA=Object.defineProperties,XA=Object.getOwnPropertyDescriptors,_p=Object.getOwnPropertySymbols,QA=Object.prototype.hasOwnProperty,JA=Object.prototype.propertyIsEnumerable,vp=(e,t,n)=>t in e?YA(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const Dp=Z.forwardRef(((e,t)=>f.createElement(Ue,((e,t)=>KA(e,XA(t)))(((e,t)=>{for(var n in t||(t={}))QA.call(t,n)&&vp(e,n,t[n]);if(_p)for(var n of _p(t))JA.call(t,n)&&vp(e,n,t[n]);return e})({ref:t},e),{weights:WA}))));Dp.displayName="X";const ou={plus:Ep,chatBubble:V2,support:ap,search2:U2,search:dp,magic:lp};function nb({settings:e,isOpen:t,toggleOpen:n}){if(t)return null;const r=ou.hasOwnProperty(null==e?void 0:e.chatIcon)?ou[e.chatIcon]:ou.plus;return w.jsx("button",{style:{backgroundColor:e.buttonColor},id:"anything-llm-embed-chat-button",onClick:n,className:"hover:allm-cursor-pointer allm-border-none allm-flex allm-items-center allm-justify-center allm-p-4 allm-rounded-full allm-text-white allm-text-2xl hover:allm-opacity-95","aria-label":"Toggle Menu",children:w.jsx(r,{className:"text-white"})})}const ko="data:image/svg+xml,%3csvg%20width='49'%20height='49'%20viewBox='0%200%2049%2049'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3crect%20x='0.898438'%20y='0.5'%20width='48'%20height='48'%20rx='24'%20fill='%23222628'%20fill-opacity='0.8'/%3e%3cpath%20fill-rule='evenodd'%20clip-rule='evenodd'%20d='M12.0173%2014.2937C10.4387%2014.2937%209.14844%2015.584%209.14844%2017.1626V31.8372C9.14844%2033.4156%2010.4365%2034.7061%2012.0173%2034.7061H17.5428C18.4316%2034.7061%2019.2557%2034.3035%2019.8034%2033.6063L19.8041%2033.6054L32.4126%2017.4869H37.4552V31.509H32.4204L29.8951%2027.9721L29.8867%2027.9615C29.4483%2027.4042%2028.602%2027.4042%2028.1635%2027.9615L27.52%2028.7815L27.5178%2028.7843C27.2188%2029.1751%2027.2113%2029.7217%2027.512%2030.1178L29.985%2033.5936L29.9935%2033.6044C30.5415%2034.302%2031.3696%2034.7042%2032.2541%2034.7042H37.7795C39.3643%2034.7042%2040.6484%2033.4175%2040.6484%2031.8353V17.1626C40.6484%2015.5827%2039.3646%2014.2937%2037.7795%2014.2937H32.2541C31.3673%2014.2937%2030.5407%2014.6964%2029.9928%2015.3964L17.3858%2031.511H12.3417V17.4889H17.3757L20.133%2021.2573L20.1386%2021.2645C20.5782%2021.8273%2021.4253%2021.8239%2021.8647%2021.2666L21.8661%2021.2648L22.505%2020.4477L22.5069%2020.4453C22.8064%2020.0538%2022.8139%2019.5046%2022.5076%2019.1075L19.8102%2015.4041L19.804%2015.3963C19.2562%2014.6965%2018.4318%2014.2937%2017.5428%2014.2937H12.0173Z'%20fill='white'/%3e%3cpath%20d='M19.8034%2033.6063L20.0392%2033.7915L20.0394%2033.7912L19.8034%2033.6063ZM19.8041%2033.6054L20.0401%2033.7903L20.0403%2033.7901L19.8041%2033.6054ZM32.4126%2017.4869V17.1871H32.2665L32.1764%2017.3022L32.4126%2017.4869ZM37.4552%2017.4869H37.755V17.1871H37.4552V17.4869ZM37.4552%2031.509V31.8089H37.755V31.509H37.4552ZM32.4204%2031.509L32.1763%2031.6833L32.266%2031.8089H32.4204V31.509ZM29.8951%2027.9721L30.1394%2027.7977L30.1307%2027.7867L29.8951%2027.9721ZM29.8867%2027.9615L29.6511%2028.1469L29.6511%2028.1469L29.8867%2027.9615ZM28.1635%2027.9615L27.9279%2027.7761L27.9277%2027.7764L28.1635%2027.9615ZM27.52%2028.7815L27.2841%2028.5964L27.2819%2028.5993L27.52%2028.7815ZM27.5178%2028.7843L27.7559%2028.9665L27.7559%2028.9665L27.5178%2028.7843ZM27.512%2030.1178L27.7564%2029.9439L27.7508%2029.9365L27.512%2030.1178ZM29.985%2033.5936L29.7407%2033.7674L29.7448%2033.7732L29.7492%2033.7788L29.985%2033.5936ZM29.9935%2033.6044L30.2293%2033.4191L30.2293%2033.4191L29.9935%2033.6044ZM29.9928%2015.3964L29.7567%2015.2116L29.7566%2015.2116L29.9928%2015.3964ZM17.3858%2031.511V31.8108H17.5319L17.6219%2031.6957L17.3858%2031.511ZM12.3417%2031.511H12.0418V31.8108H12.3417V31.511ZM12.3417%2017.4889V17.189H12.0418V17.4889H12.3417ZM17.3757%2017.4889L17.6177%2017.3118L17.5278%2017.189H17.3757V17.4889ZM20.133%2021.2573L19.8909%2021.4345L19.8967%2021.4419L20.133%2021.2573ZM20.1386%2021.2645L19.9023%2021.4491V21.4491L20.1386%2021.2645ZM21.8647%2021.2666L22.1001%2021.4522L22.1005%2021.4517L21.8647%2021.2666ZM21.8661%2021.2648L22.1019%2021.45L22.1023%2021.4495L21.8661%2021.2648ZM22.505%2020.4477L22.7412%2020.6324L22.7431%2020.63L22.505%2020.4477ZM22.5069%2020.4453L22.7449%2020.6276L22.745%2020.6275L22.5069%2020.4453ZM22.5076%2019.1075L22.2651%2019.2841L22.2702%2019.2907L22.5076%2019.1075ZM19.8102%2015.4041L20.0527%2015.2275L20.0463%2015.2193L19.8102%2015.4041ZM19.804%2015.3963L19.5679%2015.5811L19.5679%2015.5811L19.804%2015.3963ZM9.44828%2017.1626C9.44828%2015.7496%2010.6043%2014.5935%2012.0173%2014.5935V13.9939C10.2731%2013.9939%208.8486%2015.4184%208.8486%2017.1626H9.44828ZM9.44828%2031.8372V17.1626H8.8486V31.8372H9.44828ZM12.0173%2034.4063C10.6022%2034.4063%209.44828%2033.2501%209.44828%2031.8372H8.8486C8.8486%2033.581%2010.2707%2035.006%2012.0173%2035.006V34.4063ZM17.5428%2034.4063H12.0173V35.006H17.5428V34.4063ZM19.5676%2033.4211C19.0766%2034.0462%2018.3393%2034.4063%2017.5428%2034.4063V35.006C18.524%2035.006%2019.4349%2034.5608%2020.0392%2033.7915L19.5676%2033.4211ZM19.5681%2033.4205L19.5674%2033.4214L20.0394%2033.7912L20.0401%2033.7903L19.5681%2033.4205ZM32.1764%2017.3022L19.5679%2033.4206L20.0403%2033.7901L32.6488%2017.6717L32.1764%2017.3022ZM37.4552%2017.1871H32.4126V17.7868H37.4552V17.1871ZM37.755%2031.509V17.4869H37.1553V31.509H37.755ZM32.4204%2031.8089H37.4552V31.2092H32.4204V31.8089ZM29.651%2028.1464L32.1763%2031.6833L32.6644%2031.3348L30.1391%2027.7979L29.651%2028.1464ZM29.6511%2028.1469L29.6594%2028.1575L30.1307%2027.7867L30.1224%2027.7761L29.6511%2028.1469ZM28.3992%2028.1469C28.7176%2027.7422%2029.3327%2027.7422%2029.6511%2028.1469L30.1224%2027.7761C29.5639%2027.0662%2028.4864%2027.0662%2027.9279%2027.7761L28.3992%2028.1469ZM27.7558%2028.9666L28.3994%2028.1466L27.9277%2027.7764L27.2841%2028.5964L27.7558%2028.9666ZM27.7559%2028.9665L27.7581%2028.9637L27.2819%2028.5993L27.2797%2028.6021L27.7559%2028.9665ZM27.7508%2029.9365C27.5333%2029.65%2027.5374%2029.2521%2027.7559%2028.9665L27.2797%2028.6021C26.9002%2029.098%2026.8893%2029.7935%2027.2732%2030.2991L27.7508%2029.9365ZM30.2293%2033.4197L27.7563%2029.944L27.2677%2030.2916L29.7407%2033.7674L30.2293%2033.4197ZM30.2293%2033.4191L30.2208%2033.4083L29.7492%2033.7788L29.7577%2033.7896L30.2293%2033.4191ZM32.2541%2034.4044C31.4617%2034.4044%2030.7205%2034.0445%2030.2293%2033.4191L29.7577%2033.7896C30.3625%2034.5595%2031.2775%2035.0041%2032.2541%2035.0041V34.4044ZM37.7795%2034.4044H32.2541V35.0041H37.7795V34.4044ZM40.3486%2031.8353C40.3486%2033.2521%2039.1985%2034.4044%2037.7795%2034.4044V35.0041C39.5301%2035.0041%2040.9483%2033.5829%2040.9483%2031.8353H40.3486ZM40.3486%2017.1626V31.8353H40.9483V17.1626H40.3486ZM37.7795%2014.5935C39.1987%2014.5935%2040.3486%2015.7479%2040.3486%2017.1626H40.9483C40.9483%2015.4174%2039.5305%2013.9939%2037.7795%2013.9939V14.5935ZM32.2541%2014.5935H37.7795V13.9939H32.2541V14.5935ZM30.2289%2015.5812C30.72%2014.9537%2031.4596%2014.5935%2032.2541%2014.5935V13.9939C31.2749%2013.9939%2030.3613%2014.4391%2029.7567%2015.2116L30.2289%2015.5812ZM17.6219%2031.6957L30.2289%2015.5811L29.7566%2015.2116L17.1496%2031.3262L17.6219%2031.6957ZM12.3417%2031.8108H17.3858V31.2111H12.3417V31.8108ZM12.0418%2017.4889V31.511H12.6415V17.4889H12.0418ZM17.3757%2017.189H12.3417V17.7887H17.3757V17.189ZM20.375%2021.0803L17.6177%2017.3118L17.1337%2017.6659L19.891%2021.4344L20.375%2021.0803ZM20.3749%2021.08L20.3693%2021.0728L19.8967%2021.4419L19.9023%2021.4491L20.3749%2021.08ZM21.6292%2021.0809C21.3091%2021.4869%2020.6937%2021.488%2020.3749%2021.08L19.9023%2021.4491C20.4627%2022.1665%2021.5415%2022.1608%2022.1001%2021.4522L21.6292%2021.0809ZM21.6302%2021.0796L21.6288%2021.0814L22.1005%2021.4517L22.1019%2021.45L21.6302%2021.0796ZM22.2688%2020.263L21.6299%2021.0801L22.1023%2021.4495L22.7412%2020.6324L22.2688%2020.263ZM22.2688%2020.263L22.2669%2020.2654L22.7431%2020.63L22.7449%2020.6276L22.2688%2020.263ZM22.2702%2019.2907C22.4916%2019.5777%2022.4877%2019.977%2022.2687%2020.2631L22.745%2020.6275C23.1252%2020.1307%2023.1363%2019.4315%2022.7449%2018.9243L22.2702%2019.2907ZM19.5678%2015.5807L22.2652%2019.284L22.7499%2018.931L20.0525%2015.2276L19.5678%2015.5807ZM19.5679%2015.5811L19.5741%2015.589L20.0463%2015.2193L20.0401%2015.2114L19.5679%2015.5811ZM17.5428%2014.5935C18.3394%2014.5935%2019.0768%2014.9537%2019.5679%2015.5811L20.0401%2015.2114C19.4357%2014.4393%2018.5243%2013.9939%2017.5428%2013.9939V14.5935ZM12.0173%2014.5935H17.5428V13.9939H12.0173V14.5935Z'%20fill='white'/%3e%3c/svg%3e";function ob(e){let t,n,r,o=!1;return function(s){void 0===t?(t=s,n=0,r=-1):t=function(e,t){const n=new Uint8Array(e.length+t.length);return n.set(e),n.set(t,e.length),n}(t,s);const i=t.length;let l=0;for(;n{const m=Object.assign({},r);let A;function b(){A.abort(),document.hidden||N()}m.accept||(m.accept=au),l||document.addEventListener("visibilitychange",b);let C=1e3,h=0;function g(){document.removeEventListener("visibilitychange",b),window.clearTimeout(h),A.abort()}null==n||n.addEventListener("abort",(()=>{g(),d()}));const E=u??window.fetch,v=o??cb;async function N(){var _;A=new AbortController;try{const S=await E(e,Object.assign(Object.assign({},c),{headers:m,signal:A.signal}));await v(S),await async function(e,t){const n=e.getReader();let r;for(;!(r=await n.read()).done;)t(r.value)}(S.body,ob(function(e,t,n){let r={data:"",event:"",id:"",retry:void 0};const o=new TextDecoder;return function(s,i){if(0===s.length)null==n||n(r),r={data:"",event:"",id:"",retry:void 0};else if(i>0){const l=o.decode(s.subarray(0,i)),u=i+(32===s[i+1]?2:1),c=o.decode(s.subarray(u));switch(l){case"data":r.data=r.data?r.data+"\n"+c:c;break;case"event":r.event=c;break;case"id":e(r.id=c);break;case"retry":const d=parseInt(c,10);isNaN(d)||t(r.retry=d)}}}}((R=>{R?m[Tp]=R:delete m[Tp]}),(R=>{C=R}),a))),null==s||s(),g(),d()}catch(S){if(!A.signal.aborted)try{const R=null!==(_=null==i?void 0:i(S))&&void 0!==_?_:C;window.clearTimeout(h),h=window.setTimeout(N,R)}catch(R){g(),p(R)}}}N()}))}function cb(e){const t=e.headers.get("content-type");if(null==t||!t.startsWith(au))throw new Error(`Expected content-type to be ${au}, Actual: ${t}`)}const ds={embedSessionHistory:async function(e,t){const{embedId:n,baseApiUrl:r}=e;return await fetch(`${r}/${n}/${t}`).then((o=>{if(o.ok)return o.json();throw new Error("Invalid response from server")})).then((o=>o.history.map((a=>({...a,id:zn(),sender:"user"===a.role?"user":"system",textResponse:a.content,close:!1}))))).catch((o=>(console.error(o),[])))},resetEmbedChatSession:async function(e,t){const{baseApiUrl:n,embedId:r}=e;return await fetch(`${n}/${r}/${t}`,{method:"DELETE"}).then((o=>o.ok)).catch((()=>!1))},streamChat:async function(e,t,n,r){const{baseApiUrl:o,embedId:a,username:s}=t,i={prompt:(null==t?void 0:t.prompt)??null,model:(null==t?void 0:t.model)??null,temperature:(null==t?void 0:t.temperature)??null},l=new AbortController;await ub(`${o}/${a}/stream-chat`,{method:"POST",body:JSON.stringify({message:n,sessionId:e,username:s,...i}),signal:l.signal,openWhenHidden:!0,async onopen(u){if(!u.ok)throw u.status>=400?(await u.json().then((c=>{r(c)})).catch((()=>{r({id:zn(),type:"abort",textResponse:null,sources:[],close:!0,error:`An error occurred while streaming response. Code ${u.status}`})})),l.abort(),new Error):(r({id:zn(),type:"abort",textResponse:null,sources:[],close:!0,error:"An error occurred while streaming response. Unknown Error."}),l.abort(),new Error("Unknown Error"))},async onmessage(u){try{const c=JSON.parse(u.data);r(c)}catch{}},onerror(u){throw r({id:zn(),type:"abort",textResponse:null,sources:[],close:!0,error:`An error occurred while streaming response. ${u.message}`}),l.abort(),new Error}})}};function Cp({sessionId:e,settings:t={},iconUrl:n=null,closeChat:r,setChatHistory:o}){const[a,s]=Z.useState(!1),i=Z.useRef(),l=Z.useRef();return Z.useEffect((()=>{function c(d){i.current&&!i.current.contains(d.target)&&l.current&&!l.current.contains(d.target)&&s(!1)}return document.addEventListener("mousedown",c),()=>{document.removeEventListener("mousedown",c)}}),[i]),w.jsxs("div",{style:{borderBottom:"1px solid #E9E9E9"},className:"allm-flex allm-items-center allm-relative allm-rounded-t-2xl",id:"anything-llm-header",children:[w.jsx("div",{className:"allm-flex allm-justify-center allm-items-center allm-w-full allm-h-[76px]",children:w.jsx("img",{style:{maxWidth:48,maxHeight:48},src:n??ko,alt:n?"Brand":"AnythingLLM Logo"})}),w.jsxs("div",{className:"allm-absolute allm-right-0 allm-flex allm-gap-x-1 allm-items-center allm-px-[22px]",children:[t.loaded&&w.jsx("button",{ref:l,type:"button",onClick:()=>s(!a),className:"allm-bg-transparent hover:allm-cursor-pointer allm-border-none hover:allm-bg-gray-100 allm-rounded-sm allm-text-slate-800/60","aria-label":"Options",children:w.jsx(J2,{size:20,weight:"fill"})}),w.jsx("button",{type:"button",onClick:r,className:"allm-bg-transparent hover:allm-cursor-pointer allm-border-none hover:allm-bg-gray-100 allm-rounded-sm allm-text-slate-800/60","aria-label":"Close",children:w.jsx(Dp,{size:20,weight:"bold"})})]}),w.jsx(db,{settings:t,showing:a,resetChat:async()=>{await ds.resetEmbedChatSession(t,e),o([]),s(!1)},sessionId:e,menuRef:i})]})}function db({settings:e,showing:t,resetChat:n,sessionId:r,menuRef:o}){return t?w.jsxs("div",{ref:o,className:"allm-bg-white allm-absolute allm-z-10 allm-flex allm-flex-col allm-gap-y-1 allm-rounded-xl allm-shadow-lg allm-top-[64px] allm-right-[46px]",children:[w.jsxs("button",{onClick:n,className:"hover:allm-cursor-pointer allm-bg-white allm-gap-x-[12px] hover:allm-bg-gray-100 allm-rounded-lg allm-border-none allm-flex allm-items-center allm-text-base allm-text-[#7A7D7E] allm-font-bold allm-px-4",children:[w.jsx(k2,{size:24}),w.jsx("p",{className:"allm-text-[14px]",children:"Reset Chat"})]}),w.jsx(fb,{email:e.supportEmail}),w.jsx(pb,{sessionId:r})]}):null}function pb({sessionId:e}){if(!e)return null;const[t,n]=Z.useState(!1);return t?w.jsxs("div",{className:"hover:allm-cursor-pointer allm-bg-white allm-gap-x-[12px] hover:allm-bg-gray-100 allm-rounded-lg allm-border-none allm-flex allm-items-center allm-text-base allm-text-[#7A7D7E] allm-font-bold allm-px-4",children:[w.jsx($2,{size:24}),w.jsx("p",{className:"allm-text-[14px] allm-font-sans",children:"Copied!"})]}):w.jsxs("button",{onClick:()=>{navigator.clipboard.writeText(e),n(!0),setTimeout((()=>n(!1)),1e3)},className:"hover:allm-cursor-pointer allm-bg-white allm-gap-x-[12px] hover:allm-bg-gray-100 allm-rounded-lg allm-border-none allm-flex allm-items-center allm-text-base allm-text-[#7A7D7E] allm-font-bold allm-px-4",children:[w.jsx(K2,{size:24}),w.jsx("p",{className:"allm-text-[14px]",children:"Session ID"})]})}function fb({email:e=null}){if(!e)return null;const t=`Inquiry from ${window.location.origin}`;return w.jsxs("a",{href:`mailto:${e}?Subject=${encodeURIComponent(t)}`,className:"allm-no-underline hover:allm-underline hover:allm-cursor-pointer allm-bg-white allm-gap-x-[12px] hover:allm-bg-gray-100 allm-rounded-lg allm-border-none allm-flex allm-items-center allm-text-base allm-text-[#7A7D7E] allm-font-bold allm-px-4",children:[w.jsx(np,{size:24}),w.jsx("p",{className:"allm-text-[14px] allm-font-sans",children:"Email Support"})]})}function mb(){const e=C2();return e?w.jsx("div",{className:"allm-text-xs allm-text-gray-300 allm-w-full allm-text-center",children:e}):null}var ps={exports:{}};/*! https://mths.be/he v1.2.0 by @mathias | MIT license */!function(e,t){!function(n){var r=t,o=e&&e.exports==r&&e,a="object"==typeof Nt&&Nt;(a.global===a||a.window===a)&&(n=a);var s=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,i=/[\x01-\x7F]/g,l=/[\x01-\t\x0B\f\x0E-\x1F\x7F\x81\x8D\x8F\x90\x9D\xA0-\uFFFF]/g,u=/<\u20D2|=\u20E5|>\u20D2|\u205F\u200A|\u219D\u0338|\u2202\u0338|\u2220\u20D2|\u2229\uFE00|\u222A\uFE00|\u223C\u20D2|\u223D\u0331|\u223E\u0333|\u2242\u0338|\u224B\u0338|\u224D\u20D2|\u224E\u0338|\u224F\u0338|\u2250\u0338|\u2261\u20E5|\u2264\u20D2|\u2265\u20D2|\u2266\u0338|\u2267\u0338|\u2268\uFE00|\u2269\uFE00|\u226A\u0338|\u226A\u20D2|\u226B\u0338|\u226B\u20D2|\u227F\u0338|\u2282\u20D2|\u2283\u20D2|\u228A\uFE00|\u228B\uFE00|\u228F\u0338|\u2290\u0338|\u2293\uFE00|\u2294\uFE00|\u22B4\u20D2|\u22B5\u20D2|\u22D8\u0338|\u22D9\u0338|\u22DA\uFE00|\u22DB\uFE00|\u22F5\u0338|\u22F9\u0338|\u2933\u0338|\u29CF\u0338|\u29D0\u0338|\u2A6D\u0338|\u2A70\u0338|\u2A7D\u0338|\u2A7E\u0338|\u2AA1\u0338|\u2AA2\u0338|\u2AAC\uFE00|\u2AAD\uFE00|\u2AAF\u0338|\u2AB0\u0338|\u2AC5\u0338|\u2AC6\u0338|\u2ACB\uFE00|\u2ACC\uFE00|\u2AFD\u20E5|[\xA0-\u0113\u0116-\u0122\u0124-\u012B\u012E-\u014D\u0150-\u017E\u0192\u01B5\u01F5\u0237\u02C6\u02C7\u02D8-\u02DD\u0311\u0391-\u03A1\u03A3-\u03A9\u03B1-\u03C9\u03D1\u03D2\u03D5\u03D6\u03DC\u03DD\u03F0\u03F1\u03F5\u03F6\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E\u045F\u2002-\u2005\u2007-\u2010\u2013-\u2016\u2018-\u201A\u201C-\u201E\u2020-\u2022\u2025\u2026\u2030-\u2035\u2039\u203A\u203E\u2041\u2043\u2044\u204F\u2057\u205F-\u2063\u20AC\u20DB\u20DC\u2102\u2105\u210A-\u2113\u2115-\u211E\u2122\u2124\u2127-\u2129\u212C\u212D\u212F-\u2131\u2133-\u2138\u2145-\u2148\u2153-\u215E\u2190-\u219B\u219D-\u21A7\u21A9-\u21AE\u21B0-\u21B3\u21B5-\u21B7\u21BA-\u21DB\u21DD\u21E4\u21E5\u21F5\u21FD-\u2205\u2207-\u2209\u220B\u220C\u220F-\u2214\u2216-\u2218\u221A\u221D-\u2238\u223A-\u2257\u2259\u225A\u225C\u225F-\u2262\u2264-\u228B\u228D-\u229B\u229D-\u22A5\u22A7-\u22B0\u22B2-\u22BB\u22BD-\u22DB\u22DE-\u22E3\u22E6-\u22F7\u22F9-\u22FE\u2305\u2306\u2308-\u2310\u2312\u2313\u2315\u2316\u231C-\u231F\u2322\u2323\u232D\u232E\u2336\u233D\u233F\u237C\u23B0\u23B1\u23B4-\u23B6\u23DC-\u23DF\u23E2\u23E7\u2423\u24C8\u2500\u2502\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C\u2550-\u256C\u2580\u2584\u2588\u2591-\u2593\u25A1\u25AA\u25AB\u25AD\u25AE\u25B1\u25B3-\u25B5\u25B8\u25B9\u25BD-\u25BF\u25C2\u25C3\u25CA\u25CB\u25EC\u25EF\u25F8-\u25FC\u2605\u2606\u260E\u2640\u2642\u2660\u2663\u2665\u2666\u266A\u266D-\u266F\u2713\u2717\u2720\u2736\u2758\u2772\u2773\u27C8\u27C9\u27E6-\u27ED\u27F5-\u27FA\u27FC\u27FF\u2902-\u2905\u290C-\u2913\u2916\u2919-\u2920\u2923-\u292A\u2933\u2935-\u2939\u293C\u293D\u2945\u2948-\u294B\u294E-\u2976\u2978\u2979\u297B-\u297F\u2985\u2986\u298B-\u2996\u299A\u299C\u299D\u29A4-\u29B7\u29B9\u29BB\u29BC\u29BE-\u29C5\u29C9\u29CD-\u29D0\u29DC-\u29DE\u29E3-\u29E5\u29EB\u29F4\u29F6\u2A00-\u2A02\u2A04\u2A06\u2A0C\u2A0D\u2A10-\u2A17\u2A22-\u2A27\u2A29\u2A2A\u2A2D-\u2A31\u2A33-\u2A3C\u2A3F\u2A40\u2A42-\u2A4D\u2A50\u2A53-\u2A58\u2A5A-\u2A5D\u2A5F\u2A66\u2A6A\u2A6D-\u2A75\u2A77-\u2A9A\u2A9D-\u2AA2\u2AA4-\u2AB0\u2AB3-\u2AC8\u2ACB\u2ACC\u2ACF-\u2ADB\u2AE4\u2AE6-\u2AE9\u2AEB-\u2AF3\u2AFD\uFB00-\uFB04]|\uD835[\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDCCF\uDD04\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDD6B]/g,c={"­":"shy","‌":"zwnj","‍":"zwj","‎":"lrm","⁣":"ic","⁢":"it","⁡":"af","‏":"rlm","​":"ZeroWidthSpace","⁠":"NoBreak","̑":"DownBreve","⃛":"tdot","⃜":"DotDot","\t":"Tab","\n":"NewLine"," ":"puncsp"," ":"MediumSpace"," ":"thinsp"," ":"hairsp"," ":"emsp13"," ":"ensp"," ":"emsp14"," ":"emsp"," ":"numsp"," ":"nbsp","  ":"ThickSpace","‾":"oline",_:"lowbar","‐":"dash","–":"ndash","—":"mdash","―":"horbar",",":"comma",";":"semi","⁏":"bsemi",":":"colon","⩴":"Colone","!":"excl","¡":"iexcl","?":"quest","¿":"iquest",".":"period","‥":"nldr","…":"mldr","·":"middot","'":"apos","‘":"lsquo","’":"rsquo","‚":"sbquo","‹":"lsaquo","›":"rsaquo",'"':"quot","“":"ldquo","”":"rdquo","„":"bdquo","«":"laquo","»":"raquo","(":"lpar",")":"rpar","[":"lsqb","]":"rsqb","{":"lcub","}":"rcub","⌈":"lceil","⌉":"rceil","⌊":"lfloor","⌋":"rfloor","⦅":"lopar","⦆":"ropar","⦋":"lbrke","⦌":"rbrke","⦍":"lbrkslu","⦎":"rbrksld","⦏":"lbrksld","⦐":"rbrkslu","⦑":"langd","⦒":"rangd","⦓":"lparlt","⦔":"rpargt","⦕":"gtlPar","⦖":"ltrPar","⟦":"lobrk","⟧":"robrk","⟨":"lang","⟩":"rang","⟪":"Lang","⟫":"Rang","⟬":"loang","⟭":"roang","❲":"lbbrk","❳":"rbbrk","‖":"Vert","§":"sect","¶":"para","@":"commat","*":"ast","/":"sol",undefined:null,"&":"amp","#":"num","%":"percnt","‰":"permil","‱":"pertenk","†":"dagger","‡":"Dagger","•":"bull","⁃":"hybull","′":"prime","″":"Prime","‴":"tprime","⁗":"qprime","‵":"bprime","⁁":"caret","`":"grave","´":"acute","˜":"tilde","^":"Hat","¯":"macr","˘":"breve","˙":"dot","¨":"die","˚":"ring","˝":"dblac","¸":"cedil","˛":"ogon","ˆ":"circ","ˇ":"caron","°":"deg","©":"copy","®":"reg","℗":"copysr","℘":"wp","℞":"rx","℧":"mho","℩":"iiota","←":"larr","↚":"nlarr","→":"rarr","↛":"nrarr","↑":"uarr","↓":"darr","↔":"harr","↮":"nharr","↕":"varr","↖":"nwarr","↗":"nearr","↘":"searr","↙":"swarr","↝":"rarrw","↝̸":"nrarrw","↞":"Larr","↟":"Uarr","↠":"Rarr","↡":"Darr","↢":"larrtl","↣":"rarrtl","↤":"mapstoleft","↥":"mapstoup","↦":"map","↧":"mapstodown","↩":"larrhk","↪":"rarrhk","↫":"larrlp","↬":"rarrlp","↭":"harrw","↰":"lsh","↱":"rsh","↲":"ldsh","↳":"rdsh","↵":"crarr","↶":"cularr","↷":"curarr","↺":"olarr","↻":"orarr","↼":"lharu","↽":"lhard","↾":"uharr","↿":"uharl","⇀":"rharu","⇁":"rhard","⇂":"dharr","⇃":"dharl","⇄":"rlarr","⇅":"udarr","⇆":"lrarr","⇇":"llarr","⇈":"uuarr","⇉":"rrarr","⇊":"ddarr","⇋":"lrhar","⇌":"rlhar","⇐":"lArr","⇍":"nlArr","⇑":"uArr","⇒":"rArr","⇏":"nrArr","⇓":"dArr","⇔":"iff","⇎":"nhArr","⇕":"vArr","⇖":"nwArr","⇗":"neArr","⇘":"seArr","⇙":"swArr","⇚":"lAarr","⇛":"rAarr","⇝":"zigrarr","⇤":"larrb","⇥":"rarrb","⇵":"duarr","⇽":"loarr","⇾":"roarr","⇿":"hoarr","∀":"forall","∁":"comp","∂":"part","∂̸":"npart","∃":"exist","∄":"nexist","∅":"empty","∇":"Del","∈":"in","∉":"notin","∋":"ni","∌":"notni","϶":"bepsi","∏":"prod","∐":"coprod","∑":"sum","+":"plus","±":"pm","÷":"div","×":"times","<":"lt","≮":"nlt","<⃒":"nvlt","=":"equals","≠":"ne","=⃥":"bne","⩵":"Equal",">":"gt","≯":"ngt",">⃒":"nvgt","¬":"not","|":"vert","¦":"brvbar","−":"minus","∓":"mp","∔":"plusdo","⁄":"frasl","∖":"setmn","∗":"lowast","∘":"compfn","√":"Sqrt","∝":"prop","∞":"infin","∟":"angrt","∠":"ang","∠⃒":"nang","∡":"angmsd","∢":"angsph","∣":"mid","∤":"nmid","∥":"par","∦":"npar","∧":"and","∨":"or","∩":"cap","∩︀":"caps","∪":"cup","∪︀":"cups","∫":"int","∬":"Int","∭":"tint","⨌":"qint","∮":"oint","∯":"Conint","∰":"Cconint","∱":"cwint","∲":"cwconint","∳":"awconint","∴":"there4","∵":"becaus","∶":"ratio","∷":"Colon","∸":"minusd","∺":"mDDot","∻":"homtht","∼":"sim","≁":"nsim","∼⃒":"nvsim","∽":"bsim","∽̱":"race","∾":"ac","∾̳":"acE","∿":"acd","≀":"wr","≂":"esim","≂̸":"nesim","≃":"sime","≄":"nsime","≅":"cong","≇":"ncong","≆":"simne","≈":"ap","≉":"nap","≊":"ape","≋":"apid","≋̸":"napid","≌":"bcong","≍":"CupCap","≭":"NotCupCap","≍⃒":"nvap","≎":"bump","≎̸":"nbump","≏":"bumpe","≏̸":"nbumpe","≐":"doteq","≐̸":"nedot","≑":"eDot","≒":"efDot","≓":"erDot","≔":"colone","≕":"ecolon","≖":"ecir","≗":"cire","≙":"wedgeq","≚":"veeeq","≜":"trie","≟":"equest","≡":"equiv","≢":"nequiv","≡⃥":"bnequiv","≤":"le","≰":"nle","≤⃒":"nvle","≥":"ge","≱":"nge","≥⃒":"nvge","≦":"lE","≦̸":"nlE","≧":"gE","≧̸":"ngE","≨︀":"lvnE","≨":"lnE","≩":"gnE","≩︀":"gvnE","≪":"ll","≪̸":"nLtv","≪⃒":"nLt","≫":"gg","≫̸":"nGtv","≫⃒":"nGt","≬":"twixt","≲":"lsim","≴":"nlsim","≳":"gsim","≵":"ngsim","≶":"lg","≸":"ntlg","≷":"gl","≹":"ntgl","≺":"pr","⊀":"npr","≻":"sc","⊁":"nsc","≼":"prcue","⋠":"nprcue","≽":"sccue","⋡":"nsccue","≾":"prsim","≿":"scsim","≿̸":"NotSucceedsTilde","⊂":"sub","⊄":"nsub","⊂⃒":"vnsub","⊃":"sup","⊅":"nsup","⊃⃒":"vnsup","⊆":"sube","⊈":"nsube","⊇":"supe","⊉":"nsupe","⊊︀":"vsubne","⊊":"subne","⊋︀":"vsupne","⊋":"supne","⊍":"cupdot","⊎":"uplus","⊏":"sqsub","⊏̸":"NotSquareSubset","⊐":"sqsup","⊐̸":"NotSquareSuperset","⊑":"sqsube","⋢":"nsqsube","⊒":"sqsupe","⋣":"nsqsupe","⊓":"sqcap","⊓︀":"sqcaps","⊔":"sqcup","⊔︀":"sqcups","⊕":"oplus","⊖":"ominus","⊗":"otimes","⊘":"osol","⊙":"odot","⊚":"ocir","⊛":"oast","⊝":"odash","⊞":"plusb","⊟":"minusb","⊠":"timesb","⊡":"sdotb","⊢":"vdash","⊬":"nvdash","⊣":"dashv","⊤":"top","⊥":"bot","⊧":"models","⊨":"vDash","⊭":"nvDash","⊩":"Vdash","⊮":"nVdash","⊪":"Vvdash","⊫":"VDash","⊯":"nVDash","⊰":"prurel","⊲":"vltri","⋪":"nltri","⊳":"vrtri","⋫":"nrtri","⊴":"ltrie","⋬":"nltrie","⊴⃒":"nvltrie","⊵":"rtrie","⋭":"nrtrie","⊵⃒":"nvrtrie","⊶":"origof","⊷":"imof","⊸":"mumap","⊹":"hercon","⊺":"intcal","⊻":"veebar","⊽":"barvee","⊾":"angrtvb","⊿":"lrtri","⋀":"Wedge","⋁":"Vee","⋂":"xcap","⋃":"xcup","⋄":"diam","⋅":"sdot","⋆":"Star","⋇":"divonx","⋈":"bowtie","⋉":"ltimes","⋊":"rtimes","⋋":"lthree","⋌":"rthree","⋍":"bsime","⋎":"cuvee","⋏":"cuwed","⋐":"Sub","⋑":"Sup","⋒":"Cap","⋓":"Cup","⋔":"fork","⋕":"epar","⋖":"ltdot","⋗":"gtdot","⋘":"Ll","⋘̸":"nLl","⋙":"Gg","⋙̸":"nGg","⋚︀":"lesg","⋚":"leg","⋛":"gel","⋛︀":"gesl","⋞":"cuepr","⋟":"cuesc","⋦":"lnsim","⋧":"gnsim","⋨":"prnsim","⋩":"scnsim","⋮":"vellip","⋯":"ctdot","⋰":"utdot","⋱":"dtdot","⋲":"disin","⋳":"isinsv","⋴":"isins","⋵":"isindot","⋵̸":"notindot","⋶":"notinvc","⋷":"notinvb","⋹":"isinE","⋹̸":"notinE","⋺":"nisd","⋻":"xnis","⋼":"nis","⋽":"notnivc","⋾":"notnivb","⌅":"barwed","⌆":"Barwed","⌌":"drcrop","⌍":"dlcrop","⌎":"urcrop","⌏":"ulcrop","⌐":"bnot","⌒":"profline","⌓":"profsurf","⌕":"telrec","⌖":"target","⌜":"ulcorn","⌝":"urcorn","⌞":"dlcorn","⌟":"drcorn","⌢":"frown","⌣":"smile","⌭":"cylcty","⌮":"profalar","⌶":"topbot","⌽":"ovbar","⌿":"solbar","⍼":"angzarr","⎰":"lmoust","⎱":"rmoust","⎴":"tbrk","⎵":"bbrk","⎶":"bbrktbrk","⏜":"OverParenthesis","⏝":"UnderParenthesis","⏞":"OverBrace","⏟":"UnderBrace","⏢":"trpezium","⏧":"elinters","␣":"blank","─":"boxh","│":"boxv","┌":"boxdr","┐":"boxdl","└":"boxur","┘":"boxul","├":"boxvr","┤":"boxvl","┬":"boxhd","┴":"boxhu","┼":"boxvh","═":"boxH","║":"boxV","╒":"boxdR","╓":"boxDr","╔":"boxDR","╕":"boxdL","╖":"boxDl","╗":"boxDL","╘":"boxuR","╙":"boxUr","╚":"boxUR","╛":"boxuL","╜":"boxUl","╝":"boxUL","╞":"boxvR","╟":"boxVr","╠":"boxVR","╡":"boxvL","╢":"boxVl","╣":"boxVL","╤":"boxHd","╥":"boxhD","╦":"boxHD","╧":"boxHu","╨":"boxhU","╩":"boxHU","╪":"boxvH","╫":"boxVh","╬":"boxVH","▀":"uhblk","▄":"lhblk","█":"block","░":"blk14","▒":"blk12","▓":"blk34","□":"squ","▪":"squf","▫":"EmptyVerySmallSquare","▭":"rect","▮":"marker","▱":"fltns","△":"xutri","▴":"utrif","▵":"utri","▸":"rtrif","▹":"rtri","▽":"xdtri","▾":"dtrif","▿":"dtri","◂":"ltrif","◃":"ltri","◊":"loz","○":"cir","◬":"tridot","◯":"xcirc","◸":"ultri","◹":"urtri","◺":"lltri","◻":"EmptySmallSquare","◼":"FilledSmallSquare","★":"starf","☆":"star","☎":"phone","♀":"female","♂":"male","♠":"spades","♣":"clubs","♥":"hearts","♦":"diams","♪":"sung","✓":"check","✗":"cross","✠":"malt","✶":"sext","❘":"VerticalSeparator","⟈":"bsolhsub","⟉":"suphsol","⟵":"xlarr","⟶":"xrarr","⟷":"xharr","⟸":"xlArr","⟹":"xrArr","⟺":"xhArr","⟼":"xmap","⟿":"dzigrarr","⤂":"nvlArr","⤃":"nvrArr","⤄":"nvHarr","⤅":"Map","⤌":"lbarr","⤍":"rbarr","⤎":"lBarr","⤏":"rBarr","⤐":"RBarr","⤑":"DDotrahd","⤒":"UpArrowBar","⤓":"DownArrowBar","⤖":"Rarrtl","⤙":"latail","⤚":"ratail","⤛":"lAtail","⤜":"rAtail","⤝":"larrfs","⤞":"rarrfs","⤟":"larrbfs","⤠":"rarrbfs","⤣":"nwarhk","⤤":"nearhk","⤥":"searhk","⤦":"swarhk","⤧":"nwnear","⤨":"toea","⤩":"tosa","⤪":"swnwar","⤳":"rarrc","⤳̸":"nrarrc","⤵":"cudarrr","⤶":"ldca","⤷":"rdca","⤸":"cudarrl","⤹":"larrpl","⤼":"curarrm","⤽":"cularrp","⥅":"rarrpl","⥈":"harrcir","⥉":"Uarrocir","⥊":"lurdshar","⥋":"ldrushar","⥎":"LeftRightVector","⥏":"RightUpDownVector","⥐":"DownLeftRightVector","⥑":"LeftUpDownVector","⥒":"LeftVectorBar","⥓":"RightVectorBar","⥔":"RightUpVectorBar","⥕":"RightDownVectorBar","⥖":"DownLeftVectorBar","⥗":"DownRightVectorBar","⥘":"LeftUpVectorBar","⥙":"LeftDownVectorBar","⥚":"LeftTeeVector","⥛":"RightTeeVector","⥜":"RightUpTeeVector","⥝":"RightDownTeeVector","⥞":"DownLeftTeeVector","⥟":"DownRightTeeVector","⥠":"LeftUpTeeVector","⥡":"LeftDownTeeVector","⥢":"lHar","⥣":"uHar","⥤":"rHar","⥥":"dHar","⥦":"luruhar","⥧":"ldrdhar","⥨":"ruluhar","⥩":"rdldhar","⥪":"lharul","⥫":"llhard","⥬":"rharul","⥭":"lrhard","⥮":"udhar","⥯":"duhar","⥰":"RoundImplies","⥱":"erarr","⥲":"simrarr","⥳":"larrsim","⥴":"rarrsim","⥵":"rarrap","⥶":"ltlarr","⥸":"gtrarr","⥹":"subrarr","⥻":"suplarr","⥼":"lfisht","⥽":"rfisht","⥾":"ufisht","⥿":"dfisht","⦚":"vzigzag","⦜":"vangrt","⦝":"angrtvbd","⦤":"ange","⦥":"range","⦦":"dwangle","⦧":"uwangle","⦨":"angmsdaa","⦩":"angmsdab","⦪":"angmsdac","⦫":"angmsdad","⦬":"angmsdae","⦭":"angmsdaf","⦮":"angmsdag","⦯":"angmsdah","⦰":"bemptyv","⦱":"demptyv","⦲":"cemptyv","⦳":"raemptyv","⦴":"laemptyv","⦵":"ohbar","⦶":"omid","⦷":"opar","⦹":"operp","⦻":"olcross","⦼":"odsold","⦾":"olcir","⦿":"ofcir","⧀":"olt","⧁":"ogt","⧂":"cirscir","⧃":"cirE","⧄":"solb","⧅":"bsolb","⧉":"boxbox","⧍":"trisb","⧎":"rtriltri","⧏":"LeftTriangleBar","⧏̸":"NotLeftTriangleBar","⧐":"RightTriangleBar","⧐̸":"NotRightTriangleBar","⧜":"iinfin","⧝":"infintie","⧞":"nvinfin","⧣":"eparsl","⧤":"smeparsl","⧥":"eqvparsl","⧫":"lozf","⧴":"RuleDelayed","⧶":"dsol","⨀":"xodot","⨁":"xoplus","⨂":"xotime","⨄":"xuplus","⨆":"xsqcup","⨍":"fpartint","⨐":"cirfnint","⨑":"awint","⨒":"rppolint","⨓":"scpolint","⨔":"npolint","⨕":"pointint","⨖":"quatint","⨗":"intlarhk","⨢":"pluscir","⨣":"plusacir","⨤":"simplus","⨥":"plusdu","⨦":"plussim","⨧":"plustwo","⨩":"mcomma","⨪":"minusdu","⨭":"loplus","⨮":"roplus","⨯":"Cross","⨰":"timesd","⨱":"timesbar","⨳":"smashp","⨴":"lotimes","⨵":"rotimes","⨶":"otimesas","⨷":"Otimes","⨸":"odiv","⨹":"triplus","⨺":"triminus","⨻":"tritime","⨼":"iprod","⨿":"amalg","⩀":"capdot","⩂":"ncup","⩃":"ncap","⩄":"capand","⩅":"cupor","⩆":"cupcap","⩇":"capcup","⩈":"cupbrcap","⩉":"capbrcup","⩊":"cupcup","⩋":"capcap","⩌":"ccups","⩍":"ccaps","⩐":"ccupssm","⩓":"And","⩔":"Or","⩕":"andand","⩖":"oror","⩗":"orslope","⩘":"andslope","⩚":"andv","⩛":"orv","⩜":"andd","⩝":"ord","⩟":"wedbar","⩦":"sdote","⩪":"simdot","⩭":"congdot","⩭̸":"ncongdot","⩮":"easter","⩯":"apacir","⩰":"apE","⩰̸":"napE","⩱":"eplus","⩲":"pluse","⩳":"Esim","⩷":"eDDot","⩸":"equivDD","⩹":"ltcir","⩺":"gtcir","⩻":"ltquest","⩼":"gtquest","⩽":"les","⩽̸":"nles","⩾":"ges","⩾̸":"nges","⩿":"lesdot","⪀":"gesdot","⪁":"lesdoto","⪂":"gesdoto","⪃":"lesdotor","⪄":"gesdotol","⪅":"lap","⪆":"gap","⪇":"lne","⪈":"gne","⪉":"lnap","⪊":"gnap","⪋":"lEg","⪌":"gEl","⪍":"lsime","⪎":"gsime","⪏":"lsimg","⪐":"gsiml","⪑":"lgE","⪒":"glE","⪓":"lesges","⪔":"gesles","⪕":"els","⪖":"egs","⪗":"elsdot","⪘":"egsdot","⪙":"el","⪚":"eg","⪝":"siml","⪞":"simg","⪟":"simlE","⪠":"simgE","⪡":"LessLess","⪡̸":"NotNestedLessLess","⪢":"GreaterGreater","⪢̸":"NotNestedGreaterGreater","⪤":"glj","⪥":"gla","⪦":"ltcc","⪧":"gtcc","⪨":"lescc","⪩":"gescc","⪪":"smt","⪫":"lat","⪬":"smte","⪬︀":"smtes","⪭":"late","⪭︀":"lates","⪮":"bumpE","⪯":"pre","⪯̸":"npre","⪰":"sce","⪰̸":"nsce","⪳":"prE","⪴":"scE","⪵":"prnE","⪶":"scnE","⪷":"prap","⪸":"scap","⪹":"prnap","⪺":"scnap","⪻":"Pr","⪼":"Sc","⪽":"subdot","⪾":"supdot","⪿":"subplus","⫀":"supplus","⫁":"submult","⫂":"supmult","⫃":"subedot","⫄":"supedot","⫅":"subE","⫅̸":"nsubE","⫆":"supE","⫆̸":"nsupE","⫇":"subsim","⫈":"supsim","⫋︀":"vsubnE","⫋":"subnE","⫌︀":"vsupnE","⫌":"supnE","⫏":"csub","⫐":"csup","⫑":"csube","⫒":"csupe","⫓":"subsup","⫔":"supsub","⫕":"subsub","⫖":"supsup","⫗":"suphsub","⫘":"supdsub","⫙":"forkv","⫚":"topfork","⫛":"mlcp","⫤":"Dashv","⫦":"Vdashl","⫧":"Barv","⫨":"vBar","⫩":"vBarv","⫫":"Vbar","⫬":"Not","⫭":"bNot","⫮":"rnmid","⫯":"cirmid","⫰":"midcir","⫱":"topcir","⫲":"nhpar","⫳":"parsim","⫽":"parsl","⫽⃥":"nparsl","♭":"flat","♮":"natur","♯":"sharp","¤":"curren","¢":"cent",$:"dollar","£":"pound","¥":"yen","€":"euro","¹":"sup1","½":"half","⅓":"frac13","¼":"frac14","⅕":"frac15","⅙":"frac16","⅛":"frac18","²":"sup2","⅔":"frac23","⅖":"frac25","³":"sup3","¾":"frac34","⅗":"frac35","⅜":"frac38","⅘":"frac45","⅚":"frac56","⅝":"frac58","⅞":"frac78","𝒶":"ascr","𝕒":"aopf","𝔞":"afr","𝔸":"Aopf","𝔄":"Afr","𝒜":"Ascr","ª":"ordf","á":"aacute","Á":"Aacute","à":"agrave","À":"Agrave","ă":"abreve","Ă":"Abreve","â":"acirc","Â":"Acirc","å":"aring","Å":"angst","ä":"auml","Ä":"Auml","ã":"atilde","Ã":"Atilde","ą":"aogon","Ą":"Aogon","ā":"amacr","Ā":"Amacr","æ":"aelig","Æ":"AElig","𝒷":"bscr","𝕓":"bopf","𝔟":"bfr","𝔹":"Bopf","ℬ":"Bscr","𝔅":"Bfr","𝔠":"cfr","𝒸":"cscr","𝕔":"copf","ℭ":"Cfr","𝒞":"Cscr","ℂ":"Copf","ć":"cacute","Ć":"Cacute","ĉ":"ccirc","Ĉ":"Ccirc","č":"ccaron","Č":"Ccaron","ċ":"cdot","Ċ":"Cdot","ç":"ccedil","Ç":"Ccedil","℅":"incare","𝔡":"dfr","ⅆ":"dd","𝕕":"dopf","𝒹":"dscr","𝒟":"Dscr","𝔇":"Dfr","ⅅ":"DD","𝔻":"Dopf","ď":"dcaron","Ď":"Dcaron","đ":"dstrok","Đ":"Dstrok","ð":"eth","Ð":"ETH","ⅇ":"ee","ℯ":"escr","𝔢":"efr","𝕖":"eopf","ℰ":"Escr","𝔈":"Efr","𝔼":"Eopf","é":"eacute","É":"Eacute","è":"egrave","È":"Egrave","ê":"ecirc","Ê":"Ecirc","ě":"ecaron","Ě":"Ecaron","ë":"euml","Ë":"Euml","ė":"edot","Ė":"Edot","ę":"eogon","Ę":"Eogon","ē":"emacr","Ē":"Emacr","𝔣":"ffr","𝕗":"fopf","𝒻":"fscr","𝔉":"Ffr","𝔽":"Fopf","ℱ":"Fscr","ff":"fflig","ffi":"ffilig","ffl":"ffllig","fi":"filig",fj:"fjlig","fl":"fllig","ƒ":"fnof","ℊ":"gscr","𝕘":"gopf","𝔤":"gfr","𝒢":"Gscr","𝔾":"Gopf","𝔊":"Gfr","ǵ":"gacute","ğ":"gbreve","Ğ":"Gbreve","ĝ":"gcirc","Ĝ":"Gcirc","ġ":"gdot","Ġ":"Gdot","Ģ":"Gcedil","𝔥":"hfr","ℎ":"planckh","𝒽":"hscr","𝕙":"hopf","ℋ":"Hscr","ℌ":"Hfr","ℍ":"Hopf","ĥ":"hcirc","Ĥ":"Hcirc","ℏ":"hbar","ħ":"hstrok","Ħ":"Hstrok","𝕚":"iopf","𝔦":"ifr","𝒾":"iscr","ⅈ":"ii","𝕀":"Iopf","ℐ":"Iscr","ℑ":"Im","í":"iacute","Í":"Iacute","ì":"igrave","Ì":"Igrave","î":"icirc","Î":"Icirc","ï":"iuml","Ï":"Iuml","ĩ":"itilde","Ĩ":"Itilde","İ":"Idot","į":"iogon","Į":"Iogon","ī":"imacr","Ī":"Imacr","ij":"ijlig","IJ":"IJlig","ı":"imath","𝒿":"jscr","𝕛":"jopf","𝔧":"jfr","𝒥":"Jscr","𝔍":"Jfr","𝕁":"Jopf","ĵ":"jcirc","Ĵ":"Jcirc","ȷ":"jmath","𝕜":"kopf","𝓀":"kscr","𝔨":"kfr","𝒦":"Kscr","𝕂":"Kopf","𝔎":"Kfr","ķ":"kcedil","Ķ":"Kcedil","𝔩":"lfr","𝓁":"lscr","ℓ":"ell","𝕝":"lopf","ℒ":"Lscr","𝔏":"Lfr","𝕃":"Lopf","ĺ":"lacute","Ĺ":"Lacute","ľ":"lcaron","Ľ":"Lcaron","ļ":"lcedil","Ļ":"Lcedil","ł":"lstrok","Ł":"Lstrok","ŀ":"lmidot","Ŀ":"Lmidot","𝔪":"mfr","𝕞":"mopf","𝓂":"mscr","𝔐":"Mfr","𝕄":"Mopf","ℳ":"Mscr","𝔫":"nfr","𝕟":"nopf","𝓃":"nscr","ℕ":"Nopf","𝒩":"Nscr","𝔑":"Nfr","ń":"nacute","Ń":"Nacute","ň":"ncaron","Ň":"Ncaron","ñ":"ntilde","Ñ":"Ntilde","ņ":"ncedil","Ņ":"Ncedil","№":"numero","ŋ":"eng","Ŋ":"ENG","𝕠":"oopf","𝔬":"ofr","ℴ":"oscr","𝒪":"Oscr","𝔒":"Ofr","𝕆":"Oopf","º":"ordm","ó":"oacute","Ó":"Oacute","ò":"ograve","Ò":"Ograve","ô":"ocirc","Ô":"Ocirc","ö":"ouml","Ö":"Ouml","ő":"odblac","Ő":"Odblac","õ":"otilde","Õ":"Otilde","ø":"oslash","Ø":"Oslash","ō":"omacr","Ō":"Omacr","œ":"oelig","Œ":"OElig","𝔭":"pfr","𝓅":"pscr","𝕡":"popf","ℙ":"Popf","𝔓":"Pfr","𝒫":"Pscr","𝕢":"qopf","𝔮":"qfr","𝓆":"qscr","𝒬":"Qscr","𝔔":"Qfr","ℚ":"Qopf","ĸ":"kgreen","𝔯":"rfr","𝕣":"ropf","𝓇":"rscr","ℛ":"Rscr","ℜ":"Re","ℝ":"Ropf","ŕ":"racute","Ŕ":"Racute","ř":"rcaron","Ř":"Rcaron","ŗ":"rcedil","Ŗ":"Rcedil","𝕤":"sopf","𝓈":"sscr","𝔰":"sfr","𝕊":"Sopf","𝔖":"Sfr","𝒮":"Sscr","Ⓢ":"oS","ś":"sacute","Ś":"Sacute","ŝ":"scirc","Ŝ":"Scirc","š":"scaron","Š":"Scaron","ş":"scedil","Ş":"Scedil","ß":"szlig","𝔱":"tfr","𝓉":"tscr","𝕥":"topf","𝒯":"Tscr","𝔗":"Tfr","𝕋":"Topf","ť":"tcaron","Ť":"Tcaron","ţ":"tcedil","Ţ":"Tcedil","™":"trade","ŧ":"tstrok","Ŧ":"Tstrok","𝓊":"uscr","𝕦":"uopf","𝔲":"ufr","𝕌":"Uopf","𝔘":"Ufr","𝒰":"Uscr","ú":"uacute","Ú":"Uacute","ù":"ugrave","Ù":"Ugrave","ŭ":"ubreve","Ŭ":"Ubreve","û":"ucirc","Û":"Ucirc","ů":"uring","Ů":"Uring","ü":"uuml","Ü":"Uuml","ű":"udblac","Ű":"Udblac","ũ":"utilde","Ũ":"Utilde","ų":"uogon","Ų":"Uogon","ū":"umacr","Ū":"Umacr","𝔳":"vfr","𝕧":"vopf","𝓋":"vscr","𝔙":"Vfr","𝕍":"Vopf","𝒱":"Vscr","𝕨":"wopf","𝓌":"wscr","𝔴":"wfr","𝒲":"Wscr","𝕎":"Wopf","𝔚":"Wfr","ŵ":"wcirc","Ŵ":"Wcirc","𝔵":"xfr","𝓍":"xscr","𝕩":"xopf","𝕏":"Xopf","𝔛":"Xfr","𝒳":"Xscr","𝔶":"yfr","𝓎":"yscr","𝕪":"yopf","𝒴":"Yscr","𝔜":"Yfr","𝕐":"Yopf","ý":"yacute","Ý":"Yacute","ŷ":"ycirc","Ŷ":"Ycirc","ÿ":"yuml","Ÿ":"Yuml","𝓏":"zscr","𝔷":"zfr","𝕫":"zopf","ℨ":"Zfr","ℤ":"Zopf","𝒵":"Zscr","ź":"zacute","Ź":"Zacute","ž":"zcaron","Ž":"Zcaron","ż":"zdot","Ż":"Zdot","Ƶ":"imped","þ":"thorn","Þ":"THORN","ʼn":"napos","α":"alpha","Α":"Alpha","β":"beta","Β":"Beta","γ":"gamma","Γ":"Gamma","δ":"delta","Δ":"Delta","ε":"epsi","ϵ":"epsiv","Ε":"Epsilon","ϝ":"gammad","Ϝ":"Gammad","ζ":"zeta","Ζ":"Zeta","η":"eta","Η":"Eta","θ":"theta","ϑ":"thetav","Θ":"Theta","ι":"iota","Ι":"Iota","κ":"kappa","ϰ":"kappav","Κ":"Kappa","λ":"lambda","Λ":"Lambda","μ":"mu","µ":"micro","Μ":"Mu","ν":"nu","Ν":"Nu","ξ":"xi","Ξ":"Xi","ο":"omicron","Ο":"Omicron","π":"pi","ϖ":"piv","Π":"Pi","ρ":"rho","ϱ":"rhov","Ρ":"Rho","σ":"sigma","Σ":"Sigma","ς":"sigmaf","τ":"tau","Τ":"Tau","υ":"upsi","Υ":"Upsilon","ϒ":"Upsi","φ":"phi","ϕ":"phiv","Φ":"Phi","χ":"chi","Χ":"Chi","ψ":"psi","Ψ":"Psi","ω":"omega","Ω":"ohm","а":"acy","А":"Acy","б":"bcy","Б":"Bcy","в":"vcy","В":"Vcy","г":"gcy","Г":"Gcy","ѓ":"gjcy","Ѓ":"GJcy","д":"dcy","Д":"Dcy","ђ":"djcy","Ђ":"DJcy","е":"iecy","Е":"IEcy","ё":"iocy","Ё":"IOcy","є":"jukcy","Є":"Jukcy","ж":"zhcy","Ж":"ZHcy","з":"zcy","З":"Zcy","ѕ":"dscy","Ѕ":"DScy","и":"icy","И":"Icy","і":"iukcy","І":"Iukcy","ї":"yicy","Ї":"YIcy","й":"jcy","Й":"Jcy","ј":"jsercy","Ј":"Jsercy","к":"kcy","К":"Kcy","ќ":"kjcy","Ќ":"KJcy","л":"lcy","Л":"Lcy","љ":"ljcy","Љ":"LJcy","м":"mcy","М":"Mcy","н":"ncy","Н":"Ncy","њ":"njcy","Њ":"NJcy","о":"ocy","О":"Ocy","п":"pcy","П":"Pcy","р":"rcy","Р":"Rcy","с":"scy","С":"Scy","т":"tcy","Т":"Tcy","ћ":"tshcy","Ћ":"TSHcy","у":"ucy","У":"Ucy","ў":"ubrcy","Ў":"Ubrcy","ф":"fcy","Ф":"Fcy","х":"khcy","Х":"KHcy","ц":"tscy","Ц":"TScy","ч":"chcy","Ч":"CHcy","џ":"dzcy","Џ":"DZcy","ш":"shcy","Ш":"SHcy","щ":"shchcy","Щ":"SHCHcy","ъ":"hardcy","Ъ":"HARDcy","ы":"ycy","Ы":"Ycy","ь":"softcy","Ь":"SOFTcy","э":"ecy","Э":"Ecy","ю":"yucy","Ю":"YUcy","я":"yacy","Я":"YAcy","ℵ":"aleph","ℶ":"beth","ℷ":"gimel","ℸ":"daleth"},d=/["&'<>`]/g,p={'"':""","&":"&","'":"'","<":"<",">":">","`":"`"},m=/&#(?:[xX][^a-fA-F0-9]|[^0-9xX])/,A=/[\0-\x08\x0B\x0E-\x1F\x7F-\x9F\uFDD0-\uFDEF\uFFFE\uFFFF]|[\uD83F\uD87F\uD8BF\uD8FF\uD93F\uD97F\uD9BF\uD9FF\uDA3F\uDA7F\uDABF\uDAFF\uDB3F\uDB7F\uDBBF\uDBFF][\uDFFE\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,b=/&(CounterClockwiseContourIntegral|DoubleLongLeftRightArrow|ClockwiseContourIntegral|NotNestedGreaterGreater|NotSquareSupersetEqual|DiacriticalDoubleAcute|NotRightTriangleEqual|NotSucceedsSlantEqual|NotPrecedesSlantEqual|CloseCurlyDoubleQuote|NegativeVeryThinSpace|DoubleContourIntegral|FilledVerySmallSquare|CapitalDifferentialD|OpenCurlyDoubleQuote|EmptyVerySmallSquare|NestedGreaterGreater|DoubleLongRightArrow|NotLeftTriangleEqual|NotGreaterSlantEqual|ReverseUpEquilibrium|DoubleLeftRightArrow|NotSquareSubsetEqual|NotDoubleVerticalBar|RightArrowLeftArrow|NotGreaterFullEqual|NotRightTriangleBar|SquareSupersetEqual|DownLeftRightVector|DoubleLongLeftArrow|leftrightsquigarrow|LeftArrowRightArrow|NegativeMediumSpace|blacktriangleright|RightDownVectorBar|PrecedesSlantEqual|RightDoubleBracket|SucceedsSlantEqual|NotLeftTriangleBar|RightTriangleEqual|SquareIntersection|RightDownTeeVector|ReverseEquilibrium|NegativeThickSpace|longleftrightarrow|Longleftrightarrow|LongLeftRightArrow|DownRightTeeVector|DownRightVectorBar|GreaterSlantEqual|SquareSubsetEqual|LeftDownVectorBar|LeftDoubleBracket|VerticalSeparator|rightleftharpoons|NotGreaterGreater|NotSquareSuperset|blacktriangleleft|blacktriangledown|NegativeThinSpace|LeftDownTeeVector|NotLessSlantEqual|leftrightharpoons|DoubleUpDownArrow|DoubleVerticalBar|LeftTriangleEqual|FilledSmallSquare|twoheadrightarrow|NotNestedLessLess|DownLeftTeeVector|DownLeftVectorBar|RightAngleBracket|NotTildeFullEqual|NotReverseElement|RightUpDownVector|DiacriticalTilde|NotSucceedsTilde|circlearrowright|NotPrecedesEqual|rightharpoondown|DoubleRightArrow|NotSucceedsEqual|NonBreakingSpace|NotRightTriangle|LessEqualGreater|RightUpTeeVector|LeftAngleBracket|GreaterFullEqual|DownArrowUpArrow|RightUpVectorBar|twoheadleftarrow|GreaterEqualLess|downharpoonright|RightTriangleBar|ntrianglerighteq|NotSupersetEqual|LeftUpDownVector|DiacriticalAcute|rightrightarrows|vartriangleright|UpArrowDownArrow|DiacriticalGrave|UnderParenthesis|EmptySmallSquare|LeftUpVectorBar|leftrightarrows|DownRightVector|downharpoonleft|trianglerighteq|ShortRightArrow|OverParenthesis|DoubleLeftArrow|DoubleDownArrow|NotSquareSubset|bigtriangledown|ntrianglelefteq|UpperRightArrow|curvearrowright|vartriangleleft|NotLeftTriangle|nleftrightarrow|LowerRightArrow|NotHumpDownHump|NotGreaterTilde|rightthreetimes|LeftUpTeeVector|NotGreaterEqual|straightepsilon|LeftTriangleBar|rightsquigarrow|ContourIntegral|rightleftarrows|CloseCurlyQuote|RightDownVector|LeftRightVector|nLeftrightarrow|leftharpoondown|circlearrowleft|SquareSuperset|OpenCurlyQuote|hookrightarrow|HorizontalLine|DiacriticalDot|NotLessGreater|ntriangleright|DoubleRightTee|InvisibleComma|InvisibleTimes|LowerLeftArrow|DownLeftVector|NotSubsetEqual|curvearrowleft|trianglelefteq|NotVerticalBar|TildeFullEqual|downdownarrows|NotGreaterLess|RightTeeVector|ZeroWidthSpace|looparrowright|LongRightArrow|doublebarwedge|ShortLeftArrow|ShortDownArrow|RightVectorBar|GreaterGreater|ReverseElement|rightharpoonup|LessSlantEqual|leftthreetimes|upharpoonright|rightarrowtail|LeftDownVector|Longrightarrow|NestedLessLess|UpperLeftArrow|nshortparallel|leftleftarrows|leftrightarrow|Leftrightarrow|LeftRightArrow|longrightarrow|upharpoonleft|RightArrowBar|ApplyFunction|LeftTeeVector|leftarrowtail|NotEqualTilde|varsubsetneqq|varsupsetneqq|RightTeeArrow|SucceedsEqual|SucceedsTilde|LeftVectorBar|SupersetEqual|hookleftarrow|DifferentialD|VerticalTilde|VeryThinSpace|blacktriangle|bigtriangleup|LessFullEqual|divideontimes|leftharpoonup|UpEquilibrium|ntriangleleft|RightTriangle|measuredangle|shortparallel|longleftarrow|Longleftarrow|LongLeftArrow|DoubleLeftTee|Poincareplane|PrecedesEqual|triangleright|DoubleUpArrow|RightUpVector|fallingdotseq|looparrowleft|PrecedesTilde|NotTildeEqual|NotTildeTilde|smallsetminus|Proportional|triangleleft|triangledown|UnderBracket|NotHumpEqual|exponentiale|ExponentialE|NotLessTilde|HilbertSpace|RightCeiling|blacklozenge|varsupsetneq|HumpDownHump|GreaterEqual|VerticalLine|LeftTeeArrow|NotLessEqual|DownTeeArrow|LeftTriangle|varsubsetneq|Intersection|NotCongruent|DownArrowBar|LeftUpVector|LeftArrowBar|risingdotseq|GreaterTilde|RoundImplies|SquareSubset|ShortUpArrow|NotSuperset|quaternions|precnapprox|backepsilon|preccurlyeq|OverBracket|blacksquare|MediumSpace|VerticalBar|circledcirc|circleddash|CircleMinus|CircleTimes|LessGreater|curlyeqprec|curlyeqsucc|diamondsuit|UpDownArrow|Updownarrow|RuleDelayed|Rrightarrow|updownarrow|RightVector|nRightarrow|nrightarrow|eqslantless|LeftCeiling|Equilibrium|SmallCircle|expectation|NotSucceeds|thickapprox|GreaterLess|SquareUnion|NotPrecedes|NotLessLess|straightphi|succnapprox|succcurlyeq|SubsetEqual|sqsupseteq|Proportion|Laplacetrf|ImaginaryI|supsetneqq|NotGreater|gtreqqless|NotElement|ThickSpace|TildeEqual|TildeTilde|Fouriertrf|rmoustache|EqualTilde|eqslantgtr|UnderBrace|LeftVector|UpArrowBar|nLeftarrow|nsubseteqq|subsetneqq|nsupseteqq|nleftarrow|succapprox|lessapprox|UpTeeArrow|upuparrows|curlywedge|lesseqqgtr|varepsilon|varnothing|RightFloor|complement|CirclePlus|sqsubseteq|Lleftarrow|circledast|RightArrow|Rightarrow|rightarrow|lmoustache|Bernoullis|precapprox|mapstoleft|mapstodown|longmapsto|dotsquare|downarrow|DoubleDot|nsubseteq|supsetneq|leftarrow|nsupseteq|subsetneq|ThinSpace|ngeqslant|subseteqq|HumpEqual|NotSubset|triangleq|NotCupCap|lesseqgtr|heartsuit|TripleDot|Leftarrow|Coproduct|Congruent|varpropto|complexes|gvertneqq|LeftArrow|LessTilde|supseteqq|MinusPlus|CircleDot|nleqslant|NotExists|gtreqless|nparallel|UnionPlus|LeftFloor|checkmark|CenterDot|centerdot|Mellintrf|gtrapprox|bigotimes|OverBrace|spadesuit|therefore|pitchfork|rationals|PlusMinus|Backslash|Therefore|DownBreve|backsimeq|backprime|DownArrow|nshortmid|Downarrow|lvertneqq|eqvparsl|imagline|imagpart|infintie|integers|Integral|intercal|LessLess|Uarrocir|intlarhk|sqsupset|angmsdaf|sqsubset|llcorner|vartheta|cupbrcap|lnapprox|Superset|SuchThat|succnsim|succneqq|angmsdag|biguplus|curlyvee|trpezium|Succeeds|NotTilde|bigwedge|angmsdah|angrtvbd|triminus|cwconint|fpartint|lrcorner|smeparsl|subseteq|urcorner|lurdshar|laemptyv|DDotrahd|approxeq|ldrushar|awconint|mapstoup|backcong|shortmid|triangle|geqslant|gesdotol|timesbar|circledR|circledS|setminus|multimap|naturals|scpolint|ncongdot|RightTee|boxminus|gnapprox|boxtimes|andslope|thicksim|angmsdaa|varsigma|cirfnint|rtriltri|angmsdab|rppolint|angmsdac|barwedge|drbkarow|clubsuit|thetasym|bsolhsub|capbrcup|dzigrarr|doteqdot|DotEqual|dotminus|UnderBar|NotEqual|realpart|otimesas|ulcorner|hksearow|hkswarow|parallel|PartialD|elinters|emptyset|plusacir|bbrktbrk|angmsdad|pointint|bigoplus|angmsdae|Precedes|bigsqcup|varkappa|notindot|supseteq|precneqq|precnsim|profalar|profline|profsurf|leqslant|lesdotor|raemptyv|subplus|notnivb|notnivc|subrarr|zigrarr|vzigzag|submult|subedot|Element|between|cirscir|larrbfs|larrsim|lotimes|lbrksld|lbrkslu|lozenge|ldrdhar|dbkarow|bigcirc|epsilon|simrarr|simplus|ltquest|Epsilon|luruhar|gtquest|maltese|npolint|eqcolon|npreceq|bigodot|ddagger|gtrless|bnequiv|harrcir|ddotseq|equivDD|backsim|demptyv|nsqsube|nsqsupe|Upsilon|nsubset|upsilon|minusdu|nsucceq|swarrow|nsupset|coloneq|searrow|boxplus|napprox|natural|asympeq|alefsym|congdot|nearrow|bigstar|diamond|supplus|tritime|LeftTee|nvinfin|triplus|NewLine|nvltrie|nvrtrie|nwarrow|nexists|Diamond|ruluhar|Implies|supmult|angzarr|suplarr|suphsub|questeq|because|digamma|Because|olcross|bemptyv|omicron|Omicron|rotimes|NoBreak|intprod|angrtvb|orderof|uwangle|suphsol|lesdoto|orslope|DownTee|realine|cudarrl|rdldhar|OverBar|supedot|lessdot|supdsub|topfork|succsim|rbrkslu|rbrksld|pertenk|cudarrr|isindot|planckh|lessgtr|pluscir|gesdoto|plussim|plustwo|lesssim|cularrp|rarrsim|Cayleys|notinva|notinvb|notinvc|UpArrow|Uparrow|uparrow|NotLess|dwangle|precsim|Product|curarrm|Cconint|dotplus|rarrbfs|ccupssm|Cedilla|cemptyv|notniva|quatint|frac35|frac38|frac45|frac56|frac58|frac78|tridot|xoplus|gacute|gammad|Gammad|lfisht|lfloor|bigcup|sqsupe|gbreve|Gbreve|lharul|sqsube|sqcups|Gcedil|apacir|llhard|lmidot|Lmidot|lmoust|andand|sqcaps|approx|Abreve|spades|circeq|tprime|divide|topcir|Assign|topbot|gesdot|divonx|xuplus|timesd|gesles|atilde|solbar|SOFTcy|loplus|timesb|lowast|lowbar|dlcorn|dlcrop|softcy|dollar|lparlt|thksim|lrhard|Atilde|lsaquo|smashp|bigvee|thinsp|wreath|bkarow|lsquor|lstrok|Lstrok|lthree|ltimes|ltlarr|DotDot|simdot|ltrPar|weierp|xsqcup|angmsd|sigmav|sigmaf|zeetrf|Zcaron|zcaron|mapsto|vsupne|thetav|cirmid|marker|mcomma|Zacute|vsubnE|there4|gtlPar|vsubne|bottom|gtrarr|SHCHcy|shchcy|midast|midcir|middot|minusb|minusd|gtrdot|bowtie|sfrown|mnplus|models|colone|seswar|Colone|mstpos|searhk|gtrsim|nacute|Nacute|boxbox|telrec|hairsp|Tcedil|nbumpe|scnsim|ncaron|Ncaron|ncedil|Ncedil|hamilt|Scedil|nearhk|hardcy|HARDcy|tcedil|Tcaron|commat|nequiv|nesear|tcaron|target|hearts|nexist|varrho|scedil|Scaron|scaron|hellip|Sacute|sacute|hercon|swnwar|compfn|rtimes|rthree|rsquor|rsaquo|zacute|wedgeq|homtht|barvee|barwed|Barwed|rpargt|horbar|conint|swarhk|roplus|nltrie|hslash|hstrok|Hstrok|rmoust|Conint|bprime|hybull|hyphen|iacute|Iacute|supsup|supsub|supsim|varphi|coprod|brvbar|agrave|Supset|supset|igrave|Igrave|notinE|Agrave|iiiint|iinfin|copysr|wedbar|Verbar|vangrt|becaus|incare|verbar|inodot|bullet|drcorn|intcal|drcrop|cularr|vellip|Utilde|bumpeq|cupcap|dstrok|Dstrok|CupCap|cupcup|cupdot|eacute|Eacute|supdot|iquest|easter|ecaron|Ecaron|ecolon|isinsv|utilde|itilde|Itilde|curarr|succeq|Bumpeq|cacute|ulcrop|nparsl|Cacute|nprcue|egrave|Egrave|nrarrc|nrarrw|subsup|subsub|nrtrie|jsercy|nsccue|Jsercy|kappav|kcedil|Kcedil|subsim|ulcorn|nsimeq|egsdot|veebar|kgreen|capand|elsdot|Subset|subset|curren|aacute|lacute|Lacute|emptyv|ntilde|Ntilde|lagran|lambda|Lambda|capcap|Ugrave|langle|subdot|emsp13|numero|emsp14|nvdash|nvDash|nVdash|nVDash|ugrave|ufisht|nvHarr|larrfs|nvlArr|larrhk|larrlp|larrpl|nvrArr|Udblac|nwarhk|larrtl|nwnear|oacute|Oacute|latail|lAtail|sstarf|lbrace|odblac|Odblac|lbrack|udblac|odsold|eparsl|lcaron|Lcaron|ograve|Ograve|lcedil|Lcedil|Aacute|ssmile|ssetmn|squarf|ldquor|capcup|ominus|cylcty|rharul|eqcirc|dagger|rfloor|rfisht|Dagger|daleth|equals|origof|capdot|equest|dcaron|Dcaron|rdquor|oslash|Oslash|otilde|Otilde|otimes|Otimes|urcrop|Ubreve|ubreve|Yacute|Uacute|uacute|Rcedil|rcedil|urcorn|parsim|Rcaron|Vdashl|rcaron|Tstrok|percnt|period|permil|Exists|yacute|rbrack|rbrace|phmmat|ccaron|Ccaron|planck|ccedil|plankv|tstrok|female|plusdo|plusdu|ffilig|plusmn|ffllig|Ccedil|rAtail|dfisht|bernou|ratail|Rarrtl|rarrtl|angsph|rarrpl|rarrlp|rarrhk|xwedge|xotime|forall|ForAll|Vvdash|vsupnE|preceq|bigcap|frac12|frac13|frac14|primes|rarrfs|prnsim|frac15|Square|frac16|square|lesdot|frac18|frac23|propto|prurel|rarrap|rangle|puncsp|frac25|Racute|qprime|racute|lesges|frac34|abreve|AElig|eqsim|utdot|setmn|urtri|Equal|Uring|seArr|uring|searr|dashv|Dashv|mumap|nabla|iogon|Iogon|sdote|sdotb|scsim|napid|napos|equiv|natur|Acirc|dblac|erarr|nbump|iprod|erDot|ucirc|awint|esdot|angrt|ncong|isinE|scnap|Scirc|scirc|ndash|isins|Ubrcy|nearr|neArr|isinv|nedot|ubrcy|acute|Ycirc|iukcy|Iukcy|xutri|nesim|caret|jcirc|Jcirc|caron|twixt|ddarr|sccue|exist|jmath|sbquo|ngeqq|angst|ccaps|lceil|ngsim|UpTee|delta|Delta|rtrif|nharr|nhArr|nhpar|rtrie|jukcy|Jukcy|kappa|rsquo|Kappa|nlarr|nlArr|TSHcy|rrarr|aogon|Aogon|fflig|xrarr|tshcy|ccirc|nleqq|filig|upsih|nless|dharl|nlsim|fjlig|ropar|nltri|dharr|robrk|roarr|fllig|fltns|roang|rnmid|subnE|subne|lAarr|trisb|Ccirc|acirc|ccups|blank|VDash|forkv|Vdash|langd|cedil|blk12|blk14|laquo|strns|diams|notin|vDash|larrb|blk34|block|disin|uplus|vdash|vBarv|aelig|starf|Wedge|check|xrArr|lates|lbarr|lBarr|notni|lbbrk|bcong|frasl|lbrke|frown|vrtri|vprop|vnsup|gamma|Gamma|wedge|xodot|bdquo|srarr|doteq|ldquo|boxdl|boxdL|gcirc|Gcirc|boxDl|boxDL|boxdr|boxdR|boxDr|TRADE|trade|rlhar|boxDR|vnsub|npart|vltri|rlarr|boxhd|boxhD|nprec|gescc|nrarr|nrArr|boxHd|boxHD|boxhu|boxhU|nrtri|boxHu|clubs|boxHU|times|colon|Colon|gimel|xlArr|Tilde|nsime|tilde|nsmid|nspar|THORN|thorn|xlarr|nsube|nsubE|thkap|xhArr|comma|nsucc|boxul|boxuL|nsupe|nsupE|gneqq|gnsim|boxUl|boxUL|grave|boxur|boxuR|boxUr|boxUR|lescc|angle|bepsi|boxvh|varpi|boxvH|numsp|Theta|gsime|gsiml|theta|boxVh|boxVH|boxvl|gtcir|gtdot|boxvL|boxVl|boxVL|crarr|cross|Cross|nvsim|boxvr|nwarr|nwArr|sqsup|dtdot|Uogon|lhard|lharu|dtrif|ocirc|Ocirc|lhblk|duarr|odash|sqsub|Hacek|sqcup|llarr|duhar|oelig|OElig|ofcir|boxvR|uogon|lltri|boxVr|csube|uuarr|ohbar|csupe|ctdot|olarr|olcir|harrw|oline|sqcap|omacr|Omacr|omega|Omega|boxVR|aleph|lneqq|lnsim|loang|loarr|rharu|lobrk|hcirc|operp|oplus|rhard|Hcirc|orarr|Union|order|ecirc|Ecirc|cuepr|szlig|cuesc|breve|reals|eDDot|Breve|hoarr|lopar|utrif|rdquo|Umacr|umacr|efDot|swArr|ultri|alpha|rceil|ovbar|swarr|Wcirc|wcirc|smtes|smile|bsemi|lrarr|aring|parsl|lrhar|bsime|uhblk|lrtri|cupor|Aring|uharr|uharl|slarr|rbrke|bsolb|lsime|rbbrk|RBarr|lsimg|phone|rBarr|rbarr|icirc|lsquo|Icirc|emacr|Emacr|ratio|simne|plusb|simlE|simgE|simeq|pluse|ltcir|ltdot|empty|xharr|xdtri|iexcl|Alpha|ltrie|rarrw|pound|ltrif|xcirc|bumpe|prcue|bumpE|asymp|amacr|cuvee|Sigma|sigma|iiint|udhar|iiota|ijlig|IJlig|supnE|imacr|Imacr|prime|Prime|image|prnap|eogon|Eogon|rarrc|mdash|mDDot|cuwed|imath|supne|imped|Amacr|udarr|prsim|micro|rarrb|cwint|raquo|infin|eplus|range|rangd|Ucirc|radic|minus|amalg|veeeq|rAarr|epsiv|ycirc|quest|sharp|quot|zwnj|Qscr|race|qscr|Qopf|qopf|qint|rang|Rang|Zscr|zscr|Zopf|zopf|rarr|rArr|Rarr|Pscr|pscr|prop|prod|prnE|prec|ZHcy|zhcy|prap|Zeta|zeta|Popf|popf|Zdot|plus|zdot|Yuml|yuml|phiv|YUcy|yucy|Yscr|yscr|perp|Yopf|yopf|part|para|YIcy|Ouml|rcub|yicy|YAcy|rdca|ouml|osol|Oscr|rdsh|yacy|real|oscr|xvee|andd|rect|andv|Xscr|oror|ordm|ordf|xscr|ange|aopf|Aopf|rHar|Xopf|opar|Oopf|xopf|xnis|rhov|oopf|omid|xmap|oint|apid|apos|ogon|ascr|Ascr|odot|odiv|xcup|xcap|ocir|oast|nvlt|nvle|nvgt|nvge|nvap|Wscr|wscr|auml|ntlg|ntgl|nsup|nsub|nsim|Nscr|nscr|nsce|Wopf|ring|npre|wopf|npar|Auml|Barv|bbrk|Nopf|nopf|nmid|nLtv|beta|ropf|Ropf|Beta|beth|nles|rpar|nleq|bnot|bNot|nldr|NJcy|rscr|Rscr|Vscr|vscr|rsqb|njcy|bopf|nisd|Bopf|rtri|Vopf|nGtv|ngtr|vopf|boxh|boxH|boxv|nges|ngeq|boxV|bscr|scap|Bscr|bsim|Vert|vert|bsol|bull|bump|caps|cdot|ncup|scnE|ncap|nbsp|napE|Cdot|cent|sdot|Vbar|nang|vBar|chcy|Mscr|mscr|sect|semi|CHcy|Mopf|mopf|sext|circ|cire|mldr|mlcp|cirE|comp|shcy|SHcy|vArr|varr|cong|copf|Copf|copy|COPY|malt|male|macr|lvnE|cscr|ltri|sime|ltcc|simg|Cscr|siml|csub|Uuml|lsqb|lsim|uuml|csup|Lscr|lscr|utri|smid|lpar|cups|smte|lozf|darr|Lopf|Uscr|solb|lopf|sopf|Sopf|lneq|uscr|spar|dArr|lnap|Darr|dash|Sqrt|LJcy|ljcy|lHar|dHar|Upsi|upsi|diam|lesg|djcy|DJcy|leqq|dopf|Dopf|dscr|Dscr|dscy|ldsh|ldca|squf|DScy|sscr|Sscr|dsol|lcub|late|star|Star|Uopf|Larr|lArr|larr|uopf|dtri|dzcy|sube|subE|Lang|lang|Kscr|kscr|Kopf|kopf|KJcy|kjcy|KHcy|khcy|DZcy|ecir|edot|eDot|Jscr|jscr|succ|Jopf|jopf|Edot|uHar|emsp|ensp|Iuml|iuml|eopf|isin|Iscr|iscr|Eopf|epar|sung|epsi|escr|sup1|sup2|sup3|Iota|iota|supe|supE|Iopf|iopf|IOcy|iocy|Escr|esim|Esim|imof|Uarr|QUOT|uArr|uarr|euml|IEcy|iecy|Idot|Euml|euro|excl|Hscr|hscr|Hopf|hopf|TScy|tscy|Tscr|hbar|tscr|flat|tbrk|fnof|hArr|harr|half|fopf|Fopf|tdot|gvnE|fork|trie|gtcc|fscr|Fscr|gdot|gsim|Gscr|gscr|Gopf|gopf|gneq|Gdot|tosa|gnap|Topf|topf|geqq|toea|GJcy|gjcy|tint|gesl|mid|Sfr|ggg|top|ges|gla|glE|glj|geq|gne|gEl|gel|gnE|Gcy|gcy|gap|Tfr|tfr|Tcy|tcy|Hat|Tau|Ffr|tau|Tab|hfr|Hfr|ffr|Fcy|fcy|icy|Icy|iff|ETH|eth|ifr|Ifr|Eta|eta|int|Int|Sup|sup|ucy|Ucy|Sum|sum|jcy|ENG|ufr|Ufr|eng|Jcy|jfr|els|ell|egs|Efr|efr|Jfr|uml|kcy|Kcy|Ecy|ecy|kfr|Kfr|lap|Sub|sub|lat|lcy|Lcy|leg|Dot|dot|lEg|leq|les|squ|div|die|lfr|Lfr|lgE|Dfr|dfr|Del|deg|Dcy|dcy|lne|lnE|sol|loz|smt|Cup|lrm|cup|lsh|Lsh|sim|shy|map|Map|mcy|Mcy|mfr|Mfr|mho|gfr|Gfr|sfr|cir|Chi|chi|nap|Cfr|vcy|Vcy|cfr|Scy|scy|ncy|Ncy|vee|Vee|Cap|cap|nfr|scE|sce|Nfr|nge|ngE|nGg|vfr|Vfr|ngt|bot|nGt|nis|niv|Rsh|rsh|nle|nlE|bne|Bfr|bfr|nLl|nlt|nLt|Bcy|bcy|not|Not|rlm|wfr|Wfr|npr|nsc|num|ocy|ast|Ocy|ofr|xfr|Xfr|Ofr|ogt|ohm|apE|olt|Rho|ape|rho|Rfr|rfr|ord|REG|ang|reg|orv|And|and|AMP|Rcy|amp|Afr|ycy|Ycy|yen|yfr|Yfr|rcy|par|pcy|Pcy|pfr|Pfr|phi|Phi|afr|Acy|acy|zcy|Zcy|piv|acE|acd|zfr|Zfr|pre|prE|psi|Psi|qfr|Qfr|zwj|Or|ge|Gg|gt|gg|el|oS|lt|Lt|LT|Re|lg|gl|eg|ne|Im|it|le|DD|wp|wr|nu|Nu|dd|lE|Sc|sc|pi|Pi|ee|af|ll|Ll|rx|gE|xi|pm|Xi|ic|pr|Pr|in|ni|mp|mu|ac|Mu|or|ap|Gt|GT|ii);|&(Aacute|Agrave|Atilde|Ccedil|Eacute|Egrave|Iacute|Igrave|Ntilde|Oacute|Ograve|Oslash|Otilde|Uacute|Ugrave|Yacute|aacute|agrave|atilde|brvbar|ccedil|curren|divide|eacute|egrave|frac12|frac14|frac34|iacute|igrave|iquest|middot|ntilde|oacute|ograve|oslash|otilde|plusmn|uacute|ugrave|yacute|AElig|Acirc|Aring|Ecirc|Icirc|Ocirc|THORN|Ucirc|acirc|acute|aelig|aring|cedil|ecirc|icirc|iexcl|laquo|micro|ocirc|pound|raquo|szlig|thorn|times|ucirc|Auml|COPY|Euml|Iuml|Ouml|QUOT|Uuml|auml|cent|copy|euml|iuml|macr|nbsp|ordf|ordm|ouml|para|quot|sect|sup1|sup2|sup3|uuml|yuml|AMP|ETH|REG|amp|deg|eth|not|reg|shy|uml|yen|GT|LT|gt|lt)(?!;)([=a-zA-Z0-9]?)|&#([0-9]+)(;?)|&#[xX]([a-fA-F0-9]+)(;?)|&([0-9a-zA-Z]+)/g,C={aacute:"á",Aacute:"Á",abreve:"ă",Abreve:"Ă",ac:"∾",acd:"∿",acE:"∾̳",acirc:"â",Acirc:"Â",acute:"´",acy:"а",Acy:"А",aelig:"æ",AElig:"Æ",af:"⁡",afr:"𝔞",Afr:"𝔄",agrave:"à",Agrave:"À",alefsym:"ℵ",aleph:"ℵ",alpha:"α",Alpha:"Α",amacr:"ā",Amacr:"Ā",amalg:"⨿",amp:"&",AMP:"&",and:"∧",And:"⩓",andand:"⩕",andd:"⩜",andslope:"⩘",andv:"⩚",ang:"∠",ange:"⦤",angle:"∠",angmsd:"∡",angmsdaa:"⦨",angmsdab:"⦩",angmsdac:"⦪",angmsdad:"⦫",angmsdae:"⦬",angmsdaf:"⦭",angmsdag:"⦮",angmsdah:"⦯",angrt:"∟",angrtvb:"⊾",angrtvbd:"⦝",angsph:"∢",angst:"Å",angzarr:"⍼",aogon:"ą",Aogon:"Ą",aopf:"𝕒",Aopf:"𝔸",ap:"≈",apacir:"⩯",ape:"≊",apE:"⩰",apid:"≋",apos:"'",ApplyFunction:"⁡",approx:"≈",approxeq:"≊",aring:"å",Aring:"Å",ascr:"𝒶",Ascr:"𝒜",Assign:"≔",ast:"*",asymp:"≈",asympeq:"≍",atilde:"ã",Atilde:"Ã",auml:"ä",Auml:"Ä",awconint:"∳",awint:"⨑",backcong:"≌",backepsilon:"϶",backprime:"‵",backsim:"∽",backsimeq:"⋍",Backslash:"∖",Barv:"⫧",barvee:"⊽",barwed:"⌅",Barwed:"⌆",barwedge:"⌅",bbrk:"⎵",bbrktbrk:"⎶",bcong:"≌",bcy:"б",Bcy:"Б",bdquo:"„",becaus:"∵",because:"∵",Because:"∵",bemptyv:"⦰",bepsi:"϶",bernou:"ℬ",Bernoullis:"ℬ",beta:"β",Beta:"Β",beth:"ℶ",between:"≬",bfr:"𝔟",Bfr:"𝔅",bigcap:"⋂",bigcirc:"◯",bigcup:"⋃",bigodot:"⨀",bigoplus:"⨁",bigotimes:"⨂",bigsqcup:"⨆",bigstar:"★",bigtriangledown:"▽",bigtriangleup:"△",biguplus:"⨄",bigvee:"⋁",bigwedge:"⋀",bkarow:"⤍",blacklozenge:"⧫",blacksquare:"▪",blacktriangle:"▴",blacktriangledown:"▾",blacktriangleleft:"◂",blacktriangleright:"▸",blank:"␣",blk12:"▒",blk14:"░",blk34:"▓",block:"█",bne:"=⃥",bnequiv:"≡⃥",bnot:"⌐",bNot:"⫭",bopf:"𝕓",Bopf:"𝔹",bot:"⊥",bottom:"⊥",bowtie:"⋈",boxbox:"⧉",boxdl:"┐",boxdL:"╕",boxDl:"╖",boxDL:"╗",boxdr:"┌",boxdR:"╒",boxDr:"╓",boxDR:"╔",boxh:"─",boxH:"═",boxhd:"┬",boxhD:"╥",boxHd:"╤",boxHD:"╦",boxhu:"┴",boxhU:"╨",boxHu:"╧",boxHU:"╩",boxminus:"⊟",boxplus:"⊞",boxtimes:"⊠",boxul:"┘",boxuL:"╛",boxUl:"╜",boxUL:"╝",boxur:"└",boxuR:"╘",boxUr:"╙",boxUR:"╚",boxv:"│",boxV:"║",boxvh:"┼",boxvH:"╪",boxVh:"╫",boxVH:"╬",boxvl:"┤",boxvL:"╡",boxVl:"╢",boxVL:"╣",boxvr:"├",boxvR:"╞",boxVr:"╟",boxVR:"╠",bprime:"‵",breve:"˘",Breve:"˘",brvbar:"¦",bscr:"𝒷",Bscr:"ℬ",bsemi:"⁏",bsim:"∽",bsime:"⋍",bsol:"\\",bsolb:"⧅",bsolhsub:"⟈",bull:"•",bullet:"•",bump:"≎",bumpe:"≏",bumpE:"⪮",bumpeq:"≏",Bumpeq:"≎",cacute:"ć",Cacute:"Ć",cap:"∩",Cap:"⋒",capand:"⩄",capbrcup:"⩉",capcap:"⩋",capcup:"⩇",capdot:"⩀",CapitalDifferentialD:"ⅅ",caps:"∩︀",caret:"⁁",caron:"ˇ",Cayleys:"ℭ",ccaps:"⩍",ccaron:"č",Ccaron:"Č",ccedil:"ç",Ccedil:"Ç",ccirc:"ĉ",Ccirc:"Ĉ",Cconint:"∰",ccups:"⩌",ccupssm:"⩐",cdot:"ċ",Cdot:"Ċ",cedil:"¸",Cedilla:"¸",cemptyv:"⦲",cent:"¢",centerdot:"·",CenterDot:"·",cfr:"𝔠",Cfr:"ℭ",chcy:"ч",CHcy:"Ч",check:"✓",checkmark:"✓",chi:"χ",Chi:"Χ",cir:"○",circ:"ˆ",circeq:"≗",circlearrowleft:"↺",circlearrowright:"↻",circledast:"⊛",circledcirc:"⊚",circleddash:"⊝",CircleDot:"⊙",circledR:"®",circledS:"Ⓢ",CircleMinus:"⊖",CirclePlus:"⊕",CircleTimes:"⊗",cire:"≗",cirE:"⧃",cirfnint:"⨐",cirmid:"⫯",cirscir:"⧂",ClockwiseContourIntegral:"∲",CloseCurlyDoubleQuote:"”",CloseCurlyQuote:"’",clubs:"♣",clubsuit:"♣",colon:":",Colon:"∷",colone:"≔",Colone:"⩴",coloneq:"≔",comma:",",commat:"@",comp:"∁",compfn:"∘",complement:"∁",complexes:"ℂ",cong:"≅",congdot:"⩭",Congruent:"≡",conint:"∮",Conint:"∯",ContourIntegral:"∮",copf:"𝕔",Copf:"ℂ",coprod:"∐",Coproduct:"∐",copy:"©",COPY:"©",copysr:"℗",CounterClockwiseContourIntegral:"∳",crarr:"↵",cross:"✗",Cross:"⨯",cscr:"𝒸",Cscr:"𝒞",csub:"⫏",csube:"⫑",csup:"⫐",csupe:"⫒",ctdot:"⋯",cudarrl:"⤸",cudarrr:"⤵",cuepr:"⋞",cuesc:"⋟",cularr:"↶",cularrp:"⤽",cup:"∪",Cup:"⋓",cupbrcap:"⩈",cupcap:"⩆",CupCap:"≍",cupcup:"⩊",cupdot:"⊍",cupor:"⩅",cups:"∪︀",curarr:"↷",curarrm:"⤼",curlyeqprec:"⋞",curlyeqsucc:"⋟",curlyvee:"⋎",curlywedge:"⋏",curren:"¤",curvearrowleft:"↶",curvearrowright:"↷",cuvee:"⋎",cuwed:"⋏",cwconint:"∲",cwint:"∱",cylcty:"⌭",dagger:"†",Dagger:"‡",daleth:"ℸ",darr:"↓",dArr:"⇓",Darr:"↡",dash:"‐",dashv:"⊣",Dashv:"⫤",dbkarow:"⤏",dblac:"˝",dcaron:"ď",Dcaron:"Ď",dcy:"д",Dcy:"Д",dd:"ⅆ",DD:"ⅅ",ddagger:"‡",ddarr:"⇊",DDotrahd:"⤑",ddotseq:"⩷",deg:"°",Del:"∇",delta:"δ",Delta:"Δ",demptyv:"⦱",dfisht:"⥿",dfr:"𝔡",Dfr:"𝔇",dHar:"⥥",dharl:"⇃",dharr:"⇂",DiacriticalAcute:"´",DiacriticalDot:"˙",DiacriticalDoubleAcute:"˝",DiacriticalGrave:"`",DiacriticalTilde:"˜",diam:"⋄",diamond:"⋄",Diamond:"⋄",diamondsuit:"♦",diams:"♦",die:"¨",DifferentialD:"ⅆ",digamma:"ϝ",disin:"⋲",div:"÷",divide:"÷",divideontimes:"⋇",divonx:"⋇",djcy:"ђ",DJcy:"Ђ",dlcorn:"⌞",dlcrop:"⌍",dollar:"$",dopf:"𝕕",Dopf:"𝔻",dot:"˙",Dot:"¨",DotDot:"⃜",doteq:"≐",doteqdot:"≑",DotEqual:"≐",dotminus:"∸",dotplus:"∔",dotsquare:"⊡",doublebarwedge:"⌆",DoubleContourIntegral:"∯",DoubleDot:"¨",DoubleDownArrow:"⇓",DoubleLeftArrow:"⇐",DoubleLeftRightArrow:"⇔",DoubleLeftTee:"⫤",DoubleLongLeftArrow:"⟸",DoubleLongLeftRightArrow:"⟺",DoubleLongRightArrow:"⟹",DoubleRightArrow:"⇒",DoubleRightTee:"⊨",DoubleUpArrow:"⇑",DoubleUpDownArrow:"⇕",DoubleVerticalBar:"∥",downarrow:"↓",Downarrow:"⇓",DownArrow:"↓",DownArrowBar:"⤓",DownArrowUpArrow:"⇵",DownBreve:"̑",downdownarrows:"⇊",downharpoonleft:"⇃",downharpoonright:"⇂",DownLeftRightVector:"⥐",DownLeftTeeVector:"⥞",DownLeftVector:"↽",DownLeftVectorBar:"⥖",DownRightTeeVector:"⥟",DownRightVector:"⇁",DownRightVectorBar:"⥗",DownTee:"⊤",DownTeeArrow:"↧",drbkarow:"⤐",drcorn:"⌟",drcrop:"⌌",dscr:"𝒹",Dscr:"𝒟",dscy:"ѕ",DScy:"Ѕ",dsol:"⧶",dstrok:"đ",Dstrok:"Đ",dtdot:"⋱",dtri:"▿",dtrif:"▾",duarr:"⇵",duhar:"⥯",dwangle:"⦦",dzcy:"џ",DZcy:"Џ",dzigrarr:"⟿",eacute:"é",Eacute:"É",easter:"⩮",ecaron:"ě",Ecaron:"Ě",ecir:"≖",ecirc:"ê",Ecirc:"Ê",ecolon:"≕",ecy:"э",Ecy:"Э",eDDot:"⩷",edot:"ė",eDot:"≑",Edot:"Ė",ee:"ⅇ",efDot:"≒",efr:"𝔢",Efr:"𝔈",eg:"⪚",egrave:"è",Egrave:"È",egs:"⪖",egsdot:"⪘",el:"⪙",Element:"∈",elinters:"⏧",ell:"ℓ",els:"⪕",elsdot:"⪗",emacr:"ē",Emacr:"Ē",empty:"∅",emptyset:"∅",EmptySmallSquare:"◻",emptyv:"∅",EmptyVerySmallSquare:"▫",emsp:" ",emsp13:" ",emsp14:" ",eng:"ŋ",ENG:"Ŋ",ensp:" ",eogon:"ę",Eogon:"Ę",eopf:"𝕖",Eopf:"𝔼",epar:"⋕",eparsl:"⧣",eplus:"⩱",epsi:"ε",epsilon:"ε",Epsilon:"Ε",epsiv:"ϵ",eqcirc:"≖",eqcolon:"≕",eqsim:"≂",eqslantgtr:"⪖",eqslantless:"⪕",Equal:"⩵",equals:"=",EqualTilde:"≂",equest:"≟",Equilibrium:"⇌",equiv:"≡",equivDD:"⩸",eqvparsl:"⧥",erarr:"⥱",erDot:"≓",escr:"ℯ",Escr:"ℰ",esdot:"≐",esim:"≂",Esim:"⩳",eta:"η",Eta:"Η",eth:"ð",ETH:"Ð",euml:"ë",Euml:"Ë",euro:"€",excl:"!",exist:"∃",Exists:"∃",expectation:"ℰ",exponentiale:"ⅇ",ExponentialE:"ⅇ",fallingdotseq:"≒",fcy:"ф",Fcy:"Ф",female:"♀",ffilig:"ffi",fflig:"ff",ffllig:"ffl",ffr:"𝔣",Ffr:"𝔉",filig:"fi",FilledSmallSquare:"◼",FilledVerySmallSquare:"▪",fjlig:"fj",flat:"♭",fllig:"fl",fltns:"▱",fnof:"ƒ",fopf:"𝕗",Fopf:"𝔽",forall:"∀",ForAll:"∀",fork:"⋔",forkv:"⫙",Fouriertrf:"ℱ",fpartint:"⨍",frac12:"½",frac13:"⅓",frac14:"¼",frac15:"⅕",frac16:"⅙",frac18:"⅛",frac23:"⅔",frac25:"⅖",frac34:"¾",frac35:"⅗",frac38:"⅜",frac45:"⅘",frac56:"⅚",frac58:"⅝",frac78:"⅞",frasl:"⁄",frown:"⌢",fscr:"𝒻",Fscr:"ℱ",gacute:"ǵ",gamma:"γ",Gamma:"Γ",gammad:"ϝ",Gammad:"Ϝ",gap:"⪆",gbreve:"ğ",Gbreve:"Ğ",Gcedil:"Ģ",gcirc:"ĝ",Gcirc:"Ĝ",gcy:"г",Gcy:"Г",gdot:"ġ",Gdot:"Ġ",ge:"≥",gE:"≧",gel:"⋛",gEl:"⪌",geq:"≥",geqq:"≧",geqslant:"⩾",ges:"⩾",gescc:"⪩",gesdot:"⪀",gesdoto:"⪂",gesdotol:"⪄",gesl:"⋛︀",gesles:"⪔",gfr:"𝔤",Gfr:"𝔊",gg:"≫",Gg:"⋙",ggg:"⋙",gimel:"ℷ",gjcy:"ѓ",GJcy:"Ѓ",gl:"≷",gla:"⪥",glE:"⪒",glj:"⪤",gnap:"⪊",gnapprox:"⪊",gne:"⪈",gnE:"≩",gneq:"⪈",gneqq:"≩",gnsim:"⋧",gopf:"𝕘",Gopf:"𝔾",grave:"`",GreaterEqual:"≥",GreaterEqualLess:"⋛",GreaterFullEqual:"≧",GreaterGreater:"⪢",GreaterLess:"≷",GreaterSlantEqual:"⩾",GreaterTilde:"≳",gscr:"ℊ",Gscr:"𝒢",gsim:"≳",gsime:"⪎",gsiml:"⪐",gt:">",Gt:"≫",GT:">",gtcc:"⪧",gtcir:"⩺",gtdot:"⋗",gtlPar:"⦕",gtquest:"⩼",gtrapprox:"⪆",gtrarr:"⥸",gtrdot:"⋗",gtreqless:"⋛",gtreqqless:"⪌",gtrless:"≷",gtrsim:"≳",gvertneqq:"≩︀",gvnE:"≩︀",Hacek:"ˇ",hairsp:" ",half:"½",hamilt:"ℋ",hardcy:"ъ",HARDcy:"Ъ",harr:"↔",hArr:"⇔",harrcir:"⥈",harrw:"↭",Hat:"^",hbar:"ℏ",hcirc:"ĥ",Hcirc:"Ĥ",hearts:"♥",heartsuit:"♥",hellip:"…",hercon:"⊹",hfr:"𝔥",Hfr:"ℌ",HilbertSpace:"ℋ",hksearow:"⤥",hkswarow:"⤦",hoarr:"⇿",homtht:"∻",hookleftarrow:"↩",hookrightarrow:"↪",hopf:"𝕙",Hopf:"ℍ",horbar:"―",HorizontalLine:"─",hscr:"𝒽",Hscr:"ℋ",hslash:"ℏ",hstrok:"ħ",Hstrok:"Ħ",HumpDownHump:"≎",HumpEqual:"≏",hybull:"⁃",hyphen:"‐",iacute:"í",Iacute:"Í",ic:"⁣",icirc:"î",Icirc:"Î",icy:"и",Icy:"И",Idot:"İ",iecy:"е",IEcy:"Е",iexcl:"¡",iff:"⇔",ifr:"𝔦",Ifr:"ℑ",igrave:"ì",Igrave:"Ì",ii:"ⅈ",iiiint:"⨌",iiint:"∭",iinfin:"⧜",iiota:"℩",ijlig:"ij",IJlig:"IJ",Im:"ℑ",imacr:"ī",Imacr:"Ī",image:"ℑ",ImaginaryI:"ⅈ",imagline:"ℐ",imagpart:"ℑ",imath:"ı",imof:"⊷",imped:"Ƶ",Implies:"⇒",in:"∈",incare:"℅",infin:"∞",infintie:"⧝",inodot:"ı",int:"∫",Int:"∬",intcal:"⊺",integers:"ℤ",Integral:"∫",intercal:"⊺",Intersection:"⋂",intlarhk:"⨗",intprod:"⨼",InvisibleComma:"⁣",InvisibleTimes:"⁢",iocy:"ё",IOcy:"Ё",iogon:"į",Iogon:"Į",iopf:"𝕚",Iopf:"𝕀",iota:"ι",Iota:"Ι",iprod:"⨼",iquest:"¿",iscr:"𝒾",Iscr:"ℐ",isin:"∈",isindot:"⋵",isinE:"⋹",isins:"⋴",isinsv:"⋳",isinv:"∈",it:"⁢",itilde:"ĩ",Itilde:"Ĩ",iukcy:"і",Iukcy:"І",iuml:"ï",Iuml:"Ï",jcirc:"ĵ",Jcirc:"Ĵ",jcy:"й",Jcy:"Й",jfr:"𝔧",Jfr:"𝔍",jmath:"ȷ",jopf:"𝕛",Jopf:"𝕁",jscr:"𝒿",Jscr:"𝒥",jsercy:"ј",Jsercy:"Ј",jukcy:"є",Jukcy:"Є",kappa:"κ",Kappa:"Κ",kappav:"ϰ",kcedil:"ķ",Kcedil:"Ķ",kcy:"к",Kcy:"К",kfr:"𝔨",Kfr:"𝔎",kgreen:"ĸ",khcy:"х",KHcy:"Х",kjcy:"ќ",KJcy:"Ќ",kopf:"𝕜",Kopf:"𝕂",kscr:"𝓀",Kscr:"𝒦",lAarr:"⇚",lacute:"ĺ",Lacute:"Ĺ",laemptyv:"⦴",lagran:"ℒ",lambda:"λ",Lambda:"Λ",lang:"⟨",Lang:"⟪",langd:"⦑",langle:"⟨",lap:"⪅",Laplacetrf:"ℒ",laquo:"«",larr:"←",lArr:"⇐",Larr:"↞",larrb:"⇤",larrbfs:"⤟",larrfs:"⤝",larrhk:"↩",larrlp:"↫",larrpl:"⤹",larrsim:"⥳",larrtl:"↢",lat:"⪫",latail:"⤙",lAtail:"⤛",late:"⪭",lates:"⪭︀",lbarr:"⤌",lBarr:"⤎",lbbrk:"❲",lbrace:"{",lbrack:"[",lbrke:"⦋",lbrksld:"⦏",lbrkslu:"⦍",lcaron:"ľ",Lcaron:"Ľ",lcedil:"ļ",Lcedil:"Ļ",lceil:"⌈",lcub:"{",lcy:"л",Lcy:"Л",ldca:"⤶",ldquo:"“",ldquor:"„",ldrdhar:"⥧",ldrushar:"⥋",ldsh:"↲",le:"≤",lE:"≦",LeftAngleBracket:"⟨",leftarrow:"←",Leftarrow:"⇐",LeftArrow:"←",LeftArrowBar:"⇤",LeftArrowRightArrow:"⇆",leftarrowtail:"↢",LeftCeiling:"⌈",LeftDoubleBracket:"⟦",LeftDownTeeVector:"⥡",LeftDownVector:"⇃",LeftDownVectorBar:"⥙",LeftFloor:"⌊",leftharpoondown:"↽",leftharpoonup:"↼",leftleftarrows:"⇇",leftrightarrow:"↔",Leftrightarrow:"⇔",LeftRightArrow:"↔",leftrightarrows:"⇆",leftrightharpoons:"⇋",leftrightsquigarrow:"↭",LeftRightVector:"⥎",LeftTee:"⊣",LeftTeeArrow:"↤",LeftTeeVector:"⥚",leftthreetimes:"⋋",LeftTriangle:"⊲",LeftTriangleBar:"⧏",LeftTriangleEqual:"⊴",LeftUpDownVector:"⥑",LeftUpTeeVector:"⥠",LeftUpVector:"↿",LeftUpVectorBar:"⥘",LeftVector:"↼",LeftVectorBar:"⥒",leg:"⋚",lEg:"⪋",leq:"≤",leqq:"≦",leqslant:"⩽",les:"⩽",lescc:"⪨",lesdot:"⩿",lesdoto:"⪁",lesdotor:"⪃",lesg:"⋚︀",lesges:"⪓",lessapprox:"⪅",lessdot:"⋖",lesseqgtr:"⋚",lesseqqgtr:"⪋",LessEqualGreater:"⋚",LessFullEqual:"≦",LessGreater:"≶",lessgtr:"≶",LessLess:"⪡",lesssim:"≲",LessSlantEqual:"⩽",LessTilde:"≲",lfisht:"⥼",lfloor:"⌊",lfr:"𝔩",Lfr:"𝔏",lg:"≶",lgE:"⪑",lHar:"⥢",lhard:"↽",lharu:"↼",lharul:"⥪",lhblk:"▄",ljcy:"љ",LJcy:"Љ",ll:"≪",Ll:"⋘",llarr:"⇇",llcorner:"⌞",Lleftarrow:"⇚",llhard:"⥫",lltri:"◺",lmidot:"ŀ",Lmidot:"Ŀ",lmoust:"⎰",lmoustache:"⎰",lnap:"⪉",lnapprox:"⪉",lne:"⪇",lnE:"≨",lneq:"⪇",lneqq:"≨",lnsim:"⋦",loang:"⟬",loarr:"⇽",lobrk:"⟦",longleftarrow:"⟵",Longleftarrow:"⟸",LongLeftArrow:"⟵",longleftrightarrow:"⟷",Longleftrightarrow:"⟺",LongLeftRightArrow:"⟷",longmapsto:"⟼",longrightarrow:"⟶",Longrightarrow:"⟹",LongRightArrow:"⟶",looparrowleft:"↫",looparrowright:"↬",lopar:"⦅",lopf:"𝕝",Lopf:"𝕃",loplus:"⨭",lotimes:"⨴",lowast:"∗",lowbar:"_",LowerLeftArrow:"↙",LowerRightArrow:"↘",loz:"◊",lozenge:"◊",lozf:"⧫",lpar:"(",lparlt:"⦓",lrarr:"⇆",lrcorner:"⌟",lrhar:"⇋",lrhard:"⥭",lrm:"‎",lrtri:"⊿",lsaquo:"‹",lscr:"𝓁",Lscr:"ℒ",lsh:"↰",Lsh:"↰",lsim:"≲",lsime:"⪍",lsimg:"⪏",lsqb:"[",lsquo:"‘",lsquor:"‚",lstrok:"ł",Lstrok:"Ł",lt:"<",Lt:"≪",LT:"<",ltcc:"⪦",ltcir:"⩹",ltdot:"⋖",lthree:"⋋",ltimes:"⋉",ltlarr:"⥶",ltquest:"⩻",ltri:"◃",ltrie:"⊴",ltrif:"◂",ltrPar:"⦖",lurdshar:"⥊",luruhar:"⥦",lvertneqq:"≨︀",lvnE:"≨︀",macr:"¯",male:"♂",malt:"✠",maltese:"✠",map:"↦",Map:"⤅",mapsto:"↦",mapstodown:"↧",mapstoleft:"↤",mapstoup:"↥",marker:"▮",mcomma:"⨩",mcy:"м",Mcy:"М",mdash:"—",mDDot:"∺",measuredangle:"∡",MediumSpace:" ",Mellintrf:"ℳ",mfr:"𝔪",Mfr:"𝔐",mho:"℧",micro:"µ",mid:"∣",midast:"*",midcir:"⫰",middot:"·",minus:"−",minusb:"⊟",minusd:"∸",minusdu:"⨪",MinusPlus:"∓",mlcp:"⫛",mldr:"…",mnplus:"∓",models:"⊧",mopf:"𝕞",Mopf:"𝕄",mp:"∓",mscr:"𝓂",Mscr:"ℳ",mstpos:"∾",mu:"μ",Mu:"Μ",multimap:"⊸",mumap:"⊸",nabla:"∇",nacute:"ń",Nacute:"Ń",nang:"∠⃒",nap:"≉",napE:"⩰̸",napid:"≋̸",napos:"ʼn",napprox:"≉",natur:"♮",natural:"♮",naturals:"ℕ",nbsp:" ",nbump:"≎̸",nbumpe:"≏̸",ncap:"⩃",ncaron:"ň",Ncaron:"Ň",ncedil:"ņ",Ncedil:"Ņ",ncong:"≇",ncongdot:"⩭̸",ncup:"⩂",ncy:"н",Ncy:"Н",ndash:"–",ne:"≠",nearhk:"⤤",nearr:"↗",neArr:"⇗",nearrow:"↗",nedot:"≐̸",NegativeMediumSpace:"​",NegativeThickSpace:"​",NegativeThinSpace:"​",NegativeVeryThinSpace:"​",nequiv:"≢",nesear:"⤨",nesim:"≂̸",NestedGreaterGreater:"≫",NestedLessLess:"≪",NewLine:"\n",nexist:"∄",nexists:"∄",nfr:"𝔫",Nfr:"𝔑",nge:"≱",ngE:"≧̸",ngeq:"≱",ngeqq:"≧̸",ngeqslant:"⩾̸",nges:"⩾̸",nGg:"⋙̸",ngsim:"≵",ngt:"≯",nGt:"≫⃒",ngtr:"≯",nGtv:"≫̸",nharr:"↮",nhArr:"⇎",nhpar:"⫲",ni:"∋",nis:"⋼",nisd:"⋺",niv:"∋",njcy:"њ",NJcy:"Њ",nlarr:"↚",nlArr:"⇍",nldr:"‥",nle:"≰",nlE:"≦̸",nleftarrow:"↚",nLeftarrow:"⇍",nleftrightarrow:"↮",nLeftrightarrow:"⇎",nleq:"≰",nleqq:"≦̸",nleqslant:"⩽̸",nles:"⩽̸",nless:"≮",nLl:"⋘̸",nlsim:"≴",nlt:"≮",nLt:"≪⃒",nltri:"⋪",nltrie:"⋬",nLtv:"≪̸",nmid:"∤",NoBreak:"⁠",NonBreakingSpace:" ",nopf:"𝕟",Nopf:"ℕ",not:"¬",Not:"⫬",NotCongruent:"≢",NotCupCap:"≭",NotDoubleVerticalBar:"∦",NotElement:"∉",NotEqual:"≠",NotEqualTilde:"≂̸",NotExists:"∄",NotGreater:"≯",NotGreaterEqual:"≱",NotGreaterFullEqual:"≧̸",NotGreaterGreater:"≫̸",NotGreaterLess:"≹",NotGreaterSlantEqual:"⩾̸",NotGreaterTilde:"≵",NotHumpDownHump:"≎̸",NotHumpEqual:"≏̸",notin:"∉",notindot:"⋵̸",notinE:"⋹̸",notinva:"∉",notinvb:"⋷",notinvc:"⋶",NotLeftTriangle:"⋪",NotLeftTriangleBar:"⧏̸",NotLeftTriangleEqual:"⋬",NotLess:"≮",NotLessEqual:"≰",NotLessGreater:"≸",NotLessLess:"≪̸",NotLessSlantEqual:"⩽̸",NotLessTilde:"≴",NotNestedGreaterGreater:"⪢̸",NotNestedLessLess:"⪡̸",notni:"∌",notniva:"∌",notnivb:"⋾",notnivc:"⋽",NotPrecedes:"⊀",NotPrecedesEqual:"⪯̸",NotPrecedesSlantEqual:"⋠",NotReverseElement:"∌",NotRightTriangle:"⋫",NotRightTriangleBar:"⧐̸",NotRightTriangleEqual:"⋭",NotSquareSubset:"⊏̸",NotSquareSubsetEqual:"⋢",NotSquareSuperset:"⊐̸",NotSquareSupersetEqual:"⋣",NotSubset:"⊂⃒",NotSubsetEqual:"⊈",NotSucceeds:"⊁",NotSucceedsEqual:"⪰̸",NotSucceedsSlantEqual:"⋡",NotSucceedsTilde:"≿̸",NotSuperset:"⊃⃒",NotSupersetEqual:"⊉",NotTilde:"≁",NotTildeEqual:"≄",NotTildeFullEqual:"≇",NotTildeTilde:"≉",NotVerticalBar:"∤",npar:"∦",nparallel:"∦",nparsl:"⫽⃥",npart:"∂̸",npolint:"⨔",npr:"⊀",nprcue:"⋠",npre:"⪯̸",nprec:"⊀",npreceq:"⪯̸",nrarr:"↛",nrArr:"⇏",nrarrc:"⤳̸",nrarrw:"↝̸",nrightarrow:"↛",nRightarrow:"⇏",nrtri:"⋫",nrtrie:"⋭",nsc:"⊁",nsccue:"⋡",nsce:"⪰̸",nscr:"𝓃",Nscr:"𝒩",nshortmid:"∤",nshortparallel:"∦",nsim:"≁",nsime:"≄",nsimeq:"≄",nsmid:"∤",nspar:"∦",nsqsube:"⋢",nsqsupe:"⋣",nsub:"⊄",nsube:"⊈",nsubE:"⫅̸",nsubset:"⊂⃒",nsubseteq:"⊈",nsubseteqq:"⫅̸",nsucc:"⊁",nsucceq:"⪰̸",nsup:"⊅",nsupe:"⊉",nsupE:"⫆̸",nsupset:"⊃⃒",nsupseteq:"⊉",nsupseteqq:"⫆̸",ntgl:"≹",ntilde:"ñ",Ntilde:"Ñ",ntlg:"≸",ntriangleleft:"⋪",ntrianglelefteq:"⋬",ntriangleright:"⋫",ntrianglerighteq:"⋭",nu:"ν",Nu:"Ν",num:"#",numero:"№",numsp:" ",nvap:"≍⃒",nvdash:"⊬",nvDash:"⊭",nVdash:"⊮",nVDash:"⊯",nvge:"≥⃒",nvgt:">⃒",nvHarr:"⤄",nvinfin:"⧞",nvlArr:"⤂",nvle:"≤⃒",nvlt:"<⃒",nvltrie:"⊴⃒",nvrArr:"⤃",nvrtrie:"⊵⃒",nvsim:"∼⃒",nwarhk:"⤣",nwarr:"↖",nwArr:"⇖",nwarrow:"↖",nwnear:"⤧",oacute:"ó",Oacute:"Ó",oast:"⊛",ocir:"⊚",ocirc:"ô",Ocirc:"Ô",ocy:"о",Ocy:"О",odash:"⊝",odblac:"ő",Odblac:"Ő",odiv:"⨸",odot:"⊙",odsold:"⦼",oelig:"œ",OElig:"Œ",ofcir:"⦿",ofr:"𝔬",Ofr:"𝔒",ogon:"˛",ograve:"ò",Ograve:"Ò",ogt:"⧁",ohbar:"⦵",ohm:"Ω",oint:"∮",olarr:"↺",olcir:"⦾",olcross:"⦻",oline:"‾",olt:"⧀",omacr:"ō",Omacr:"Ō",omega:"ω",Omega:"Ω",omicron:"ο",Omicron:"Ο",omid:"⦶",ominus:"⊖",oopf:"𝕠",Oopf:"𝕆",opar:"⦷",OpenCurlyDoubleQuote:"“",OpenCurlyQuote:"‘",operp:"⦹",oplus:"⊕",or:"∨",Or:"⩔",orarr:"↻",ord:"⩝",order:"ℴ",orderof:"ℴ",ordf:"ª",ordm:"º",origof:"⊶",oror:"⩖",orslope:"⩗",orv:"⩛",oS:"Ⓢ",oscr:"ℴ",Oscr:"𝒪",oslash:"ø",Oslash:"Ø",osol:"⊘",otilde:"õ",Otilde:"Õ",otimes:"⊗",Otimes:"⨷",otimesas:"⨶",ouml:"ö",Ouml:"Ö",ovbar:"⌽",OverBar:"‾",OverBrace:"⏞",OverBracket:"⎴",OverParenthesis:"⏜",par:"∥",para:"¶",parallel:"∥",parsim:"⫳",parsl:"⫽",part:"∂",PartialD:"∂",pcy:"п",Pcy:"П",percnt:"%",period:".",permil:"‰",perp:"⊥",pertenk:"‱",pfr:"𝔭",Pfr:"𝔓",phi:"φ",Phi:"Φ",phiv:"ϕ",phmmat:"ℳ",phone:"☎",pi:"π",Pi:"Π",pitchfork:"⋔",piv:"ϖ",planck:"ℏ",planckh:"ℎ",plankv:"ℏ",plus:"+",plusacir:"⨣",plusb:"⊞",pluscir:"⨢",plusdo:"∔",plusdu:"⨥",pluse:"⩲",PlusMinus:"±",plusmn:"±",plussim:"⨦",plustwo:"⨧",pm:"±",Poincareplane:"ℌ",pointint:"⨕",popf:"𝕡",Popf:"ℙ",pound:"£",pr:"≺",Pr:"⪻",prap:"⪷",prcue:"≼",pre:"⪯",prE:"⪳",prec:"≺",precapprox:"⪷",preccurlyeq:"≼",Precedes:"≺",PrecedesEqual:"⪯",PrecedesSlantEqual:"≼",PrecedesTilde:"≾",preceq:"⪯",precnapprox:"⪹",precneqq:"⪵",precnsim:"⋨",precsim:"≾",prime:"′",Prime:"″",primes:"ℙ",prnap:"⪹",prnE:"⪵",prnsim:"⋨",prod:"∏",Product:"∏",profalar:"⌮",profline:"⌒",profsurf:"⌓",prop:"∝",Proportion:"∷",Proportional:"∝",propto:"∝",prsim:"≾",prurel:"⊰",pscr:"𝓅",Pscr:"𝒫",psi:"ψ",Psi:"Ψ",puncsp:" ",qfr:"𝔮",Qfr:"𝔔",qint:"⨌",qopf:"𝕢",Qopf:"ℚ",qprime:"⁗",qscr:"𝓆",Qscr:"𝒬",quaternions:"ℍ",quatint:"⨖",quest:"?",questeq:"≟",quot:'"',QUOT:'"',rAarr:"⇛",race:"∽̱",racute:"ŕ",Racute:"Ŕ",radic:"√",raemptyv:"⦳",rang:"⟩",Rang:"⟫",rangd:"⦒",range:"⦥",rangle:"⟩",raquo:"»",rarr:"→",rArr:"⇒",Rarr:"↠",rarrap:"⥵",rarrb:"⇥",rarrbfs:"⤠",rarrc:"⤳",rarrfs:"⤞",rarrhk:"↪",rarrlp:"↬",rarrpl:"⥅",rarrsim:"⥴",rarrtl:"↣",Rarrtl:"⤖",rarrw:"↝",ratail:"⤚",rAtail:"⤜",ratio:"∶",rationals:"ℚ",rbarr:"⤍",rBarr:"⤏",RBarr:"⤐",rbbrk:"❳",rbrace:"}",rbrack:"]",rbrke:"⦌",rbrksld:"⦎",rbrkslu:"⦐",rcaron:"ř",Rcaron:"Ř",rcedil:"ŗ",Rcedil:"Ŗ",rceil:"⌉",rcub:"}",rcy:"р",Rcy:"Р",rdca:"⤷",rdldhar:"⥩",rdquo:"”",rdquor:"”",rdsh:"↳",Re:"ℜ",real:"ℜ",realine:"ℛ",realpart:"ℜ",reals:"ℝ",rect:"▭",reg:"®",REG:"®",ReverseElement:"∋",ReverseEquilibrium:"⇋",ReverseUpEquilibrium:"⥯",rfisht:"⥽",rfloor:"⌋",rfr:"𝔯",Rfr:"ℜ",rHar:"⥤",rhard:"⇁",rharu:"⇀",rharul:"⥬",rho:"ρ",Rho:"Ρ",rhov:"ϱ",RightAngleBracket:"⟩",rightarrow:"→",Rightarrow:"⇒",RightArrow:"→",RightArrowBar:"⇥",RightArrowLeftArrow:"⇄",rightarrowtail:"↣",RightCeiling:"⌉",RightDoubleBracket:"⟧",RightDownTeeVector:"⥝",RightDownVector:"⇂",RightDownVectorBar:"⥕",RightFloor:"⌋",rightharpoondown:"⇁",rightharpoonup:"⇀",rightleftarrows:"⇄",rightleftharpoons:"⇌",rightrightarrows:"⇉",rightsquigarrow:"↝",RightTee:"⊢",RightTeeArrow:"↦",RightTeeVector:"⥛",rightthreetimes:"⋌",RightTriangle:"⊳",RightTriangleBar:"⧐",RightTriangleEqual:"⊵",RightUpDownVector:"⥏",RightUpTeeVector:"⥜",RightUpVector:"↾",RightUpVectorBar:"⥔",RightVector:"⇀",RightVectorBar:"⥓",ring:"˚",risingdotseq:"≓",rlarr:"⇄",rlhar:"⇌",rlm:"‏",rmoust:"⎱",rmoustache:"⎱",rnmid:"⫮",roang:"⟭",roarr:"⇾",robrk:"⟧",ropar:"⦆",ropf:"𝕣",Ropf:"ℝ",roplus:"⨮",rotimes:"⨵",RoundImplies:"⥰",rpar:")",rpargt:"⦔",rppolint:"⨒",rrarr:"⇉",Rrightarrow:"⇛",rsaquo:"›",rscr:"𝓇",Rscr:"ℛ",rsh:"↱",Rsh:"↱",rsqb:"]",rsquo:"’",rsquor:"’",rthree:"⋌",rtimes:"⋊",rtri:"▹",rtrie:"⊵",rtrif:"▸",rtriltri:"⧎",RuleDelayed:"⧴",ruluhar:"⥨",rx:"℞",sacute:"ś",Sacute:"Ś",sbquo:"‚",sc:"≻",Sc:"⪼",scap:"⪸",scaron:"š",Scaron:"Š",sccue:"≽",sce:"⪰",scE:"⪴",scedil:"ş",Scedil:"Ş",scirc:"ŝ",Scirc:"Ŝ",scnap:"⪺",scnE:"⪶",scnsim:"⋩",scpolint:"⨓",scsim:"≿",scy:"с",Scy:"С",sdot:"⋅",sdotb:"⊡",sdote:"⩦",searhk:"⤥",searr:"↘",seArr:"⇘",searrow:"↘",sect:"§",semi:";",seswar:"⤩",setminus:"∖",setmn:"∖",sext:"✶",sfr:"𝔰",Sfr:"𝔖",sfrown:"⌢",sharp:"♯",shchcy:"щ",SHCHcy:"Щ",shcy:"ш",SHcy:"Ш",ShortDownArrow:"↓",ShortLeftArrow:"←",shortmid:"∣",shortparallel:"∥",ShortRightArrow:"→",ShortUpArrow:"↑",shy:"­",sigma:"σ",Sigma:"Σ",sigmaf:"ς",sigmav:"ς",sim:"∼",simdot:"⩪",sime:"≃",simeq:"≃",simg:"⪞",simgE:"⪠",siml:"⪝",simlE:"⪟",simne:"≆",simplus:"⨤",simrarr:"⥲",slarr:"←",SmallCircle:"∘",smallsetminus:"∖",smashp:"⨳",smeparsl:"⧤",smid:"∣",smile:"⌣",smt:"⪪",smte:"⪬",smtes:"⪬︀",softcy:"ь",SOFTcy:"Ь",sol:"/",solb:"⧄",solbar:"⌿",sopf:"𝕤",Sopf:"𝕊",spades:"♠",spadesuit:"♠",spar:"∥",sqcap:"⊓",sqcaps:"⊓︀",sqcup:"⊔",sqcups:"⊔︀",Sqrt:"√",sqsub:"⊏",sqsube:"⊑",sqsubset:"⊏",sqsubseteq:"⊑",sqsup:"⊐",sqsupe:"⊒",sqsupset:"⊐",sqsupseteq:"⊒",squ:"□",square:"□",Square:"□",SquareIntersection:"⊓",SquareSubset:"⊏",SquareSubsetEqual:"⊑",SquareSuperset:"⊐",SquareSupersetEqual:"⊒",SquareUnion:"⊔",squarf:"▪",squf:"▪",srarr:"→",sscr:"𝓈",Sscr:"𝒮",ssetmn:"∖",ssmile:"⌣",sstarf:"⋆",star:"☆",Star:"⋆",starf:"★",straightepsilon:"ϵ",straightphi:"ϕ",strns:"¯",sub:"⊂",Sub:"⋐",subdot:"⪽",sube:"⊆",subE:"⫅",subedot:"⫃",submult:"⫁",subne:"⊊",subnE:"⫋",subplus:"⪿",subrarr:"⥹",subset:"⊂",Subset:"⋐",subseteq:"⊆",subseteqq:"⫅",SubsetEqual:"⊆",subsetneq:"⊊",subsetneqq:"⫋",subsim:"⫇",subsub:"⫕",subsup:"⫓",succ:"≻",succapprox:"⪸",succcurlyeq:"≽",Succeeds:"≻",SucceedsEqual:"⪰",SucceedsSlantEqual:"≽",SucceedsTilde:"≿",succeq:"⪰",succnapprox:"⪺",succneqq:"⪶",succnsim:"⋩",succsim:"≿",SuchThat:"∋",sum:"∑",Sum:"∑",sung:"♪",sup:"⊃",Sup:"⋑",sup1:"¹",sup2:"²",sup3:"³",supdot:"⪾",supdsub:"⫘",supe:"⊇",supE:"⫆",supedot:"⫄",Superset:"⊃",SupersetEqual:"⊇",suphsol:"⟉",suphsub:"⫗",suplarr:"⥻",supmult:"⫂",supne:"⊋",supnE:"⫌",supplus:"⫀",supset:"⊃",Supset:"⋑",supseteq:"⊇",supseteqq:"⫆",supsetneq:"⊋",supsetneqq:"⫌",supsim:"⫈",supsub:"⫔",supsup:"⫖",swarhk:"⤦",swarr:"↙",swArr:"⇙",swarrow:"↙",swnwar:"⤪",szlig:"ß",Tab:"\t",target:"⌖",tau:"τ",Tau:"Τ",tbrk:"⎴",tcaron:"ť",Tcaron:"Ť",tcedil:"ţ",Tcedil:"Ţ",tcy:"т",Tcy:"Т",tdot:"⃛",telrec:"⌕",tfr:"𝔱",Tfr:"𝔗",there4:"∴",therefore:"∴",Therefore:"∴",theta:"θ",Theta:"Θ",thetasym:"ϑ",thetav:"ϑ",thickapprox:"≈",thicksim:"∼",ThickSpace:"  ",thinsp:" ",ThinSpace:" ",thkap:"≈",thksim:"∼",thorn:"þ",THORN:"Þ",tilde:"˜",Tilde:"∼",TildeEqual:"≃",TildeFullEqual:"≅",TildeTilde:"≈",times:"×",timesb:"⊠",timesbar:"⨱",timesd:"⨰",tint:"∭",toea:"⤨",top:"⊤",topbot:"⌶",topcir:"⫱",topf:"𝕥",Topf:"𝕋",topfork:"⫚",tosa:"⤩",tprime:"‴",trade:"™",TRADE:"™",triangle:"▵",triangledown:"▿",triangleleft:"◃",trianglelefteq:"⊴",triangleq:"≜",triangleright:"▹",trianglerighteq:"⊵",tridot:"◬",trie:"≜",triminus:"⨺",TripleDot:"⃛",triplus:"⨹",trisb:"⧍",tritime:"⨻",trpezium:"⏢",tscr:"𝓉",Tscr:"𝒯",tscy:"ц",TScy:"Ц",tshcy:"ћ",TSHcy:"Ћ",tstrok:"ŧ",Tstrok:"Ŧ",twixt:"≬",twoheadleftarrow:"↞",twoheadrightarrow:"↠",uacute:"ú",Uacute:"Ú",uarr:"↑",uArr:"⇑",Uarr:"↟",Uarrocir:"⥉",ubrcy:"ў",Ubrcy:"Ў",ubreve:"ŭ",Ubreve:"Ŭ",ucirc:"û",Ucirc:"Û",ucy:"у",Ucy:"У",udarr:"⇅",udblac:"ű",Udblac:"Ű",udhar:"⥮",ufisht:"⥾",ufr:"𝔲",Ufr:"𝔘",ugrave:"ù",Ugrave:"Ù",uHar:"⥣",uharl:"↿",uharr:"↾",uhblk:"▀",ulcorn:"⌜",ulcorner:"⌜",ulcrop:"⌏",ultri:"◸",umacr:"ū",Umacr:"Ū",uml:"¨",UnderBar:"_",UnderBrace:"⏟",UnderBracket:"⎵",UnderParenthesis:"⏝",Union:"⋃",UnionPlus:"⊎",uogon:"ų",Uogon:"Ų",uopf:"𝕦",Uopf:"𝕌",uparrow:"↑",Uparrow:"⇑",UpArrow:"↑",UpArrowBar:"⤒",UpArrowDownArrow:"⇅",updownarrow:"↕",Updownarrow:"⇕",UpDownArrow:"↕",UpEquilibrium:"⥮",upharpoonleft:"↿",upharpoonright:"↾",uplus:"⊎",UpperLeftArrow:"↖",UpperRightArrow:"↗",upsi:"υ",Upsi:"ϒ",upsih:"ϒ",upsilon:"υ",Upsilon:"Υ",UpTee:"⊥",UpTeeArrow:"↥",upuparrows:"⇈",urcorn:"⌝",urcorner:"⌝",urcrop:"⌎",uring:"ů",Uring:"Ů",urtri:"◹",uscr:"𝓊",Uscr:"𝒰",utdot:"⋰",utilde:"ũ",Utilde:"Ũ",utri:"▵",utrif:"▴",uuarr:"⇈",uuml:"ü",Uuml:"Ü",uwangle:"⦧",vangrt:"⦜",varepsilon:"ϵ",varkappa:"ϰ",varnothing:"∅",varphi:"ϕ",varpi:"ϖ",varpropto:"∝",varr:"↕",vArr:"⇕",varrho:"ϱ",varsigma:"ς",varsubsetneq:"⊊︀",varsubsetneqq:"⫋︀",varsupsetneq:"⊋︀",varsupsetneqq:"⫌︀",vartheta:"ϑ",vartriangleleft:"⊲",vartriangleright:"⊳",vBar:"⫨",Vbar:"⫫",vBarv:"⫩",vcy:"в",Vcy:"В",vdash:"⊢",vDash:"⊨",Vdash:"⊩",VDash:"⊫",Vdashl:"⫦",vee:"∨",Vee:"⋁",veebar:"⊻",veeeq:"≚",vellip:"⋮",verbar:"|",Verbar:"‖",vert:"|",Vert:"‖",VerticalBar:"∣",VerticalLine:"|",VerticalSeparator:"❘",VerticalTilde:"≀",VeryThinSpace:" ",vfr:"𝔳",Vfr:"𝔙",vltri:"⊲",vnsub:"⊂⃒",vnsup:"⊃⃒",vopf:"𝕧",Vopf:"𝕍",vprop:"∝",vrtri:"⊳",vscr:"𝓋",Vscr:"𝒱",vsubne:"⊊︀",vsubnE:"⫋︀",vsupne:"⊋︀",vsupnE:"⫌︀",Vvdash:"⊪",vzigzag:"⦚",wcirc:"ŵ",Wcirc:"Ŵ",wedbar:"⩟",wedge:"∧",Wedge:"⋀",wedgeq:"≙",weierp:"℘",wfr:"𝔴",Wfr:"𝔚",wopf:"𝕨",Wopf:"𝕎",wp:"℘",wr:"≀",wreath:"≀",wscr:"𝓌",Wscr:"𝒲",xcap:"⋂",xcirc:"◯",xcup:"⋃",xdtri:"▽",xfr:"𝔵",Xfr:"𝔛",xharr:"⟷",xhArr:"⟺",xi:"ξ",Xi:"Ξ",xlarr:"⟵",xlArr:"⟸",xmap:"⟼",xnis:"⋻",xodot:"⨀",xopf:"𝕩",Xopf:"𝕏",xoplus:"⨁",xotime:"⨂",xrarr:"⟶",xrArr:"⟹",xscr:"𝓍",Xscr:"𝒳",xsqcup:"⨆",xuplus:"⨄",xutri:"△",xvee:"⋁",xwedge:"⋀",yacute:"ý",Yacute:"Ý",yacy:"я",YAcy:"Я",ycirc:"ŷ",Ycirc:"Ŷ",ycy:"ы",Ycy:"Ы",yen:"¥",yfr:"𝔶",Yfr:"𝔜",yicy:"ї",YIcy:"Ї",yopf:"𝕪",Yopf:"𝕐",yscr:"𝓎",Yscr:"𝒴",yucy:"ю",YUcy:"Ю",yuml:"ÿ",Yuml:"Ÿ",zacute:"ź",Zacute:"Ź",zcaron:"ž",Zcaron:"Ž",zcy:"з",Zcy:"З",zdot:"ż",Zdot:"Ż",zeetrf:"ℨ",ZeroWidthSpace:"​",zeta:"ζ",Zeta:"Ζ",zfr:"𝔷",Zfr:"ℨ",zhcy:"ж",ZHcy:"Ж",zigrarr:"⇝",zopf:"𝕫",Zopf:"ℤ",zscr:"𝓏",Zscr:"𝒵",zwj:"‍",zwnj:"‌"},h={aacute:"á",Aacute:"Á",acirc:"â",Acirc:"Â",acute:"´",aelig:"æ",AElig:"Æ",agrave:"à",Agrave:"À",amp:"&",AMP:"&",aring:"å",Aring:"Å",atilde:"ã",Atilde:"Ã",auml:"ä",Auml:"Ä",brvbar:"¦",ccedil:"ç",Ccedil:"Ç",cedil:"¸",cent:"¢",copy:"©",COPY:"©",curren:"¤",deg:"°",divide:"÷",eacute:"é",Eacute:"É",ecirc:"ê",Ecirc:"Ê",egrave:"è",Egrave:"È",eth:"ð",ETH:"Ð",euml:"ë",Euml:"Ë",frac12:"½",frac14:"¼",frac34:"¾",gt:">",GT:">",iacute:"í",Iacute:"Í",icirc:"î",Icirc:"Î",iexcl:"¡",igrave:"ì",Igrave:"Ì",iquest:"¿",iuml:"ï",Iuml:"Ï",laquo:"«",lt:"<",LT:"<",macr:"¯",micro:"µ",middot:"·",nbsp:" ",not:"¬",ntilde:"ñ",Ntilde:"Ñ",oacute:"ó",Oacute:"Ó",ocirc:"ô",Ocirc:"Ô",ograve:"ò",Ograve:"Ò",ordf:"ª",ordm:"º",oslash:"ø",Oslash:"Ø",otilde:"õ",Otilde:"Õ",ouml:"ö",Ouml:"Ö",para:"¶",plusmn:"±",pound:"£",quot:'"',QUOT:'"',raquo:"»",reg:"®",REG:"®",sect:"§",shy:"­",sup1:"¹",sup2:"²",sup3:"³",szlig:"ß",thorn:"þ",THORN:"Þ",times:"×",uacute:"ú",Uacute:"Ú",ucirc:"û",Ucirc:"Û",ugrave:"ù",Ugrave:"Ù",uml:"¨",uuml:"ü",Uuml:"Ü",yacute:"ý",Yacute:"Ý",yen:"¥",yuml:"ÿ"},g={0:"�",128:"€",130:"‚",131:"ƒ",132:"„",133:"…",134:"†",135:"‡",136:"ˆ",137:"‰",138:"Š",139:"‹",140:"Œ",142:"Ž",145:"‘",146:"’",147:"“",148:"”",149:"•",150:"–",151:"—",152:"˜",153:"™",154:"š",155:"›",156:"œ",158:"ž",159:"Ÿ"},E=[1,2,3,4,5,6,7,8,11,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,64976,64977,64978,64979,64980,64981,64982,64983,64984,64985,64986,64987,64988,64989,64990,64991,64992,64993,64994,64995,64996,64997,64998,64999,65e3,65001,65002,65003,65004,65005,65006,65007,65534,65535,131070,131071,196606,196607,262142,262143,327678,327679,393214,393215,458750,458751,524286,524287,589822,589823,655358,655359,720894,720895,786430,786431,851966,851967,917502,917503,983038,983039,1048574,1048575,1114110,1114111],v=String.fromCharCode,_={}.hasOwnProperty,S=function(y,L){return _.call(y,L)},q=function(y,L){if(!y)return L;var H,k={};for(H in L)k[H]=S(y,H)?y[H]:L[H];return k},I=function(y,L){var k="";return y>=55296&&y<=57343||y>1114111?(L&&Y("character reference outside the permissible Unicode range"),"�"):S(g,y)?(L&&Y("disallowed character reference"),g[y]):(L&&function(y,L){for(var k=-1,H=y.length;++k65535&&(k+=v((y-=65536)>>>10&1023|55296),y=56320|1023&y),k+=v(y))},Q=function(y){return"&#x"+y.toString(16).toUpperCase()+";"},ie=function(y){return"&#"+y+";"},Y=function(y){throw Error("Parse error: "+y)},O=function(y,L){(L=q(L,O.options)).strict&&A.test(y)&&Y("forbidden code point");var H=L.encodeEverything,j=L.useNamedReferences,me=L.allowUnsafeSymbols,ce=L.decimal?ie:Q,se=function(le){return ce(le.charCodeAt(0))};return H?(y=y.replace(i,(function(le){return j&&S(c,le)?"&"+c[le]+";":se(le)})),j&&(y=y.replace(/>\u20D2/g,">⃒").replace(/<\u20D2/g,"<⃒").replace(/fj/g,"fj")),j&&(y=y.replace(u,(function(le){return"&"+c[le]+";"})))):j?(me||(y=y.replace(d,(function(le){return"&"+c[le]+";"}))),y=(y=y.replace(/>\u20D2/g,">⃒").replace(/<\u20D2/g,"<⃒")).replace(u,(function(le){return"&"+c[le]+";"}))):me||(y=y.replace(d,se)),y.replace(s,(function(le){var gt=le.charCodeAt(0),Ft=le.charCodeAt(1);return ce(1024*(gt-55296)+Ft-56320+65536)})).replace(l,se)};O.options={allowUnsafeSymbols:!1,encodeEverything:!1,strict:!1,useNamedReferences:!1,decimal:!1};var G=function(y,L){var k=(L=q(L,G.options)).strict;return k&&m.test(y)&&Y("malformed character reference"),y.replace(b,(function(H,j,me,ce,se,le,gt,Ft,ht){var Ge,Ct,te,Re,ke,Ce;return j?C[ke=j]:me?(ke=me,(Ce=ce)&&L.isAttributeValue?(k&&"="==Ce&&Y("`&` did not start a character reference"),H):(k&&Y("named character reference was not terminated by a semicolon"),h[ke]+(Ce||""))):se?(te=se,Ct=le,k&&!Ct&&Y("character reference was not terminated by a semicolon"),Ge=parseInt(te,10),I(Ge,k)):gt?(Re=gt,Ct=Ft,k&&!Ct&&Y("character reference was not terminated by a semicolon"),Ge=parseInt(Re,16),I(Ge,k)):(k&&Y("named character reference was not terminated by a semicolon"),H)}))};G.options={isAttributeValue:!1,strict:!1};var x={version:"1.2.0",encode:O,decode:G,escape:function(y){return y.replace(d,(function(L){return p[L]}))},unescape:G};if(r&&!r.nodeType)if(o)o.exports=x;else for(var T in x)S(x,T)&&(r[T]=x[T]);else n.he=x}(Nt)}(ps,ps.exports);var hb=ps.exports,ae={},Np={Aacute:"Á",aacute:"á",Abreve:"Ă",abreve:"ă",ac:"∾",acd:"∿",acE:"∾̳",Acirc:"Â",acirc:"â",acute:"´",Acy:"А",acy:"а",AElig:"Æ",aelig:"æ",af:"⁡",Afr:"𝔄",afr:"𝔞",Agrave:"À",agrave:"à",alefsym:"ℵ",aleph:"ℵ",Alpha:"Α",alpha:"α",Amacr:"Ā",amacr:"ā",amalg:"⨿",amp:"&",AMP:"&",andand:"⩕",And:"⩓",and:"∧",andd:"⩜",andslope:"⩘",andv:"⩚",ang:"∠",ange:"⦤",angle:"∠",angmsdaa:"⦨",angmsdab:"⦩",angmsdac:"⦪",angmsdad:"⦫",angmsdae:"⦬",angmsdaf:"⦭",angmsdag:"⦮",angmsdah:"⦯",angmsd:"∡",angrt:"∟",angrtvb:"⊾",angrtvbd:"⦝",angsph:"∢",angst:"Å",angzarr:"⍼",Aogon:"Ą",aogon:"ą",Aopf:"𝔸",aopf:"𝕒",apacir:"⩯",ap:"≈",apE:"⩰",ape:"≊",apid:"≋",apos:"'",ApplyFunction:"⁡",approx:"≈",approxeq:"≊",Aring:"Å",aring:"å",Ascr:"𝒜",ascr:"𝒶",Assign:"≔",ast:"*",asymp:"≈",asympeq:"≍",Atilde:"Ã",atilde:"ã",Auml:"Ä",auml:"ä",awconint:"∳",awint:"⨑",backcong:"≌",backepsilon:"϶",backprime:"‵",backsim:"∽",backsimeq:"⋍",Backslash:"∖",Barv:"⫧",barvee:"⊽",barwed:"⌅",Barwed:"⌆",barwedge:"⌅",bbrk:"⎵",bbrktbrk:"⎶",bcong:"≌",Bcy:"Б",bcy:"б",bdquo:"„",becaus:"∵",because:"∵",Because:"∵",bemptyv:"⦰",bepsi:"϶",bernou:"ℬ",Bernoullis:"ℬ",Beta:"Β",beta:"β",beth:"ℶ",between:"≬",Bfr:"𝔅",bfr:"𝔟",bigcap:"⋂",bigcirc:"◯",bigcup:"⋃",bigodot:"⨀",bigoplus:"⨁",bigotimes:"⨂",bigsqcup:"⨆",bigstar:"★",bigtriangledown:"▽",bigtriangleup:"△",biguplus:"⨄",bigvee:"⋁",bigwedge:"⋀",bkarow:"⤍",blacklozenge:"⧫",blacksquare:"▪",blacktriangle:"▴",blacktriangledown:"▾",blacktriangleleft:"◂",blacktriangleright:"▸",blank:"␣",blk12:"▒",blk14:"░",blk34:"▓",block:"█",bne:"=⃥",bnequiv:"≡⃥",bNot:"⫭",bnot:"⌐",Bopf:"𝔹",bopf:"𝕓",bot:"⊥",bottom:"⊥",bowtie:"⋈",boxbox:"⧉",boxdl:"┐",boxdL:"╕",boxDl:"╖",boxDL:"╗",boxdr:"┌",boxdR:"╒",boxDr:"╓",boxDR:"╔",boxh:"─",boxH:"═",boxhd:"┬",boxHd:"╤",boxhD:"╥",boxHD:"╦",boxhu:"┴",boxHu:"╧",boxhU:"╨",boxHU:"╩",boxminus:"⊟",boxplus:"⊞",boxtimes:"⊠",boxul:"┘",boxuL:"╛",boxUl:"╜",boxUL:"╝",boxur:"└",boxuR:"╘",boxUr:"╙",boxUR:"╚",boxv:"│",boxV:"║",boxvh:"┼",boxvH:"╪",boxVh:"╫",boxVH:"╬",boxvl:"┤",boxvL:"╡",boxVl:"╢",boxVL:"╣",boxvr:"├",boxvR:"╞",boxVr:"╟",boxVR:"╠",bprime:"‵",breve:"˘",Breve:"˘",brvbar:"¦",bscr:"𝒷",Bscr:"ℬ",bsemi:"⁏",bsim:"∽",bsime:"⋍",bsolb:"⧅",bsol:"\\",bsolhsub:"⟈",bull:"•",bullet:"•",bump:"≎",bumpE:"⪮",bumpe:"≏",Bumpeq:"≎",bumpeq:"≏",Cacute:"Ć",cacute:"ć",capand:"⩄",capbrcup:"⩉",capcap:"⩋",cap:"∩",Cap:"⋒",capcup:"⩇",capdot:"⩀",CapitalDifferentialD:"ⅅ",caps:"∩︀",caret:"⁁",caron:"ˇ",Cayleys:"ℭ",ccaps:"⩍",Ccaron:"Č",ccaron:"č",Ccedil:"Ç",ccedil:"ç",Ccirc:"Ĉ",ccirc:"ĉ",Cconint:"∰",ccups:"⩌",ccupssm:"⩐",Cdot:"Ċ",cdot:"ċ",cedil:"¸",Cedilla:"¸",cemptyv:"⦲",cent:"¢",centerdot:"·",CenterDot:"·",cfr:"𝔠",Cfr:"ℭ",CHcy:"Ч",chcy:"ч",check:"✓",checkmark:"✓",Chi:"Χ",chi:"χ",circ:"ˆ",circeq:"≗",circlearrowleft:"↺",circlearrowright:"↻",circledast:"⊛",circledcirc:"⊚",circleddash:"⊝",CircleDot:"⊙",circledR:"®",circledS:"Ⓢ",CircleMinus:"⊖",CirclePlus:"⊕",CircleTimes:"⊗",cir:"○",cirE:"⧃",cire:"≗",cirfnint:"⨐",cirmid:"⫯",cirscir:"⧂",ClockwiseContourIntegral:"∲",CloseCurlyDoubleQuote:"”",CloseCurlyQuote:"’",clubs:"♣",clubsuit:"♣",colon:":",Colon:"∷",Colone:"⩴",colone:"≔",coloneq:"≔",comma:",",commat:"@",comp:"∁",compfn:"∘",complement:"∁",complexes:"ℂ",cong:"≅",congdot:"⩭",Congruent:"≡",conint:"∮",Conint:"∯",ContourIntegral:"∮",copf:"𝕔",Copf:"ℂ",coprod:"∐",Coproduct:"∐",copy:"©",COPY:"©",copysr:"℗",CounterClockwiseContourIntegral:"∳",crarr:"↵",cross:"✗",Cross:"⨯",Cscr:"𝒞",cscr:"𝒸",csub:"⫏",csube:"⫑",csup:"⫐",csupe:"⫒",ctdot:"⋯",cudarrl:"⤸",cudarrr:"⤵",cuepr:"⋞",cuesc:"⋟",cularr:"↶",cularrp:"⤽",cupbrcap:"⩈",cupcap:"⩆",CupCap:"≍",cup:"∪",Cup:"⋓",cupcup:"⩊",cupdot:"⊍",cupor:"⩅",cups:"∪︀",curarr:"↷",curarrm:"⤼",curlyeqprec:"⋞",curlyeqsucc:"⋟",curlyvee:"⋎",curlywedge:"⋏",curren:"¤",curvearrowleft:"↶",curvearrowright:"↷",cuvee:"⋎",cuwed:"⋏",cwconint:"∲",cwint:"∱",cylcty:"⌭",dagger:"†",Dagger:"‡",daleth:"ℸ",darr:"↓",Darr:"↡",dArr:"⇓",dash:"‐",Dashv:"⫤",dashv:"⊣",dbkarow:"⤏",dblac:"˝",Dcaron:"Ď",dcaron:"ď",Dcy:"Д",dcy:"д",ddagger:"‡",ddarr:"⇊",DD:"ⅅ",dd:"ⅆ",DDotrahd:"⤑",ddotseq:"⩷",deg:"°",Del:"∇",Delta:"Δ",delta:"δ",demptyv:"⦱",dfisht:"⥿",Dfr:"𝔇",dfr:"𝔡",dHar:"⥥",dharl:"⇃",dharr:"⇂",DiacriticalAcute:"´",DiacriticalDot:"˙",DiacriticalDoubleAcute:"˝",DiacriticalGrave:"`",DiacriticalTilde:"˜",diam:"⋄",diamond:"⋄",Diamond:"⋄",diamondsuit:"♦",diams:"♦",die:"¨",DifferentialD:"ⅆ",digamma:"ϝ",disin:"⋲",div:"÷",divide:"÷",divideontimes:"⋇",divonx:"⋇",DJcy:"Ђ",djcy:"ђ",dlcorn:"⌞",dlcrop:"⌍",dollar:"$",Dopf:"𝔻",dopf:"𝕕",Dot:"¨",dot:"˙",DotDot:"⃜",doteq:"≐",doteqdot:"≑",DotEqual:"≐",dotminus:"∸",dotplus:"∔",dotsquare:"⊡",doublebarwedge:"⌆",DoubleContourIntegral:"∯",DoubleDot:"¨",DoubleDownArrow:"⇓",DoubleLeftArrow:"⇐",DoubleLeftRightArrow:"⇔",DoubleLeftTee:"⫤",DoubleLongLeftArrow:"⟸",DoubleLongLeftRightArrow:"⟺",DoubleLongRightArrow:"⟹",DoubleRightArrow:"⇒",DoubleRightTee:"⊨",DoubleUpArrow:"⇑",DoubleUpDownArrow:"⇕",DoubleVerticalBar:"∥",DownArrowBar:"⤓",downarrow:"↓",DownArrow:"↓",Downarrow:"⇓",DownArrowUpArrow:"⇵",DownBreve:"̑",downdownarrows:"⇊",downharpoonleft:"⇃",downharpoonright:"⇂",DownLeftRightVector:"⥐",DownLeftTeeVector:"⥞",DownLeftVectorBar:"⥖",DownLeftVector:"↽",DownRightTeeVector:"⥟",DownRightVectorBar:"⥗",DownRightVector:"⇁",DownTeeArrow:"↧",DownTee:"⊤",drbkarow:"⤐",drcorn:"⌟",drcrop:"⌌",Dscr:"𝒟",dscr:"𝒹",DScy:"Ѕ",dscy:"ѕ",dsol:"⧶",Dstrok:"Đ",dstrok:"đ",dtdot:"⋱",dtri:"▿",dtrif:"▾",duarr:"⇵",duhar:"⥯",dwangle:"⦦",DZcy:"Џ",dzcy:"џ",dzigrarr:"⟿",Eacute:"É",eacute:"é",easter:"⩮",Ecaron:"Ě",ecaron:"ě",Ecirc:"Ê",ecirc:"ê",ecir:"≖",ecolon:"≕",Ecy:"Э",ecy:"э",eDDot:"⩷",Edot:"Ė",edot:"ė",eDot:"≑",ee:"ⅇ",efDot:"≒",Efr:"𝔈",efr:"𝔢",eg:"⪚",Egrave:"È",egrave:"è",egs:"⪖",egsdot:"⪘",el:"⪙",Element:"∈",elinters:"⏧",ell:"ℓ",els:"⪕",elsdot:"⪗",Emacr:"Ē",emacr:"ē",empty:"∅",emptyset:"∅",EmptySmallSquare:"◻",emptyv:"∅",EmptyVerySmallSquare:"▫",emsp13:" ",emsp14:" ",emsp:" ",ENG:"Ŋ",eng:"ŋ",ensp:" ",Eogon:"Ę",eogon:"ę",Eopf:"𝔼",eopf:"𝕖",epar:"⋕",eparsl:"⧣",eplus:"⩱",epsi:"ε",Epsilon:"Ε",epsilon:"ε",epsiv:"ϵ",eqcirc:"≖",eqcolon:"≕",eqsim:"≂",eqslantgtr:"⪖",eqslantless:"⪕",Equal:"⩵",equals:"=",EqualTilde:"≂",equest:"≟",Equilibrium:"⇌",equiv:"≡",equivDD:"⩸",eqvparsl:"⧥",erarr:"⥱",erDot:"≓",escr:"ℯ",Escr:"ℰ",esdot:"≐",Esim:"⩳",esim:"≂",Eta:"Η",eta:"η",ETH:"Ð",eth:"ð",Euml:"Ë",euml:"ë",euro:"€",excl:"!",exist:"∃",Exists:"∃",expectation:"ℰ",exponentiale:"ⅇ",ExponentialE:"ⅇ",fallingdotseq:"≒",Fcy:"Ф",fcy:"ф",female:"♀",ffilig:"ffi",fflig:"ff",ffllig:"ffl",Ffr:"𝔉",ffr:"𝔣",filig:"fi",FilledSmallSquare:"◼",FilledVerySmallSquare:"▪",fjlig:"fj",flat:"♭",fllig:"fl",fltns:"▱",fnof:"ƒ",Fopf:"𝔽",fopf:"𝕗",forall:"∀",ForAll:"∀",fork:"⋔",forkv:"⫙",Fouriertrf:"ℱ",fpartint:"⨍",frac12:"½",frac13:"⅓",frac14:"¼",frac15:"⅕",frac16:"⅙",frac18:"⅛",frac23:"⅔",frac25:"⅖",frac34:"¾",frac35:"⅗",frac38:"⅜",frac45:"⅘",frac56:"⅚",frac58:"⅝",frac78:"⅞",frasl:"⁄",frown:"⌢",fscr:"𝒻",Fscr:"ℱ",gacute:"ǵ",Gamma:"Γ",gamma:"γ",Gammad:"Ϝ",gammad:"ϝ",gap:"⪆",Gbreve:"Ğ",gbreve:"ğ",Gcedil:"Ģ",Gcirc:"Ĝ",gcirc:"ĝ",Gcy:"Г",gcy:"г",Gdot:"Ġ",gdot:"ġ",ge:"≥",gE:"≧",gEl:"⪌",gel:"⋛",geq:"≥",geqq:"≧",geqslant:"⩾",gescc:"⪩",ges:"⩾",gesdot:"⪀",gesdoto:"⪂",gesdotol:"⪄",gesl:"⋛︀",gesles:"⪔",Gfr:"𝔊",gfr:"𝔤",gg:"≫",Gg:"⋙",ggg:"⋙",gimel:"ℷ",GJcy:"Ѓ",gjcy:"ѓ",gla:"⪥",gl:"≷",glE:"⪒",glj:"⪤",gnap:"⪊",gnapprox:"⪊",gne:"⪈",gnE:"≩",gneq:"⪈",gneqq:"≩",gnsim:"⋧",Gopf:"𝔾",gopf:"𝕘",grave:"`",GreaterEqual:"≥",GreaterEqualLess:"⋛",GreaterFullEqual:"≧",GreaterGreater:"⪢",GreaterLess:"≷",GreaterSlantEqual:"⩾",GreaterTilde:"≳",Gscr:"𝒢",gscr:"ℊ",gsim:"≳",gsime:"⪎",gsiml:"⪐",gtcc:"⪧",gtcir:"⩺",gt:">",GT:">",Gt:"≫",gtdot:"⋗",gtlPar:"⦕",gtquest:"⩼",gtrapprox:"⪆",gtrarr:"⥸",gtrdot:"⋗",gtreqless:"⋛",gtreqqless:"⪌",gtrless:"≷",gtrsim:"≳",gvertneqq:"≩︀",gvnE:"≩︀",Hacek:"ˇ",hairsp:" ",half:"½",hamilt:"ℋ",HARDcy:"Ъ",hardcy:"ъ",harrcir:"⥈",harr:"↔",hArr:"⇔",harrw:"↭",Hat:"^",hbar:"ℏ",Hcirc:"Ĥ",hcirc:"ĥ",hearts:"♥",heartsuit:"♥",hellip:"…",hercon:"⊹",hfr:"𝔥",Hfr:"ℌ",HilbertSpace:"ℋ",hksearow:"⤥",hkswarow:"⤦",hoarr:"⇿",homtht:"∻",hookleftarrow:"↩",hookrightarrow:"↪",hopf:"𝕙",Hopf:"ℍ",horbar:"―",HorizontalLine:"─",hscr:"𝒽",Hscr:"ℋ",hslash:"ℏ",Hstrok:"Ħ",hstrok:"ħ",HumpDownHump:"≎",HumpEqual:"≏",hybull:"⁃",hyphen:"‐",Iacute:"Í",iacute:"í",ic:"⁣",Icirc:"Î",icirc:"î",Icy:"И",icy:"и",Idot:"İ",IEcy:"Е",iecy:"е",iexcl:"¡",iff:"⇔",ifr:"𝔦",Ifr:"ℑ",Igrave:"Ì",igrave:"ì",ii:"ⅈ",iiiint:"⨌",iiint:"∭",iinfin:"⧜",iiota:"℩",IJlig:"IJ",ijlig:"ij",Imacr:"Ī",imacr:"ī",image:"ℑ",ImaginaryI:"ⅈ",imagline:"ℐ",imagpart:"ℑ",imath:"ı",Im:"ℑ",imof:"⊷",imped:"Ƶ",Implies:"⇒",incare:"℅",in:"∈",infin:"∞",infintie:"⧝",inodot:"ı",intcal:"⊺",int:"∫",Int:"∬",integers:"ℤ",Integral:"∫",intercal:"⊺",Intersection:"⋂",intlarhk:"⨗",intprod:"⨼",InvisibleComma:"⁣",InvisibleTimes:"⁢",IOcy:"Ё",iocy:"ё",Iogon:"Į",iogon:"į",Iopf:"𝕀",iopf:"𝕚",Iota:"Ι",iota:"ι",iprod:"⨼",iquest:"¿",iscr:"𝒾",Iscr:"ℐ",isin:"∈",isindot:"⋵",isinE:"⋹",isins:"⋴",isinsv:"⋳",isinv:"∈",it:"⁢",Itilde:"Ĩ",itilde:"ĩ",Iukcy:"І",iukcy:"і",Iuml:"Ï",iuml:"ï",Jcirc:"Ĵ",jcirc:"ĵ",Jcy:"Й",jcy:"й",Jfr:"𝔍",jfr:"𝔧",jmath:"ȷ",Jopf:"𝕁",jopf:"𝕛",Jscr:"𝒥",jscr:"𝒿",Jsercy:"Ј",jsercy:"ј",Jukcy:"Є",jukcy:"є",Kappa:"Κ",kappa:"κ",kappav:"ϰ",Kcedil:"Ķ",kcedil:"ķ",Kcy:"К",kcy:"к",Kfr:"𝔎",kfr:"𝔨",kgreen:"ĸ",KHcy:"Х",khcy:"х",KJcy:"Ќ",kjcy:"ќ",Kopf:"𝕂",kopf:"𝕜",Kscr:"𝒦",kscr:"𝓀",lAarr:"⇚",Lacute:"Ĺ",lacute:"ĺ",laemptyv:"⦴",lagran:"ℒ",Lambda:"Λ",lambda:"λ",lang:"⟨",Lang:"⟪",langd:"⦑",langle:"⟨",lap:"⪅",Laplacetrf:"ℒ",laquo:"«",larrb:"⇤",larrbfs:"⤟",larr:"←",Larr:"↞",lArr:"⇐",larrfs:"⤝",larrhk:"↩",larrlp:"↫",larrpl:"⤹",larrsim:"⥳",larrtl:"↢",latail:"⤙",lAtail:"⤛",lat:"⪫",late:"⪭",lates:"⪭︀",lbarr:"⤌",lBarr:"⤎",lbbrk:"❲",lbrace:"{",lbrack:"[",lbrke:"⦋",lbrksld:"⦏",lbrkslu:"⦍",Lcaron:"Ľ",lcaron:"ľ",Lcedil:"Ļ",lcedil:"ļ",lceil:"⌈",lcub:"{",Lcy:"Л",lcy:"л",ldca:"⤶",ldquo:"“",ldquor:"„",ldrdhar:"⥧",ldrushar:"⥋",ldsh:"↲",le:"≤",lE:"≦",LeftAngleBracket:"⟨",LeftArrowBar:"⇤",leftarrow:"←",LeftArrow:"←",Leftarrow:"⇐",LeftArrowRightArrow:"⇆",leftarrowtail:"↢",LeftCeiling:"⌈",LeftDoubleBracket:"⟦",LeftDownTeeVector:"⥡",LeftDownVectorBar:"⥙",LeftDownVector:"⇃",LeftFloor:"⌊",leftharpoondown:"↽",leftharpoonup:"↼",leftleftarrows:"⇇",leftrightarrow:"↔",LeftRightArrow:"↔",Leftrightarrow:"⇔",leftrightarrows:"⇆",leftrightharpoons:"⇋",leftrightsquigarrow:"↭",LeftRightVector:"⥎",LeftTeeArrow:"↤",LeftTee:"⊣",LeftTeeVector:"⥚",leftthreetimes:"⋋",LeftTriangleBar:"⧏",LeftTriangle:"⊲",LeftTriangleEqual:"⊴",LeftUpDownVector:"⥑",LeftUpTeeVector:"⥠",LeftUpVectorBar:"⥘",LeftUpVector:"↿",LeftVectorBar:"⥒",LeftVector:"↼",lEg:"⪋",leg:"⋚",leq:"≤",leqq:"≦",leqslant:"⩽",lescc:"⪨",les:"⩽",lesdot:"⩿",lesdoto:"⪁",lesdotor:"⪃",lesg:"⋚︀",lesges:"⪓",lessapprox:"⪅",lessdot:"⋖",lesseqgtr:"⋚",lesseqqgtr:"⪋",LessEqualGreater:"⋚",LessFullEqual:"≦",LessGreater:"≶",lessgtr:"≶",LessLess:"⪡",lesssim:"≲",LessSlantEqual:"⩽",LessTilde:"≲",lfisht:"⥼",lfloor:"⌊",Lfr:"𝔏",lfr:"𝔩",lg:"≶",lgE:"⪑",lHar:"⥢",lhard:"↽",lharu:"↼",lharul:"⥪",lhblk:"▄",LJcy:"Љ",ljcy:"љ",llarr:"⇇",ll:"≪",Ll:"⋘",llcorner:"⌞",Lleftarrow:"⇚",llhard:"⥫",lltri:"◺",Lmidot:"Ŀ",lmidot:"ŀ",lmoustache:"⎰",lmoust:"⎰",lnap:"⪉",lnapprox:"⪉",lne:"⪇",lnE:"≨",lneq:"⪇",lneqq:"≨",lnsim:"⋦",loang:"⟬",loarr:"⇽",lobrk:"⟦",longleftarrow:"⟵",LongLeftArrow:"⟵",Longleftarrow:"⟸",longleftrightarrow:"⟷",LongLeftRightArrow:"⟷",Longleftrightarrow:"⟺",longmapsto:"⟼",longrightarrow:"⟶",LongRightArrow:"⟶",Longrightarrow:"⟹",looparrowleft:"↫",looparrowright:"↬",lopar:"⦅",Lopf:"𝕃",lopf:"𝕝",loplus:"⨭",lotimes:"⨴",lowast:"∗",lowbar:"_",LowerLeftArrow:"↙",LowerRightArrow:"↘",loz:"◊",lozenge:"◊",lozf:"⧫",lpar:"(",lparlt:"⦓",lrarr:"⇆",lrcorner:"⌟",lrhar:"⇋",lrhard:"⥭",lrm:"‎",lrtri:"⊿",lsaquo:"‹",lscr:"𝓁",Lscr:"ℒ",lsh:"↰",Lsh:"↰",lsim:"≲",lsime:"⪍",lsimg:"⪏",lsqb:"[",lsquo:"‘",lsquor:"‚",Lstrok:"Ł",lstrok:"ł",ltcc:"⪦",ltcir:"⩹",lt:"<",LT:"<",Lt:"≪",ltdot:"⋖",lthree:"⋋",ltimes:"⋉",ltlarr:"⥶",ltquest:"⩻",ltri:"◃",ltrie:"⊴",ltrif:"◂",ltrPar:"⦖",lurdshar:"⥊",luruhar:"⥦",lvertneqq:"≨︀",lvnE:"≨︀",macr:"¯",male:"♂",malt:"✠",maltese:"✠",Map:"⤅",map:"↦",mapsto:"↦",mapstodown:"↧",mapstoleft:"↤",mapstoup:"↥",marker:"▮",mcomma:"⨩",Mcy:"М",mcy:"м",mdash:"—",mDDot:"∺",measuredangle:"∡",MediumSpace:" ",Mellintrf:"ℳ",Mfr:"𝔐",mfr:"𝔪",mho:"℧",micro:"µ",midast:"*",midcir:"⫰",mid:"∣",middot:"·",minusb:"⊟",minus:"−",minusd:"∸",minusdu:"⨪",MinusPlus:"∓",mlcp:"⫛",mldr:"…",mnplus:"∓",models:"⊧",Mopf:"𝕄",mopf:"𝕞",mp:"∓",mscr:"𝓂",Mscr:"ℳ",mstpos:"∾",Mu:"Μ",mu:"μ",multimap:"⊸",mumap:"⊸",nabla:"∇",Nacute:"Ń",nacute:"ń",nang:"∠⃒",nap:"≉",napE:"⩰̸",napid:"≋̸",napos:"ʼn",napprox:"≉",natural:"♮",naturals:"ℕ",natur:"♮",nbsp:" ",nbump:"≎̸",nbumpe:"≏̸",ncap:"⩃",Ncaron:"Ň",ncaron:"ň",Ncedil:"Ņ",ncedil:"ņ",ncong:"≇",ncongdot:"⩭̸",ncup:"⩂",Ncy:"Н",ncy:"н",ndash:"–",nearhk:"⤤",nearr:"↗",neArr:"⇗",nearrow:"↗",ne:"≠",nedot:"≐̸",NegativeMediumSpace:"​",NegativeThickSpace:"​",NegativeThinSpace:"​",NegativeVeryThinSpace:"​",nequiv:"≢",nesear:"⤨",nesim:"≂̸",NestedGreaterGreater:"≫",NestedLessLess:"≪",NewLine:"\n",nexist:"∄",nexists:"∄",Nfr:"𝔑",nfr:"𝔫",ngE:"≧̸",nge:"≱",ngeq:"≱",ngeqq:"≧̸",ngeqslant:"⩾̸",nges:"⩾̸",nGg:"⋙̸",ngsim:"≵",nGt:"≫⃒",ngt:"≯",ngtr:"≯",nGtv:"≫̸",nharr:"↮",nhArr:"⇎",nhpar:"⫲",ni:"∋",nis:"⋼",nisd:"⋺",niv:"∋",NJcy:"Њ",njcy:"њ",nlarr:"↚",nlArr:"⇍",nldr:"‥",nlE:"≦̸",nle:"≰",nleftarrow:"↚",nLeftarrow:"⇍",nleftrightarrow:"↮",nLeftrightarrow:"⇎",nleq:"≰",nleqq:"≦̸",nleqslant:"⩽̸",nles:"⩽̸",nless:"≮",nLl:"⋘̸",nlsim:"≴",nLt:"≪⃒",nlt:"≮",nltri:"⋪",nltrie:"⋬",nLtv:"≪̸",nmid:"∤",NoBreak:"⁠",NonBreakingSpace:" ",nopf:"𝕟",Nopf:"ℕ",Not:"⫬",not:"¬",NotCongruent:"≢",NotCupCap:"≭",NotDoubleVerticalBar:"∦",NotElement:"∉",NotEqual:"≠",NotEqualTilde:"≂̸",NotExists:"∄",NotGreater:"≯",NotGreaterEqual:"≱",NotGreaterFullEqual:"≧̸",NotGreaterGreater:"≫̸",NotGreaterLess:"≹",NotGreaterSlantEqual:"⩾̸",NotGreaterTilde:"≵",NotHumpDownHump:"≎̸",NotHumpEqual:"≏̸",notin:"∉",notindot:"⋵̸",notinE:"⋹̸",notinva:"∉",notinvb:"⋷",notinvc:"⋶",NotLeftTriangleBar:"⧏̸",NotLeftTriangle:"⋪",NotLeftTriangleEqual:"⋬",NotLess:"≮",NotLessEqual:"≰",NotLessGreater:"≸",NotLessLess:"≪̸",NotLessSlantEqual:"⩽̸",NotLessTilde:"≴",NotNestedGreaterGreater:"⪢̸",NotNestedLessLess:"⪡̸",notni:"∌",notniva:"∌",notnivb:"⋾",notnivc:"⋽",NotPrecedes:"⊀",NotPrecedesEqual:"⪯̸",NotPrecedesSlantEqual:"⋠",NotReverseElement:"∌",NotRightTriangleBar:"⧐̸",NotRightTriangle:"⋫",NotRightTriangleEqual:"⋭",NotSquareSubset:"⊏̸",NotSquareSubsetEqual:"⋢",NotSquareSuperset:"⊐̸",NotSquareSupersetEqual:"⋣",NotSubset:"⊂⃒",NotSubsetEqual:"⊈",NotSucceeds:"⊁",NotSucceedsEqual:"⪰̸",NotSucceedsSlantEqual:"⋡",NotSucceedsTilde:"≿̸",NotSuperset:"⊃⃒",NotSupersetEqual:"⊉",NotTilde:"≁",NotTildeEqual:"≄",NotTildeFullEqual:"≇",NotTildeTilde:"≉",NotVerticalBar:"∤",nparallel:"∦",npar:"∦",nparsl:"⫽⃥",npart:"∂̸",npolint:"⨔",npr:"⊀",nprcue:"⋠",nprec:"⊀",npreceq:"⪯̸",npre:"⪯̸",nrarrc:"⤳̸",nrarr:"↛",nrArr:"⇏",nrarrw:"↝̸",nrightarrow:"↛",nRightarrow:"⇏",nrtri:"⋫",nrtrie:"⋭",nsc:"⊁",nsccue:"⋡",nsce:"⪰̸",Nscr:"𝒩",nscr:"𝓃",nshortmid:"∤",nshortparallel:"∦",nsim:"≁",nsime:"≄",nsimeq:"≄",nsmid:"∤",nspar:"∦",nsqsube:"⋢",nsqsupe:"⋣",nsub:"⊄",nsubE:"⫅̸",nsube:"⊈",nsubset:"⊂⃒",nsubseteq:"⊈",nsubseteqq:"⫅̸",nsucc:"⊁",nsucceq:"⪰̸",nsup:"⊅",nsupE:"⫆̸",nsupe:"⊉",nsupset:"⊃⃒",nsupseteq:"⊉",nsupseteqq:"⫆̸",ntgl:"≹",Ntilde:"Ñ",ntilde:"ñ",ntlg:"≸",ntriangleleft:"⋪",ntrianglelefteq:"⋬",ntriangleright:"⋫",ntrianglerighteq:"⋭",Nu:"Ν",nu:"ν",num:"#",numero:"№",numsp:" ",nvap:"≍⃒",nvdash:"⊬",nvDash:"⊭",nVdash:"⊮",nVDash:"⊯",nvge:"≥⃒",nvgt:">⃒",nvHarr:"⤄",nvinfin:"⧞",nvlArr:"⤂",nvle:"≤⃒",nvlt:"<⃒",nvltrie:"⊴⃒",nvrArr:"⤃",nvrtrie:"⊵⃒",nvsim:"∼⃒",nwarhk:"⤣",nwarr:"↖",nwArr:"⇖",nwarrow:"↖",nwnear:"⤧",Oacute:"Ó",oacute:"ó",oast:"⊛",Ocirc:"Ô",ocirc:"ô",ocir:"⊚",Ocy:"О",ocy:"о",odash:"⊝",Odblac:"Ő",odblac:"ő",odiv:"⨸",odot:"⊙",odsold:"⦼",OElig:"Œ",oelig:"œ",ofcir:"⦿",Ofr:"𝔒",ofr:"𝔬",ogon:"˛",Ograve:"Ò",ograve:"ò",ogt:"⧁",ohbar:"⦵",ohm:"Ω",oint:"∮",olarr:"↺",olcir:"⦾",olcross:"⦻",oline:"‾",olt:"⧀",Omacr:"Ō",omacr:"ō",Omega:"Ω",omega:"ω",Omicron:"Ο",omicron:"ο",omid:"⦶",ominus:"⊖",Oopf:"𝕆",oopf:"𝕠",opar:"⦷",OpenCurlyDoubleQuote:"“",OpenCurlyQuote:"‘",operp:"⦹",oplus:"⊕",orarr:"↻",Or:"⩔",or:"∨",ord:"⩝",order:"ℴ",orderof:"ℴ",ordf:"ª",ordm:"º",origof:"⊶",oror:"⩖",orslope:"⩗",orv:"⩛",oS:"Ⓢ",Oscr:"𝒪",oscr:"ℴ",Oslash:"Ø",oslash:"ø",osol:"⊘",Otilde:"Õ",otilde:"õ",otimesas:"⨶",Otimes:"⨷",otimes:"⊗",Ouml:"Ö",ouml:"ö",ovbar:"⌽",OverBar:"‾",OverBrace:"⏞",OverBracket:"⎴",OverParenthesis:"⏜",para:"¶",parallel:"∥",par:"∥",parsim:"⫳",parsl:"⫽",part:"∂",PartialD:"∂",Pcy:"П",pcy:"п",percnt:"%",period:".",permil:"‰",perp:"⊥",pertenk:"‱",Pfr:"𝔓",pfr:"𝔭",Phi:"Φ",phi:"φ",phiv:"ϕ",phmmat:"ℳ",phone:"☎",Pi:"Π",pi:"π",pitchfork:"⋔",piv:"ϖ",planck:"ℏ",planckh:"ℎ",plankv:"ℏ",plusacir:"⨣",plusb:"⊞",pluscir:"⨢",plus:"+",plusdo:"∔",plusdu:"⨥",pluse:"⩲",PlusMinus:"±",plusmn:"±",plussim:"⨦",plustwo:"⨧",pm:"±",Poincareplane:"ℌ",pointint:"⨕",popf:"𝕡",Popf:"ℙ",pound:"£",prap:"⪷",Pr:"⪻",pr:"≺",prcue:"≼",precapprox:"⪷",prec:"≺",preccurlyeq:"≼",Precedes:"≺",PrecedesEqual:"⪯",PrecedesSlantEqual:"≼",PrecedesTilde:"≾",preceq:"⪯",precnapprox:"⪹",precneqq:"⪵",precnsim:"⋨",pre:"⪯",prE:"⪳",precsim:"≾",prime:"′",Prime:"″",primes:"ℙ",prnap:"⪹",prnE:"⪵",prnsim:"⋨",prod:"∏",Product:"∏",profalar:"⌮",profline:"⌒",profsurf:"⌓",prop:"∝",Proportional:"∝",Proportion:"∷",propto:"∝",prsim:"≾",prurel:"⊰",Pscr:"𝒫",pscr:"𝓅",Psi:"Ψ",psi:"ψ",puncsp:" ",Qfr:"𝔔",qfr:"𝔮",qint:"⨌",qopf:"𝕢",Qopf:"ℚ",qprime:"⁗",Qscr:"𝒬",qscr:"𝓆",quaternions:"ℍ",quatint:"⨖",quest:"?",questeq:"≟",quot:'"',QUOT:'"',rAarr:"⇛",race:"∽̱",Racute:"Ŕ",racute:"ŕ",radic:"√",raemptyv:"⦳",rang:"⟩",Rang:"⟫",rangd:"⦒",range:"⦥",rangle:"⟩",raquo:"»",rarrap:"⥵",rarrb:"⇥",rarrbfs:"⤠",rarrc:"⤳",rarr:"→",Rarr:"↠",rArr:"⇒",rarrfs:"⤞",rarrhk:"↪",rarrlp:"↬",rarrpl:"⥅",rarrsim:"⥴",Rarrtl:"⤖",rarrtl:"↣",rarrw:"↝",ratail:"⤚",rAtail:"⤜",ratio:"∶",rationals:"ℚ",rbarr:"⤍",rBarr:"⤏",RBarr:"⤐",rbbrk:"❳",rbrace:"}",rbrack:"]",rbrke:"⦌",rbrksld:"⦎",rbrkslu:"⦐",Rcaron:"Ř",rcaron:"ř",Rcedil:"Ŗ",rcedil:"ŗ",rceil:"⌉",rcub:"}",Rcy:"Р",rcy:"р",rdca:"⤷",rdldhar:"⥩",rdquo:"”",rdquor:"”",rdsh:"↳",real:"ℜ",realine:"ℛ",realpart:"ℜ",reals:"ℝ",Re:"ℜ",rect:"▭",reg:"®",REG:"®",ReverseElement:"∋",ReverseEquilibrium:"⇋",ReverseUpEquilibrium:"⥯",rfisht:"⥽",rfloor:"⌋",rfr:"𝔯",Rfr:"ℜ",rHar:"⥤",rhard:"⇁",rharu:"⇀",rharul:"⥬",Rho:"Ρ",rho:"ρ",rhov:"ϱ",RightAngleBracket:"⟩",RightArrowBar:"⇥",rightarrow:"→",RightArrow:"→",Rightarrow:"⇒",RightArrowLeftArrow:"⇄",rightarrowtail:"↣",RightCeiling:"⌉",RightDoubleBracket:"⟧",RightDownTeeVector:"⥝",RightDownVectorBar:"⥕",RightDownVector:"⇂",RightFloor:"⌋",rightharpoondown:"⇁",rightharpoonup:"⇀",rightleftarrows:"⇄",rightleftharpoons:"⇌",rightrightarrows:"⇉",rightsquigarrow:"↝",RightTeeArrow:"↦",RightTee:"⊢",RightTeeVector:"⥛",rightthreetimes:"⋌",RightTriangleBar:"⧐",RightTriangle:"⊳",RightTriangleEqual:"⊵",RightUpDownVector:"⥏",RightUpTeeVector:"⥜",RightUpVectorBar:"⥔",RightUpVector:"↾",RightVectorBar:"⥓",RightVector:"⇀",ring:"˚",risingdotseq:"≓",rlarr:"⇄",rlhar:"⇌",rlm:"‏",rmoustache:"⎱",rmoust:"⎱",rnmid:"⫮",roang:"⟭",roarr:"⇾",robrk:"⟧",ropar:"⦆",ropf:"𝕣",Ropf:"ℝ",roplus:"⨮",rotimes:"⨵",RoundImplies:"⥰",rpar:")",rpargt:"⦔",rppolint:"⨒",rrarr:"⇉",Rrightarrow:"⇛",rsaquo:"›",rscr:"𝓇",Rscr:"ℛ",rsh:"↱",Rsh:"↱",rsqb:"]",rsquo:"’",rsquor:"’",rthree:"⋌",rtimes:"⋊",rtri:"▹",rtrie:"⊵",rtrif:"▸",rtriltri:"⧎",RuleDelayed:"⧴",ruluhar:"⥨",rx:"℞",Sacute:"Ś",sacute:"ś",sbquo:"‚",scap:"⪸",Scaron:"Š",scaron:"š",Sc:"⪼",sc:"≻",sccue:"≽",sce:"⪰",scE:"⪴",Scedil:"Ş",scedil:"ş",Scirc:"Ŝ",scirc:"ŝ",scnap:"⪺",scnE:"⪶",scnsim:"⋩",scpolint:"⨓",scsim:"≿",Scy:"С",scy:"с",sdotb:"⊡",sdot:"⋅",sdote:"⩦",searhk:"⤥",searr:"↘",seArr:"⇘",searrow:"↘",sect:"§",semi:";",seswar:"⤩",setminus:"∖",setmn:"∖",sext:"✶",Sfr:"𝔖",sfr:"𝔰",sfrown:"⌢",sharp:"♯",SHCHcy:"Щ",shchcy:"щ",SHcy:"Ш",shcy:"ш",ShortDownArrow:"↓",ShortLeftArrow:"←",shortmid:"∣",shortparallel:"∥",ShortRightArrow:"→",ShortUpArrow:"↑",shy:"­",Sigma:"Σ",sigma:"σ",sigmaf:"ς",sigmav:"ς",sim:"∼",simdot:"⩪",sime:"≃",simeq:"≃",simg:"⪞",simgE:"⪠",siml:"⪝",simlE:"⪟",simne:"≆",simplus:"⨤",simrarr:"⥲",slarr:"←",SmallCircle:"∘",smallsetminus:"∖",smashp:"⨳",smeparsl:"⧤",smid:"∣",smile:"⌣",smt:"⪪",smte:"⪬",smtes:"⪬︀",SOFTcy:"Ь",softcy:"ь",solbar:"⌿",solb:"⧄",sol:"/",Sopf:"𝕊",sopf:"𝕤",spades:"♠",spadesuit:"♠",spar:"∥",sqcap:"⊓",sqcaps:"⊓︀",sqcup:"⊔",sqcups:"⊔︀",Sqrt:"√",sqsub:"⊏",sqsube:"⊑",sqsubset:"⊏",sqsubseteq:"⊑",sqsup:"⊐",sqsupe:"⊒",sqsupset:"⊐",sqsupseteq:"⊒",square:"□",Square:"□",SquareIntersection:"⊓",SquareSubset:"⊏",SquareSubsetEqual:"⊑",SquareSuperset:"⊐",SquareSupersetEqual:"⊒",SquareUnion:"⊔",squarf:"▪",squ:"□",squf:"▪",srarr:"→",Sscr:"𝒮",sscr:"𝓈",ssetmn:"∖",ssmile:"⌣",sstarf:"⋆",Star:"⋆",star:"☆",starf:"★",straightepsilon:"ϵ",straightphi:"ϕ",strns:"¯",sub:"⊂",Sub:"⋐",subdot:"⪽",subE:"⫅",sube:"⊆",subedot:"⫃",submult:"⫁",subnE:"⫋",subne:"⊊",subplus:"⪿",subrarr:"⥹",subset:"⊂",Subset:"⋐",subseteq:"⊆",subseteqq:"⫅",SubsetEqual:"⊆",subsetneq:"⊊",subsetneqq:"⫋",subsim:"⫇",subsub:"⫕",subsup:"⫓",succapprox:"⪸",succ:"≻",succcurlyeq:"≽",Succeeds:"≻",SucceedsEqual:"⪰",SucceedsSlantEqual:"≽",SucceedsTilde:"≿",succeq:"⪰",succnapprox:"⪺",succneqq:"⪶",succnsim:"⋩",succsim:"≿",SuchThat:"∋",sum:"∑",Sum:"∑",sung:"♪",sup1:"¹",sup2:"²",sup3:"³",sup:"⊃",Sup:"⋑",supdot:"⪾",supdsub:"⫘",supE:"⫆",supe:"⊇",supedot:"⫄",Superset:"⊃",SupersetEqual:"⊇",suphsol:"⟉",suphsub:"⫗",suplarr:"⥻",supmult:"⫂",supnE:"⫌",supne:"⊋",supplus:"⫀",supset:"⊃",Supset:"⋑",supseteq:"⊇",supseteqq:"⫆",supsetneq:"⊋",supsetneqq:"⫌",supsim:"⫈",supsub:"⫔",supsup:"⫖",swarhk:"⤦",swarr:"↙",swArr:"⇙",swarrow:"↙",swnwar:"⤪",szlig:"ß",Tab:"\t",target:"⌖",Tau:"Τ",tau:"τ",tbrk:"⎴",Tcaron:"Ť",tcaron:"ť",Tcedil:"Ţ",tcedil:"ţ",Tcy:"Т",tcy:"т",tdot:"⃛",telrec:"⌕",Tfr:"𝔗",tfr:"𝔱",there4:"∴",therefore:"∴",Therefore:"∴",Theta:"Θ",theta:"θ",thetasym:"ϑ",thetav:"ϑ",thickapprox:"≈",thicksim:"∼",ThickSpace:"  ",ThinSpace:" ",thinsp:" ",thkap:"≈",thksim:"∼",THORN:"Þ",thorn:"þ",tilde:"˜",Tilde:"∼",TildeEqual:"≃",TildeFullEqual:"≅",TildeTilde:"≈",timesbar:"⨱",timesb:"⊠",times:"×",timesd:"⨰",tint:"∭",toea:"⤨",topbot:"⌶",topcir:"⫱",top:"⊤",Topf:"𝕋",topf:"𝕥",topfork:"⫚",tosa:"⤩",tprime:"‴",trade:"™",TRADE:"™",triangle:"▵",triangledown:"▿",triangleleft:"◃",trianglelefteq:"⊴",triangleq:"≜",triangleright:"▹",trianglerighteq:"⊵",tridot:"◬",trie:"≜",triminus:"⨺",TripleDot:"⃛",triplus:"⨹",trisb:"⧍",tritime:"⨻",trpezium:"⏢",Tscr:"𝒯",tscr:"𝓉",TScy:"Ц",tscy:"ц",TSHcy:"Ћ",tshcy:"ћ",Tstrok:"Ŧ",tstrok:"ŧ",twixt:"≬",twoheadleftarrow:"↞",twoheadrightarrow:"↠",Uacute:"Ú",uacute:"ú",uarr:"↑",Uarr:"↟",uArr:"⇑",Uarrocir:"⥉",Ubrcy:"Ў",ubrcy:"ў",Ubreve:"Ŭ",ubreve:"ŭ",Ucirc:"Û",ucirc:"û",Ucy:"У",ucy:"у",udarr:"⇅",Udblac:"Ű",udblac:"ű",udhar:"⥮",ufisht:"⥾",Ufr:"𝔘",ufr:"𝔲",Ugrave:"Ù",ugrave:"ù",uHar:"⥣",uharl:"↿",uharr:"↾",uhblk:"▀",ulcorn:"⌜",ulcorner:"⌜",ulcrop:"⌏",ultri:"◸",Umacr:"Ū",umacr:"ū",uml:"¨",UnderBar:"_",UnderBrace:"⏟",UnderBracket:"⎵",UnderParenthesis:"⏝",Union:"⋃",UnionPlus:"⊎",Uogon:"Ų",uogon:"ų",Uopf:"𝕌",uopf:"𝕦",UpArrowBar:"⤒",uparrow:"↑",UpArrow:"↑",Uparrow:"⇑",UpArrowDownArrow:"⇅",updownarrow:"↕",UpDownArrow:"↕",Updownarrow:"⇕",UpEquilibrium:"⥮",upharpoonleft:"↿",upharpoonright:"↾",uplus:"⊎",UpperLeftArrow:"↖",UpperRightArrow:"↗",upsi:"υ",Upsi:"ϒ",upsih:"ϒ",Upsilon:"Υ",upsilon:"υ",UpTeeArrow:"↥",UpTee:"⊥",upuparrows:"⇈",urcorn:"⌝",urcorner:"⌝",urcrop:"⌎",Uring:"Ů",uring:"ů",urtri:"◹",Uscr:"𝒰",uscr:"𝓊",utdot:"⋰",Utilde:"Ũ",utilde:"ũ",utri:"▵",utrif:"▴",uuarr:"⇈",Uuml:"Ü",uuml:"ü",uwangle:"⦧",vangrt:"⦜",varepsilon:"ϵ",varkappa:"ϰ",varnothing:"∅",varphi:"ϕ",varpi:"ϖ",varpropto:"∝",varr:"↕",vArr:"⇕",varrho:"ϱ",varsigma:"ς",varsubsetneq:"⊊︀",varsubsetneqq:"⫋︀",varsupsetneq:"⊋︀",varsupsetneqq:"⫌︀",vartheta:"ϑ",vartriangleleft:"⊲",vartriangleright:"⊳",vBar:"⫨",Vbar:"⫫",vBarv:"⫩",Vcy:"В",vcy:"в",vdash:"⊢",vDash:"⊨",Vdash:"⊩",VDash:"⊫",Vdashl:"⫦",veebar:"⊻",vee:"∨",Vee:"⋁",veeeq:"≚",vellip:"⋮",verbar:"|",Verbar:"‖",vert:"|",Vert:"‖",VerticalBar:"∣",VerticalLine:"|",VerticalSeparator:"❘",VerticalTilde:"≀",VeryThinSpace:" ",Vfr:"𝔙",vfr:"𝔳",vltri:"⊲",vnsub:"⊂⃒",vnsup:"⊃⃒",Vopf:"𝕍",vopf:"𝕧",vprop:"∝",vrtri:"⊳",Vscr:"𝒱",vscr:"𝓋",vsubnE:"⫋︀",vsubne:"⊊︀",vsupnE:"⫌︀",vsupne:"⊋︀",Vvdash:"⊪",vzigzag:"⦚",Wcirc:"Ŵ",wcirc:"ŵ",wedbar:"⩟",wedge:"∧",Wedge:"⋀",wedgeq:"≙",weierp:"℘",Wfr:"𝔚",wfr:"𝔴",Wopf:"𝕎",wopf:"𝕨",wp:"℘",wr:"≀",wreath:"≀",Wscr:"𝒲",wscr:"𝓌",xcap:"⋂",xcirc:"◯",xcup:"⋃",xdtri:"▽",Xfr:"𝔛",xfr:"𝔵",xharr:"⟷",xhArr:"⟺",Xi:"Ξ",xi:"ξ",xlarr:"⟵",xlArr:"⟸",xmap:"⟼",xnis:"⋻",xodot:"⨀",Xopf:"𝕏",xopf:"𝕩",xoplus:"⨁",xotime:"⨂",xrarr:"⟶",xrArr:"⟹",Xscr:"𝒳",xscr:"𝓍",xsqcup:"⨆",xuplus:"⨄",xutri:"△",xvee:"⋁",xwedge:"⋀",Yacute:"Ý",yacute:"ý",YAcy:"Я",yacy:"я",Ycirc:"Ŷ",ycirc:"ŷ",Ycy:"Ы",ycy:"ы",yen:"¥",Yfr:"𝔜",yfr:"𝔶",YIcy:"Ї",yicy:"ї",Yopf:"𝕐",yopf:"𝕪",Yscr:"𝒴",yscr:"𝓎",YUcy:"Ю",yucy:"ю",yuml:"ÿ",Yuml:"Ÿ",Zacute:"Ź",zacute:"ź",Zcaron:"Ž",zcaron:"ž",Zcy:"З",zcy:"з",Zdot:"Ż",zdot:"ż",zeetrf:"ℨ",ZeroWidthSpace:"​",Zeta:"Ζ",zeta:"ζ",zfr:"𝔷",Zfr:"ℨ",ZHcy:"Ж",zhcy:"ж",zigrarr:"⇝",zopf:"𝕫",Zopf:"ℤ",Zscr:"𝒵",zscr:"𝓏",zwj:"‍",zwnj:"‌"},su=/[!-#%-\*,-\/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4E\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD803[\uDF55-\uDF59]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD806[\uDC3B\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2]|\uD807[\uDC41-\uDC45\uDC70\uDC71\uDEF7\uDEF8]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD81B[\uDE97-\uDE9A]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/,xr={},Sp={};function fs(e,t,n){var r,o,a,s,i,l="";for("string"!=typeof t&&(n=t,t=fs.defaultChars),typeof n>"u"&&(n=!0),i=function(e){var t,n,r=Sp[e];if(r)return r;for(r=Sp[e]=[],t=0;t<128;t++)n=String.fromCharCode(t),/^[0-9a-z]$/i.test(n)?r.push(n):r.push("%"+("0"+t.toString(16).toUpperCase()).slice(-2));for(t=0;t=55296&&a<=57343){if(a>=55296&&a<=56319&&r+1=56320&&s<=57343)){l+=encodeURIComponent(e[r]+e[r+1]),r++;continue}l+="%EF%BF%BD"}else l+=encodeURIComponent(e[r]);return l}fs.defaultChars=";/?:@&=+$,-_.!~*'()#",fs.componentChars="-_.!~*'()";var Ab=fs,wp={};function ms(e,t){var n;return"string"!=typeof t&&(t=ms.defaultChars),n=function(e){var t,n,r=wp[e];if(r)return r;for(r=wp[e]=[],t=0;t<128;t++)n=String.fromCharCode(t),r.push(n);for(t=0;t=55296&&c<=57343?"���":String.fromCharCode(c),o+=6):240==(248&s)&&o+91114111?d+="����":(c-=65536,d+=String.fromCharCode(55296+(c>>10),56320+(1023&c))),o+=9):d+="�";return d}))}ms.defaultChars=";/?:@&=+$,#",ms.componentChars="";var _b=ms;function gs(){this.protocol=null,this.slashes=null,this.auth=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.pathname=null}var Db=/^([a-z0-9.+-]+:)/i,yb=/:[0-9]*$/,Tb=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,Nb=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),Sb=["'"].concat(Nb),xp=["%","/","?",";","#"].concat(Sb),Rp=["/","?","#"],Lp=/^[+a-z0-9A-Z_-]{0,63}$/,xb=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,Op={javascript:!0,"javascript:":!0},kp={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0};gs.prototype.parse=function(e,t){var n,r,o,a,s,i=e;if(i=i.trim(),!t&&1===e.split("#").length){var l=Tb.exec(i);if(l)return this.pathname=l[1],l[2]&&(this.search=l[2]),this}var u=Db.exec(i);if(u&&(o=(u=u[0]).toLowerCase(),this.protocol=u,i=i.substr(u.length)),(t||u||i.match(/^\/\/[^@\/]+@[^@\/]+/))&&((s="//"===i.substr(0,2))&&!(u&&Op[u])&&(i=i.substr(2),this.slashes=!0)),!Op[u]&&(s||u&&!kp[u])){var d,p,c=-1;for(n=0;n127?h+="x":h+=C[g];if(!h.match(Lp)){var v=b.slice(0,n),N=b.slice(n+1),_=C.match(xb);_&&(v.push(_[1]),N.unshift(_[2])),N.length&&(i=N.join(".")+i),this.hostname=v.join(".");break}}}}this.hostname.length>255&&(this.hostname=""),A&&(this.hostname=this.hostname.substr(1,this.hostname.length-2))}var S=i.indexOf("#");-1!==S&&(this.hash=i.substr(S),i=i.slice(0,S));var R=i.indexOf("?");return-1!==R&&(this.search=i.substr(R),i=i.slice(0,R)),i&&(this.pathname=i),kp[o]&&this.hostname&&!this.pathname&&(this.pathname=""),this},gs.prototype.parseHost=function(e){var t=yb.exec(e);t&&(":"!==(t=t[0])&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)};var Lb=function(e,t){if(e&&e instanceof gs)return e;var n=new gs;return n.parse(e,t),n};xr.encode=Ab,xr.decode=_b,xr.format=function(t){var n="";return n+=t.protocol||"",n+=t.slashes?"//":"",n+=t.auth?t.auth+"@":"",t.hostname&&-1!==t.hostname.indexOf(":")?n+="["+t.hostname+"]":n+=t.hostname||"",n+=t.port?":"+t.port:"",n+=t.pathname||"",n+=t.search||"",n+=t.hash||""},xr.parse=Lb;var iu,Ip,lu,Fp,uu,Pp,cu,Up,Hp,Gn={};function Mp(){return Ip||(Ip=1,iu=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/),iu}function Bp(){return Fp||(Fp=1,lu=/[\0-\x1F\x7F-\x9F]/),lu}function qp(){return Up||(Up=1,cu=/[ \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/),cu}function kb(){return Hp||(Hp=1,Gn.Any=Mp(),Gn.Cc=Bp(),Gn.Cf=(Pp||(Pp=1,uu=/[\xAD\u0600-\u0605\u061C\u06DD\u070F\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804[\uDCBD\uDCCD]|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/),uu),Gn.P=su,Gn.Z=qp()),Gn}!function(e){var r=Object.prototype.hasOwnProperty;function o(O,G){return r.call(O,G)}function i(O){return!(O>=55296&&O<=57343||O>=64976&&O<=65007||65535==(65535&O)||65534==(65535&O)||O>=0&&O<=8||11===O||O>=14&&O<=31||O>=127&&O<=159||O>1114111)}function l(O){if(O>65535){var G=55296+((O-=65536)>>10),P=56320+(1023&O);return String.fromCharCode(G,P)}return String.fromCharCode(O)}var u=/\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g,d=new RegExp(u.source+"|"+/&([a-z#][a-z0-9]{1,31});/gi.source,"gi"),p=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))$/i,m=Np;var h=/[&<>"]/,g=/[&<>"]/g,E={"&":"&","<":"<",">":">",'"':"""};function v(O){return E[O]}var _=/[.?*+^$[\]\\(){}|-]/g;var I=su;e.lib={},e.lib.mdurl=xr,e.lib.ucmicro=kb(),e.assign=function(O){return Array.prototype.slice.call(arguments,1).forEach((function(P){if(P){if("object"!=typeof P)throw new TypeError(P+"must be object");Object.keys(P).forEach((function(x){O[x]=P[x]}))}})),O},e.isString=function(O){return"[object String]"===function(O){return Object.prototype.toString.call(O)}(O)},e.has=o,e.unescapeMd=function(O){return O.indexOf("\\")<0?O:O.replace(u,"$1")},e.unescapeAll=function(O){return O.indexOf("\\")<0&&O.indexOf("&")<0?O:O.replace(d,(function(G,P,x){return P||function(O,G){var P;return o(m,G)?m[G]:35===G.charCodeAt(0)&&p.test(G)&&i(P="x"===G[1].toLowerCase()?parseInt(G.slice(2),16):parseInt(G.slice(1),10))?l(P):O}(G,x)}))},e.isValidEntityCode=i,e.fromCodePoint=l,e.escapeHtml=function(O){return h.test(O)?O.replace(g,v):O},e.arrayReplaceAt=function(O,G,P){return[].concat(O.slice(0,G),P,O.slice(G+1))},e.isSpace=function(O){switch(O){case 9:case 32:return!0}return!1},e.isWhiteSpace=function(O){if(O>=8192&&O<=8202)return!0;switch(O){case 9:case 10:case 11:case 12:case 13:case 32:case 160:case 5760:case 8239:case 8287:case 12288:return!0}return!1},e.isMdAsciiPunct=function(O){switch(O){case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:case 58:case 59:case 60:case 61:case 62:case 63:case 64:case 91:case 92:case 93:case 94:case 95:case 96:case 123:case 124:case 125:case 126:return!0;default:return!1}},e.isPunctChar=function(O){return I.test(O)},e.escapeRE=function(O){return O.replace(_,"\\$&")},e.normalizeReference=function(O){return O=O.trim().replace(/\s+/g," "),"Ṿ"==="ẞ".toLowerCase()&&(O=O.replace(/ẞ/g,"ß")),O.toLowerCase().toUpperCase()}}(ae);var hs={},Vp=ae.unescapeAll,Fb=ae.unescapeAll;hs.parseLinkLabel=function(t,n,r){var o,a,s,i,l=-1,u=t.posMax,c=t.pos;for(t.pos=n+1,o=1;t.pos32)return i;if(41===o){if(0===a)break;a--}s++}return n===s||0!==a||(i.str=Vp(t.slice(n,s)),i.pos=s,i.ok=!0),i},hs.parseLinkTitle=function(t,n,r){var o,a,s=0,i=n,l={ok:!1,pos:0,lines:0,str:""};if(i>=r||34!==(a=t.charCodeAt(i))&&39!==a&&40!==a)return l;for(i++,40===a&&(a=41);i"+$n(a.content)+""},zt.code_block=function(e,t,n,r,o){var a=e[t];return""+$n(e[t].content)+"\n"},zt.fence=function(e,t,n,r,o){var u,c,d,p,m,a=e[t],s=a.info?Ub(a.info).trim():"",i="",l="";return s&&(i=(d=s.split(/(\s+)/g))[0],l=d.slice(2).join("")),0===(u=n.highlight&&n.highlight(a.content,i,l)||$n(a.content)).indexOf(""+u+"\n"):"
"+u+"
\n"},zt.image=function(e,t,n,r,o){var a=e[t];return a.attrs[a.attrIndex("alt")][1]=o.renderInlineAsText(a.children,n,r),o.renderToken(e,t,n)},zt.hardbreak=function(e,t,n){return n.xhtmlOut?"
\n":"
\n"},zt.softbreak=function(e,t,n){return n.breaks?n.xhtmlOut?"
\n":"
\n":"\n"},zt.text=function(e,t){return $n(e[t].content)},zt.html_block=function(e,t){return e[t].content},zt.html_inline=function(e,t){return e[t].content},Rr.prototype.renderAttrs=function(t){var n,r,o;if(!t.attrs)return"";for(o="",n=0,r=t.attrs.length;n\n":">")},Rr.prototype.renderInline=function(e,t,n){for(var r,o="",a=this.rules,s=0,i=e.length;s\s]/i.test(e)}function Wb(e){return/^<\/a\s*>/i.test(e)}var zp=/\+-|\.\.|\?\?\?\?|!!!!|,,|--/,Kb=/\((c|tm|r)\)/i,Xb=/\((c|tm|r)\)/gi,Qb={c:"©",r:"®",tm:"™"};function Jb(e,t){return Qb[t.toLowerCase()]}function e_(e){var t,n,r=0;for(t=e.length-1;t>=0;t--)"text"===(n=e[t]).type&&!r&&(n.content=n.content.replace(Xb,Jb)),"link_open"===n.type&&"auto"===n.info&&r--,"link_close"===n.type&&"auto"===n.info&&r++}function t_(e){var t,n,r=0;for(t=e.length-1;t>=0;t--)"text"===(n=e[t]).type&&!r&&zp.test(n.content)&&(n.content=n.content.replace(/\+-/g,"±").replace(/\.{2,}/g,"…").replace(/([?!])…/g,"$1..").replace(/([?!]){4,}/g,"$1$1$1").replace(/,{2,}/g,",").replace(/(^|[^-])---(?=[^-]|$)/gm,"$1—").replace(/(^|\s)--(?=\s|$)/gm,"$1–").replace(/(^|[^-\s])--(?=[^-\s]|$)/gm,"$1–")),"link_open"===n.type&&"auto"===n.info&&r--,"link_close"===n.type&&"auto"===n.info&&r++}var Gp=ae.isWhiteSpace,$p=ae.isPunctChar,Zp=ae.isMdAsciiPunct,r_=/['"]/,jp=/['"]/g;function Es(e,t,n){return e.slice(0,t)+n+e.slice(t+1)}function o_(e,t){var n,r,o,a,s,i,l,u,c,d,p,m,A,b,C,h,g,E,v,N,_;for(v=[],n=0;n=0&&!(v[g].level<=l);g--);if(v.length=g+1,"text"===r.type){s=0,i=(o=r.content).length;e:for(;s=0)c=o.charCodeAt(a.index-1);else for(g=n-1;g>=0&&"softbreak"!==e[g].type&&"hardbreak"!==e[g].type;g--)if(e[g].content){c=e[g].content.charCodeAt(e[g].content.length-1);break}if(d=32,s=48&&c<=57&&(h=C=!1),C&&h&&(C=p,h=m),C||h){if(h)for(g=v.length-1;g>=0&&(u=v[g],!(v[g].level=0&&(r=this.attrs[n][1]),r},Lr.prototype.attrJoin=function(t,n){var r=this.attrIndex(t);r<0?this.attrPush([t,n]):this.attrs[r][1]=this.attrs[r][1]+" "+n};var pu=Lr,i_=pu;function Yp(e,t,n){this.src=e,this.env=n,this.tokens=[],this.inlineMode=!1,this.md=t}Yp.prototype.Token=i_;var l_=Yp,u_=du,fu=[["normalize",function(t){var n;n=(n=t.src.replace(Hb,"\n")).replace(Vb,"�"),t.src=n}],["block",function(t){var n;t.inlineMode?((n=new t.Token("inline","",0)).content=t.src,n.map=[0,1],n.children=[],t.tokens.push(n)):t.md.block.parse(t.src,t.md,t.env,t.tokens)}],["inline",function(t){var r,o,a,n=t.tokens;for(o=0,a=n.length;o=0;n--)if("link_close"!==(i=a[n]).type){if("html_inline"===i.type&&(jb(i.content)&&A>0&&A--,Wb(i.content)&&A++),!(A>0)&&"text"===i.type&&t.md.linkify.test(i.content)){for(c=i.content,E=t.md.linkify.match(c),l=[],m=i.level,p=0,E.length>0&&0===E[0].index&&n>0&&"text_special"===a[n-1].type&&(E=E.slice(1)),u=0;up&&((s=new t.Token("text","",0)).content=c.slice(p,d),s.level=m,l.push(s)),(s=new t.Token("link_open","a",1)).attrs=[["href",C]],s.level=m++,s.markup="linkify",s.info="auto",l.push(s),(s=new t.Token("text","",0)).content=h,s.level=m,l.push(s),(s=new t.Token("link_close","a",-1)).level=--m,s.markup="linkify",s.info="auto",l.push(s),p=E[u].lastIndex);p=0;n--)"inline"===t.tokens[n].type&&(Kb.test(t.tokens[n].content)&&e_(t.tokens[n].children),zp.test(t.tokens[n].content)&&t_(t.tokens[n].children))}],["smartquotes",function(t){var n;if(t.md.options.typographer)for(n=t.tokens.length-1;n>=0;n--)"inline"!==t.tokens[n].type||!r_.test(t.tokens[n].content)||o_(t.tokens[n].children,t)}],["text_join",function(t){var n,r,o,a,s,i,l=t.tokens;for(n=0,r=l.length;n=a||((n=e.src.charCodeAt(o++))<48||n>57))return-1;for(;;){if(o>=a)return-1;if(!((n=e.src.charCodeAt(o++))>=48&&n<=57)){if(41===n||46===n)break;return-1}if(o-r>=10)return-1}return o`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*\\/?>",tf="<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>",k_=new RegExp("^(?:"+ef+"|"+tf+"|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|<[?][\\s\\S]*?[?]>|]*>|)"),I_=new RegExp("^(?:"+ef+"|"+tf+")");bs.HTML_TAG_RE=k_,bs.HTML_OPEN_CLOSE_TAG_RE=I_;var M_=["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","h1","h2","h3","h4","h5","h6","head","header","hr","html","iframe","legend","li","link","main","menu","menuitem","nav","noframes","ol","optgroup","option","p","param","section","source","summary","table","tbody","td","tfoot","th","thead","title","tr","track","ul"],F_=bs.HTML_OPEN_CLOSE_TAG_RE,Or=[[/^<(script|pre|style|textarea)(?=(\s|>|$))/i,/<\/(script|pre|style|textarea)>/i,!0],[/^/,!0],[/^<\?/,/\?>/,!0],[/^/,!0],[/^/,!0],[new RegExp("^|$))","i"),/^$/,!0],[new RegExp(F_.source+"\\s*$"),/^$/,!1]],nf=ae.isSpace,rf=pu,_s=ae.isSpace;function Gt(e,t,n,r){var o,a,s,i,l,u,c,d;for(this.src=e,this.md=t,this.env=n,this.tokens=r,this.bMarks=[],this.eMarks=[],this.tShift=[],this.sCount=[],this.bsCount=[],this.blkIndent=0,this.line=0,this.lineMax=0,this.tight=!1,this.ddIndent=-1,this.listIndent=-1,this.parentType="root",this.level=0,this.result="",d=!1,s=i=u=c=0,l=(a=this.src).length;i0&&this.level++,this.tokens.push(r),r},Gt.prototype.isEmpty=function(t){return this.bMarks[t]+this.tShift[t]>=this.eMarks[t]},Gt.prototype.skipEmptyLines=function(t){for(var n=this.lineMax;tn;)if(!_s(this.src.charCodeAt(--t)))return t+1;return t},Gt.prototype.skipChars=function(t,n){for(var r=this.src.length;tr;)if(n!==this.src.charCodeAt(--t))return t+1;return t},Gt.prototype.getLines=function(t,n,r,o){var a,s,i,l,u,c,d,p=t;if(t>=n)return"";for(c=new Array(n-t),a=0;pr?new Array(s-r+1).join(" ")+this.src.slice(l,u):this.src.slice(l,u)}return c.join("")},Gt.prototype.Token=rf;var H_=Gt,V_=du,vs=[["table",function(t,n,r,o){var a,s,i,l,u,c,d,p,m,A,b,C,h,g,E,v,N,_;if(n+2>r||(c=n+1,t.sCount[c]=4||(i=t.bMarks[c]+t.tShift[c])>=t.eMarks[c]||124!==(N=t.src.charCodeAt(i++))&&45!==N&&58!==N||i>=t.eMarks[c]||124!==(_=t.src.charCodeAt(i++))&&45!==_&&58!==_&&!gu(_)||45===N&&gu(_))return!1;for(;i=4||((d=Kp(s)).length&&""===d[0]&&d.shift(),d.length&&""===d[d.length-1]&&d.pop(),0===(p=d.length)||p!==A.length))return!1;if(o)return!0;for(g=t.parentType,t.parentType="table",v=t.md.block.ruler.getRules("blockquote"),(m=t.push("table_open","table",1)).map=C=[n,0],(m=t.push("thead_open","thead",1)).map=[n,n+1],(m=t.push("tr_open","tr",1)).map=[n,n+1],l=0;l=4)break;for((d=Kp(s)).length&&""===d[0]&&d.shift(),d.length&&""===d[d.length-1]&&d.pop(),c===n+2&&((m=t.push("tbody_open","tbody",1)).map=h=[n+2,0]),(m=t.push("tr_open","tr",1)).map=[c,c+1],l=0;l=4))break;a=++o}return t.line=a,(s=t.push("code_block","code",0)).content=t.getLines(n,a,4+t.blkIndent,!1)+"\n",s.map=[n,t.line],!0}],["fence",function(t,n,r,o){var a,s,i,l,u,c,d,p=!1,m=t.bMarks[n]+t.tShift[n],A=t.eMarks[n];if(t.sCount[n]-t.blkIndent>=4||m+3>A||126!==(a=t.src.charCodeAt(m))&&96!==a||(u=m,(s=(m=t.skipChars(m,a))-u)<3)||(d=t.src.slice(u,m),i=t.src.slice(m,A),96===a&&i.indexOf(String.fromCharCode(a))>=0))return!1;if(o)return!0;for(l=n;!(++l>=r||(m=u=t.bMarks[l]+t.tShift[l],A=t.eMarks[l],m=4||(m=t.skipChars(m,a),m-u=4||62!==t.src.charCodeAt(I))return!1;if(o)return!0;for(A=[],b=[],g=[],E=[],_=t.md.block.ruler.getRules("blockquote"),h=t.parentType,t.parentType="blockquote",p=n;p=(Q=t.eMarks[p])));p++)if(62!==t.src.charCodeAt(I++)||R){if(c)break;for(N=!1,i=0,u=_.length;i=Q,b.push(t.bsCount[p]),t.bsCount[p]=t.sCount[p]+1+(v?1:0),g.push(t.sCount[p]),t.sCount[p]=m-l,E.push(t.tShift[p]),t.tShift[p]=I-t.bMarks[p]}for(C=t.blkIndent,t.blkIndent=0,(S=t.push("blockquote_open","blockquote",1)).markup=">",S.map=d=[n,0],t.md.block.tokenize(t,n,p),(S=t.push("blockquote_close","blockquote",-1)).markup=">",t.lineMax=q,t.parentType=h,d[1]=t.line,i=0;i=4||42!==(a=t.src.charCodeAt(u++))&&45!==a&&95!==a)return!1;for(s=1;u=4||t.listIndent>=0&&t.sCount[P]-t.listIndent>=4&&t.sCount[P]=t.blkIndent&&(x=!0),(I=Jp(t,P))>=0){if(d=!0,ie=t.bMarks[P]+t.tShift[P],h=Number(t.src.slice(ie,I-1)),x&&1!==h)return!1}else{if(!((I=Qp(t,P))>=0))return!1;d=!1}if(x&&t.skipSpaces(I)>=t.eMarks[P])return!1;if(o)return!0;for(C=t.src.charCodeAt(I-1),b=t.tokens.length,d?(G=t.push("ordered_list_open","ol",1),1!==h&&(G.attrs=[["start",h]])):G=t.push("bullet_list_open","ul",1),G.map=A=[P,0],G.markup=String.fromCharCode(C),Q=!1,O=t.md.block.ruler.getRules("list"),N=t.parentType,t.parentType="list";P=g?1:E-c)>4&&(u=1),l=c+u,(G=t.push("list_item_open","li",1)).markup=String.fromCharCode(C),G.map=p=[P,0],d&&(G.info=t.src.slice(ie,I-1)),R=t.tight,S=t.tShift[P],_=t.sCount[P],v=t.listIndent,t.listIndent=t.blkIndent,t.blkIndent=l,t.tight=!0,t.tShift[P]=s-t.bMarks[P],t.sCount[P]=E,s>=g&&t.isEmpty(P+1)?t.line=Math.min(t.line+2,r):t.md.block.tokenize(t,P,r,!0),(!t.tight||Q)&&(T=!1),Q=t.line-P>1&&t.isEmpty(t.line-1),t.blkIndent=t.listIndent,t.listIndent=v,t.tShift[P]=S,t.sCount[P]=_,t.tight=R,(G=t.push("list_item_close","li",-1)).markup=String.fromCharCode(C),P=t.line,p[1]=P,P>=r||t.sCount[P]=4)break;for(Y=!1,i=0,m=O.length;i=4||91!==t.src.charCodeAt(_))return!1;for(;++_3||t.sCount[R]<0)){for(g=!1,c=0,d=E.length;c"u"&&(t.env.references={}),typeof t.env.references[p]>"u"&&(t.env.references[p]={title:v,href:u}),t.parentType=A,t.line=n+N+1),!0)}],["html_block",function(t,n,r,o){var a,s,i,l,u=t.bMarks[n]+t.tShift[n],c=t.eMarks[n];if(t.sCount[n]-t.blkIndent>=4||!t.md.options.html||60!==t.src.charCodeAt(u))return!1;for(l=t.src.slice(u,c),a=0;a=4||(35!==(a=t.src.charCodeAt(u))||u>=c))return!1;for(s=1,a=t.src.charCodeAt(++u);35===a&&u6||uu&&nf(t.src.charCodeAt(i-1))&&(c=i),t.line=n+1,(l=t.push("heading_open","h"+String(s),1)).markup="########".slice(0,s),l.map=[n,t.line],(l=t.push("inline","",0)).content=t.src.slice(u,c).trim(),l.map=[n,t.line],l.children=[],(l=t.push("heading_close","h"+String(s),-1)).markup="########".slice(0,s)),!0)},["paragraph","reference","blockquote"]],["lheading",function(t,n,r){var o,a,s,i,l,u,c,d,p,A,m=n+1,b=t.md.block.ruler.getRules("paragraph");if(t.sCount[n]-t.blkIndent>=4)return!1;for(A=t.parentType,t.parentType="paragraph";m3)){if(t.sCount[m]>=t.blkIndent&&((u=t.bMarks[m]+t.tShift[m])<(c=t.eMarks[m])&&((45===(p=t.src.charCodeAt(u))||61===p)&&(u=t.skipChars(u,p),(u=t.skipSpaces(u))>=c)))){d=61===p?1:2;break}if(!(t.sCount[m]<0)){for(a=!1,s=0,i=b.length;s3||t.sCount[c]<0)){for(a=!1,s=0,i=d.length;s=n||e.sCount[l]=c){e.line=n;break}for(a=e.line,o=0;o=e.line)throw new Error("block rule didn't increment state.line");break}if(!r)throw new Error("none of the block rules matched");e.tight=!u,e.isEmpty(e.line-1)&&(u=!0),(l=e.line)?@[]^_`{|}~-".split("").forEach((function(e){Eu[e.charCodeAt(0)]=1}));var ys={};function af(e,t){var n,r,o,a,s,i=[],l=t.length;for(n=0;n=0;n--)(95===(r=t[n]).marker||42===r.marker)&&-1!==r.end&&(o=t[r.end],i=n>0&&t[n-1].end===r.end+1&&t[n-1].marker===r.marker&&t[n-1].token===r.token-1&&t[r.end+1].token===o.token+1,s=String.fromCharCode(r.marker),(a=e.tokens[r.token]).type=i?"strong_open":"em_open",a.tag=i?"strong":"em",a.nesting=1,a.markup=i?s+s:s,a.content="",(a=e.tokens[o.token]).type=i?"strong_close":"em_close",a.tag=i?"strong":"em",a.nesting=-1,a.markup=i?s+s:s,a.content="",i&&(e.tokens[t[n-1].token].content="",e.tokens[t[r.end+1].token].content="",n--))}Ts.tokenize=function(t,n){var r,o,s=t.pos,i=t.src.charCodeAt(s);if(n||95!==i&&42!==i)return!1;for(o=t.scanDelims(t.pos,42===i),r=0;r\x00-\x20]*)$/,sv=bs.HTML_TAG_RE;var lf=Np,dv=ae.has,pv=ae.isValidEntityCode,uf=ae.fromCodePoint,fv=/^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i,mv=/^&([a-z][a-z0-9]{1,31});/i;function cf(e){var t,n,r,o,a,s,i,l,u={},c=e.length;if(c){var d=0,p=-2,m=[];for(t=0;ta;n-=m[n]+1)if((o=e[n]).marker===r.marker&&o.open&&o.end<0&&(i=!1,(o.close||r.open)&&(o.length+r.length)%3==0&&(o.length%3!=0||r.length%3!=0)&&(i=!0),!i)){l=n>0&&!e[n-1].open?m[n-1]+1:0,m[t]=t-n+l,m[n]=l,r.open=!1,o.end=t,o.close=!1,s=-1,p=-2;break}-1!==s&&(u[r.marker][(r.open?3:0)+(r.length||0)%3]=s)}}}var _u=pu,df=ae.isWhiteSpace,pf=ae.isPunctChar,ff=ae.isMdAsciiPunct;function Io(e,t,n,r){this.src=e,this.env=n,this.md=t,this.tokens=r,this.tokens_meta=Array(r.length),this.pos=0,this.posMax=this.src.length,this.level=0,this.pending="",this.pendingLevel=0,this.cache={},this.delimiters=[],this._prev_delimiters=[],this.backticks={},this.backticksScanned=!1,this.linkLevel=0}Io.prototype.pushPending=function(){var e=new _u("text","",0);return e.content=this.pending,e.level=this.pendingLevel,this.tokens.push(e),this.pending="",e},Io.prototype.push=function(e,t,n){this.pending&&this.pushPending();var r=new _u(e,t,n),o=null;return n<0&&(this.level--,this.delimiters=this._prev_delimiters.pop()),r.level=this.level,n>0&&(this.level++,this._prev_delimiters.push(this.delimiters),this.delimiters=[],o={delimiters:this.delimiters}),this.pendingLevel=this.level,this.tokens.push(r),this.tokens_meta.push(o),r},Io.prototype.scanDelims=function(e,t){var r,o,a,s,i,l,u,c,d,n=e,p=!0,m=!0,A=this.posMax,b=this.src.charCodeAt(e);for(r=e>0?this.src.charCodeAt(e-1):32;n0||(r=t.pos,o=t.posMax,r+3>o)||58!==t.src.charCodeAt(r)||47!==t.src.charCodeAt(r+1)||47!==t.src.charCodeAt(r+2)||(a=t.pending.match(Z_),!a)||(s=a[1],i=t.md.linkify.matchAtStart(t.src.slice(r-s.length)),!i)||(l=i.url,l.length<=s.length)||(l=l.replace(/\*+$/,""),u=t.md.normalizeLink(l),!t.md.validateLink(u))||(n||(t.pending=t.pending.slice(0,-s.length),(c=t.push("link_open","a",1)).attrs=[["href",u]],c.markup="linkify",c.info="auto",(c=t.push("text","",0)).content=t.md.normalizeLinkText(l),(c=t.push("link_close","a",-1)).markup="linkify",c.info="auto"),t.pos+=l.length-s.length,0))}],["newline",function(t,n){var r,o,a,s=t.pos;if(10!==t.src.charCodeAt(s))return!1;if(r=t.pending.length-1,o=t.posMax,!n)if(r>=0&&32===t.pending.charCodeAt(r))if(r>=1&&32===t.pending.charCodeAt(r-1)){for(a=r-1;a>=1&&32===t.pending.charCodeAt(a-1);)a--;t.pending=t.pending.slice(0,a),t.push("hardbreak","br",0)}else t.pending=t.pending.slice(0,-1),t.push("softbreak","br",0);else t.push("softbreak","br",0);for(s++;s=u)return!1;if(10===(r=t.src.charCodeAt(l))){for(n||t.push("hardbreak","br",0),l++;l=55296&&r<=56319&&l+1=56320&&o<=57343&&(s+=t.src[l+1],l++)),a="\\"+s,n||(i=t.push("text_special","",0),r<256&&0!==Eu[r]?i.content=s:i.content=a,i.markup=a,i.info="escape"),t.pos=l+1,!0}],["backticks",function(t,n){var r,o,a,s,i,l,u,c,d=t.pos;if(96!==t.src.charCodeAt(d))return!1;for(r=d,d++,o=t.posMax;d=b)return!1;if(C=l,(u=t.md.helpers.parseLinkDestination(t.src,l,t.posMax)).ok){for(p=t.md.normalizeLink(u.str),t.md.validateLink(p)?l=u.pos:p="",C=l;l=b||41!==t.src.charCodeAt(l))&&(h=!0),l++}if(h){if(typeof t.env.references>"u")return!1;if(l=0?a=t.src.slice(C,l++):l=s+1):l=s+1,a||(a=t.src.slice(i,s)),!(c=t.env.references[J_(a)]))return t.pos=A,!1;p=c.href,m=c.title}return n||(t.pos=i,t.posMax=s,t.push("link_open","a",1).attrs=r=[["href",p]],m&&r.push(["title",m]),t.linkLevel++,t.md.inline.tokenize(t),t.linkLevel--,t.push("link_close","a",-1)),t.pos=l,t.posMax=b,!0}],["image",function(t,n){var r,o,a,s,i,l,u,c,d,p,m,A,b,C="",h=t.pos,g=t.posMax;if(33!==t.src.charCodeAt(t.pos)||91!==t.src.charCodeAt(t.pos+1)||(l=t.pos+2,(i=t.md.helpers.parseLinkLabel(t,t.pos+1,!1))<0))return!1;if((u=i+1)=g)return!1;for(b=u,(d=t.md.helpers.parseLinkDestination(t.src,u,t.posMax)).ok&&(C=t.md.normalizeLink(d.str),t.md.validateLink(C)?u=d.pos:C=""),b=u;u=g||41!==t.src.charCodeAt(u))return t.pos=h,!1;u++}else{if(typeof t.env.references>"u")return!1;if(u=0?s=t.src.slice(b,u++):u=i+1):u=i+1,s||(s=t.src.slice(l,i)),!(c=t.env.references[tv(s)]))return t.pos=h,!1;C=c.href,p=c.title}return n||(a=t.src.slice(l,i),t.md.inline.parse(a,t.md,t.env,A=[]),(m=t.push("image","img",0)).attrs=r=[["src",C],["alt",""]],m.children=A,m.content=a,p&&r.push(["title",p])),t.pos=u,t.posMax=g,!0}],["autolink",function(t,n){var r,o,a,s,i,l,u=t.pos;if(60!==t.src.charCodeAt(u))return!1;for(i=t.pos,l=t.posMax;;){if(++u>=l||60===(s=t.src.charCodeAt(u)))return!1;if(62===s)break}return r=t.src.slice(i+1,u),ov.test(r)?(o=t.md.normalizeLink(r),!!t.md.validateLink(o)&&(n||((a=t.push("link_open","a",1)).attrs=[["href",o]],a.markup="autolink",a.info="auto",(a=t.push("text","",0)).content=t.md.normalizeLinkText(r),(a=t.push("link_close","a",-1)).markup="autolink",a.info="auto"),t.pos+=r.length+2,!0)):!!rv.test(r)&&(o=t.md.normalizeLink("mailto:"+r),!!t.md.validateLink(o)&&(n||((a=t.push("link_open","a",1)).attrs=[["href",o]],a.markup="autolink",a.info="auto",(a=t.push("text","",0)).content=t.md.normalizeLinkText(r),(a=t.push("link_close","a",-1)).markup="autolink",a.info="auto"),t.pos+=r.length+2,!0))}],["html_inline",function(t,n){var r,o,a,s,i=t.pos;return!(!t.md.options.html||(a=t.posMax,60!==t.src.charCodeAt(i)||i+2>=a)||(r=t.src.charCodeAt(i+1),33!==r&&63!==r&&47!==r&&!function(e){var t=32|e;return t>=97&&t<=122}(r))||(o=t.src.slice(i).match(sv),!o))&&(n||((s=t.push("html_inline","",0)).content=o[0],function(e){return/^\s]/i.test(e)}(s.content)&&t.linkLevel++,function(e){return/^<\/a\s*>/i.test(e)}(s.content)&&t.linkLevel--),t.pos+=o[0].length,!0)}],["entity",function(t,n){var o,a,s,i=t.pos,l=t.posMax;if(38!==t.src.charCodeAt(i)||i+1>=l)return!1;if(35===t.src.charCodeAt(i+1)){if(a=t.src.slice(i).match(fv))return n||(o="x"===a[1][0].toLowerCase()?parseInt(a[1].slice(1),16):parseInt(a[1],10),(s=t.push("text_special","",0)).content=pv(o)?uf(o):uf(65533),s.markup=a[0],s.info="entity"),t.pos+=a[0].length,!0}else if((a=t.src.slice(i).match(mv))&&dv(lf,a[1]))return n||((s=t.push("text_special","",0)).content=lf[a[1]],s.markup=a[0],s.info="entity"),t.pos+=a[0].length,!0;return!1}]],Du=[["balance_pairs",function(t){var n,r=t.tokens_meta,o=t.tokens_meta.length;for(cf(t.delimiters),n=0;n0&&o++,"text"===a[n].type&&n+1=e.pos)throw new Error("inline rule didn't increment state.pos");break}}else e.pos=e.posMax;t||e.pos++,i[r]=e.pos}},Mo.prototype.tokenize=function(e){for(var t,n,r,o=this.ruler.getRules(""),a=o.length,s=e.posMax,i=e.md.options.maxNesting;e.pos=e.pos)throw new Error("inline rule didn't increment state.pos");break}if(t){if(e.pos>=s)break}else e.pending+=e.src[e.pos++]}e.pending&&e.pushPending()},Mo.prototype.parse=function(e,t,n,r){var o,a,s,i=new this.State(e,t,n,r);for(this.tokenize(i),s=(a=this.ruler2.getRules("")).length,o=0;o=3&&":"===e[t-3]||t>=3&&"/"===e[t-3]?0:r.match(n.re.no_http)[0].length:0}},"mailto:":{validate:function(e,t,n){var r=e.slice(t);return n.re.mailto||(n.re.mailto=new RegExp("^"+n.re.src_email_name+"@"+n.re.src_host_strict,"i")),n.re.mailto.test(r)?r.match(n.re.mailto)[0].length:0}}},Sv="a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]",wv="biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф".split("|");function Ns(e){var t=e.re=(gf||(gf=1,yu=function(e){var t={};e=e||{},t.src_Any=Mp().source,t.src_Cc=Bp().source,t.src_Z=qp().source,t.src_P=su.source,t.src_ZPCc=[t.src_Z,t.src_P,t.src_Cc].join("|"),t.src_ZCc=[t.src_Z,t.src_Cc].join("|");var n="[><|]";return t.src_pseudo_letter="(?:(?![><|]|"+t.src_ZPCc+")"+t.src_Any+")",t.src_ip4="(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",t.src_auth="(?:(?:(?!"+t.src_ZCc+"|[@/\\[\\]()]).)+@)?",t.src_port="(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?",t.src_host_terminator="(?=$|[><|]|"+t.src_ZPCc+")(?!"+(e["---"]?"-(?!--)|":"-|")+"_|:\\d|\\.-|\\.(?!$|"+t.src_ZPCc+"))",t.src_path="(?:[/?#](?:(?!"+t.src_ZCc+"|"+n+"|[()[\\]{}.,\"'?!\\-;]).|\\[(?:(?!"+t.src_ZCc+"|\\]).)*\\]|\\((?:(?!"+t.src_ZCc+"|[)]).)*\\)|\\{(?:(?!"+t.src_ZCc+'|[}]).)*\\}|\\"(?:(?!'+t.src_ZCc+'|["]).)+\\"|\\\'(?:(?!'+t.src_ZCc+"|[']).)+\\'|\\'(?="+t.src_pseudo_letter+"|[-])|\\.{2,}[a-zA-Z0-9%/&]|\\.(?!"+t.src_ZCc+"|[.]|$)|"+(e["---"]?"\\-(?!--(?:[^-]|$))(?:-*)|":"\\-+|")+",(?!"+t.src_ZCc+"|$)|;(?!"+t.src_ZCc+"|$)|\\!+(?!"+t.src_ZCc+"|[!]|$)|\\?(?!"+t.src_ZCc+"|[?]|$))+|\\/)?",t.src_email_name='[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*',t.src_xn="xn--[a-z0-9\\-]{1,59}",t.src_domain_root="(?:"+t.src_xn+"|"+t.src_pseudo_letter+"{1,63})",t.src_domain="(?:"+t.src_xn+"|(?:"+t.src_pseudo_letter+")|(?:"+t.src_pseudo_letter+"(?:-|"+t.src_pseudo_letter+"){0,61}"+t.src_pseudo_letter+"))",t.src_host="(?:(?:(?:(?:"+t.src_domain+")\\.)*"+t.src_domain+"))",t.tpl_host_fuzzy="(?:"+t.src_ip4+"|(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%)))",t.tpl_host_no_ip_fuzzy="(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%))",t.src_host_strict=t.src_host+t.src_host_terminator,t.tpl_host_fuzzy_strict=t.tpl_host_fuzzy+t.src_host_terminator,t.src_host_port_strict=t.src_host+t.src_port+t.src_host_terminator,t.tpl_host_port_fuzzy_strict=t.tpl_host_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_port_no_ip_fuzzy_strict=t.tpl_host_no_ip_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_fuzzy_test="localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:"+t.src_ZPCc+"|>|$))",t.tpl_email_fuzzy='(^|[><|]|"|\\(|'+t.src_ZCc+")("+t.src_email_name+"@"+t.tpl_host_fuzzy_strict+")",t.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_fuzzy_strict+t.src_path+")",t.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_no_ip_fuzzy_strict+t.src_path+")",t}),yu)(e.__opts__),n=e.__tlds__.slice();function r(i){return i.replace("%TLDS%",t.src_tlds)}e.onCompile(),e.__tlds_replaced__||n.push(Sv),n.push(t.src_xn),t.src_tlds=n.join("|"),t.email_fuzzy=RegExp(r(t.tpl_email_fuzzy),"i"),t.link_fuzzy=RegExp(r(t.tpl_link_fuzzy),"i"),t.link_no_ip_fuzzy=RegExp(r(t.tpl_link_no_ip_fuzzy),"i"),t.host_fuzzy_test=RegExp(r(t.tpl_host_fuzzy_test),"i");var o=[];function a(i,l){throw new Error('(LinkifyIt) Invalid schema "'+i+'": '+l)}e.__compiled__={},Object.keys(e.__schemas__).forEach((function(i){var l=e.__schemas__[i];if(null!==l){var u={validate:null,link:null};if(e.__compiled__[i]=u,function(e){return"[object Object]"===Cs(e)}(l))return!function(e){return"[object RegExp]"===Cs(e)}(l.validate)?hf(l.validate)?u.validate=l.validate:a(i,l):u.validate=function(e){return function(t,n){var r=t.slice(n);return e.test(r)?r.match(e)[0].length:0}}(l.validate),void(hf(l.normalize)?u.normalize=l.normalize:l.normalize?a(i,l):u.normalize=function(e,t){t.normalize(e)});if(function(e){return"[object String]"===Cs(e)}(l))return void o.push(i);a(i,l)}})),o.forEach((function(i){e.__compiled__[e.__schemas__[i]]&&(e.__compiled__[i].validate=e.__compiled__[e.__schemas__[i]].validate,e.__compiled__[i].normalize=e.__compiled__[e.__schemas__[i]].normalize)})),e.__compiled__[""]={validate:null,normalize:function(e,t){t.normalize(e)}};var s=Object.keys(e.__compiled__).filter((function(i){return i.length>0&&e.__compiled__[i]})).map(Tv).join("|");e.re.schema_test=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+s+")","i"),e.re.schema_search=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+s+")","ig"),e.re.schema_at_start=RegExp("^"+e.re.schema_search.source,"i"),e.re.pretest=RegExp("("+e.re.schema_test.source+")|("+e.re.host_fuzzy_test.source+")|@","i"),function(e){e.__index__=-1,e.__text_cache__=""}(e)}function Lv(e,t){var n=e.__index__,r=e.__last_index__,o=e.__text_cache__.slice(n,r);this.schema=e.__schema__.toLowerCase(),this.index=n+t,this.lastIndex=r+t,this.raw=o,this.text=o,this.url=o}function Cu(e,t){var n=new Lv(e,t);return e.__compiled__[n.schema].normalize(n,e),n}function ft(e,t){if(!(this instanceof ft))return new ft(e,t);t||function(e){return Object.keys(e||{}).reduce((function(t,n){return t||Ef.hasOwnProperty(n)}),!1)}(e)&&(t=e,e={}),this.__opts__=Tu({},Ef,t),this.__index__=-1,this.__last_index__=-1,this.__schema__="",this.__text_cache__="",this.__schemas__=Tu({},Nv,e),this.__compiled__={},this.__tlds__=wv,this.__tlds_replaced__=!1,this.re={},Ns(this)}ft.prototype.add=function(t,n){return this.__schemas__[t]=n,Ns(this),this},ft.prototype.set=function(t){return this.__opts__=Tu(this.__opts__,t),this},ft.prototype.test=function(t){if(this.__text_cache__=t,this.__index__=-1,!t.length)return!1;var n,r,o,a,s,i,l,u;if(this.re.schema_test.test(t))for((l=this.re.schema_search).lastIndex=0;null!==(n=l.exec(t));)if(a=this.testSchemaAt(t,n[2],l.lastIndex)){this.__schema__=n[2],this.__index__=n.index+n[1].length,this.__last_index__=n.index+n[0].length+a;break}return this.__opts__.fuzzyLink&&this.__compiled__["http:"]&&((u=t.search(this.re.host_fuzzy_test))>=0&&(this.__index__<0||u=0&&null!==(o=t.match(this.re.email_fuzzy))&&(s=o.index+o[1].length,i=o.index+o[0].length,(this.__index__<0||sthis.__last_index__)&&(this.__schema__="mailto:",this.__index__=s,this.__last_index__=i))),this.__index__>=0},ft.prototype.pretest=function(t){return this.re.pretest.test(t)},ft.prototype.testSchemaAt=function(t,n,r){return this.__compiled__[n.toLowerCase()]?this.__compiled__[n.toLowerCase()].validate(t,r,this):0},ft.prototype.match=function(t){var n=0,r=[];this.__index__>=0&&this.__text_cache__===t&&(r.push(Cu(this,n)),n=this.__last_index__);for(var o=n?t.slice(n):t;this.test(o);)r.push(Cu(this,n)),o=o.slice(this.__last_index__),n+=this.__last_index__;return r.length?r:null},ft.prototype.matchAtStart=function(t){if(this.__text_cache__=t,this.__index__=-1,!t.length)return null;var n=this.re.schema_at_start.exec(t);if(!n)return null;var r=this.testSchemaAt(t,n[2],n[0].length);return r?(this.__schema__=n[2],this.__index__=n.index+n[1].length,this.__last_index__=n.index+n[0].length+r,Cu(this,0)):null},ft.prototype.tlds=function(t,n){return t=Array.isArray(t)?t:[t],n?(this.__tlds__=this.__tlds__.concat(t).sort().filter((function(r,o,a){return r!==a[o-1]})).reverse(),Ns(this),this):(this.__tlds__=t.slice(),this.__tlds_replaced__=!0,Ns(this),this)},ft.prototype.normalize=function(t){t.schema||(t.url="http://"+t.url),"mailto:"===t.schema&&!/^mailto:/i.test(t.url)&&(t.url="mailto:"+t.url)},ft.prototype.onCompile=function(){};var Ov=ft;const kr=2147483647,Mv=/^xn--/,Fv=/[^\0-\x7F]/,Bv=/[\x2E\u3002\uFF0E\uFF61]/g,Pv={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},Zt=Math.floor,wu=String.fromCharCode;function Sn(e){throw new RangeError(Pv[e])}function Df(e,t){const n=e.split("@");let r="";n.length>1&&(r=n[0]+"@",e=n[1]);const a=function(e,t){const n=[];let r=e.length;for(;r--;)n[r]=t(e[r]);return n}((e=e.replace(Bv,".")).split("."),t).join(".");return r+a}function xu(e){const t=[];let n=0;const r=e.length;for(;n=55296&&o<=56319&&nString.fromCodePoint(...e),qv=function(e){return e>=48&&e<58?e-48+26:e>=65&&e<91?e-65:e>=97&&e<123?e-97:36},Tf=function(e,t){return e+22+75*(e<26)-((0!=t)<<5)},Cf=function(e,t,n){let r=0;for(e=n?Zt(e/700):e>>1,e+=Zt(e/t);e>455;r+=36)e=Zt(e/35);return Zt(r+36*e/(e+38))},Ru=function(e){const t=[],n=e.length;let r=0,o=128,a=72,s=e.lastIndexOf("-");s<0&&(s=0);for(let i=0;i=128&&Sn("not-basic"),t.push(e.charCodeAt(i));for(let i=s>0?s+1:0;i=n&&Sn("invalid-input");const p=qv(e.charCodeAt(i++));p>=36&&Sn("invalid-input"),p>Zt((kr-r)/c)&&Sn("overflow"),r+=p*c;const m=d<=a?1:d>=a+26?26:d-a;if(pZt(kr/A)&&Sn("overflow"),c*=A}const u=t.length+1;a=Cf(r-l,u,0==l),Zt(r/u)>kr-o&&Sn("overflow"),o+=Zt(r/u),r%=u,t.splice(r++,0,o)}return String.fromCodePoint(...t)},Lu=function(e){const t=[],n=(e=xu(e)).length;let r=128,o=0,a=72;for(const l of e)l<128&&t.push(wu(l));const s=t.length;let i=s;for(s&&t.push("-");i=r&&cZt((kr-o)/u)&&Sn("overflow"),o+=(l-r)*u,r=l;for(const c of e)if(ckr&&Sn("overflow"),c===r){let d=o;for(let p=36;;p+=36){const m=p<=a?1:p>=a+26?26:p-a;if(d=0))try{t.hostname=wf.toASCII(t.hostname)}catch{}return Zn.encode(Zn.format(t))}function nD(e){var t=Zn.parse(e,!0);if(t.hostname&&(!t.protocol||xf.indexOf(t.protocol)>=0))try{t.hostname=wf.toUnicode(t.hostname)}catch{}return Zn.decode(Zn.format(t),Zn.decode.defaultChars+"%")}function yt(e,t){if(!(this instanceof yt))return new yt(e,t);t||Bo.isString(e)||(t=e||{},e="default"),this.inline=new Yv,this.block=new Wv,this.core=new jv,this.renderer=new Zv,this.linkify=new Kv,this.validateLink=eD,this.normalizeLink=tD,this.normalizeLinkText=nD,this.utils=Bo,this.helpers=Bo.assign({},$v),this.options={},this.configure(e),t&&this.set(t)}yt.prototype.set=function(e){return Bo.assign(this.options,e),this},yt.prototype.configure=function(e){var n,t=this;if(Bo.isString(e)&&!(e=Xv[n=e]))throw new Error('Wrong `markdown-it` preset "'+n+'", check name');if(!e)throw new Error("Wrong `markdown-it` preset, can't be empty");return e.options&&t.set(e.options),e.components&&Object.keys(e.components).forEach((function(r){e.components[r].rules&&t[r].ruler.enableOnly(e.components[r].rules),e.components[r].rules2&&t[r].ruler2.enableOnly(e.components[r].rules2)})),this},yt.prototype.enable=function(e,t){var n=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(o){n=n.concat(this[o].ruler.enable(e,!0))}),this),n=n.concat(this.inline.ruler2.enable(e,!0));var r=e.filter((function(o){return n.indexOf(o)<0}));if(r.length&&!t)throw new Error("MarkdownIt. Failed to enable unknown rule(s): "+r);return this},yt.prototype.disable=function(e,t){var n=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(o){n=n.concat(this[o].ruler.disable(e,!0))}),this),n=n.concat(this.inline.ruler2.disable(e,!0));var r=e.filter((function(o){return n.indexOf(o)<0}));if(r.length&&!t)throw new Error("MarkdownIt. Failed to disable unknown rule(s): "+r);return this},yt.prototype.use=function(e){var t=[this].concat(Array.prototype.slice.call(arguments,1));return e.apply(e,t),this},yt.prototype.parse=function(e,t){if("string"!=typeof e)throw new Error("Input data should be a String");var n=new this.core.State(e,this,t);return this.core.process(n),n.tokens},yt.prototype.render=function(e,t){return t=t||{},this.renderer.render(this.parse(e,t),this.options,t)},yt.prototype.parseInline=function(e,t){var n=new this.core.State(e,this,t);return n.inlineMode=!0,this.core.process(n),n.tokens},yt.prototype.renderInline=function(e,t){return t=t||{},this.renderer.render(this.parseInline(e,t),this.options,t)};const aD=jo(yt);function Rf(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((t=>{const n=e[t],r=typeof n;("object"===r||"function"===r)&&!Object.isFrozen(n)&&Rf(n)})),e}class Lf{constructor(t){void 0===t.data&&(t.data={}),this.data=t.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function Of(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function wn(e,...t){const n=Object.create(null);for(const r in e)n[r]=e[r];return t.forEach((function(r){for(const o in r)n[o]=r[o]})),n}const kf=e=>!!e.scope;class lD{constructor(t,n){this.buffer="",this.classPrefix=n.classPrefix,t.walk(this)}addText(t){this.buffer+=Of(t)}openNode(t){if(!kf(t))return;const n=((e,{prefix:t})=>{if(e.startsWith("language:"))return e.replace("language:","language-");if(e.includes(".")){const n=e.split(".");return[`${t}${n.shift()}`,...n.map(((r,o)=>`${r}${"_".repeat(o+1)}`))].join(" ")}return`${t}${e}`})(t.scope,{prefix:this.classPrefix});this.span(n)}closeNode(t){kf(t)&&(this.buffer+="")}value(){return this.buffer}span(t){this.buffer+=``}}const If=(e={})=>{const t={children:[]};return Object.assign(t,e),t};class Ou{constructor(){this.rootNode=If(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(t){this.top.children.push(t)}openNode(t){const n=If({scope:t});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(t){return this.constructor._walk(t,this.rootNode)}static _walk(t,n){return"string"==typeof n?t.addText(n):n.children&&(t.openNode(n),n.children.forEach((r=>this._walk(t,r))),t.closeNode(n)),t}static _collapse(t){"string"!=typeof t&&t.children&&(t.children.every((n=>"string"==typeof n))?t.children=[t.children.join("")]:t.children.forEach((n=>{Ou._collapse(n)})))}}class uD extends Ou{constructor(t){super(),this.options=t}addText(t){""!==t&&this.add(t)}startScope(t){this.openNode(t)}endScope(){this.closeNode()}__addSublanguage(t,n){const r=t.root;n&&(r.scope=`language:${n}`),this.add(r)}toHTML(){return new lD(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function Po(e){return e?"string"==typeof e?e:e.source:null}function Mf(e){return jn("(?=",e,")")}function cD(e){return jn("(?:",e,")*")}function dD(e){return jn("(?:",e,")?")}function jn(...e){return e.map((n=>Po(n))).join("")}function ku(...e){return"("+(function(e){const t=e[e.length-1];return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}(e).capture?"":"?:")+e.map((r=>Po(r))).join("|")+")"}function Ff(e){return new RegExp(e.toString()+"|").exec("").length-1}const mD=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function Iu(e,{joinWith:t}){let n=0;return e.map((r=>{n+=1;const o=n;let a=Po(r),s="";for(;a.length>0;){const i=mD.exec(a);if(!i){s+=a;break}s+=a.substring(0,i.index),a=a.substring(i.index+i[0].length),"\\"===i[0][0]&&i[1]?s+="\\"+String(Number(i[1])+o):(s+=i[0],"("===i[0]&&n++)}return s})).map((r=>`(${r})`)).join(t)}const Bf="[a-zA-Z]\\w*",Mu="[a-zA-Z_]\\w*",Pf="\\b\\d+(\\.\\d+)?",Uf="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",qf="\\b(0b[01]+)",Uo={begin:"\\\\[\\s\\S]",relevance:0},AD={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[Uo]},bD={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[Uo]},Ss=function(e,t,n={}){const r=wn({scope:"comment",begin:e,end:t,contains:[]},n);r.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 o=ku("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 r.contains.push({begin:jn(/[ ]+/,"(",o,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),r},vD=Ss("//","$"),DD=Ss("/\\*","\\*/"),yD=Ss("#","$"),TD={scope:"number",begin:Pf,relevance:0},CD={scope:"number",begin:Uf,relevance:0},ND={scope:"number",begin:qf,relevance:0},SD={scope:"regexp",begin:/\/(?=[^/\n]*\/)/,end:/\/[gimuy]*/,contains:[Uo,{begin:/\[/,end:/\]/,relevance:0,contains:[Uo]}]},wD={scope:"title",begin:Bf,relevance:0},xD={scope:"title",begin:Mu,relevance:0},RD={begin:"\\.\\s*"+Mu,relevance:0};var ws=Object.freeze({__proto__:null,APOS_STRING_MODE:AD,BACKSLASH_ESCAPE:Uo,BINARY_NUMBER_MODE:ND,BINARY_NUMBER_RE:qf,COMMENT:Ss,C_BLOCK_COMMENT_MODE:DD,C_LINE_COMMENT_MODE:vD,C_NUMBER_MODE:CD,C_NUMBER_RE:Uf,END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(t,n)=>{n.data._beginMatch=t[1]},"on:end":(t,n)=>{n.data._beginMatch!==t[1]&&n.ignoreMatch()}})},HASH_COMMENT_MODE:yD,IDENT_RE:Bf,MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:RD,NUMBER_MODE:TD,NUMBER_RE:Pf,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:bD,REGEXP_MODE:SD,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const t=/^#![ ]*\//;return e.binary&&(e.begin=jn(t,/.*\b/,e.binary,/\b.*/)),wn({scope:"meta",begin:t,end:/$/,relevance:0,"on:begin":(n,r)=>{0!==n.index&&r.ignoreMatch()}},e)},TITLE_MODE:wD,UNDERSCORE_IDENT_RE:Mu,UNDERSCORE_TITLE_MODE:xD});function LD(e,t){"."===e.input[e.index-1]&&t.ignoreMatch()}function OD(e,t){void 0!==e.className&&(e.scope=e.className,delete e.className)}function kD(e,t){t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=LD,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,void 0===e.relevance&&(e.relevance=0))}function ID(e,t){Array.isArray(e.illegal)&&(e.illegal=ku(...e.illegal))}function MD(e,t){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function FD(e,t){void 0===e.relevance&&(e.relevance=1)}const BD=(e,t)=>{if(!e.beforeMatch)return;if(e.starts)throw new Error("beforeMatch cannot be used with starts");const n=Object.assign({},e);Object.keys(e).forEach((r=>{delete e[r]})),e.keywords=n.keywords,e.begin=jn(n.beforeMatch,Mf(n.begin)),e.starts={relevance:0,contains:[Object.assign(n,{endsParent:!0})]},e.relevance=0,delete n.beforeMatch},PD=["of","and","for","in","not","or","if","then","parent","list","value"],UD="keyword";function Hf(e,t,n=UD){const r=Object.create(null);return"string"==typeof e?o(n,e.split(" ")):Array.isArray(e)?o(n,e):Object.keys(e).forEach((function(a){Object.assign(r,Hf(e[a],t,a))})),r;function o(a,s){t&&(s=s.map((i=>i.toLowerCase()))),s.forEach((function(i){const l=i.split("|");r[l[0]]=[a,qD(l[0],l[1])]}))}}function qD(e,t){return t?Number(t):function(e){return PD.includes(e.toLowerCase())}(e)?0:1}const Vf={},Wn=e=>{console.error(e)},zf=(e,...t)=>{console.log(`WARN: ${e}`,...t)},Ir=(e,t)=>{Vf[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),Vf[`${e}/${t}`]=!0)},xs=new Error;function Gf(e,t,{key:n}){let r=0;const o=e[n],a={},s={};for(let i=1;i<=t.length;i++)s[i+r]=o[i],a[i+r]=!0,r+=Ff(t[i-1]);e[n]=s,e[n]._emit=a,e[n]._multi=!0}function $D(e){(function(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}),function(e){if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw Wn("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),xs;if("object"!=typeof e.beginScope||null===e.beginScope)throw Wn("beginScope must be object"),xs;Gf(e,e.begin,{key:"beginScope"}),e.begin=Iu(e.begin,{joinWith:""})}}(e),function(e){if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw Wn("skip, excludeEnd, returnEnd not compatible with endScope: {}"),xs;if("object"!=typeof e.endScope||null===e.endScope)throw Wn("endScope must be object"),xs;Gf(e,e.end,{key:"endScope"}),e.end=Iu(e.end,{joinWith:""})}}(e)}function ZD(e){function t(s,i){return new RegExp(Po(s),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(i?"g":""))}class n{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(i,l){l.position=this.position++,this.matchIndexes[this.matchAt]=l,this.regexes.push([l,i]),this.matchAt+=Ff(i)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const i=this.regexes.map((l=>l[1]));this.matcherRe=t(Iu(i,{joinWith:"|"}),!0),this.lastIndex=0}exec(i){this.matcherRe.lastIndex=this.lastIndex;const l=this.matcherRe.exec(i);if(!l)return null;const u=l.findIndex(((d,p)=>p>0&&void 0!==d)),c=this.matchIndexes[u];return l.splice(0,u),Object.assign(l,c)}}class r{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(i){if(this.multiRegexes[i])return this.multiRegexes[i];const l=new n;return this.rules.slice(i).forEach((([u,c])=>l.addRule(u,c))),l.compile(),this.multiRegexes[i]=l,l}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(i,l){this.rules.push([i,l]),"begin"===l.type&&this.count++}exec(i){const l=this.getMatcher(this.regexIndex);l.lastIndex=this.lastIndex;let u=l.exec(i);if(this.resumingScanAtSamePosition()&&(!u||u.index!==this.lastIndex)){const c=this.getMatcher(0);c.lastIndex=this.lastIndex+1,u=c.exec(i)}return u&&(this.regexIndex+=u.position+1,this.regexIndex===this.count&&this.considerAll()),u}}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=wn(e.classNameAliases||{}),function a(s,i){const l=s;if(s.isCompiled)return l;[OD,MD,$D,BD].forEach((c=>c(s,i))),e.compilerExtensions.forEach((c=>c(s,i))),s.__beforeBegin=null,[kD,ID,FD].forEach((c=>c(s,i))),s.isCompiled=!0;let u=null;return"object"==typeof s.keywords&&s.keywords.$pattern&&(s.keywords=Object.assign({},s.keywords),u=s.keywords.$pattern,delete s.keywords.$pattern),u=u||/\w+/,s.keywords&&(s.keywords=Hf(s.keywords,e.case_insensitive)),l.keywordPatternRe=t(u,!0),i&&(s.begin||(s.begin=/\B|\b/),l.beginRe=t(l.begin),!s.end&&!s.endsWithParent&&(s.end=/\B|\b/),s.end&&(l.endRe=t(l.end)),l.terminatorEnd=Po(l.end)||"",s.endsWithParent&&i.terminatorEnd&&(l.terminatorEnd+=(s.end?"|":"")+i.terminatorEnd)),s.illegal&&(l.illegalRe=t(s.illegal)),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(c){return function(e){return e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((function(t){return wn(e,{variants:null},t)}))),e.cachedVariants?e.cachedVariants:$f(e)?wn(e,{starts:e.starts?wn(e.starts):null}):Object.isFrozen(e)?wn(e):e}("self"===c?s:c)}))),s.contains.forEach((function(c){a(c,l)})),s.starts&&a(s.starts,i),l.matcher=function(s){const i=new r;return s.contains.forEach((l=>i.addRule(l.begin,{rule:l,type:"begin"}))),s.terminatorEnd&&i.addRule(s.terminatorEnd,{type:"end"}),s.illegal&&i.addRule(s.illegal,{type:"illegal"}),i}(l),l}(e)}function $f(e){return!!e&&(e.endsWithParent||$f(e.starts))}class YD extends Error{constructor(t,n){super(t),this.name="HTMLInjectionError",this.html=n}}const Fu=Of,Zf=wn,jf=Symbol("nomatch"),Wf=function(e){const t=Object.create(null),n=Object.create(null),r=[];let o=!0;const a="Could not find the language '{}', did you forget to load/include a language module?",s={disableAutodetect:!0,name:"Plain text",contains:[]};let i={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:uD};function l(x){return i.noHighlightRe.test(x)}function c(x,T,y){let L="",k="";"object"==typeof T?(L=x,y=T.ignoreIllegals,k=T.language):(Ir("10.7.0","highlight(lang, code, ...args) has been deprecated."),Ir("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),k=x,L=T),void 0===y&&(y=!0);const H={code:L,language:k};G("before:highlight",H);const j=H.result?H.result:d(H.language,H.code,y);return j.code=H.code,G("after:highlight",j),j}function d(x,T,y,L){const k=Object.create(null);function H(B,V){return B.keywords[V]}function j(){if(!W.keywords)return void ye.addText(oe);let B=0;W.keywordPatternRe.lastIndex=0;let V=W.keywordPatternRe.exec(oe),K="";for(;V;){K+=oe.substring(B,V.index);const ne=Ce.case_insensitive?V[0].toLowerCase():V[0],ge=H(W,ne);if(ge){const[$e,zo]=ge;if(ye.addText(K),K="",k[ne]=(k[ne]||0)+1,k[ne]<=7&&(Bt+=zo),$e.startsWith("_"))K+=V[0];else{const Go=Ce.classNameAliases[$e]||$e;se(V[0],Go)}}else K+=V[0];B=W.keywordPatternRe.lastIndex,V=W.keywordPatternRe.exec(oe)}K+=oe.substring(B),ye.addText(K)}function ce(){null!=W.subLanguage?function(){if(""===oe)return;let B=null;if("string"==typeof W.subLanguage){if(!t[W.subLanguage])return void ye.addText(oe);B=d(W.subLanguage,oe,!0,Ur[W.subLanguage]),Ur[W.subLanguage]=B._top}else B=m(oe,W.subLanguage.length?W.subLanguage:null);W.relevance>0&&(Bt+=B.relevance),ye.__addSublanguage(B._emitter,B.language)}():j(),oe=""}function se(B,V){""!==B&&(ye.startScope(V),ye.addText(B),ye.endScope())}function le(B,V){let K=1;const ne=V.length-1;for(;K<=ne;){if(!B._emit[K]){K++;continue}const ge=Ce.classNameAliases[B[K]]||B[K],$e=V[K];ge?se($e,ge):(oe=$e,j(),oe=""),K++}}function gt(B,V){return B.scope&&"string"==typeof B.scope&&ye.openNode(Ce.classNameAliases[B.scope]||B.scope),B.beginScope&&(B.beginScope._wrap?(se(oe,Ce.classNameAliases[B.beginScope._wrap]||B.beginScope._wrap),oe=""):B.beginScope._multi&&(le(B.beginScope,V),oe="")),W=Object.create(B,{parent:{value:W}}),W}function Ft(B,V,K){let ne=function(e,t){const n=e&&e.exec(t);return n&&0===n.index}(B.endRe,K);if(ne){if(B["on:end"]){const ge=new Lf(B);B["on:end"](V,ge),ge.isMatchIgnored&&(ne=!1)}if(ne){for(;B.endsParent&&B.parent;)B=B.parent;return B}}if(B.endsWithParent)return Ft(B.parent,V,K)}function ht(B){return 0===W.matcher.regexIndex?(oe+=B[0],1):(Hr=!0,0)}function Ct(B){const V=B[0],K=T.substring(B.index),ne=Ft(W,B,K);if(!ne)return jf;const ge=W;W.endScope&&W.endScope._wrap?(ce(),se(V,W.endScope._wrap)):W.endScope&&W.endScope._multi?(ce(),le(W.endScope,B)):ge.skip?oe+=V:(ge.returnEnd||ge.excludeEnd||(oe+=V),ce(),ge.excludeEnd&&(oe=V));do{W.scope&&ye.closeNode(),!W.skip&&!W.subLanguage&&(Bt+=W.relevance),W=W.parent}while(W!==ne.parent);return ne.starts&>(ne.starts,B),ge.returnEnd?0:V.length}let Re={};function ke(B,V){const K=V&&V[0];if(oe+=B,null==K)return ce(),0;if("begin"===Re.type&&"end"===V.type&&Re.index===V.index&&""===K){if(oe+=T.slice(V.index,V.index+1),!o){const ne=new Error(`0 width match regex (${x})`);throw ne.languageName=x,ne.badRule=Re.rule,ne}return 1}if(Re=V,"begin"===V.type)return function(B){const V=B[0],K=B.rule,ne=new Lf(K),ge=[K.__beforeBegin,K["on:begin"]];for(const $e of ge)if($e&&($e(B,ne),ne.isMatchIgnored))return ht(V);return K.skip?oe+=V:(K.excludeBegin&&(oe+=V),ce(),!K.returnBegin&&!K.excludeBegin&&(oe=V)),gt(K,B),K.returnBegin?0:V.length}(V);if("illegal"===V.type&&!y){const ne=new Error('Illegal lexeme "'+K+'" for mode "'+(W.scope||"")+'"');throw ne.mode=W,ne}if("end"===V.type){const ne=Ct(V);if(ne!==jf)return ne}if("illegal"===V.type&&""===K)return 1;if(qr>1e5&&qr>3*V.index)throw new Error("potential infinite loop, way more iterations than matches");return oe+=K,K.length}const Ce=q(x);if(!Ce)throw Wn(a.replace("{}",x)),new Error('Unknown language: "'+x+'"');const Hs=ZD(Ce);let Pr="",W=L||Hs;const Ur={},ye=new i.__emitter(i);!function(){const B=[];for(let V=W;V!==Ce;V=V.parent)V.scope&&B.unshift(V.scope);B.forEach((V=>ye.openNode(V)))}();let oe="",Bt=0,jt=0,qr=0,Hr=!1;try{if(Ce.__emitTokens)Ce.__emitTokens(T,ye);else{for(W.matcher.considerAll();;){qr++,Hr?Hr=!1:W.matcher.considerAll(),W.matcher.lastIndex=jt;const B=W.matcher.exec(T);if(!B)break;const K=ke(T.substring(jt,B.index),B);jt=B.index+K}ke(T.substring(jt))}return ye.finalize(),Pr=ye.toHTML(),{language:x,value:Pr,relevance:Bt,illegal:!1,_emitter:ye,_top:W}}catch(B){if(B.message&&B.message.includes("Illegal"))return{language:x,value:Fu(T),illegal:!0,relevance:0,_illegalBy:{message:B.message,index:jt,context:T.slice(jt-100,jt+100),mode:B.mode,resultSoFar:Pr},_emitter:ye};if(o)return{language:x,value:Fu(T),illegal:!1,relevance:0,errorRaised:B,_emitter:ye,_top:W};throw B}}function m(x,T){T=T||i.languages||Object.keys(t);const y=function(x){const T={value:Fu(x),illegal:!1,relevance:0,_top:s,_emitter:new i.__emitter(i)};return T._emitter.addText(x),T}(x),L=T.filter(q).filter(Q).map((ce=>d(ce,x,!1)));L.unshift(y);const k=L.sort(((ce,se)=>{if(ce.relevance!==se.relevance)return se.relevance-ce.relevance;if(ce.language&&se.language){if(q(ce.language).supersetOf===se.language)return 1;if(q(se.language).supersetOf===ce.language)return-1}return 0})),[H,j]=k,me=H;return me.secondBest=j,me}function b(x){let T=null;const y=function(x){let T=x.className+" ";T+=x.parentNode?x.parentNode.className:"";const y=i.languageDetectRe.exec(T);if(y){const L=q(y[1]);return L||(zf(a.replace("{}",y[1])),zf("Falling back to no-highlight mode for this block.",x)),L?y[1]:"no-highlight"}return T.split(/\s+/).find((L=>l(L)||q(L)))}(x);if(l(y))return;if(G("before:highlightElement",{el:x,language:y}),x.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",x);if(x.children.length>0&&(i.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(x)),i.throwUnescapedHTML))throw new YD("One of your code blocks includes unescaped HTML.",x.innerHTML);T=x;const L=T.textContent,k=y?c(L,{language:y,ignoreIllegals:!0}):m(L);x.innerHTML=k.value,x.dataset.highlighted="yes",function(x,T,y){const L=T&&n[T]||y;x.classList.add("hljs"),x.classList.add(`language-${L}`)}(x,y,k.language),x.result={language:k.language,re:k.relevance,relevance:k.relevance},k.secondBest&&(x.secondBest={language:k.secondBest.language,relevance:k.secondBest.relevance}),G("after:highlightElement",{el:x,result:k,text:L})}let E=!1;function v(){"loading"!==document.readyState?document.querySelectorAll(i.cssSelector).forEach(b):E=!0}function q(x){return x=(x||"").toLowerCase(),t[x]||t[n[x]]}function I(x,{languageName:T}){"string"==typeof x&&(x=[x]),x.forEach((y=>{n[y.toLowerCase()]=T}))}function Q(x){const T=q(x);return T&&!T.disableAutodetect}function G(x,T){const y=x;r.forEach((function(L){L[y]&&L[y](T)}))}typeof window<"u"&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(function(){E&&v()}),!1),Object.assign(e,{highlight:c,highlightAuto:m,highlightAll:v,highlightElement:b,highlightBlock:function(x){return Ir("10.7.0","highlightBlock will be removed entirely in v12.0"),Ir("10.7.0","Please use highlightElement now."),b(x)},configure:function(x){i=Zf(i,x)},initHighlighting:()=>{v(),Ir("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},initHighlightingOnLoad:function(){v(),Ir("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")},registerLanguage:function(x,T){let y=null;try{y=T(e)}catch(L){if(Wn("Language definition for '{}' could not be registered.".replace("{}",x)),!o)throw L;Wn(L),y=s}y.name||(y.name=x),t[x]=y,y.rawDefinition=T.bind(null,e),y.aliases&&I(y.aliases,{languageName:x})},unregisterLanguage:function(x){delete t[x];for(const T of Object.keys(n))n[T]===x&&delete n[T]},listLanguages:function(){return Object.keys(t)},getLanguage:q,registerAliases:I,autoDetection:Q,inherit:Zf,addPlugin:function(x){(function(x){x["before:highlightBlock"]&&!x["before:highlightElement"]&&(x["before:highlightElement"]=T=>{x["before:highlightBlock"](Object.assign({block:T.el},T))}),x["after:highlightBlock"]&&!x["after:highlightElement"]&&(x["after:highlightElement"]=T=>{x["after:highlightBlock"](Object.assign({block:T.el},T))})})(x),r.push(x)},removePlugin:function(x){const T=r.indexOf(x);-1!==T&&r.splice(T,1)}}),e.debugMode=function(){o=!1},e.safeMode=function(){o=!0},e.versionString="11.9.0",e.regex={concat:jn,lookahead:Mf,either:ku,optional:dD,anyNumberOfTimes:cD};for(const x in ws)"object"==typeof ws[x]&&Rf(ws[x]);return Object.assign(e,ws),e},Mr=Wf({});Mr.newInstance=()=>Wf({});var XD=Mr;Mr.HighlightJS=Mr,Mr.default=Mr;const X=jo(XD);const oy=["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","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],ay=["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"],sy=["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"],iy=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],ly=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","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-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-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","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","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","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","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","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","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-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-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","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","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse();var Fr="[0-9](_*[0-9])*",Rs=`\\.(${Fr})`,Ls="[0-9a-fA-F](_*[0-9a-fA-F])*",Yf={className:"number",variants:[{begin:`(\\b(${Fr})((${Rs})|\\.)?|(${Rs}))[eE][+-]?(${Fr})[fFdD]?\\b`},{begin:`\\b(${Fr})((${Rs})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${Rs})[fFdD]?\\b`},{begin:`\\b(${Fr})[fFdD]\\b`},{begin:`\\b0[xX]((${Ls})\\.?|(${Ls})?\\.(${Ls}))[pP][+-]?(${Fr})[fFdD]?\\b`},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${Ls})[lL]?\\b`},{begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}],relevance:0};function Kf(e,t,n){return-1===n?"":e.replace(t,(r=>Kf(e,t,n-1)))}const Xf="[A-Za-z$_][0-9A-Za-z$_]*",gy=["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"],hy=["true","false","null","undefined","NaN","Infinity"],Qf=["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"],Jf=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],e1=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],Ey=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],Ay=[].concat(e1,Qf,Jf);var Br="[0-9](_*[0-9])*",Os=`\\.(${Br})`,ks="[0-9a-fA-F](_*[0-9a-fA-F])*",vy={className:"number",variants:[{begin:`(\\b(${Br})((${Os})|\\.)?|(${Os}))[eE][+-]?(${Br})[fFdD]?\\b`},{begin:`\\b(${Br})((${Os})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${Os})[fFdD]?\\b`},{begin:`\\b(${Br})[fFdD]\\b`},{begin:`\\b0[xX]((${ks})\\.?|(${ks})?\\.(${ks}))[pP][+-]?(${Br})[fFdD]?\\b`},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${ks})[lL]?\\b`},{begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}],relevance:0};const Ty=["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","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],Cy=["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"],t1=["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"],n1=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],Ny=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","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-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-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","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","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","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","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","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","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-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-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","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","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse(),Sy=t1.concat(n1);const $y=["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","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],Zy=["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"],jy=["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"],Wy=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],Yy=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","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-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-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","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","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","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","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","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","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-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-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","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","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse();function r1(e){return e?"string"==typeof e?e:e.source:null}function Is(e){return de("(?=",e,")")}function de(...e){return e.map((n=>r1(n))).join("")}function ot(...e){return"("+(function(e){const t=e[e.length-1];return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}(e).capture?"":"?:")+e.map((r=>r1(r))).join("|")+")"}const Bu=e=>de(/\b/,e,/\w$/.test(e)?/\b/:/\B/),e3=["Protocol","Type"].map(Bu),o1=["init","self"].map(Bu),t3=["Any","Self"],Pu=["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","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"],a1=["false","nil","true"],n3=["assignment","associativity","higherThan","left","lowerThan","none","right"],r3=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],s1=["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"],i1=ot(/[/=\-+!*%<>&|^~?]/,/[\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]/),l1=ot(i1,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),Uu=de(i1,l1,"*"),u1=ot(/[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]/),Ms=ot(u1,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),on=de(u1,Ms,"*"),qu=de(/[A-Z]/,Ms,"*"),o3=["attached","autoclosure",de(/convention\(/,ot("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",de(/objc\(/,on,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],a3=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"];const Fs="[A-Za-z$_][0-9A-Za-z$_]*",c1=["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"],d1=["true","false","null","undefined","NaN","Infinity"],p1=["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"],f1=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],m1=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],g1=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],h1=[].concat(m1,p1,f1);X.registerLanguage("apache",(function(e){const r={className:"number",begin:/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{1,5})?/};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:/<\/?/,end:/>/,contains:[r,{className:"number",begin:/:\d{1,5}/},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{_:["order","deny","allow","setenv","rewriterule","rewriteengine","rewritecond","documentroot","sethandler","errordocument","loadmodule","options","header","listen","serverroot","servername"]},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:/\s\[/,end:/\]$/},{className:"variable",begin:/[\$%]\{/,end:/\}/,contains:["self",{className:"number",begin:/[$%]\d+/}]},r,{className:"number",begin:/\b\d+/},e.QUOTE_STRING_MODE]}}],illegal:/\S/}})),X.registerLanguage("bash",(function(e){const t=e.regex,n={},r={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[n]}]};Object.assign(n,{className:"variable",variants:[{begin:t.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},r]});const o={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},a={begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,n,o]};o.contains.push(s);const c={begin:/\$?\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,n]},p=e.SHEBANG({binary:`(${["fish","bash","zsh","sh","csh","ksh","tcsh","dash","scsh"].join("|")})`,relevance:10}),m={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"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/,keyword:["if","then","else","elif","fi","for","while","until","in","do","done","case","esac","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","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:[p,e.SHEBANG(),m,c,e.HASH_COMMENT_MODE,a,{match:/(\/[a-z._-]+)+/},s,{match:/\\"/},{className:"string",begin:/'/,end:/'/},{match:/\\'/},n]}})),X.registerLanguage("c",(function(e){const t=e.regex,n=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),r="decltype\\(auto\\)",o="[a-zA-Z_]\\w*::",s="("+r+"|"+t.optional(o)+"[a-zA-Z_]\\w*"+t.optional("<[^<>]+>")+")",i={className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{match:/\batomic_[a-z]{3,6}\b/}]},u={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})"/})]},c={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},d={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(u,{className:"string"}),{className:"string",begin:/<.*?>/},n,e.C_BLOCK_COMMENT_MODE]},p={className:"title",begin:t.optional(o)+e.IDENT_RE,relevance:0},m=t.optional(o)+e.IDENT_RE+"\\s*\\(",C={keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","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","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","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"},h=[d,i,n,e.C_BLOCK_COMMENT_MODE,c,u],g={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:C,contains:h.concat([{begin:/\(/,end:/\)/,keywords:C,contains:h.concat(["self"]),relevance:0}]),relevance:0},E={begin:"("+s+"[\\*&\\s]+)+"+m,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:C,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:r,keywords:C,relevance:0},{begin:m,returnBegin:!0,contains:[e.inherit(p,{className:"title.function"})],relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:C,relevance:0,contains:[n,e.C_BLOCK_COMMENT_MODE,u,c,i,{begin:/\(/,end:/\)/,keywords:C,relevance:0,contains:["self",n,e.C_BLOCK_COMMENT_MODE,u,c,i]}]},i,n,e.C_BLOCK_COMMENT_MODE,d]};return{name:"C",aliases:["h"],keywords:C,disableAutodetect:!0,illegal:"=]/,contains:[{beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:d,strings:u,keywords:C}}})),X.registerLanguage("cpp",(function(e){const t=e.regex,n=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),r="decltype\\(auto\\)",o="[a-zA-Z_]\\w*::",s="(?!struct)("+r+"|"+t.optional(o)+"[a-zA-Z_]\\w*"+t.optional("<[^<>]+>")+")",i={className:"type",begin:"\\b[a-z\\d_]*_t\\b"},u={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})"/})]},c={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},d={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(u,{className:"string"}),{className:"string",begin:/<.*?>/},n,e.C_BLOCK_COMMENT_MODE]},p={className:"title",begin:t.optional(o)+e.IDENT_RE,relevance:0},m=t.optional(o)+e.IDENT_RE+"\\s*\\(",v={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","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"]},N={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:t.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,t.lookahead(/(<[^<>]+>|)\s*\(/))},_=[N,d,i,n,e.C_BLOCK_COMMENT_MODE,c,u],S={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:v,contains:_.concat([{begin:/\(/,end:/\)/,keywords:v,contains:_.concat(["self"]),relevance:0}]),relevance:0},R={className:"function",begin:"("+s+"[\\*&\\s]+)+"+m,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:v,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:r,keywords:v,relevance:0},{begin:m,returnBegin:!0,contains:[p],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[u,c]},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:v,relevance:0,contains:[n,e.C_BLOCK_COMMENT_MODE,u,c,i,{begin:/\(/,end:/\)/,keywords:v,relevance:0,contains:["self",n,e.C_BLOCK_COMMENT_MODE,u,c,i]}]},i,n,e.C_BLOCK_COMMENT_MODE,d]};return{name:"C++",aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:v,illegal:"",keywords:v,contains:["self",i]},{begin:e.IDENT_RE+"::",keywords:v},{match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],className:{1:"keyword",3:"title.class"}}])}})),X.registerLanguage("csharp",(function(e){const s={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","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","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"]},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),l={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},u={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},c=e.inherit(u,{illegal:/\n/}),d={className:"subst",begin:/\{/,end:/\}/,keywords:s},p=e.inherit(d,{illegal:/\n/}),m={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/},e.BACKSLASH_ESCAPE,p]},A={className:"string",begin:/\$@"/,end:'"',contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},d]},b=e.inherit(A,{illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},p]});d.contains=[A,m,u,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,l,e.C_BLOCK_COMMENT_MODE],p.contains=[b,m,c,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,l,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];const C={variants:[A,m,u,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},h={begin:"<",end:">",contains:[{beginKeywords:"in out"},i]},g=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",E={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:s,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"}},C,l,{beginKeywords:"class interface",relevance:0,end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,h,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,contains:[i,h,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:"("+g+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:s,contains:[{beginKeywords:["public","private","protected","static","internal","protected","abstract","async","extern","override","unsafe","virtual","new","sealed","partial"].join(" "),relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,contains:[e.TITLE_MODE,h],relevance:0},{match:/\(\)/},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,relevance:0,contains:[C,l,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},E]}})),X.registerLanguage("css",(function(e){const t=e.regex,n=(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_-]*/}}))(e),i=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"},contains:[n.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/},n.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},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{begin:":("+sy.join("|")+")"},{begin:":(:)?("+iy.join("|")+")"}]},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ly.join("|")+")\\b"},{begin:/:/,end:/[;}{]/,contains:[n.BLOCK_COMMENT,n.HEXCOLOR,n.IMPORTANT,n.CSS_NUMBER_MODE,...i,{begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri"},contains:[...i,{className:"string",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]},n.FUNCTION_DISPATCH]},{begin:t.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:ay.join(" ")},contains:[{begin:/[a-z-]+(?=:)/,className:"attribute"},...i,n.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"\\b("+oy.join("|")+")\\b"}]}})),X.registerLanguage("diff",(function(e){const t=e.regex;return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,match:t.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/)},{className:"comment",variants:[{begin:t.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/),end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/,end:/$/}]}})),X.registerLanguage("go",(function(e){const a={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:a,illegal:")?",/~~~/g,2),l={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"],literal:["false","true","null"],type:["char","boolean","long","float","int","byte","short","double"],built_in:["super","this"]},u={className:"meta",begin:"@"+n,contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},c={className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0};return{name:"Java",aliases:["jsp"],keywords:l,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+/,n],className:{1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{begin:[t.concat(/(?!else)/,n),/\s+/,n,/\s+/,/=(?!=)/],className:{1:"type",3:"variable",5:"operator"}},{begin:[/record/,/\s+/,n],className:{1:"keyword",3:"title.class"},contains:[c,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"new throw return else",relevance:0},{begin:["(?:"+r+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{2:"title.function"},keywords:l,contains:[{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[u,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,Yf,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},Yf,u]}})),X.registerLanguage("javascript",(function(e){const t=e.regex,r=Xf,o_begin="<>",o_end="",s={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(T,y)=>{const L=T[0].length+T.index,k=T.input[L];if("<"===k||","===k)return void y.ignoreMatch();let H;">"===k&&(((T,{after:y})=>{const L="",x={match:[/const|var|let/,/\s+/,r,/\s*/,/=\s*/,/(async\s*)?/,t.lookahead(P)],keywords:"async",className:{1:"keyword",3:"title.function"},contains:[_]};var T;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{PARAMS_CONTAINS:N,CLASS_REFERENCE:R},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,m,A,b,C,g,{match:/\$\d+/},d,R,{className:"attr",begin:r+t.lookahead(":"),relevance:0},x,{begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",relevance:0,contains:[g,e.REGEXP_MODE,{className:"function",begin:P,returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i,contains:N}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:o_begin,end:o_end},{match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:s.begin,"on:begin":s.isTrulyOpeningTag,end:s.end}],subLanguage:"xml",contains:[{begin:s.begin,end:s.end,skip:!0,contains:["self"]}]}]},I,{beginKeywords:"while if switch catch for"},{begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,label:"func.def",contains:[_,e.inherit(e.TITLE_MODE,{begin:r,className:"title.function"})]},{match:/\.\.\./,relevance:0},O,{match:"\\$"+r,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},contains:[_]},Y,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"},S,G,{match:/\$[(.]/}]}})),X.registerLanguage("json",(function(e){const r=["true","false","null"],o={scope:"literal",beginKeywords:r.join(" ")};return{name:"JSON",keywords:{literal:r},contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0},e.QUOTE_STRING_MODE,o,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],illegal:"\\S"}})),X.registerLanguage("kotlin",(function(e){const t={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"},r={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},o={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},a={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},s={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[a,o]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,a,o]}]};o.contains.push(s);const i={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(s,{className:"string"}),"self"]}]},u=vy,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},p=d;return p.variants[1].contains=[d],d.variants[1].contains=[p],{name:"Kotlin",aliases:["kt","kts"],keywords:t,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+/}]}},r,i,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:t,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:t,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,i,l,s,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},i,l]},s,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},u]}})),X.registerLanguage("less",(function(e){const t=(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_-]*/}}))(e),n=Sy,o="[\\w-]+",a="("+o+"|@\\{"+o+"\\})",s=[],i=[],l=function(E){return{className:"string",begin:"~?"+E+".*?"+E}},u=function(E,v,N){return{className:E,begin:v,relevance:N}},c={$pattern:/[a-z-]+/,keyword:"and or not only",attribute:Cy.join(" ")},d={begin:"\\(",end:"\\)",contains:i,keywords:c,relevance:0};i.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l("'"),l('"'),t.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},t.HEXCOLOR,d,u("variable","@@?"+o,10),u("variable","@\\{"+o+"\\}"),u("built_in","~?`[^`]*?`"),{className:"attribute",begin:o+"\\s*:",end:":",returnBegin:!0,excludeEnd:!0},t.IMPORTANT,{beginKeywords:"and not"},t.FUNCTION_DISPATCH);const p=i.concat({begin:/\{/,end:/\}/,contains:s}),m={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(i)},A={begin:a+"\\s*:",returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+Ny.join("|")+")\\b",end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:i}}]},b={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:i,relevance:0}},C={className:"variable",variants:[{begin:"@"+o+"\\s*:",relevance:15},{begin:"@"+o}],starts:{end:"[;}]",returnEnd:!0,contains:p}},h={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:a,end:/\{/}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,m,u("keyword","all\\b"),u("variable","@\\{"+o+"\\}"),{begin:"\\b("+Ty.join("|")+")\\b",className:"selector-tag"},t.CSS_NUMBER_MODE,u("selector-tag",a,0),u("selector-id","#"+a),u("selector-class","\\."+a,0),u("selector-tag","&",0),t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",begin:":("+t1.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+n1.join("|")+")"},{begin:/\(/,end:/\)/,relevance:0,contains:p},{begin:"!important"},t.FUNCTION_DISPATCH]},g={begin:o+`:(:)?(${n.join("|")})`,returnBegin:!0,contains:[h]};return s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,b,C,g,A,h,m,t.FUNCTION_DISPATCH),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:s}})),X.registerLanguage("lua",(function(e){const t="\\[=*\\[",n="\\]=*\\]",r={begin:t,end:n,contains:["self"]},o=[e.COMMENT("--(?!"+t+")","$"),e.COMMENT("--"+t,n,{contains:[r],relevance:10})];return{name:"Lua",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:o.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:o}].concat(o)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:t,end:n,contains:[r],relevance:5}])}})),X.registerLanguage("makefile",(function(e){const t={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%",subLanguage:"xml",relevance:0},l={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}]},u={className:"strong",contains:[],variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}]},c={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{begin:/_(?![_\s])/,end:/_/,relevance:0}]},d=e.inherit(u,{contains:[]}),p=e.inherit(c,{contains:[]});u.contains.push(p),c.contains.push(d);let m=[n,l];return[u,c,d,p].forEach((C=>{C.contains=C.contains.concat(m)})),m=m.concat(u,c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:m},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:m}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},u,c,{className:"quote",begin:"^>\\s+",contains:m,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:"$"},l,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}})),X.registerLanguage("nginx",(function(e){const t=e.regex,n={className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{\w+\}/},{begin:t.concat(/[$@]/,e.UNDERSCORE_IDENT_RE)}]},o={endsWithParent:!0,keywords:{$pattern:/[a-z_]{2,}|\/dev\/poll/,literal:["on","off","yes","no","true","false","none","blocked","debug","info","notice","warn","error","crit","select","break","last","permanent","redirect","kqueue","rtsig","epoll","poll","/dev/poll"]},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^",end:"\\s|\\{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|\\{|;",returnEnd:!0},{begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number",begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{className:"number",begin:"\\b\\d+[kKmMgGdshdwy]?\\b",relevance:0},n]};return{name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{beginKeywords:"upstream location",end:/;|\{/,contains:o.contains,keywords:{section:"upstream location"}},{className:"section",begin:t.concat(e.UNDERSCORE_IDENT_RE+t.lookahead(/\s+\{/)),relevance:0},{begin:t.lookahead(e.UNDERSCORE_IDENT_RE+"\\s"),end:";|\\{",contains:[{className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:o}],relevance:0}],illegal:"[^\\s\\}\\{]"}})),X.registerLanguage("objectivec",(function(e){const n=/[a-zA-Z@][a-zA-Z0-9_]*/,l={$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:"("+l.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:l,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}})),X.registerLanguage("perl",(function(e){const t=e.regex,r=/[dualxmsipngr]{0,12}/,o={$pattern:/[\w.]+/,keyword:["abs","accept","alarm","and","atan2","bind","binmode","bless","break","caller","chdir","chmod","chomp","chop","chown","chr","chroot","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","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","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"].join(" ")},a={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:o},s={begin:/->\{/,end:/\}/},i={variants:[{begin:/\$\d/},{begin:t.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])")},{begin:/[$%@][^\s\w{]/,relevance:0}]},l=[e.BACKSLASH_ESCAPE,a,i],u=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],c=(m,A,b="\\1")=>{const C="\\1"===b?b:t.concat(b,A);return t.concat(t.concat("(?:",m,")"),A,/(?:\\.|[^\\\/])*?/,C,/(?:\\.|[^\\\/])*?/,b,r)},d=(m,A,b)=>t.concat(t.concat("(?:",m,")"),A,/(?:\\.|[^\\\/])*?/,b,r),p=[i,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{endsWithParent:!0}),s,{className:"string",contains:l,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}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{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:c("s|tr|y",t.either(...u,{capture:!0}))},{begin:c("s|tr|y","\\(","\\)")},{begin:c("s|tr|y","\\[","\\]")},{begin:c("s|tr|y","\\{","\\}")}],relevance:2},{className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",t.either(...u,{capture:!0}),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return a.contains=p,s.contains=p,{name:"Perl",aliases:["pl","pm"],keywords:o,contains:p}})),X.registerLanguage("pgsql",(function(e){const t=e.COMMENT("--","$"),r="\\$([a-zA-Z_]?|[a-zA-Z_][a-zA-Z_0-9]*)\\$",l="BIGINT INT8 BIGSERIAL SERIAL8 BIT VARYING VARBIT BOOLEAN BOOL BOX BYTEA CHARACTER CHAR VARCHAR CIDR CIRCLE DATE DOUBLE PRECISION FLOAT8 FLOAT INET INTEGER INT INT4 INTERVAL JSON JSONB LINE LSEG|10 MACADDR MACADDR8 MONEY NUMERIC DEC DECIMAL PATH POINT POLYGON REAL FLOAT4 SMALLINT INT2 SMALLSERIAL|10 SERIAL2|10 SERIAL|10 SERIAL4|10 TEXT TIME ZONE TIMETZ|10 TIMESTAMP TIMESTAMPTZ|10 TSQUERY|10 TSVECTOR|10 TXID_SNAPSHOT|10 UUID XML NATIONAL NCHAR INT4RANGE|10 INT8RANGE|10 NUMRANGE|10 TSRANGE|10 TSTZRANGE|10 DATERANGE|10 ANYELEMENT ANYARRAY ANYNONARRAY ANYENUM ANYRANGE CSTRING INTERNAL RECORD PG_DDL_COMMAND VOID UNKNOWN OPAQUE REFCURSOR NAME OID REGPROC|10 REGPROCEDURE|10 REGOPER|10 REGOPERATOR|10 REGCLASS|10 REGTYPE|10 REGROLE|10 REGNAMESPACE|10 REGCONFIG|10 REGDICTIONARY|10 ",u=l.trim().split(" ").map((function(b){return b.split("|")[0]})).join("|"),A="ARRAY_AGG AVG BIT_AND BIT_OR BOOL_AND BOOL_OR COUNT EVERY JSON_AGG JSONB_AGG JSON_OBJECT_AGG JSONB_OBJECT_AGG MAX MIN MODE STRING_AGG SUM XMLAGG CORR COVAR_POP COVAR_SAMP REGR_AVGX REGR_AVGY REGR_COUNT REGR_INTERCEPT REGR_R2 REGR_SLOPE REGR_SXX REGR_SXY REGR_SYY STDDEV STDDEV_POP STDDEV_SAMP VARIANCE VAR_POP VAR_SAMP PERCENTILE_CONT PERCENTILE_DISC ROW_NUMBER RANK DENSE_RANK PERCENT_RANK CUME_DIST NTILE LAG LEAD FIRST_VALUE LAST_VALUE NTH_VALUE NUM_NONNULLS NUM_NULLS ABS CBRT CEIL CEILING DEGREES DIV EXP FLOOR LN LOG MOD PI POWER RADIANS ROUND SCALE SIGN SQRT TRUNC WIDTH_BUCKET RANDOM SETSEED ACOS ACOSD ASIN ASIND ATAN ATAND ATAN2 ATAN2D COS COSD COT COTD SIN SIND TAN TAND BIT_LENGTH CHAR_LENGTH CHARACTER_LENGTH LOWER OCTET_LENGTH OVERLAY POSITION SUBSTRING TREAT TRIM UPPER ASCII BTRIM CHR CONCAT CONCAT_WS CONVERT CONVERT_FROM CONVERT_TO DECODE ENCODE INITCAP LEFT LENGTH LPAD LTRIM MD5 PARSE_IDENT PG_CLIENT_ENCODING QUOTE_IDENT|10 QUOTE_LITERAL|10 QUOTE_NULLABLE|10 REGEXP_MATCH REGEXP_MATCHES REGEXP_REPLACE REGEXP_SPLIT_TO_ARRAY REGEXP_SPLIT_TO_TABLE REPEAT REPLACE REVERSE RIGHT RPAD RTRIM SPLIT_PART STRPOS SUBSTR TO_ASCII TO_HEX TRANSLATE OCTET_LENGTH GET_BIT GET_BYTE SET_BIT SET_BYTE TO_CHAR TO_DATE TO_NUMBER TO_TIMESTAMP AGE CLOCK_TIMESTAMP|10 DATE_PART DATE_TRUNC ISFINITE JUSTIFY_DAYS JUSTIFY_HOURS JUSTIFY_INTERVAL MAKE_DATE MAKE_INTERVAL|10 MAKE_TIME MAKE_TIMESTAMP|10 MAKE_TIMESTAMPTZ|10 NOW STATEMENT_TIMESTAMP|10 TIMEOFDAY TRANSACTION_TIMESTAMP|10 ENUM_FIRST ENUM_LAST ENUM_RANGE AREA CENTER DIAMETER HEIGHT ISCLOSED ISOPEN NPOINTS PCLOSE POPEN RADIUS WIDTH BOX BOUND_BOX CIRCLE LINE LSEG PATH POLYGON ABBREV BROADCAST HOST HOSTMASK MASKLEN NETMASK NETWORK SET_MASKLEN TEXT INET_SAME_FAMILY INET_MERGE MACADDR8_SET7BIT ARRAY_TO_TSVECTOR GET_CURRENT_TS_CONFIG NUMNODE PLAINTO_TSQUERY PHRASETO_TSQUERY WEBSEARCH_TO_TSQUERY QUERYTREE SETWEIGHT STRIP TO_TSQUERY TO_TSVECTOR JSON_TO_TSVECTOR JSONB_TO_TSVECTOR TS_DELETE TS_FILTER TS_HEADLINE TS_RANK TS_RANK_CD TS_REWRITE TSQUERY_PHRASE TSVECTOR_TO_ARRAY TSVECTOR_UPDATE_TRIGGER TSVECTOR_UPDATE_TRIGGER_COLUMN XMLCOMMENT XMLCONCAT XMLELEMENT XMLFOREST XMLPI XMLROOT XMLEXISTS XML_IS_WELL_FORMED XML_IS_WELL_FORMED_DOCUMENT XML_IS_WELL_FORMED_CONTENT XPATH XPATH_EXISTS XMLTABLE XMLNAMESPACES TABLE_TO_XML TABLE_TO_XMLSCHEMA TABLE_TO_XML_AND_XMLSCHEMA QUERY_TO_XML QUERY_TO_XMLSCHEMA QUERY_TO_XML_AND_XMLSCHEMA CURSOR_TO_XML CURSOR_TO_XMLSCHEMA SCHEMA_TO_XML SCHEMA_TO_XMLSCHEMA SCHEMA_TO_XML_AND_XMLSCHEMA DATABASE_TO_XML DATABASE_TO_XMLSCHEMA DATABASE_TO_XML_AND_XMLSCHEMA XMLATTRIBUTES TO_JSON TO_JSONB ARRAY_TO_JSON ROW_TO_JSON JSON_BUILD_ARRAY JSONB_BUILD_ARRAY JSON_BUILD_OBJECT JSONB_BUILD_OBJECT JSON_OBJECT JSONB_OBJECT JSON_ARRAY_LENGTH JSONB_ARRAY_LENGTH JSON_EACH JSONB_EACH JSON_EACH_TEXT JSONB_EACH_TEXT JSON_EXTRACT_PATH JSONB_EXTRACT_PATH JSON_OBJECT_KEYS JSONB_OBJECT_KEYS JSON_POPULATE_RECORD JSONB_POPULATE_RECORD JSON_POPULATE_RECORDSET JSONB_POPULATE_RECORDSET JSON_ARRAY_ELEMENTS JSONB_ARRAY_ELEMENTS JSON_ARRAY_ELEMENTS_TEXT JSONB_ARRAY_ELEMENTS_TEXT JSON_TYPEOF JSONB_TYPEOF JSON_TO_RECORD JSONB_TO_RECORD JSON_TO_RECORDSET JSONB_TO_RECORDSET JSON_STRIP_NULLS JSONB_STRIP_NULLS JSONB_SET JSONB_INSERT JSONB_PRETTY CURRVAL LASTVAL NEXTVAL SETVAL COALESCE NULLIF GREATEST LEAST ARRAY_APPEND ARRAY_CAT ARRAY_NDIMS ARRAY_DIMS ARRAY_FILL ARRAY_LENGTH ARRAY_LOWER ARRAY_POSITION ARRAY_POSITIONS ARRAY_PREPEND ARRAY_REMOVE ARRAY_REPLACE ARRAY_TO_STRING ARRAY_UPPER CARDINALITY STRING_TO_ARRAY UNNEST ISEMPTY LOWER_INC UPPER_INC LOWER_INF UPPER_INF RANGE_MERGE GENERATE_SERIES GENERATE_SUBSCRIPTS CURRENT_DATABASE CURRENT_QUERY CURRENT_SCHEMA|10 CURRENT_SCHEMAS|10 INET_CLIENT_ADDR INET_CLIENT_PORT INET_SERVER_ADDR INET_SERVER_PORT ROW_SECURITY_ACTIVE FORMAT_TYPE TO_REGCLASS TO_REGPROC TO_REGPROCEDURE TO_REGOPER TO_REGOPERATOR TO_REGTYPE TO_REGNAMESPACE TO_REGROLE COL_DESCRIPTION OBJ_DESCRIPTION SHOBJ_DESCRIPTION TXID_CURRENT TXID_CURRENT_IF_ASSIGNED TXID_CURRENT_SNAPSHOT TXID_SNAPSHOT_XIP TXID_SNAPSHOT_XMAX TXID_SNAPSHOT_XMIN TXID_VISIBLE_IN_SNAPSHOT TXID_STATUS CURRENT_SETTING SET_CONFIG BRIN_SUMMARIZE_NEW_VALUES BRIN_SUMMARIZE_RANGE BRIN_DESUMMARIZE_RANGE GIN_CLEAN_PENDING_LIST SUPPRESS_REDUNDANT_UPDATES_TRIGGER LO_FROM_BYTEA LO_PUT LO_GET LO_CREAT LO_CREATE LO_UNLINK LO_IMPORT LO_EXPORT LOREAD LOWRITE GROUPING CAST ".trim().split(" ").map((function(b){return b.split("|")[0]})).join("|");return{name:"PostgreSQL",aliases:["postgres","postgresql"],supersetOf:"sql",case_insensitive:!0,keywords:{keyword:"ABORT ALTER ANALYZE BEGIN CALL CHECKPOINT|10 CLOSE CLUSTER COMMENT COMMIT COPY CREATE DEALLOCATE DECLARE DELETE DISCARD DO DROP END EXECUTE EXPLAIN FETCH GRANT IMPORT INSERT LISTEN LOAD LOCK MOVE NOTIFY PREPARE REASSIGN|10 REFRESH REINDEX RELEASE RESET REVOKE ROLLBACK SAVEPOINT SECURITY SELECT SET SHOW START TRUNCATE UNLISTEN|10 UPDATE VACUUM|10 VALUES AGGREGATE COLLATION CONVERSION|10 DATABASE DEFAULT PRIVILEGES DOMAIN TRIGGER EXTENSION FOREIGN WRAPPER|10 TABLE FUNCTION GROUP LANGUAGE LARGE OBJECT MATERIALIZED VIEW OPERATOR CLASS FAMILY POLICY PUBLICATION|10 ROLE RULE SCHEMA SEQUENCE SERVER STATISTICS SUBSCRIPTION SYSTEM TABLESPACE CONFIGURATION DICTIONARY PARSER TEMPLATE TYPE USER MAPPING PREPARED ACCESS METHOD CAST AS TRANSFORM TRANSACTION OWNED TO INTO SESSION AUTHORIZATION INDEX PROCEDURE ASSERTION ALL ANALYSE AND ANY ARRAY ASC ASYMMETRIC|10 BOTH CASE CHECK COLLATE COLUMN CONCURRENTLY|10 CONSTRAINT CROSS DEFERRABLE RANGE DESC DISTINCT ELSE EXCEPT FOR FREEZE|10 FROM FULL HAVING ILIKE IN INITIALLY INNER INTERSECT IS ISNULL JOIN LATERAL LEADING LIKE LIMIT NATURAL NOT NOTNULL NULL OFFSET ON ONLY OR ORDER OUTER OVERLAPS PLACING PRIMARY REFERENCES RETURNING SIMILAR SOME SYMMETRIC TABLESAMPLE THEN TRAILING UNION UNIQUE USING VARIADIC|10 VERBOSE WHEN WHERE WINDOW WITH BY RETURNS INOUT OUT SETOF|10 IF STRICT CURRENT CONTINUE OWNER LOCATION OVER PARTITION WITHIN BETWEEN ESCAPE EXTERNAL INVOKER DEFINER WORK RENAME VERSION CONNECTION CONNECT TABLES TEMP TEMPORARY FUNCTIONS SEQUENCES TYPES SCHEMAS OPTION CASCADE RESTRICT ADD ADMIN EXISTS VALID VALIDATE ENABLE DISABLE REPLICA|10 ALWAYS PASSING COLUMNS PATH REF VALUE OVERRIDING IMMUTABLE STABLE VOLATILE BEFORE AFTER EACH ROW PROCEDURAL ROUTINE NO HANDLER VALIDATOR OPTIONS STORAGE OIDS|10 WITHOUT INHERIT DEPENDS CALLED INPUT LEAKPROOF|10 COST ROWS NOWAIT SEARCH UNTIL ENCRYPTED|10 PASSWORD CONFLICT|10 INSTEAD INHERITS CHARACTERISTICS WRITE CURSOR ALSO STATEMENT SHARE EXCLUSIVE INLINE ISOLATION REPEATABLE READ COMMITTED SERIALIZABLE UNCOMMITTED LOCAL GLOBAL SQL PROCEDURES RECURSIVE SNAPSHOT ROLLUP CUBE TRUSTED|10 INCLUDE FOLLOWING PRECEDING UNBOUNDED RANGE GROUPS UNENCRYPTED|10 SYSID FORMAT DELIMITER HEADER QUOTE ENCODING FILTER OFF FORCE_QUOTE FORCE_NOT_NULL FORCE_NULL COSTS BUFFERS TIMING SUMMARY DISABLE_PAGE_SKIPPING RESTART CYCLE GENERATED IDENTITY DEFERRED IMMEDIATE LEVEL LOGGED UNLOGGED OF NOTHING NONE EXCLUDE ATTRIBUTE USAGE ROUTINES TRUE FALSE NAN INFINITY ALIAS BEGIN CONSTANT DECLARE END EXCEPTION RETURN PERFORM|10 RAISE GET DIAGNOSTICS STACKED|10 FOREACH LOOP ELSIF EXIT WHILE REVERSE SLICE DEBUG LOG INFO NOTICE WARNING ASSERT OPEN SUPERUSER NOSUPERUSER CREATEDB NOCREATEDB CREATEROLE NOCREATEROLE INHERIT NOINHERIT LOGIN NOLOGIN REPLICATION NOREPLICATION BYPASSRLS NOBYPASSRLS ",built_in:"CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURRENT_CATALOG|10 CURRENT_DATE LOCALTIME LOCALTIMESTAMP CURRENT_ROLE|10 CURRENT_SCHEMA|10 SESSION_USER PUBLIC FOUND NEW OLD TG_NAME|10 TG_WHEN|10 TG_LEVEL|10 TG_OP|10 TG_RELID|10 TG_RELNAME|10 TG_TABLE_NAME|10 TG_TABLE_SCHEMA|10 TG_NARGS|10 TG_ARGV|10 TG_EVENT|10 TG_TAG|10 ROW_COUNT RESULT_OID|10 PG_CONTEXT|10 RETURNED_SQLSTATE COLUMN_NAME CONSTRAINT_NAME PG_DATATYPE_NAME|10 MESSAGE_TEXT TABLE_NAME SCHEMA_NAME PG_EXCEPTION_DETAIL|10 PG_EXCEPTION_HINT|10 PG_EXCEPTION_CONTEXT|10 SQLSTATE SQLERRM|10 SUCCESSFUL_COMPLETION WARNING DYNAMIC_RESULT_SETS_RETURNED IMPLICIT_ZERO_BIT_PADDING NULL_VALUE_ELIMINATED_IN_SET_FUNCTION PRIVILEGE_NOT_GRANTED PRIVILEGE_NOT_REVOKED STRING_DATA_RIGHT_TRUNCATION DEPRECATED_FEATURE NO_DATA NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED SQL_STATEMENT_NOT_YET_COMPLETE CONNECTION_EXCEPTION CONNECTION_DOES_NOT_EXIST CONNECTION_FAILURE SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION TRANSACTION_RESOLUTION_UNKNOWN PROTOCOL_VIOLATION TRIGGERED_ACTION_EXCEPTION FEATURE_NOT_SUPPORTED INVALID_TRANSACTION_INITIATION LOCATOR_EXCEPTION INVALID_LOCATOR_SPECIFICATION INVALID_GRANTOR INVALID_GRANT_OPERATION INVALID_ROLE_SPECIFICATION DIAGNOSTICS_EXCEPTION STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER CASE_NOT_FOUND CARDINALITY_VIOLATION DATA_EXCEPTION ARRAY_SUBSCRIPT_ERROR CHARACTER_NOT_IN_REPERTOIRE DATETIME_FIELD_OVERFLOW DIVISION_BY_ZERO ERROR_IN_ASSIGNMENT ESCAPE_CHARACTER_CONFLICT INDICATOR_OVERFLOW INTERVAL_FIELD_OVERFLOW INVALID_ARGUMENT_FOR_LOGARITHM INVALID_ARGUMENT_FOR_NTILE_FUNCTION INVALID_ARGUMENT_FOR_NTH_VALUE_FUNCTION INVALID_ARGUMENT_FOR_POWER_FUNCTION INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION INVALID_CHARACTER_VALUE_FOR_CAST INVALID_DATETIME_FORMAT INVALID_ESCAPE_CHARACTER INVALID_ESCAPE_OCTET INVALID_ESCAPE_SEQUENCE NONSTANDARD_USE_OF_ESCAPE_CHARACTER INVALID_INDICATOR_PARAMETER_VALUE INVALID_PARAMETER_VALUE INVALID_REGULAR_EXPRESSION INVALID_ROW_COUNT_IN_LIMIT_CLAUSE INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE INVALID_TABLESAMPLE_ARGUMENT INVALID_TABLESAMPLE_REPEAT INVALID_TIME_ZONE_DISPLACEMENT_VALUE INVALID_USE_OF_ESCAPE_CHARACTER MOST_SPECIFIC_TYPE_MISMATCH NULL_VALUE_NOT_ALLOWED NULL_VALUE_NO_INDICATOR_PARAMETER NUMERIC_VALUE_OUT_OF_RANGE SEQUENCE_GENERATOR_LIMIT_EXCEEDED STRING_DATA_LENGTH_MISMATCH STRING_DATA_RIGHT_TRUNCATION SUBSTRING_ERROR TRIM_ERROR UNTERMINATED_C_STRING ZERO_LENGTH_CHARACTER_STRING FLOATING_POINT_EXCEPTION INVALID_TEXT_REPRESENTATION INVALID_BINARY_REPRESENTATION BAD_COPY_FILE_FORMAT UNTRANSLATABLE_CHARACTER NOT_AN_XML_DOCUMENT INVALID_XML_DOCUMENT INVALID_XML_CONTENT INVALID_XML_COMMENT INVALID_XML_PROCESSING_INSTRUCTION INTEGRITY_CONSTRAINT_VIOLATION RESTRICT_VIOLATION NOT_NULL_VIOLATION FOREIGN_KEY_VIOLATION UNIQUE_VIOLATION CHECK_VIOLATION EXCLUSION_VIOLATION INVALID_CURSOR_STATE INVALID_TRANSACTION_STATE ACTIVE_SQL_TRANSACTION BRANCH_TRANSACTION_ALREADY_ACTIVE HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION READ_ONLY_SQL_TRANSACTION SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED NO_ACTIVE_SQL_TRANSACTION IN_FAILED_SQL_TRANSACTION IDLE_IN_TRANSACTION_SESSION_TIMEOUT INVALID_SQL_STATEMENT_NAME TRIGGERED_DATA_CHANGE_VIOLATION INVALID_AUTHORIZATION_SPECIFICATION INVALID_PASSWORD DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST DEPENDENT_OBJECTS_STILL_EXIST INVALID_TRANSACTION_TERMINATION SQL_ROUTINE_EXCEPTION FUNCTION_EXECUTED_NO_RETURN_STATEMENT MODIFYING_SQL_DATA_NOT_PERMITTED PROHIBITED_SQL_STATEMENT_ATTEMPTED READING_SQL_DATA_NOT_PERMITTED INVALID_CURSOR_NAME EXTERNAL_ROUTINE_EXCEPTION CONTAINING_SQL_NOT_PERMITTED MODIFYING_SQL_DATA_NOT_PERMITTED PROHIBITED_SQL_STATEMENT_ATTEMPTED READING_SQL_DATA_NOT_PERMITTED EXTERNAL_ROUTINE_INVOCATION_EXCEPTION INVALID_SQLSTATE_RETURNED NULL_VALUE_NOT_ALLOWED TRIGGER_PROTOCOL_VIOLATED SRF_PROTOCOL_VIOLATED EVENT_TRIGGER_PROTOCOL_VIOLATED SAVEPOINT_EXCEPTION INVALID_SAVEPOINT_SPECIFICATION INVALID_CATALOG_NAME INVALID_SCHEMA_NAME TRANSACTION_ROLLBACK TRANSACTION_INTEGRITY_CONSTRAINT_VIOLATION SERIALIZATION_FAILURE STATEMENT_COMPLETION_UNKNOWN DEADLOCK_DETECTED SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION SYNTAX_ERROR INSUFFICIENT_PRIVILEGE CANNOT_COERCE GROUPING_ERROR WINDOWING_ERROR INVALID_RECURSION INVALID_FOREIGN_KEY INVALID_NAME NAME_TOO_LONG RESERVED_NAME DATATYPE_MISMATCH INDETERMINATE_DATATYPE COLLATION_MISMATCH INDETERMINATE_COLLATION WRONG_OBJECT_TYPE GENERATED_ALWAYS UNDEFINED_COLUMN UNDEFINED_FUNCTION UNDEFINED_TABLE UNDEFINED_PARAMETER UNDEFINED_OBJECT DUPLICATE_COLUMN DUPLICATE_CURSOR DUPLICATE_DATABASE DUPLICATE_FUNCTION DUPLICATE_PREPARED_STATEMENT DUPLICATE_SCHEMA DUPLICATE_TABLE DUPLICATE_ALIAS DUPLICATE_OBJECT AMBIGUOUS_COLUMN AMBIGUOUS_FUNCTION AMBIGUOUS_PARAMETER AMBIGUOUS_ALIAS INVALID_COLUMN_REFERENCE INVALID_COLUMN_DEFINITION INVALID_CURSOR_DEFINITION INVALID_DATABASE_DEFINITION INVALID_FUNCTION_DEFINITION INVALID_PREPARED_STATEMENT_DEFINITION INVALID_SCHEMA_DEFINITION INVALID_TABLE_DEFINITION INVALID_OBJECT_DEFINITION WITH_CHECK_OPTION_VIOLATION INSUFFICIENT_RESOURCES DISK_FULL OUT_OF_MEMORY TOO_MANY_CONNECTIONS CONFIGURATION_LIMIT_EXCEEDED PROGRAM_LIMIT_EXCEEDED STATEMENT_TOO_COMPLEX TOO_MANY_COLUMNS TOO_MANY_ARGUMENTS OBJECT_NOT_IN_PREREQUISITE_STATE OBJECT_IN_USE CANT_CHANGE_RUNTIME_PARAM LOCK_NOT_AVAILABLE OPERATOR_INTERVENTION QUERY_CANCELED ADMIN_SHUTDOWN CRASH_SHUTDOWN CANNOT_CONNECT_NOW DATABASE_DROPPED SYSTEM_ERROR IO_ERROR UNDEFINED_FILE DUPLICATE_FILE SNAPSHOT_TOO_OLD CONFIG_FILE_ERROR LOCK_FILE_EXISTS FDW_ERROR FDW_COLUMN_NAME_NOT_FOUND FDW_DYNAMIC_PARAMETER_VALUE_NEEDED FDW_FUNCTION_SEQUENCE_ERROR FDW_INCONSISTENT_DESCRIPTOR_INFORMATION FDW_INVALID_ATTRIBUTE_VALUE FDW_INVALID_COLUMN_NAME FDW_INVALID_COLUMN_NUMBER FDW_INVALID_DATA_TYPE FDW_INVALID_DATA_TYPE_DESCRIPTORS FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER FDW_INVALID_HANDLE FDW_INVALID_OPTION_INDEX FDW_INVALID_OPTION_NAME FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH FDW_INVALID_STRING_FORMAT FDW_INVALID_USE_OF_NULL_POINTER FDW_TOO_MANY_HANDLES FDW_OUT_OF_MEMORY FDW_NO_SCHEMAS FDW_OPTION_NAME_NOT_FOUND FDW_REPLY_HANDLE FDW_SCHEMA_NOT_FOUND FDW_TABLE_NOT_FOUND FDW_UNABLE_TO_CREATE_EXECUTION FDW_UNABLE_TO_CREATE_REPLY FDW_UNABLE_TO_ESTABLISH_CONNECTION PLPGSQL_ERROR RAISE_EXCEPTION NO_DATA_FOUND TOO_MANY_ROWS ASSERT_FAILURE INTERNAL_ERROR DATA_CORRUPTED INDEX_CORRUPTED "},illegal:/:==|\W\s*\(\*|(^|\s)\$[a-z]|\{\{|[a-z]:\s*$|\.\.\.|TO:|DO:/,contains:[{className:"keyword",variants:[{begin:/\bTEXT\s*SEARCH\b/},{begin:/\b(PRIMARY|FOREIGN|FOR(\s+NO)?)\s+KEY\b/},{begin:/\bPARALLEL\s+(UNSAFE|RESTRICTED|SAFE)\b/},{begin:/\bSTORAGE\s+(PLAIN|EXTERNAL|EXTENDED|MAIN)\b/},{begin:/\bMATCH\s+(FULL|PARTIAL|SIMPLE)\b/},{begin:/\bNULLS\s+(FIRST|LAST)\b/},{begin:/\bEVENT\s+TRIGGER\b/},{begin:/\b(MAPPING|OR)\s+REPLACE\b/},{begin:/\b(FROM|TO)\s+(PROGRAM|STDIN|STDOUT)\b/},{begin:/\b(SHARE|EXCLUSIVE)\s+MODE\b/},{begin:/\b(LEFT|RIGHT)\s+(OUTER\s+)?JOIN\b/},{begin:/\b(FETCH|MOVE)\s+(NEXT|PRIOR|FIRST|LAST|ABSOLUTE|RELATIVE|FORWARD|BACKWARD)\b/},{begin:/\bPRESERVE\s+ROWS\b/},{begin:/\bDISCARD\s+PLANS\b/},{begin:/\bREFERENCING\s+(OLD|NEW)\b/},{begin:/\bSKIP\s+LOCKED\b/},{begin:/\bGROUPING\s+SETS\b/},{begin:/\b(BINARY|INSENSITIVE|SCROLL|NO\s+SCROLL)\s+(CURSOR|FOR)\b/},{begin:/\b(WITH|WITHOUT)\s+HOLD\b/},{begin:/\bWITH\s+(CASCADED|LOCAL)\s+CHECK\s+OPTION\b/},{begin:/\bEXCLUDE\s+(TIES|NO\s+OTHERS)\b/},{begin:/\bFORMAT\s+(TEXT|XML|JSON|YAML)\b/},{begin:/\bSET\s+((SESSION|LOCAL)\s+)?NAMES\b/},{begin:/\bIS\s+(NOT\s+)?UNKNOWN\b/},{begin:/\bSECURITY\s+LABEL\b/},{begin:/\bSTANDALONE\s+(YES|NO|NO\s+VALUE)\b/},{begin:/\bWITH\s+(NO\s+)?DATA\b/},{begin:/\b(FOREIGN|SET)\s+DATA\b/},{begin:/\bSET\s+(CATALOG|CONSTRAINTS)\b/},{begin:/\b(WITH|FOR)\s+ORDINALITY\b/},{begin:/\bIS\s+(NOT\s+)?DOCUMENT\b/},{begin:/\bXML\s+OPTION\s+(DOCUMENT|CONTENT)\b/},{begin:/\b(STRIP|PRESERVE)\s+WHITESPACE\b/},{begin:/\bNO\s+(ACTION|MAXVALUE|MINVALUE)\b/},{begin:/\bPARTITION\s+BY\s+(RANGE|LIST|HASH)\b/},{begin:/\bAT\s+TIME\s+ZONE\b/},{begin:/\bGRANTED\s+BY\b/},{begin:/\bRETURN\s+(QUERY|NEXT)\b/},{begin:/\b(ATTACH|DETACH)\s+PARTITION\b/},{begin:/\bFORCE\s+ROW\s+LEVEL\s+SECURITY\b/},{begin:/\b(INCLUDING|EXCLUDING)\s+(COMMENTS|CONSTRAINTS|DEFAULTS|IDENTITY|INDEXES|STATISTICS|STORAGE|ALL)\b/},{begin:/\bAS\s+(ASSIGNMENT|IMPLICIT|PERMISSIVE|RESTRICTIVE|ENUM|RANGE)\b/}]},{begin:/\b(FORMAT|FAMILY|VERSION)\s*\(/},{begin:/\bINCLUDE\s*\(/,keywords:"INCLUDE"},{begin:/\bRANGE(?!\s*(BETWEEN|UNBOUNDED|CURRENT|[-0-9]+))/},{begin:/\b(VERSION|OWNER|TEMPLATE|TABLESPACE|CONNECTION\s+LIMIT|PROCEDURE|RESTRICT|JOIN|PARSER|COPY|START|END|COLLATION|INPUT|ANALYZE|STORAGE|LIKE|DEFAULT|DELIMITER|ENCODING|COLUMN|CONSTRAINT|TABLE|SCHEMA)\s*=/},{begin:/\b(PG_\w+?|HAS_[A-Z_]+_PRIVILEGE)\b/,relevance:10},{begin:/\bEXTRACT\s*\(/,end:/\bFROM\b/,returnEnd:!0,keywords:{type:"CENTURY DAY DECADE DOW DOY EPOCH HOUR ISODOW ISOYEAR MICROSECONDS MILLENNIUM MILLISECONDS MINUTE MONTH QUARTER SECOND TIMEZONE TIMEZONE_HOUR TIMEZONE_MINUTE WEEK YEAR"}},{begin:/\b(XMLELEMENT|XMLPI)\s*\(\s*NAME/,keywords:{keyword:"NAME"}},{begin:/\b(XMLPARSE|XMLSERIALIZE)\s*\(\s*(DOCUMENT|CONTENT)/,keywords:{keyword:"DOCUMENT CONTENT"}},{beginKeywords:"CACHE INCREMENT MAXVALUE MINVALUE",end:e.C_NUMBER_RE,returnEnd:!0,keywords:"BY CACHE INCREMENT MAXVALUE MINVALUE"},{className:"type",begin:/\b(WITH|WITHOUT)\s+TIME\s+ZONE\b/},{className:"type",begin:/\bINTERVAL\s+(YEAR|MONTH|DAY|HOUR|MINUTE|SECOND)(\s+TO\s+(MONTH|HOUR|MINUTE|SECOND))?\b/},{begin:/\bRETURNS\s+(LANGUAGE_HANDLER|TRIGGER|EVENT_TRIGGER|FDW_HANDLER|INDEX_AM_HANDLER|TSM_HANDLER)\b/,keywords:{keyword:"RETURNS",type:"LANGUAGE_HANDLER TRIGGER EVENT_TRIGGER FDW_HANDLER INDEX_AM_HANDLER TSM_HANDLER"}},{begin:"\\b("+A+")\\s*\\("},{begin:"\\.("+u+")\\b"},{begin:"\\b("+u+")\\s+PATH\\b",keywords:{keyword:"PATH",type:l.replace("PATH ","")}},{className:"type",begin:"\\b("+u+")\\b"},{className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{className:"string",begin:"(e|E|u&|U&)'",end:"'",contains:[{begin:"\\\\."}],relevance:10},e.END_SAME_AS_BEGIN({begin:r,end:r,contains:[{subLanguage:["pgsql","perl","python","tcl","r","lua","java","php","ruby","bash","scheme","xml","json"],endsWithParent:!0}]}),{begin:'"',end:'"',contains:[{begin:'""'}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{className:"meta",variants:[{begin:"%(ROW)?TYPE",relevance:10},{begin:"\\$\\d+"},{begin:"^#\\w",end:"$"}]},{className:"symbol",begin:"<<\\s*[a-zA-Z_][a-zA-Z_0-9$]*\\s*>>",relevance:10}]}})),X.registerLanguage("php",(function(e){const t=e.regex,n=/(?![A-Za-z0-9])(?![$])/,r=t.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,n),o=t.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,n),a={scope:"variable",match:"\\$+"+r},i={scope:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]},l=e.inherit(e.APOS_STRING_MODE,{illegal:null}),p="[ \t\n]",m={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(i)}),l,{begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/,contains:e.QUOTE_STRING_MODE.contains.concat(i),"on:begin":(Y,O)=>{O.data._beginMatch=Y[1]||Y[2]},"on:end":(Y,O)=>{O.data._beginMatch!==Y[1]&&O.ignoreMatch()}},e.END_SAME_AS_BEGIN({begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/})]},A={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},b=["false","null","true"],C=["__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"],h=["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"],E={keyword:C,literal:(Y=>{const O=[];return Y.forEach((G=>{O.push(G),G.toLowerCase()===G?O.push(G.toUpperCase()):O.push(G.toLowerCase())})),O})(b),built_in:h},v=Y=>Y.map((O=>O.replace(/\|\d+$/,""))),N={variants:[{match:[/new/,t.concat(p,"+"),t.concat("(?!",v(h).join("\\b|"),"\\b)"),o],scope:{1:"keyword",4:"title.class"}}]},_=t.concat(r,"\\b(?!\\()"),S={variants:[{match:[t.concat(/::/,t.lookahead(/(?!class\b)/)),_],scope:{2:"variable.constant"}},{match:[/::/,/class/],scope:{2:"variable.language"}},{match:[o,t.concat(/::/,t.lookahead(/(?!class\b)/)),_],scope:{1:"title.class",3:"variable.constant"}},{match:[o,t.concat("::",t.lookahead(/(?!class\b)/))],scope:{1:"title.class"}},{match:[o,/::/,/class/],scope:{1:"title.class",3:"variable.language"}}]},R={scope:"attr",match:t.concat(r,t.lookahead(":"),t.lookahead(/(?!::)/))},q={relevance:0,begin:/\(/,end:/\)/,keywords:E,contains:[R,a,S,e.C_BLOCK_COMMENT_MODE,m,A,N]},I={relevance:0,match:[/\b/,t.concat("(?!fn\\b|function\\b|",v(C).join("\\b|"),"|",v(h).join("\\b|"),"\\b)"),r,t.concat(p,"*"),t.lookahead(/(?=\()/)],scope:{3:"title.function.invoke"},contains:[q]};q.contains.push(I);const Q=[R,S,e.C_BLOCK_COMMENT_MODE,m,A,N];return{case_insensitive:!1,keywords:E,contains:[{begin:t.concat(/#\[\s*/,o),beginScope:"meta",end:/]/,endScope:"meta",keywords:{literal:b,keyword:["new","array"]},contains:[{begin:/\[/,end:/]/,keywords:{literal:b,keyword:["new","array"]},contains:["self",...Q]},...Q,{scope:"meta",match:o}]},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/},a,I,S,{match:[/const/,/\s/,r],scope:{1:"keyword",3:"variable.constant"}},N,{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:E,contains:["self",a,S,e.C_BLOCK_COMMENT_MODE,m,A]}]},{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]},m,A]}})),X.registerLanguage("php-template",(function(e){return{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})]}]}})),X.registerLanguage("plaintext",(function(e){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}})),X.registerLanguage("python",(function(e){const t=e.regex,n=new RegExp("[\\p{XID_Start}_]\\p{XID_Continue}*","u"),r=["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:r,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"]},l={className:"meta",begin:/^(>>>|\.\.\.) /},u={className:"subst",begin:/\{/,end:/\}/,keywords:i,illegal:/#/},c={begin:/\{\{/,relevance:0},d={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,l],relevance:10},{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,l],relevance:10},{begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,l,c,u]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,l,c,u]},{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,c,u]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,c,u]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},p="[0-9](_?[0-9])*",m=`(\\b(${p}))?\\.(${p})|\\b(${p})\\.`,A=`\\b|${r.join("|")}`,b={className:"number",relevance:0,variants:[{begin:`(\\b(${p})|(${m}))[eE][+-]?(${p})[jJ]?(?=${A})`},{begin:`(${m})[jJ]?`},{begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${A})`},{begin:`\\b0[bB](_?[01])+[lL]?(?=${A})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${A})`},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${A})`},{begin:`\\b(${p})[jJ](?=${A})`}]},C={className:"comment",begin:t.lookahead(/# type:/),end:/$/,keywords:i,contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},h={className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",l,b,d,e.HASH_COMMENT_MODE]}]};return u.contains=[d,b,l],{name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i,illegal:/(<\/|\?)|=>/,contains:[l,b,{begin:/\bself\b/},{beginKeywords:"if",relevance:0},d,C,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,n],scope:{1:"keyword",3:"title.function"},contains:[h]},{variants:[{match:[/\bclass/,/\s+/,n,/\s*/,/\(\s*/,n,/\s*\)/]},{match:[/\bclass/,/\s+/,n]}],scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[b,h,d]}]}})),X.registerLanguage("python-repl",(function(e){return{aliases:["pycon"],contains:[{className:"meta.prompt",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}})),X.registerLanguage("r",(function(e){const t=e.regex,n=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,r=t.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]?/),o=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,a=t.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/);return{name:"R",keywords:{$pattern:n,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:t.lookahead(t.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)),endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{scope:"variable",variants:[{match:n},{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:[o,r]},{scope:{1:"operator",2:"number"},match:[/%[^%]*%/,r]},{scope:{1:"punctuation",2:"number"},match:[a,r]},{scope:{2:"number"},match:[/[^a-zA-Z0-9._]|^/,r]}]},{scope:{3:"operator"},match:[n,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:o},{match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:a},{begin:"`",end:"`",contains:[{begin:/\\./}]}]}})),X.registerLanguage("ruby",(function(e){const t=e.regex,n="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",r=t.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),o=t.concat(r,/(::\w+)*/),s={"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"]},i={className:"doctag",begin:"@[A-Za-z]+"},l={begin:"#<",end:">"},u=[e.COMMENT("#","$",{contains:[i]}),e.COMMENT("^=begin","^=end",{contains:[i],relevance:10}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/,end:/\}/,keywords:s},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:t.concat(/<<[-~]?'?/,t.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]})]}]},m="[0-9](_?[0-9])*",A={className:"number",relevance:0,variants:[{begin:`\\b([1-9](_?[0-9])*|0)(\\.(${m}))?([eE][+-]?(${m})|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:s}]},_=[d,{variants:[{match:[/class\s+/,o,/\s+<\s+/,o]},{match:[/\b(class|module)\s+/,o]}],scope:{2:"title.class",4:"title.class.inherited"},keywords:s},{match:[/(include|extend)\s+/,o],scope:{2:"title.class"},keywords:s},{relevance:0,match:[o,/\.new[. (]/],scope:{1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"},{relevance:0,match:r,scope:"title.class"},{match:[/def/,/\s+/,n],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:n}],relevance:0},A,{className:"variable",begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0,relevance:0,keywords:s},{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(l,u),relevance:0}].concat(l,u);c.contains=_,b.contains=_;const I=[{begin:/^\s*=>/,starts:{end:"$",contains:_}},{className:"meta.prompt",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])",starts:{end:"$",keywords:s,contains:_}}];return u.unshift(l),{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:s,illegal:/\/\*/,contains:[e.SHEBANG({binary:"ruby"})].concat(I).concat(u).concat(_)}})),X.registerLanguage("rust",(function(e){const t=e.regex,n={className:"title.function.invoke",relevance:0,begin:t.concat(/\b/,/(?!let|for|while|if|else|match\b)/,e.IDENT_RE,t.lookahead(/\s*\(/))},r="([ui](8|16|32|64|128|size)|f(32|64))?",s=["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!"],i=["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:i,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","unsafe","unsized","use","virtual","where","while","yield"],literal:["true","false","Some","None","Ok","Err"],built_in:s},illegal:""},n]}})),X.registerLanguage("scss",(function(e){const t=(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_-]*/}}))(e),n=Wy,r=jy,o="@[a-z-]+",i={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,t.CSS_NUMBER_MODE,{className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag",begin:"\\b("+$y.join("|")+")\\b",relevance:0},{className:"selector-pseudo",begin:":("+r.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+n.join("|")+")"},i,{begin:/\(/,end:/\)/,contains:[t.CSS_NUMBER_MODE]},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+Yy.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:[t.BLOCK_COMMENT,i,t.HEXCOLOR,t.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,t.IMPORTANT,t.FUNCTION_DISPATCH]},{begin:"@(page|font-face)",keywords:{$pattern:o,keyword:"@page @font-face"}},{begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/,keyword:"and or not only",attribute:Zy.join(" ")},contains:[{begin:o,className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute"},i,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,t.HEXCOLOR,t.CSS_NUMBER_MODE]},t.FUNCTION_DISPATCH]}})),X.registerLanguage("shell",(function(e){return{name:"Shell Session",aliases:["console","shellsession"],contains:[{className:"meta.prompt",begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/,subLanguage:"bash"}}]}})),X.registerLanguage("sql",(function(e){const t=e.regex,n=e.COMMENT("--","$"),a=["true","false","unknown"],i=["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"],c=["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"],p=["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"],m=c,A=["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=>!c.includes(E))),h={begin:t.concat(/\b/,t.either(...m),/\s*\(/),relevance:0,keywords:{built_in:m}};return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{$pattern:/\b[\w\.]+/,keyword:function(E,{exceptions:v,when:N}={}){const _=N;return v=v||[],E.map((S=>S.match(/\|\d+$/)||v.includes(S)?S:_(S)?`${S}|0`:S))}(A,{when:E=>E.length<3}),literal:a,type:i,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:[{begin:t.either(...p),relevance:0,keywords:{$pattern:/[\w\.]+/,keyword:A.concat(p),literal:a,type:i}},{className:"type",begin:t.either("double precision","large object","with timezone","without timezone")},h,{className:"variable",begin:/@[a-z0-9][a-z0-9_]*/},{className:"string",variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/,contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,n,{className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0}]}})),X.registerLanguage("swift",(function(e){const t={match:/\s+/,relevance:0},n=e.COMMENT("/\\*","\\*/",{contains:["self"]}),r=[e.C_LINE_COMMENT_MODE,n],o={match:[/\./,ot(...e3,...o1)],className:{2:"keyword"}},a={match:de(/\./,ot(...Pu)),relevance:0},s=Pu.filter((te=>"string"==typeof te)).concat(["_|0"]),l={variants:[{className:"keyword",match:ot(...Pu.filter((te=>"string"!=typeof te)).concat(t3).map(Bu),...o1)}]},u={$pattern:ot(/\b\w+/,/#\w+/),keyword:s.concat(r3),literal:a1},c=[o,a,l],m=[{match:de(/\./,ot(...s1)),relevance:0},{className:"built_in",match:de(/\b/,ot(...s1),/(?=\()/)}],A={match:/->/,relevance:0},C=[A,{className:"operator",relevance:0,variants:[{match:Uu},{match:`\\.(\\.|${l1})+`}]}],h="([0-9]_*)+",g="([0-9a-fA-F]_*)+",E={className:"number",relevance:0,variants:[{match:`\\b(${h})(\\.(${h}))?([eE][+-]?(${h}))?\\b`},{match:`\\b0x(${g})(\\.(${g}))?([pP][+-]?(${h}))?\\b`},{match:/\b0o([0-7]_*)+\b/},{match:/\b0b([01]_*)+\b/}]},v=(te="")=>({className:"subst",variants:[{match:de(/\\/,te,/[0\\tnr"']/)},{match:de(/\\/,te,/u\{[0-9a-fA-F]{1,8}\}/)}]}),N=(te="")=>({className:"subst",match:de(/\\/,te,/[\t ]*(?:[\r\n]|\r\n)/)}),_=(te="")=>({className:"subst",label:"interpol",begin:de(/\\/,te,/\(/),end:/\)/}),S=(te="")=>({begin:de(te,/"""/),end:de(/"""/,te),contains:[v(te),N(te),_(te)]}),R=(te="")=>({begin:de(te,/"/),end:de(/"/,te),contains:[v(te),_(te)]}),q={className:"string",variants:[S(),S("#"),S("##"),S("###"),R(),R("#"),R("##"),R("###")]},I=[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0,contains:[e.BACKSLASH_ESCAPE]}],Q={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//,contains:I},ie=te=>{const Re=de(te,/\//),ke=de(/\//,te);return{begin:Re,end:ke,contains:[...I,{scope:"comment",begin:`#(?!.*${ke})`,end:/$/}]}},Y={scope:"regexp",variants:[ie("###"),ie("##"),ie("#"),Q]},O={match:de(/`/,on,/`/)},x=[O,{className:"variable",match:/\$\d+/},{className:"variable",match:`\\$${Ms}+`}],k=[{match:/(@|#(un)?)available/,scope:"keyword",starts:{contains:[{begin:/\(/,end:/\)/,keywords:a3,contains:[...C,E,q]}]}},{scope:"keyword",match:de(/@/,ot(...o3))},{scope:"meta",match:de(/@/,on)}],H={match:Is(/\b[A-Z]/),relevance:0,contains:[{className:"type",match:de(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,Ms,"+")},{className:"type",match:qu,relevance:0},{match:/[?!]+/,relevance:0},{match:/\.\.\./,relevance:0},{match:de(/\s+&\s+/,Is(qu)),relevance:0}]},j={begin://,keywords:u,contains:[...r,...c,...k,A,H]};H.contains.push(j);const ce={begin:/\(/,end:/\)/,relevance:0,keywords:u,contains:["self",{match:de(on,/\s*:/),keywords:"_|0",relevance:0},...r,Y,...c,...m,...C,E,q,...x,...k,H]},se={begin://,keywords:"repeat each",contains:[...r,H]},gt={begin:/\(/,end:/\)/,keywords:u,contains:[{begin:ot(Is(de(on,/\s*:/)),Is(de(on,/\s+/,on,/\s*:/))),end:/:/,relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:on}]},...r,...c,...C,E,q,...k,H,ce],endsParent:!0,illegal:/["']/},Ft={match:[/(func|macro)/,/\s+/,ot(O.match,on,Uu)],className:{1:"keyword",3:"title.function"},contains:[se,gt,t],illegal:[/\[/,/%/]},ht={match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"},contains:[se,gt,t],illegal:/\[|%/},Ge={match:[/operator/,/\s+/,Uu],className:{1:"keyword",3:"title"}},Ct={begin:[/precedencegroup/,/\s+/,qu],className:{1:"keyword",3:"title"},contains:[H],keywords:[...n3,...a1],end:/}/};for(const te of q.variants){const Re=te.contains.find((Ce=>"interpol"===Ce.label));Re.keywords=u;const ke=[...c,...m,...C,E,q,...x];Re.contains=[...ke,{begin:/\(/,end:/\)/,contains:["self",...ke]}]}return{name:"Swift",keywords:u,contains:[...r,Ft,ht,{beginKeywords:"struct protocol class extension enum actor",end:"\\{",excludeEnd:!0,keywords:u,contains:[e.inherit(e.TITLE_MODE,{className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...c]},Ge,Ct,{beginKeywords:"import",end:/$/,contains:[...r],relevance:0},Y,...c,...m,...C,E,q,...x,...k,H,ce]}})),X.registerLanguage("typescript",(function(e){const t=function(e){const t=e.regex,r=Fs,o_begin="<>",o_end="",s={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(T,y)=>{const L=T[0].length+T.index,k=T.input[L];if("<"===k||","===k)return void y.ignoreMatch();let H;">"===k&&(((T,{after:y})=>{const L="",x={match:[/const|var|let/,/\s+/,r,/\s*/,/=\s*/,/(async\s*)?/,t.lookahead(P)],keywords:"async",className:{1:"keyword",3:"title.function"},contains:[_]};var T;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{PARAMS_CONTAINS:N,CLASS_REFERENCE:R},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,m,A,b,C,g,{match:/\$\d+/},d,R,{className:"attr",begin:r+t.lookahead(":"),relevance:0},x,{begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",relevance:0,contains:[g,e.REGEXP_MODE,{className:"function",begin:P,returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i,contains:N}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:o_begin,end:o_end},{match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:s.begin,"on:begin":s.isTrulyOpeningTag,end:s.end}],subLanguage:"xml",contains:[{begin:s.begin,end:s.end,skip:!0,contains:["self"]}]}]},I,{beginKeywords:"while if switch catch for"},{begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,label:"func.def",contains:[_,e.inherit(e.TITLE_MODE,{begin:r,className:"title.function"})]},{match:/\.\.\./,relevance:0},O,{match:"\\$"+r,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},contains:[_]},Y,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"},S,G,{match:/\$[(.]/}]}}(e),n=Fs,r=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],o={beginKeywords:"namespace",end:/\{/,excludeEnd:!0,contains:[t.exports.CLASS_REFERENCE]},a={beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:{keyword:"interface extends",built_in:r},contains:[t.exports.CLASS_REFERENCE]},l={$pattern:Fs,keyword:c1.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]),literal:d1,built_in:h1.concat(r),"variable.language":g1},u={className:"meta",begin:"@"+n},c=(p,m,A)=>{const b=p.contains.findIndex((C=>C.label===m));if(-1===b)throw new Error("can not find mode to replace");p.contains.splice(b,1,A)};return Object.assign(t.keywords,l),t.exports.PARAMS_CONTAINS.push(u),t.contains=t.contains.concat([u,o,a]),c(t,"shebang",e.SHEBANG()),c(t,"use_strict",{className:"meta",relevance:10,begin:/^\s*['"]use strict['"]/}),t.contains.find((p=>"func.def"===p.label)).relevance=0,Object.assign(t,{name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),t})),X.registerLanguage("vbnet",(function(e){const t=e.regex,o=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,s=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,i=/\d{1,2}(:\d{1,2}){1,2}/,l={className:"literal",variants:[{begin:t.concat(/# */,t.either(a,o),/ *#/)},{begin:t.concat(/# */,i,/ *#/)},{begin:t.concat(/# */,s,/ *#/)},{begin:t.concat(/# */,t.either(a,o),/ +/,t.either(s,i),/ *#/)}]},d=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}]}),p=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:/""/}]},l,{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+:/},d,p,{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:[p]}]}})),X.registerLanguage("wasm",(function(e){e.regex;const t=e.COMMENT(/\(;/,/;\)/);return t.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(/;;/,/$/),t,{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/}]}})),X.registerLanguage("xml",(function(e){const t=e.regex,n=t.concat(/[\p{L}_]/u,t.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),o={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},a={begin:/\s/,contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},s=e.inherit(a,{begin:/\(/,end:/\)/}),i=e.inherit(e.APOS_STRING_MODE,{className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),u={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:[a,l,i,s,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[a,s,l,i]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},o,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[u],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[u],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:t.concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:u}]},{className:"tag",begin:t.concat(/<\//,t.lookahead(t.concat(n,/>/))),contains:[{className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}})),X.registerLanguage("yaml",(function(e){const t="true false yes no null",n="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},s=e.inherit(a,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),p={end:",",endsWithParent:!0,excludeEnd:!0,keywords:t,relevance:0},m={begin:/\{/,end:/\}/,contains:[p],illegal:"\\n",relevance:0},A={begin:"\\[",end:"\\]",contains:[p],illegal:"\\n",relevance:0},b=[{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+!"+n},{className:"type",begin:"!<"+n+">"},{className:"type",begin:"!"+n},{className:"type",begin:"!!"+n},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:t,keywords:{literal:t}},{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},m,A,a],C=[...b];return C.pop(),C.push(s),p.contains=C,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:b}}));const E1=X,f3=aD({html:!1,typographer:!0,highlight:function(e,t){const n=zn();if(t&&E1.getLanguage(t))try{return`
\n
\n
${t}
\n \n
\n
`+E1.highlight(e,{language:t,ignoreIllegals:!0}).value+"
"}catch{}return`
\n
\n
\n \n
\n
`+hb.encode(e)+"
"}}).disable("list");function A1(e=""){return f3.render(e)}/*! @license DOMPurify 3.0.8 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.8/LICENSE */const{entries:b1,setPrototypeOf:_1,isFrozen:m3,getPrototypeOf:g3,getOwnPropertyDescriptor:Hu}=Object;let{freeze:Qe,seal:Mt,create:v1}=Object,{apply:Vu,construct:zu}=typeof Reflect<"u"&&Reflect;Qe||(Qe=function(t){return t}),Mt||(Mt=function(t){return t}),Vu||(Vu=function(t,n,r){return t.apply(n,r)}),zu||(zu=function(t,n){return new t(...n)});const Bs=Tt(Array.prototype.forEach),D1=Tt(Array.prototype.pop),qo=Tt(Array.prototype.push),Ps=Tt(String.prototype.toLowerCase),Gu=Tt(String.prototype.toString),h3=Tt(String.prototype.match),Ho=Tt(String.prototype.replace),E3=Tt(String.prototype.indexOf),A3=Tt(String.prototype.trim),mt=Tt(RegExp.prototype.test),Vo=function(e){return function(){for(var t=arguments.length,n=new Array(t),r=0;r1?n-1:0),o=1;o2&&void 0!==arguments[2]?arguments[2]:Ps;_1&&_1(e,null);let r=t.length;for(;r--;){let o=t[r];if("string"==typeof o){const a=n(o);a!==o&&(m3(t)||(t[r]=a),o=a)}e[o]=!0}return e}function _3(e){for(let t=0;t/gm),C3=Mt(/\${[\w\W]*}/gm),N3=Mt(/^data-[\-\w.\u00B7-\uFFFF]/),S3=Mt(/^aria-[\-\w]+$/),S1=Mt(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),w3=Mt(/^(?:\w+script|data):/i),x3=Mt(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),w1=Mt(/^html$/i);var x1=Object.freeze({__proto__:null,MUSTACHE_EXPR:y3,ERB_EXPR:T3,TMPLIT_EXPR:C3,DATA_ATTR:N3,ARIA_ATTR:S3,IS_ALLOWED_URI:S1,IS_SCRIPT_OR_DATA:w3,ATTR_WHITESPACE:x3,DOCTYPE_NAME:w1});var O3=function R1(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:typeof window>"u"?null:window;const t=$=>R1($);if(t.version="3.0.8",t.removed=[],!e||!e.document||9!==e.document.nodeType)return t.isSupported=!1,t;let{document:n}=e;const r=n,o=r.currentScript,{DocumentFragment:a,HTMLTemplateElement:s,Node:i,Element:l,NodeFilter:u,NamedNodeMap:c=e.NamedNodeMap||e.MozNamedAttrMap,HTMLFormElement:d,DOMParser:p,trustedTypes:m}=e,A=l.prototype,b=Us(A,"cloneNode"),C=Us(A,"nextSibling"),h=Us(A,"childNodes"),g=Us(A,"parentNode");if("function"==typeof s){const $=n.createElement("template");$.content&&$.content.ownerDocument&&(n=$.content.ownerDocument)}let E,v="";const{implementation:N,createNodeIterator:_,createDocumentFragment:S,getElementsByTagName:R}=n,{importNode:q}=r;let I={};t.isSupported="function"==typeof b1&&"function"==typeof g&&N&&void 0!==N.createHTMLDocument;const{MUSTACHE_EXPR:Q,ERB_EXPR:ie,TMPLIT_EXPR:Y,DATA_ATTR:O,ARIA_ATTR:G,IS_SCRIPT_OR_DATA:P,ATTR_WHITESPACE:x}=x1;let{IS_ALLOWED_URI:T}=x1,y=null;const L=J({},[...y1,...$u,...Zu,...ju,...T1]);let k=null;const H=J({},[...C1,...Wu,...N1,...qs]);let j=Object.seal(v1(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,ce=null,se=!0,le=!0,gt=!1,Ft=!0,ht=!1,Ge=!1,Ct=!1,te=!1,Re=!1,ke=!1,Ce=!1,Hs=!0,Pr=!1,Ur=!0,ye=!1,oe={},Bt=null;const jt=J({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let qr=null;const Hr=J({},["audio","video","img","source","image","track"]);let B=null;const V=J({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),K="http://www.w3.org/1998/Math/MathML",ne="http://www.w3.org/2000/svg",ge="http://www.w3.org/1999/xhtml";let $e=ge,zo=!1,Go=null;const m8=J({},[K,ne,ge],Gu);let $o=null;const g8=["application/xhtml+xml","text/html"];let Ie=null,Vr=null;const E8=n.createElement("form"),P1=function(D){return D instanceof RegExp||D instanceof Function},Qu=function(){let D=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!Vr||Vr!==D){if((!D||"object"!=typeof D)&&(D={}),D=Yn(D),$o=-1===g8.indexOf(D.PARSER_MEDIA_TYPE)?"text/html":D.PARSER_MEDIA_TYPE,Ie="application/xhtml+xml"===$o?Gu:Ps,y="ALLOWED_TAGS"in D?J({},D.ALLOWED_TAGS,Ie):L,k="ALLOWED_ATTR"in D?J({},D.ALLOWED_ATTR,Ie):H,Go="ALLOWED_NAMESPACES"in D?J({},D.ALLOWED_NAMESPACES,Gu):m8,B="ADD_URI_SAFE_ATTR"in D?J(Yn(V),D.ADD_URI_SAFE_ATTR,Ie):V,qr="ADD_DATA_URI_TAGS"in D?J(Yn(Hr),D.ADD_DATA_URI_TAGS,Ie):Hr,Bt="FORBID_CONTENTS"in D?J({},D.FORBID_CONTENTS,Ie):jt,me="FORBID_TAGS"in D?J({},D.FORBID_TAGS,Ie):{},ce="FORBID_ATTR"in D?J({},D.FORBID_ATTR,Ie):{},oe="USE_PROFILES"in D&&D.USE_PROFILES,se=!1!==D.ALLOW_ARIA_ATTR,le=!1!==D.ALLOW_DATA_ATTR,gt=D.ALLOW_UNKNOWN_PROTOCOLS||!1,Ft=!1!==D.ALLOW_SELF_CLOSE_IN_ATTR,ht=D.SAFE_FOR_TEMPLATES||!1,Ge=D.WHOLE_DOCUMENT||!1,Re=D.RETURN_DOM||!1,ke=D.RETURN_DOM_FRAGMENT||!1,Ce=D.RETURN_TRUSTED_TYPE||!1,te=D.FORCE_BODY||!1,Hs=!1!==D.SANITIZE_DOM,Pr=D.SANITIZE_NAMED_PROPS||!1,Ur=!1!==D.KEEP_CONTENT,ye=D.IN_PLACE||!1,T=D.ALLOWED_URI_REGEXP||S1,$e=D.NAMESPACE||ge,j=D.CUSTOM_ELEMENT_HANDLING||{},D.CUSTOM_ELEMENT_HANDLING&&P1(D.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(j.tagNameCheck=D.CUSTOM_ELEMENT_HANDLING.tagNameCheck),D.CUSTOM_ELEMENT_HANDLING&&P1(D.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(j.attributeNameCheck=D.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),D.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof D.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(j.allowCustomizedBuiltInElements=D.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),ht&&(le=!1),ke&&(Re=!0),oe&&(y=J({},T1),k=[],!0===oe.html&&(J(y,y1),J(k,C1)),!0===oe.svg&&(J(y,$u),J(k,Wu),J(k,qs)),!0===oe.svgFilters&&(J(y,Zu),J(k,Wu),J(k,qs)),!0===oe.mathMl&&(J(y,ju),J(k,N1),J(k,qs))),D.ADD_TAGS&&(y===L&&(y=Yn(y)),J(y,D.ADD_TAGS,Ie)),D.ADD_ATTR&&(k===H&&(k=Yn(k)),J(k,D.ADD_ATTR,Ie)),D.ADD_URI_SAFE_ATTR&&J(B,D.ADD_URI_SAFE_ATTR,Ie),D.FORBID_CONTENTS&&(Bt===jt&&(Bt=Yn(Bt)),J(Bt,D.FORBID_CONTENTS,Ie)),Ur&&(y["#text"]=!0),Ge&&J(y,["html","head","body"]),y.table&&(J(y,["tbody"]),delete me.tbody),D.TRUSTED_TYPES_POLICY){if("function"!=typeof D.TRUSTED_TYPES_POLICY.createHTML)throw Vo('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof D.TRUSTED_TYPES_POLICY.createScriptURL)throw Vo('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');E=D.TRUSTED_TYPES_POLICY,v=E.createHTML("")}else void 0===E&&(E=function(t,n){if("object"!=typeof t||"function"!=typeof t.createPolicy)return null;let r=null;const o="data-tt-policy-suffix";n&&n.hasAttribute(o)&&(r=n.getAttribute(o));const a="dompurify"+(r?"#"+r:"");try{return t.createPolicy(a,{createHTML:s=>s,createScriptURL:s=>s})}catch{return console.warn("TrustedTypes policy "+a+" could not be created."),null}}(m,o)),null!==E&&"string"==typeof v&&(v=E.createHTML(""));Qe&&Qe(D),Vr=D}},U1=J({},["mi","mo","mn","ms","mtext"]),q1=J({},["foreignobject","desc","title","annotation-xml"]),A8=J({},["title","style","font","a","script"]),H1=J({},[...$u,...Zu,...v3]),V1=J({},[...ju,...D3]),Xn=function(D){qo(t.removed,{element:D});try{D.parentNode.removeChild(D)}catch{D.remove()}},Ju=function(D,F){try{qo(t.removed,{attribute:F.getAttributeNode(D),from:F})}catch{qo(t.removed,{attribute:null,from:F})}if(F.removeAttribute(D),"is"===D&&!k[D])if(Re||ke)try{Xn(F)}catch{}else try{F.setAttribute(D,"")}catch{}},z1=function(D){let F=null,z=null;if(te)D=""+D;else{const je=h3(D,/^[\r\n\t ]+/);z=je&&je[0]}"application/xhtml+xml"===$o&&$e===ge&&(D=''+D+"");const he=E?E.createHTML(D):D;if($e===ge)try{F=(new p).parseFromString(he,$o)}catch{}if(!F||!F.documentElement){F=N.createDocument($e,"template",null);try{F.documentElement.innerHTML=zo?v:he}catch{}}const Ze=F.body||F.documentElement;return D&&z&&Ze.insertBefore(n.createTextNode(z),Ze.childNodes[0]||null),$e===ge?R.call(F,Ge?"html":"body")[0]:Ge?F.documentElement:Ze},G1=function(D){return _.call(D.ownerDocument||D,D,u.SHOW_ELEMENT|u.SHOW_COMMENT|u.SHOW_TEXT,null)},$1=function(D){return"function"==typeof i&&D instanceof i},an=function(D,F,z){I[D]&&Bs(I[D],(he=>{he.call(t,F,z,Vr)}))},Z1=function(D){let F=null;if(an("beforeSanitizeElements",D,null),function(D){return D instanceof d&&("string"!=typeof D.nodeName||"string"!=typeof D.textContent||"function"!=typeof D.removeChild||!(D.attributes instanceof c)||"function"!=typeof D.removeAttribute||"function"!=typeof D.setAttribute||"string"!=typeof D.namespaceURI||"function"!=typeof D.insertBefore||"function"!=typeof D.hasChildNodes)}(D))return Xn(D),!0;const z=Ie(D.nodeName);if(an("uponSanitizeElement",D,{tagName:z,allowedTags:y}),D.hasChildNodes()&&!$1(D.firstElementChild)&&mt(/<[/\w]/g,D.innerHTML)&&mt(/<[/\w]/g,D.textContent))return Xn(D),!0;if(!y[z]||me[z]){if(!me[z]&&W1(z)&&(j.tagNameCheck instanceof RegExp&&mt(j.tagNameCheck,z)||j.tagNameCheck instanceof Function&&j.tagNameCheck(z)))return!1;if(Ur&&!Bt[z]){const he=g(D)||D.parentNode,Ze=h(D)||D.childNodes;if(Ze&&he){for(let at=Ze.length-1;at>=0;--at)he.insertBefore(b(Ze[at],!0),C(D))}}return Xn(D),!0}return D instanceof l&&!function(D){let F=g(D);(!F||!F.tagName)&&(F={namespaceURI:$e,tagName:"template"});const z=Ps(D.tagName),he=Ps(F.tagName);return!!Go[D.namespaceURI]&&(D.namespaceURI===ne?F.namespaceURI===ge?"svg"===z:F.namespaceURI===K?"svg"===z&&("annotation-xml"===he||U1[he]):!!H1[z]:D.namespaceURI===K?F.namespaceURI===ge?"math"===z:F.namespaceURI===ne?"math"===z&&q1[he]:!!V1[z]:D.namespaceURI===ge?!(F.namespaceURI===ne&&!q1[he]||F.namespaceURI===K&&!U1[he])&&!V1[z]&&(A8[z]||!H1[z]):!("application/xhtml+xml"!==$o||!Go[D.namespaceURI]))}(D)||("noscript"===z||"noembed"===z||"noframes"===z)&&mt(/<\/no(script|embed|frames)/i,D.innerHTML)?(Xn(D),!0):(ht&&3===D.nodeType&&(F=D.textContent,Bs([Q,ie,Y],(he=>{F=Ho(F,he," ")})),D.textContent!==F&&(qo(t.removed,{element:D.cloneNode()}),D.textContent=F)),an("afterSanitizeElements",D,null),!1)},j1=function(D,F,z){if(Hs&&("id"===F||"name"===F)&&(z in n||z in E8))return!1;if((!le||ce[F]||!mt(O,F))&&(!se||!mt(G,F)))if(!k[F]||ce[F]){if(!(W1(D)&&(j.tagNameCheck instanceof RegExp&&mt(j.tagNameCheck,D)||j.tagNameCheck instanceof Function&&j.tagNameCheck(D))&&(j.attributeNameCheck instanceof RegExp&&mt(j.attributeNameCheck,F)||j.attributeNameCheck instanceof Function&&j.attributeNameCheck(F))||"is"===F&&j.allowCustomizedBuiltInElements&&(j.tagNameCheck instanceof RegExp&&mt(j.tagNameCheck,z)||j.tagNameCheck instanceof Function&&j.tagNameCheck(z))))return!1}else if(!B[F]&&!mt(T,Ho(z,x,""))&&("src"!==F&&"xlink:href"!==F&&"href"!==F||"script"===D||0!==E3(z,"data:")||!qr[D])&&(!gt||mt(P,Ho(z,x,"")))&&z)return!1;return!0},W1=function(D){return D.indexOf("-")>0},Y1=function(D){an("beforeSanitizeAttributes",D,null);const{attributes:F}=D;if(!F)return;const z={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:k};let he=F.length;for(;he--;){const Ze=F[he],{name:je,namespaceURI:at,value:Qn}=Ze,Zo=Ie(je);let st="value"===je?Qn:A3(Qn);if(z.attrName=Zo,z.attrValue=st,z.keepAttr=!0,z.forceKeepAttr=void 0,an("uponSanitizeAttribute",D,z),st=z.attrValue,z.forceKeepAttr||(Ju(je,D),!z.keepAttr))continue;if(!Ft&&mt(/\/>/i,st)){Ju(je,D);continue}ht&&Bs([Q,ie,Y],(X1=>{st=Ho(st,X1," ")}));const K1=Ie(D.nodeName);if(j1(K1,Zo,st)){if(Pr&&("id"===Zo||"name"===Zo)&&(Ju(je,D),st="user-content-"+st),E&&"object"==typeof m&&"function"==typeof m.getAttributeType&&!at)switch(m.getAttributeType(K1,Zo)){case"TrustedHTML":st=E.createHTML(st);break;case"TrustedScriptURL":st=E.createScriptURL(st)}try{at?D.setAttributeNS(at,je,st):D.setAttribute(je,st),D1(t.removed)}catch{}}}an("afterSanitizeAttributes",D,null)},v8=function $(D){let F=null;const z=G1(D);for(an("beforeSanitizeShadowDOM",D,null);F=z.nextNode();)an("uponSanitizeShadowNode",F,null),!Z1(F)&&(F.content instanceof a&&$(F.content),Y1(F));an("afterSanitizeShadowDOM",D,null)};return t.sanitize=function($){let D=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},F=null,z=null,he=null,Ze=null;if(zo=!$,zo&&($="\x3c!--\x3e"),"string"!=typeof $&&!$1($)){if("function"!=typeof $.toString)throw Vo("toString is not a function");if("string"!=typeof($=$.toString()))throw Vo("dirty is not a string, aborting")}if(!t.isSupported)return $;if(Ct||Qu(D),t.removed=[],"string"==typeof $&&(ye=!1),ye){if($.nodeName){const Qn=Ie($.nodeName);if(!y[Qn]||me[Qn])throw Vo("root node is forbidden and cannot be sanitized in-place")}}else if($ instanceof i)F=z1("\x3c!----\x3e"),z=F.ownerDocument.importNode($,!0),1===z.nodeType&&"BODY"===z.nodeName||"HTML"===z.nodeName?F=z:F.appendChild(z);else{if(!Re&&!ht&&!Ge&&-1===$.indexOf("<"))return E&&Ce?E.createHTML($):$;if(F=z1($),!F)return Re?null:Ce?v:""}F&&te&&Xn(F.firstChild);const je=G1(ye?$:F);for(;he=je.nextNode();)Z1(he)||(he.content instanceof a&&v8(he.content),Y1(he));if(ye)return $;if(Re){if(ke)for(Ze=S.call(F.ownerDocument);F.firstChild;)Ze.appendChild(F.firstChild);else Ze=F;return(k.shadowroot||k.shadowrootmode)&&(Ze=q.call(r,Ze,!0)),Ze}let at=Ge?F.outerHTML:F.innerHTML;return Ge&&y["!doctype"]&&F.ownerDocument&&F.ownerDocument.doctype&&F.ownerDocument.doctype.name&&mt(w1,F.ownerDocument.doctype.name)&&(at="\n"+at),ht&&Bs([Q,ie,Y],(Qn=>{at=Ho(at,Qn," ")})),E&&Ce?E.createHTML(at):at},t.setConfig=function(){Qu(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}),Ct=!0},t.clearConfig=function(){Vr=null,Ct=!1},t.isValidAttribute=function($,D,F){Vr||Qu({});const z=Ie($),he=Ie(D);return j1(z,he,F)},t.addHook=function($,D){"function"==typeof D&&(I[$]=I[$]||[],qo(I[$],D))},t.removeHook=function($){if(I[$])return D1(I[$])},t.removeHooks=function($){I[$]&&(I[$]=[])},t.removeAllHooks=function(){I={}},t}();function L1(e){if(!e)return"";try{return new Date(1e3*e).toLocaleTimeString([],{hour:"numeric",minute:"2-digit",hour12:!0})}catch{return""}}const k3=O3(window),I3=Z.forwardRef((({uuid:e=zn(),message:t,role:n,sources:r=[],error:o=!1,errorMsg:a=null,sentAt:s},i)=>{const l=pe.settings.textSize?`allm-text-[${pe.settings.textSize}px]`:"allm-text-sm";return o&&console.error(`ANYTHING_LLM_CHAT_WIDGET_ERROR: ${o}`),w.jsxs("div",{className:"py-[5px]",children:["assistant"===n&&w.jsx("div",{className:"allm-text-[10px] allm-text-gray-400 allm-ml-[54px] allm-mr-6 allm-mb-2 allm-text-left allm-font-sans",children:pe.settings.assistantName||"Anything LLM Chat Assistant"}),w.jsxs("div",{ref:i,className:"allm-flex allm-items-start allm-w-full allm-h-fit "+("user"===n?"allm-justify-end":"allm-justify-start"),children:["assistant"===n&&w.jsx("img",{src:pe.settings.assistantIcon||ko,alt:"Anything LLM Icon",className:"allm-w-9 allm-h-9 allm-flex-shrink-0 allm-ml-2 allm-mt-2",id:"anything-llm-icon"}),w.jsx("div",{style:{wordBreak:"break-word",backgroundColor:"user"===n?pe.USER_STYLES.msgBg:pe.ASSISTANT_STYLES.msgBg},className:`allm-py-[11px] allm-px-4 allm-flex allm-flex-col allm-font-sans ${o?"allm-bg-red-200 allm-rounded-lg allm-mr-[37px] allm-ml-[9px]":"user"===n?`${pe.USER_STYLES.base} allm-anything-llm-user-message`:`${pe.ASSISTANT_STYLES.base} allm-anything-llm-assistant-message`} allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)]`,children:w.jsx("div",{className:"allm-flex",children:o?w.jsxs("div",{className:"allm-p-2 allm-rounded-lg allm-bg-red-50 allm-text-red-500",children:[w.jsxs("span",{className:"allm-inline-block ",children:[w.jsx(ru,{className:"allm-h-4 allm-w-4 allm-mb-1 allm-inline-block"})," ","Could not respond to message."]}),w.jsx("p",{className:"allm-text-xs allm-font-mono allm-mt-2 allm-border-l-2 allm-border-red-500 allm-pl-2 allm-bg-red-300 allm-p-2 allm-rounded-sm",children:a||"Server error"})]}):w.jsx("span",{className:`allm-whitespace-pre-line allm-flex allm-flex-col allm-gap-y-1 ${l} allm-leading-[20px]`,dangerouslySetInnerHTML:{__html:k3.sanitize(A1(t))}})})})]},e),s&&w.jsx("div",{className:"allm-font-sans allm-text-[10px] allm-text-gray-400 allm-ml-[54px] allm-mr-6 allm-mt-2 "+("user"===n?"allm-text-right":"allm-text-left"),children:L1(s)})]})})),M3=Z.memo(I3),F3=Z.forwardRef((({uuid:e,reply:t,pending:n,error:r,sources:o=[],sentAt:a},s)=>t||0!==o.length||n||r?(r&&console.error(`ANYTHING_LLM_CHAT_WIDGET_ERROR: ${r}`),n?w.jsxs("div",{className:"allm-flex allm-items-start allm-w-full allm-h-fit allm-justify-start",children:[w.jsx("img",{src:pe.settings.assistantIcon||ko,alt:"Anything LLM Icon",className:"allm-w-9 allm-h-9 allm-flex-shrink-0 allm-ml-2"}),w.jsx("div",{style:{wordBreak:"break-word",backgroundColor:pe.ASSISTANT_STYLES.msgBg},className:`allm-py-[11px] allm-px-4 allm-flex allm-flex-col ${pe.ASSISTANT_STYLES.base} allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)]`,children:w.jsx("div",{className:"allm-flex allm-gap-x-5",children:w.jsx("div",{className:"allm-mx-4 allm-my-1 allm-dot-falling"})})})]}):r?w.jsxs("div",{className:"allm-flex allm-items-end allm-w-full allm-h-fit allm-justify-start",children:[w.jsx("img",{src:pe.settings.assistantIcon||ko,alt:"Anything LLM Icon",className:"allm-w-9 allm-h-9 allm-flex-shrink-0 allm-ml-2"}),w.jsx("div",{style:{wordBreak:"break-word"},className:"allm-py-[11px] allm-px-4 allm-rounded-lg allm-flex allm-flex-col allm-bg-red-200 allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)] allm-mr-[37px] allm-ml-[9px]",children:w.jsx("div",{className:"allm-flex allm-gap-x-5",children:w.jsxs("span",{className:"allm-inline-block allm-p-2 allm-rounded-lg allm-bg-red-50 allm-text-red-500",children:[w.jsx(ru,{className:"allm-h-4 allm-w-4 allm-mb-1 allm-inline-block"})," ","Could not respond to message.",w.jsx("span",{className:"allm-text-xs",children:"Server error"})]})})})]}):w.jsxs("div",{className:"allm-py-[5px]",children:[w.jsx("div",{className:"allm-text-[10px] allm-text-gray-400 allm-ml-[54px] allm-mr-6 allm-mb-2 allm-text-left allm-font-sans",children:pe.settings.assistantName||"Anything LLM Chat Assistant"}),w.jsxs("div",{ref:s,className:"allm-flex allm-items-start allm-w-full allm-h-fit allm-justify-start",children:[w.jsx("img",{src:pe.settings.assistantIcon||ko,alt:"Anything LLM Icon",className:"allm-w-9 allm-h-9 allm-flex-shrink-0 allm-ml-2"}),w.jsx("div",{style:{wordBreak:"break-word",backgroundColor:pe.ASSISTANT_STYLES.msgBg},className:`allm-py-[11px] allm-px-4 allm-flex allm-flex-col ${r?"allm-bg-red-200":pe.ASSISTANT_STYLES.base} allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)]`,children:w.jsx("div",{className:"allm-flex allm-gap-x-5",children:w.jsx("span",{className:"allm-font-sans allm-reply allm-whitespace-pre-line allm-font-normal allm-text-sm allm-md:text-sm allm-flex allm-flex-col allm-gap-y-1",dangerouslySetInnerHTML:{__html:A1(t)}})})})]},e),a&&w.jsx("div",{className:"allm-text-[10px] allm-text-gray-400 allm-ml-[54px] allm-mr-6 allm-mt-2 allm-text-left allm-font-sans",children:L1(a)})]})):null)),B3=Z.memo(F3);var O1=NaN,U3="[object Symbol]",q3=/^\s+|\s+$/g,H3=/^[-+]0x[0-9a-f]+$/i,V3=/^0b[01]+$/i,z3=/^0o[0-7]+$/i,G3=parseInt,$3="object"==typeof Nt&&Nt&&Nt.Object===Object&&Nt,Z3="object"==typeof self&&self&&self.Object===Object&&self,j3=$3||Z3||Function("return this")(),Y3=Object.prototype.toString,K3=Math.max,X3=Math.min,Yu=function(){return j3.Date.now()};function Ku(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function k1(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&Y3.call(e)==U3}(e))return O1;if(Ku(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=Ku(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(q3,"");var n=V3.test(e);return n||z3.test(e)?G3(e.slice(2),n?2:8):H3.test(e)?O1:+e}var t8=function(e,t,n){var r,o,a,s,i,l,u=0,c=!1,d=!1,p=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function m(_){var S=r,R=o;return r=o=void 0,u=_,s=e.apply(R,S)}function C(_){var S=_-l;return void 0===l||S>=t||S<0||d&&_-u>=a}function h(){var _=Yu();if(C(_))return g(_);i=setTimeout(h,function(_){var q=t-(_-l);return d?X3(q,a-(_-u)):q}(_))}function g(_){return i=void 0,p&&r?m(_):(r=o=void 0,s)}function N(){var _=Yu(),S=C(_);if(r=arguments,o=this,l=_,S){if(void 0===i)return function(_){return u=_,i=setTimeout(h,t),c?m(_):s}(l);if(d)return i=setTimeout(h,t),m(l)}return void 0===i&&(i=setTimeout(h,t)),s}return t=k1(t)||0,Ku(n)&&(c=!!n.leading,a=(d="maxWait"in n)?K3(k1(n.maxWait)||0,t):a,p="trailing"in n?!!n.trailing:p),N.cancel=function(){void 0!==i&&clearTimeout(i),u=0,r=l=o=i=void 0},N.flush=function(){return void 0===i?s:g(Yu())},N};const n8=jo(t8);function r8({settings:e={},history:t=[]}){const n=Z.useRef(null),[r,o]=Z.useState(!0),a=Z.useRef(null);Z.useEffect((()=>{l()}),[t]);const i=n8((()=>{if(!a.current)return;const c=a.current.scrollHeight-a.current.scrollTop-a.current.clientHeight<=40;o(c)}),100);Z.useEffect((()=>{!function(){if(!a.current)return null;const c=a.current;if(!c)return null;c.addEventListener("scroll",i)}()}),[]);const l=()=>{a.current&&a.current.scrollTo({top:a.current.scrollHeight,behavior:"smooth"})};return 0===t.length?w.jsx("div",{className:"allm-pb-[100px] allm-pt-[5px] allm-rounded-lg allm-px-2 allm-h-full allm-mt-2 allm-gap-y-2 allm-overflow-y-scroll allm-flex allm-flex-col allm-justify-start allm-no-scroll",children:w.jsxs("div",{className:"allm-flex allm-h-full allm-flex-col allm-items-center allm-justify-center",children:[w.jsx("p",{className:"allm-text-slate-400 allm-text-sm allm-font-sans allm-py-4 allm-text-center",children:(null==e?void 0:e.greeting)??"Send a chat to get started."}),w.jsx(a8,{settings:e})]})}):w.jsxs("div",{className:"allm-pb-[30px] allm-pt-[5px] allm-rounded-lg allm-px-2 allm-h-full allm-gap-y-2 allm-overflow-y-scroll allm-flex allm-flex-col allm-justify-start allm-no-scroll allm-md:max-h-[500px]",id:"chat-history",ref:a,children:[t.map(((u,c)=>{const d=c===t.length-1;return c===t.length-1&&"assistant"===u.role&&u.animate?w.jsx(B3,{ref:d?n:null,uuid:u.uuid,reply:u.content,pending:u.pending,sources:u.sources,error:u.error,closed:u.closed},u.uuid):w.jsx(M3,{ref:d?n:null,message:u.content,sentAt:u.sentAt||Date.now()/1e3,role:u.role,sources:u.sources,chatId:u.chatId,feedbackScore:u.feedbackScore,error:u.error,errorMsg:u.errorMsg},c)})),!r&&w.jsx("div",{className:"allm-fixed allm-bottom-[10rem] allm-right-[50px] allm-z-50 allm-cursor-pointer allm-animate-pulse",children:w.jsx("div",{className:"allm-flex allm-flex-col allm-items-center",children:w.jsx("div",{className:"allm-p-1 allm-rounded-full allm-border allm-border-white/10 allm-bg-black/20 hover:allm-bg-black/50",children:w.jsx(F2,{weight:"bold",className:"allm-text-white/50 allm-w-5 allm-h-5",onClick:l,id:"scroll-to-bottom-button","aria-label":"Scroll to bottom"})})})})]})}function o8(){return w.jsx("div",{className:"allm-h-full allm-w-full allm-relative",children:w.jsx("div",{className:"allm-h-full allm-max-h-[82vh] allm-pb-[100px] allm-pt-[5px] allm-bg-gray-100 allm-rounded-lg allm-px-2 allm-h-full allm-mt-2 allm-gap-y-2 allm-overflow-y-scroll allm-flex allm-flex-col allm-justify-start allm-no-scroll",children:w.jsx("div",{className:"allm-flex allm-h-full allm-flex-col allm-items-center allm-justify-center",children:w.jsx(nu,{size:14,className:"allm-text-slate-400 allm-animate-spin"})})})})}function a8({settings:e}){var t;return null!=(t=null==e?void 0:e.defaultMessages)&&t.length?w.jsx("div",{className:"allm-flex allm-flex-col allm-gap-y-2 allm-w-[75%]",children:e.defaultMessages.map(((n,r)=>w.jsx("button",{style:{opacity:0,wordBreak:"break-word",backgroundColor:pe.USER_STYLES.msgBg,fontSize:e.textSize},type:"button",onClick:()=>{window.dispatchEvent(new CustomEvent(Xu,{detail:{command:n}}))},className:"msg-suggestion allm-border-none hover:allm-shadow-[0_4px_14px_rgba(0,0,0,0.5)] allm-cursor-pointer allm-px-2 allm-py-2 allm-rounded-lg allm-text-white allm-w-full allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)]",children:n},r)))}):null}function s8({message:e,submit:t,onChange:n,inputDisabled:r,buttonDisabled:o}){const a=Z.useRef(null),s=Z.useRef(null),[i,l]=Z.useState(!1);Z.useEffect((()=>{!r&&s.current&&s.current.focus(),c()}),[r]);const c=()=>{s.current&&(s.current.style.height="auto")},p=m=>{const A=m.target;A.style.height="auto",A.style.height=0!==m.target.value.length?A.scrollHeight+"px":"auto"};return w.jsx("div",{className:"allm-w-full allm-sticky allm-bottom-0 allm-z-10 allm-flex allm-justify-center allm-items-center allm-bg-white",children:w.jsx("form",{onSubmit:m=>{l(!1),t(m)},className:"allm-flex allm-flex-col allm-gap-y-1 allm-rounded-t-lg allm-w-full allm-items-center allm-justify-center",children:w.jsx("div",{className:"allm-flex allm-items-center allm-w-full",children:w.jsx("div",{className:"allm-bg-white allm-flex allm-flex-col allm-px-4 allm-overflow-hidden allm-w-full",children:w.jsxs("div",{style:{border:"1.5px solid #22262833"},className:"allm-flex allm-items-center allm-w-full allm-rounded-2xl",children:[w.jsx("textarea",{ref:s,onKeyUp:p,onKeyDown:m=>{13==m.keyCode&&(m.shiftKey||t(m))},onChange:n,required:!0,disabled:r,onFocus:()=>l(!0),onBlur:m=>{l(!1),p(m)},value:e,className:"allm-font-sans allm-border-none allm-cursor-text allm-max-h-[100px] allm-text-[14px] allm-mx-2 allm-py-2 allm-w-full allm-text-black allm-bg-transparent placeholder:allm-text-slate-800/60 allm-resize-none active:allm-outline-none focus:allm-outline-none allm-flex-grow",placeholder:"Send a message",id:"message-input"}),w.jsxs("button",{ref:a,type:"submit",disabled:o,className:"allm-bg-transparent allm-border-none allm-inline-flex allm-justify-center allm-rounded-2xl allm-cursor-pointer allm-text-black group",id:"send-message-button","aria-label":"Send message",children:[o?w.jsx(nu,{className:"allm-w-4 allm-h-4 allm-animate-spin"}):w.jsx(mp,{size:24,className:"allm-my-3 allm-text-[#22262899]/60 group-hover:allm-text-[#22262899]/90",weight:"fill"}),w.jsx("span",{className:"allm-sr-only",children:"Send message"})]})]})})})})})}const Xu="anythingllm-embed-send-prompt";function l8({sessionId:e,settings:t,knownHistory:n=[]}){const[r,o]=Z.useState(""),[a,s]=Z.useState(!1),[i,l]=Z.useState(n);Z.useEffect((()=>{n.length!==i.length&&l([...n])}),[n]);Z.useEffect((()=>{!0===a&&async function(){const A=i.length>0?i[i.length-1]:null,b=i.length>0?i.slice(0,-1):[];var C=[...b];if(!A||null==A||!A.userMessage)return s(!1),!1;await ds.streamChat(e,t,A.userMessage,(h=>function(e,t,n,r,o){const{uuid:a,textResponse:s,type:i,sources:l=[],error:u,close:c,errorMsg:d=null}=e,p=o[o.length-1],m=null==p?void 0:p.sentAt;if("abort"===i)t(!1),n([...r,{uuid:a,content:s,role:"assistant",sources:l,closed:!0,error:u,errorMsg:d,animate:!1,pending:!1,sentAt:m}]),o.push({uuid:a,content:s,role:"assistant",sources:l,closed:!0,error:u,errorMsg:d,animate:!1,pending:!1,sentAt:m});else if("textResponse"===i)t(!1),n([...r,{uuid:a,content:s,role:"assistant",sources:l,closed:c,error:u,errorMsg:d,animate:!c,pending:!1,sentAt:m}]),o.push({uuid:a,content:s,role:"assistant",sources:l,closed:c,error:u,errorMsg:d,animate:!c,pending:!1,sentAt:m});else if("textResponseChunk"===i){const A=o.findIndex((b=>b.uuid===a));if(-1!==A){const b={...o[A]},C={...b,content:b.content+s,sources:l,error:u,errorMsg:d,closed:c,animate:!c,pending:!1,sentAt:m};o[A]=C}else o.push({uuid:a,sources:l,error:u,errorMsg:d,content:s,role:"assistant",closed:c,animate:!c,pending:!1,sentAt:m});n([...o])}}(h,s,l,b,C)))}()}),[a,i]);const p=m=>{m.detail.command&&((m,A=[],b=[])=>{if(!m||""===m)return!1;let C;C=A.length>0?[...A,{content:"",role:"assistant",pending:!0,userMessage:m,attachments:b,animate:!0}]:[...i,{content:m,role:"user",attachments:b},{content:"",role:"assistant",pending:!0,userMessage:m,animate:!0}],l(C),s(!0)})(m.detail.command,[],[])};return Z.useEffect((()=>(window.addEventListener(Xu,p),()=>{window.removeEventListener(Xu,p)})),[]),w.jsxs("div",{className:"allm-h-full allm-w-full allm-flex allm-flex-col",children:[w.jsx("div",{className:"allm-flex-grow allm-overflow-y-auto",children:w.jsx(r8,{settings:t,history:i})}),w.jsx(s8,{message:r,submit:async m=>{if(m.preventDefault(),!r||""===r)return!1;const A=[...i,{content:r,role:"user",sentAt:Math.floor(Date.now()/1e3)},{content:"",role:"assistant",pending:!0,userMessage:r,animate:!0,sentAt:Math.floor(Date.now()/1e3)}];l(A),o(""),s(!0)},onChange:m=>{o(m.target.value)},inputDisabled:a,buttonDisabled:a})]})}function I1({settings:e}){return e.noSponsor?null:w.jsx("div",{className:"allm-flex allm-w-full allm-items-center allm-justify-center",children:w.jsx("a",{style:{color:"#0119D9"},href:e.sponsorLink??"#",target:"_blank",rel:"noreferrer",className:"allm-text-xs allm-font-sans hover:allm-opacity-80 hover:allm-underline",children:e.sponsorText})})}function u8({setChatHistory:e,settings:t,sessionId:n}){return w.jsx("div",{className:"allm-w-full allm-flex allm-justify-center",children:w.jsx("button",{style:{color:"#7A7D7E"},className:"hover:allm-cursor-pointer allm-border-none allm-text-sm allm-bg-transparent hover:allm-opacity-80 hover:allm-underline",onClick:()=>(async()=>{await ds.resetEmbedChatSession(t,n),e([])})(),children:"Reset Chat"})})}function c8({closeChat:e,settings:t,sessionId:n}){const{chatHistory:r,setChatHistory:o,loading:a}=function(e=null,t=null){const[n,r]=Z.useState(!0),[o,a]=Z.useState([]);return Z.useEffect((()=>{!async function(){if(t&&e)try{const i=await ds.embedSessionHistory(e,t);a(i),r(!1)}catch(i){console.error("Error fetching historical chats:",i),r(!1)}}()}),[t,e]),{chatHistory:o,setChatHistory:a,loading:n}}(t,n);return a?w.jsxs("div",{className:"allm-flex allm-flex-col allm-h-full",children:[w.jsx(Cp,{sessionId:n,settings:t,iconUrl:t.brandImageUrl,closeChat:e,setChatHistory:o}),w.jsx(o8,{}),w.jsxs("div",{className:"allm-pt-4 allm-pb-2 allm-h-fit allm-gap-y-1",children:[w.jsx(mb,{}),w.jsx(I1,{settings:t})]})]}):(null==document||document.addEventListener("click",(function(e){var r;const t=e.target.closest("[data-code-snippet]"),n=null==(r=null==t?void 0:t.dataset)?void 0:r.code;if(!n)return!1;!function(e){var o,a,s;const t=document.querySelector(`[data-code="${e}"]`);if(!t)return!1;const n=null==(s=null==(a=null==(o=t.parentElement)?void 0:o.parentElement)?void 0:a.querySelector("pre:first-of-type"))?void 0:s.innerText;if(!n)return!1;window.navigator.clipboard.writeText(n),t.classList.add("allm-text-green-500");const r=t.innerHTML;t.innerText="Copied!",t.setAttribute("disabled",!0),setTimeout((()=>{t.classList.remove("allm-text-green-500"),t.innerHTML=r,t.removeAttribute("disabled")}),2500)}(n)})),w.jsxs("div",{className:"allm-flex allm-flex-col allm-h-full",children:[w.jsx(Cp,{sessionId:n,settings:t,iconUrl:t.brandImageUrl,closeChat:e,setChatHistory:o}),w.jsx("div",{className:"allm-flex-grow allm-overflow-y-auto",children:w.jsx(l8,{sessionId:n,settings:t,knownHistory:r})}),w.jsxs("div",{className:"allm-mt-4 allm-pb-4 allm-h-fit allm-gap-y-2 allm-z-10",children:[w.jsx(I1,{settings:t}),w.jsx(u8,{setChatHistory:o,settings:t,sessionId:n})]})]}))}const M1=document.createElement("div");document.body.appendChild(M1),js.createRoot(M1).render(w.jsx(f.StrictMode,{children:w.jsx((function(){const{isChatOpen:e,toggleOpenChat:t}=function(){var r;const[e,t]=Z.useState(!(null==(r=null==window?void 0:window.localStorage)||!r.getItem(tu))||!1);return{isChatOpen:e,toggleOpenChat:function(o){!0===o&&window.localStorage.setItem(tu,"1"),!1===o&&window.localStorage.removeItem(tu),t(o)}}}(),n=function(){const[e,t]=Z.useState({loaded:!1,...D2});return Z.useEffect((()=>{!function(){if(!document)return!1;if(!pe.settings.baseApiUrl||!pe.settings.embedId)throw new Error("[AnythingLLM Embed Module::Abort] - Invalid script tag setup detected. Missing required parameters for boot!");t({...D2,...Ch(pe.settings),loaded:!0})}()}),[document]),e}(),r=C2();if(Z.useEffect((()=>{"on"===n.openOnLoad&&t(!0)}),[n.loaded]),!n.loaded)return null;const o={"bottom-left":"allm-bottom-0 allm-left-0 allm-ml-4","bottom-right":"allm-bottom-0 allm-right-0 allm-mr-4","top-left":"allm-top-0 allm-left-0 allm-ml-4 allm-mt-4","top-right":"allm-top-0 allm-right-0 allm-mr-4 allm-mt-4"},a=n.position||"bottom-right",s=n.windowWidth??"400px",i=n.windowHeight??"700px";return w.jsxs(w.Fragment,{children:[w.jsx(kh,{}),w.jsx("div",{id:"anything-llm-embed-chat-container",className:"allm-fixed allm-inset-0 allm-z-50 "+(e?"allm-block":"allm-hidden"),children:w.jsx("div",{style:{maxWidth:s,maxHeight:i},className:`allm-h-full allm-w-full allm-bg-white allm-fixed allm-bottom-0 allm-right-0 allm-mb-4 allm-md:mr-4 allm-rounded-2xl allm-border allm-border-gray-300 allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)] ${o[a]}`,id:"anything-llm-chat",children:e&&w.jsx(c8,{closeChat:()=>t(!1),settings:n,sessionId:r})})}),!e&&w.jsx("div",{id:"anything-llm-embed-chat-button-container",className:`allm-fixed allm-bottom-0 ${o[a]} allm-mb-4 allm-z-50`,children:w.jsx(nb,{settings:n,isOpen:e,toggleOpen:()=>t(!0)})})]})}),{})}));const Kn=Object.assign({},(null==(F1=null==document?void 0:document.currentScript)?void 0:F1.dataset)||{}),pe={settings:Kn,stylesSrc:function(e=null){try{const t=new URL(e);return t.pathname=t.pathname.replace("anythingllm-chat-widget.js","anythingllm-chat-widget.min.css").replace("anythingllm-chat-widget.min.js","anythingllm-chat-widget.min.css"),t.toString()}catch{return""}}(null==(B1=null==document?void 0:document.currentScript)?void 0:B1.src),USER_STYLES:{msgBg:(null==Kn?void 0:Kn.userBgColor)??"#3DBEF5",base:"allm-text-white allm-rounded-t-[18px] allm-rounded-bl-[18px] allm-rounded-br-[4px] allm-mx-[20px]"},ASSISTANT_STYLES:{msgBg:(null==Kn?void 0:Kn.assistantBgColor)??"#FFFFFF",base:"allm-text-[#222628] allm-rounded-t-[18px] allm-rounded-br-[18px] allm-rounded-bl-[4px] allm-mr-[37px] allm-ml-[9px]"}};Jn.embedderSettings=pe,Object.defineProperty(Jn,Symbol.toStringTag,{value:"Module"})})); \ No newline at end of file diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico new file mode 100644 index 0000000..9205764 Binary files /dev/null and b/frontend/public/favicon.ico differ diff --git a/frontend/public/favicon.png b/frontend/public/favicon.png new file mode 100644 index 0000000..2dab536 Binary files /dev/null and b/frontend/public/favicon.png differ diff --git a/frontend/public/fonts/PlusJakartaSans.ttf b/frontend/public/fonts/PlusJakartaSans.ttf new file mode 100644 index 0000000..bdd4985 Binary files /dev/null and b/frontend/public/fonts/PlusJakartaSans.ttf differ diff --git a/frontend/public/robots.txt b/frontend/public/robots.txt new file mode 100644 index 0000000..77470cb --- /dev/null +++ b/frontend/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/frontend/scripts/postbuild.js b/frontend/scripts/postbuild.js new file mode 100644 index 0000000..bcba17b --- /dev/null +++ b/frontend/scripts/postbuild.js @@ -0,0 +1,8 @@ +import { renameSync } from 'fs'; +import { fileURLToPath } from 'url'; +import path from 'path'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +console.log(`Running frontend post build script...`) +renameSync(path.resolve(__dirname, '../dist/index.html'), path.resolve(__dirname, '../dist/_index.html')); +console.log(`index.html renamed to _index.html so SSR of the index page can be assumed.`); \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 0000000..9910414 --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,257 @@ +import React, { lazy, Suspense } from "react"; +import { Routes, Route } from "react-router-dom"; +import { I18nextProvider } from "react-i18next"; +import { ContextWrapper } from "@/AuthContext"; +import PrivateRoute, { + AdminRoute, + ManagerRoute, +} from "@/components/PrivateRoute"; +import { ToastContainer } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; +import Login from "@/pages/Login"; +import SimpleSSOPassthrough from "@/pages/Login/SSO/simple"; +import OnboardingFlow from "@/pages/OnboardingFlow"; +import i18n from "./i18n"; + +import { PfpProvider } from "./PfpContext"; +import { LogoProvider } from "./LogoContext"; +import { FullScreenLoader } from "./components/Preloader"; +import { ThemeProvider } from "./ThemeContext"; + +const Main = lazy(() => import("@/pages/Main")); +const InvitePage = lazy(() => import("@/pages/Invite")); +const WorkspaceChat = lazy(() => import("@/pages/WorkspaceChat")); +const AdminUsers = lazy(() => import("@/pages/Admin/Users")); +const AdminInvites = lazy(() => import("@/pages/Admin/Invitations")); +const AdminWorkspaces = lazy(() => import("@/pages/Admin/Workspaces")); +const AdminLogs = lazy(() => import("@/pages/Admin/Logging")); +const AdminAgents = lazy(() => import("@/pages/Admin/Agents")); +const GeneralChats = lazy(() => import("@/pages/GeneralSettings/Chats")); +const GeneralAppearance = lazy( + () => import("@/pages/GeneralSettings/Appearance") +); +const GeneralApiKeys = lazy(() => import("@/pages/GeneralSettings/ApiKeys")); +const GeneralLLMPreference = lazy( + () => import("@/pages/GeneralSettings/LLMPreference") +); +const GeneralTranscriptionPreference = lazy( + () => import("@/pages/GeneralSettings/TranscriptionPreference") +); +const GeneralAudioPreference = lazy( + () => import("@/pages/GeneralSettings/AudioPreference") +); +const GeneralEmbeddingPreference = lazy( + () => import("@/pages/GeneralSettings/EmbeddingPreference") +); +const EmbeddingTextSplitterPreference = lazy( + () => import("@/pages/GeneralSettings/EmbeddingTextSplitterPreference") +); +const GeneralVectorDatabase = lazy( + () => import("@/pages/GeneralSettings/VectorDatabase") +); +const GeneralSecurity = lazy(() => import("@/pages/GeneralSettings/Security")); +const GeneralBrowserExtension = lazy( + () => import("@/pages/GeneralSettings/BrowserExtensionApiKey") +); +const WorkspaceSettings = lazy(() => import("@/pages/WorkspaceSettings")); +const EmbedConfigSetup = lazy( + () => import("@/pages/GeneralSettings/EmbedConfigs") +); +const EmbedChats = lazy(() => import("@/pages/GeneralSettings/EmbedChats")); +const PrivacyAndData = lazy( + () => import("@/pages/GeneralSettings/PrivacyAndData") +); +const ExperimentalFeatures = lazy( + () => import("@/pages/Admin/ExperimentalFeatures") +); +const LiveDocumentSyncManage = lazy( + () => import("@/pages/Admin/ExperimentalFeatures/Features/LiveSync/manage") +); +const AgentBuilder = lazy(() => import("@/pages/Admin/AgentBuilder")); + +const CommunityHubTrending = lazy( + () => import("@/pages/GeneralSettings/CommunityHub/Trending") +); +const CommunityHubAuthentication = lazy( + () => import("@/pages/GeneralSettings/CommunityHub/Authentication") +); +const CommunityHubImportItem = lazy( + () => import("@/pages/GeneralSettings/CommunityHub/ImportItem") +); + +export default function App() { + return ( + + }> + + + + + + } /> + } /> + } + /> + + } + /> + } + /> + } + /> + } /> + + {/* Admin */} + } + /> + + } + /> + } + /> + + } + /> + + } + /> + } + /> + } + /> + + } + /> + + } + /> + } + /> + } + /> + } + /> + {/* Manager */} + } + /> + } + /> + } + /> + } + /> + } + /> + + } + /> + } + /> + } + /> + } + /> + } + /> + {/* Onboarding Flow */} + } /> + } + /> + + {/* Experimental feature pages */} + {/* Live Document Sync feature */} + } + /> + + } + /> + + } + /> + } + /> + + + + + + + + + ); +} diff --git a/frontend/src/AuthContext.jsx b/frontend/src/AuthContext.jsx new file mode 100644 index 0000000..34ec0cf --- /dev/null +++ b/frontend/src/AuthContext.jsx @@ -0,0 +1,32 @@ +import React, { useState, createContext } from "react"; +import { AUTH_TIMESTAMP, AUTH_TOKEN, AUTH_USER } from "@/utils/constants"; + +export const AuthContext = createContext(null); +export function ContextWrapper(props) { + const localUser = localStorage.getItem(AUTH_USER); + const localAuthToken = localStorage.getItem(AUTH_TOKEN); + const [store, setStore] = useState({ + user: localUser ? JSON.parse(localUser) : null, + authToken: localAuthToken ? localAuthToken : null, + }); + + const [actions] = useState({ + updateUser: (user, authToken = "") => { + localStorage.setItem(AUTH_USER, JSON.stringify(user)); + localStorage.setItem(AUTH_TOKEN, authToken); + setStore({ user, authToken }); + }, + unsetUser: () => { + localStorage.removeItem(AUTH_USER); + localStorage.removeItem(AUTH_TOKEN); + localStorage.removeItem(AUTH_TIMESTAMP); + setStore({ user: null, authToken: null }); + }, + }); + + return ( + + {props.children} + + ); +} diff --git a/frontend/src/LogoContext.jsx b/frontend/src/LogoContext.jsx new file mode 100644 index 0000000..3bf499d --- /dev/null +++ b/frontend/src/LogoContext.jsx @@ -0,0 +1,57 @@ +import { createContext, useEffect, useState } from "react"; +import AnythingLLM from "./media/logo/anything-llm.png"; +import AnythingLLMDark from "./media/logo/anything-llm-dark.png"; +import DefaultLoginLogoLight from "./media/illustrations/login-logo.svg"; +import DefaultLoginLogoDark from "./media/illustrations/login-logo-light.svg"; +import System from "./models/system"; + +export const REFETCH_LOGO_EVENT = "refetch-logo"; +export const LogoContext = createContext(); + +export function LogoProvider({ children }) { + const [logo, setLogo] = useState(""); + const [loginLogo, setLoginLogo] = useState(""); + const [isCustomLogo, setIsCustomLogo] = useState(false); + const DefaultLoginLogo = + localStorage.getItem("theme") !== "default" + ? DefaultLoginLogoDark + : DefaultLoginLogoLight; + + async function fetchInstanceLogo() { + try { + const { isCustomLogo, logoURL } = await System.fetchLogo(); + if (logoURL) { + setLogo(logoURL); + setLoginLogo(isCustomLogo ? logoURL : DefaultLoginLogo); + setIsCustomLogo(isCustomLogo); + } else { + localStorage.getItem("theme") !== "default" + ? setLogo(AnythingLLMDark) + : setLogo(AnythingLLM); + setLoginLogo(DefaultLoginLogo); + setIsCustomLogo(false); + } + } catch (err) { + localStorage.getItem("theme") !== "default" + ? setLogo(AnythingLLMDark) + : setLogo(AnythingLLM); + setLoginLogo(DefaultLoginLogo); + setIsCustomLogo(false); + console.error("Failed to fetch logo:", err); + } + } + + useEffect(() => { + fetchInstanceLogo(); + window.addEventListener(REFETCH_LOGO_EVENT, fetchInstanceLogo); + return () => { + window.removeEventListener(REFETCH_LOGO_EVENT, fetchInstanceLogo); + }; + }, []); + + return ( + + {children} + + ); +} diff --git a/frontend/src/PfpContext.jsx b/frontend/src/PfpContext.jsx new file mode 100644 index 0000000..3d60d55 --- /dev/null +++ b/frontend/src/PfpContext.jsx @@ -0,0 +1,30 @@ +import React, { createContext, useState, useEffect } from "react"; +import useUser from "./hooks/useUser"; +import System from "./models/system"; + +export const PfpContext = createContext(); + +export function PfpProvider({ children }) { + const [pfp, setPfp] = useState(null); + const { user } = useUser(); + + useEffect(() => { + async function fetchPfp() { + if (!user?.id) return; + try { + const pfpUrl = await System.fetchPfp(user.id); + setPfp(pfpUrl); + } catch (err) { + setPfp(null); + console.error("Failed to fetch pfp:", err); + } + } + fetchPfp(); + }, [user?.id]); + + return ( + + {children} + + ); +} diff --git a/frontend/src/ThemeContext.jsx b/frontend/src/ThemeContext.jsx new file mode 100644 index 0000000..f9a5c68 --- /dev/null +++ b/frontend/src/ThemeContext.jsx @@ -0,0 +1,16 @@ +import React, { createContext, useContext } from "react"; +import { useTheme } from "./hooks/useTheme"; + +const ThemeContext = createContext(); + +export function ThemeProvider({ children }) { + const themeValue = useTheme(); + + return ( + {children} + ); +} + +export function useThemeContext() { + return useContext(ThemeContext); +} diff --git a/frontend/src/components/CanViewChatHistory/index.jsx b/frontend/src/components/CanViewChatHistory/index.jsx new file mode 100644 index 0000000..44e7535 --- /dev/null +++ b/frontend/src/components/CanViewChatHistory/index.jsx @@ -0,0 +1,50 @@ +import { useEffect, useState } from "react"; +import { FullScreenLoader } from "@/components/Preloader"; +import System from "@/models/system"; +import paths from "@/utils/paths"; + +/** + * Protects the view from system set ups who cannot view chat history. + * If the user cannot view chat history, they are redirected to the home page. + * @param {React.ReactNode} children + */ +export function CanViewChatHistory({ children }) { + const { loading, viewable } = useCanViewChatHistory(); + if (loading) return ; + if (!viewable) { + window.location.href = paths.home(); + return ; + } + + return <>{children}; +} + +/** + * Provides the `viewable` state to the children. + * @returns {React.ReactNode} + */ +export function CanViewChatHistoryProvider({ children }) { + const { loading, viewable } = useCanViewChatHistory(); + if (loading) return null; + return <>{children({ viewable })}; +} + +/** + * Hook that fetches the can view chat history state from local storage or the system settings. + * @returns {Promise<{viewable: boolean, error: string | null}>} + */ +export function useCanViewChatHistory() { + const [loading, setLoading] = useState(true); + const [viewable, setViewable] = useState(false); + + useEffect(() => { + async function fetchViewable() { + const { viewable } = await System.fetchCanViewChatHistory(); + setViewable(viewable); + setLoading(false); + } + fetchViewable(); + }, []); + + return { loading, viewable }; +} diff --git a/frontend/src/components/ChangeWarning/index.jsx b/frontend/src/components/ChangeWarning/index.jsx new file mode 100644 index 0000000..95ca1c4 --- /dev/null +++ b/frontend/src/components/ChangeWarning/index.jsx @@ -0,0 +1,61 @@ +import { Warning, X } from "@phosphor-icons/react"; + +export default function ChangeWarningModal({ + warningText = "", + onClose, + onConfirm, +}) { + return ( +
+
+
+ +

+ WARNING - This action is irreversible +

+
+ +
+
+
+

+ {warningText.split("\\n").map((line, index) => ( + + {line} +
+
+ ))} +
+
+ Are you sure you want to proceed? +

+
+
+
+ + +
+
+ ); +} diff --git a/frontend/src/components/ChatBubble/index.jsx b/frontend/src/components/ChatBubble/index.jsx new file mode 100644 index 0000000..4ffc3d0 --- /dev/null +++ b/frontend/src/components/ChatBubble/index.jsx @@ -0,0 +1,28 @@ +import React from "react"; +import UserIcon from "../UserIcon"; +import { userFromStorage } from "@/utils/request"; + +export default function ChatBubble({ message, type, popMsg }) { + const isUser = type === "user"; + + return ( +
+
+
+ + + + {message} + +
+
+
+ ); +} diff --git a/frontend/src/components/ContextualSaveBar/index.jsx b/frontend/src/components/ContextualSaveBar/index.jsx new file mode 100644 index 0000000..42d1490 --- /dev/null +++ b/frontend/src/components/ContextualSaveBar/index.jsx @@ -0,0 +1,32 @@ +import { Warning } from "@phosphor-icons/react"; + +export default function ContextualSaveBar({ + showing = false, + onSave, + onCancel, +}) { + if (!showing) return null; + + return ( +
+
+ +

Unsaved Changes

+
+
+ + +
+
+ ); +} diff --git a/frontend/src/components/DataConnectorOption/index.jsx b/frontend/src/components/DataConnectorOption/index.jsx new file mode 100644 index 0000000..038624a --- /dev/null +++ b/frontend/src/components/DataConnectorOption/index.jsx @@ -0,0 +1,25 @@ +export default function DataConnectorOption({ slug }) { + if (!DATA_CONNECTORS.hasOwnProperty(slug)) return null; + const { path, image, name, description, link } = DATA_CONNECTORS[slug]; + + return ( + + + {link} + + + + ); +} diff --git a/frontend/src/components/DataConnectorOption/media/confluence.jpeg b/frontend/src/components/DataConnectorOption/media/confluence.jpeg new file mode 100644 index 0000000..7559663 Binary files /dev/null and b/frontend/src/components/DataConnectorOption/media/confluence.jpeg differ diff --git a/frontend/src/components/DataConnectorOption/media/github.svg b/frontend/src/components/DataConnectorOption/media/github.svg new file mode 100644 index 0000000..e017221 --- /dev/null +++ b/frontend/src/components/DataConnectorOption/media/github.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/components/DataConnectorOption/media/gitlab.svg b/frontend/src/components/DataConnectorOption/media/gitlab.svg new file mode 100644 index 0000000..0d48a00 --- /dev/null +++ b/frontend/src/components/DataConnectorOption/media/gitlab.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/components/DataConnectorOption/media/index.js b/frontend/src/components/DataConnectorOption/media/index.js new file mode 100644 index 0000000..d18803f --- /dev/null +++ b/frontend/src/components/DataConnectorOption/media/index.js @@ -0,0 +1,15 @@ +import GitHub from "./github.svg"; +import GitLab from "./gitlab.svg"; +import YouTube from "./youtube.svg"; +import Link from "./link.svg"; +import Confluence from "./confluence.jpeg"; + +const ConnectorImages = { + github: GitHub, + gitlab: GitLab, + youtube: YouTube, + websiteDepth: Link, + confluence: Confluence, +}; + +export default ConnectorImages; diff --git a/frontend/src/components/DataConnectorOption/media/link.svg b/frontend/src/components/DataConnectorOption/media/link.svg new file mode 100644 index 0000000..c957e54 --- /dev/null +++ b/frontend/src/components/DataConnectorOption/media/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/DataConnectorOption/media/youtube.svg b/frontend/src/components/DataConnectorOption/media/youtube.svg new file mode 100644 index 0000000..5fd9776 --- /dev/null +++ b/frontend/src/components/DataConnectorOption/media/youtube.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/components/DefaultChat/index.jsx b/frontend/src/components/DefaultChat/index.jsx new file mode 100644 index 0000000..4340ada --- /dev/null +++ b/frontend/src/components/DefaultChat/index.jsx @@ -0,0 +1,255 @@ +import React, { useEffect, useState } from "react"; +import { + GithubLogo, + GitMerge, + EnvelopeSimple, + Plus, +} from "@phosphor-icons/react"; +import NewWorkspaceModal, { + useNewWorkspaceModal, +} from "../Modals/NewWorkspace"; +import paths from "@/utils/paths"; +import { isMobile } from "react-device-detect"; +import { SidebarMobileHeader } from "../Sidebar"; +import ChatBubble from "../ChatBubble"; +import System from "@/models/system"; +import UserIcon from "../UserIcon"; +import { userFromStorage } from "@/utils/request"; +import useUser from "@/hooks/useUser"; +import { useTranslation, Trans } from "react-i18next"; +import Appearance from "@/models/appearance"; + +export default function DefaultChatContainer() { + const { showScrollbar } = Appearance.getSettings(); + const [mockMsgs, setMockMessages] = useState([]); + const { user } = useUser(); + const [fetchedMessages, setFetchedMessages] = useState([]); + const { + showing: showingNewWsModal, + showModal: showNewWsModal, + hideModal: hideNewWsModal, + } = useNewWorkspaceModal(); + const popMsg = !window.localStorage.getItem("anythingllm_intro"); + const { t } = useTranslation(); + + useEffect(() => { + const fetchData = async () => { + const fetchedMessages = await System.getWelcomeMessages(); + setFetchedMessages(fetchedMessages); + }; + fetchData(); + }, []); + + const MESSAGES = [ + + + + + {t("welcomeMessage.part1")} + + + , + + + + + + {t("welcomeMessage.part2")} + + + , + + + + + +
+ {t("welcomeMessage.part3")} + + +

{t("welcomeMessage.githubIssue")}

+
+
+
+
+
, + + + + + + {t("welcomeMessage.user1")} + + + , + + + + + +
+ {t("welcomeMessage.part4")} + + {(!user || user?.role !== "default") && ( + + )} +
+
+
+
, + + + + + + {t("welcomeMessage.user2")} + + + , + + + + + + + , + br:
, + }} + /> +
+
+
+
, + + + + + + {t("welcomeMessage.user3")} + + + , + + + + + + + + + , + ]; + + useEffect(() => { + function processMsgs() { + if (!!window.localStorage.getItem("anythingllm_intro")) { + setMockMessages([...MESSAGES]); + return false; + } else { + setMockMessages([MESSAGES[0]]); + } + + var timer = 500; + var messages = []; + + MESSAGES.map((child) => { + setTimeout(() => { + setMockMessages([...messages, child]); + messages.push(child); + }, timer); + timer += 2_500; + }); + window.localStorage.setItem("anythingllm_intro", 1); + } + + processMsgs(); + }, []); + + return ( +
+ {isMobile && } + {fetchedMessages.length === 0 + ? mockMsgs.map((content, i) => { + return {content}; + }) + : fetchedMessages.map((fetchedMessage, i) => { + return ( + + + + ); + })} + {showingNewWsModal && } +
+ ); +} + +function MessageContainer({ children }) { + return ( +
+
+ {children} +
+
+ ); +} + +function MessageContent({ children }) { + return
{children}
; +} + +function MessageText({ children }) { + return ( + + {children} + + ); +} diff --git a/frontend/src/components/EditingChatBubble/index.jsx b/frontend/src/components/EditingChatBubble/index.jsx new file mode 100644 index 0000000..feabd4c --- /dev/null +++ b/frontend/src/components/EditingChatBubble/index.jsx @@ -0,0 +1,69 @@ +import React, { useState } from "react"; +import { X } from "@phosphor-icons/react"; +import { useTranslation } from "react-i18next"; + +export default function EditingChatBubble({ + message, + index, + type, + handleMessageChange, + removeMessage, +}) { + const [isEditing, setIsEditing] = useState(false); + const [tempMessage, setTempMessage] = useState(message[type]); + const isUser = type === "user"; + const { t } = useTranslation(); + + return ( +
+

+ {isUser ? t("common.user") : t("appearance.message.assistant")} +

+
+ +
setIsEditing(true)} + > + {isEditing ? ( + setTempMessage(e.target.value)} + onBlur={() => { + handleMessageChange(index, type, tempMessage); + setIsEditing(false); + }} + autoFocus + className={`w-full light:text-white ${ + isUser ? "bg-[#41444C] text-white" : "bg-[#2E3036] text-white" + }`} + /> + ) : ( + tempMessage && ( +

+ {tempMessage} +

+ ) + )} +
+
+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/AzureAiOptions/index.jsx b/frontend/src/components/EmbeddingSelection/AzureAiOptions/index.jsx new file mode 100644 index 0000000..ce1b926 --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/AzureAiOptions/index.jsx @@ -0,0 +1,55 @@ +export default function AzureAiOptions({ settings }) { + return ( +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/CohereOptions/index.jsx b/frontend/src/components/EmbeddingSelection/CohereOptions/index.jsx new file mode 100644 index 0000000..70c385d --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/CohereOptions/index.jsx @@ -0,0 +1,55 @@ +export default function CohereEmbeddingOptions({ settings }) { + return ( +
+
+
+ + +
+
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/EmbedderItem/index.jsx b/frontend/src/components/EmbeddingSelection/EmbedderItem/index.jsx new file mode 100644 index 0000000..22003f7 --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/EmbedderItem/index.jsx @@ -0,0 +1,37 @@ +export default function EmbedderItem({ + name, + value, + image, + description, + checked, + onClick, +}) { + return ( +
onClick(value)} + className={`w-full p-2 rounded-md hover:cursor-pointer hover:bg-theme-bg-secondary ${ + checked ? "bg-theme-bg-secondary" : "" + }`} + > + +
+ {`${name} +
+
{name}
+
{description}
+
+
+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/GeminiOptions/index.jsx b/frontend/src/components/EmbeddingSelection/GeminiOptions/index.jsx new file mode 100644 index 0000000..c25a2b1 --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/GeminiOptions/index.jsx @@ -0,0 +1,47 @@ +export default function GeminiOptions({ settings }) { + return ( +
+
+
+ + +
+
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/GenericOpenAiOptions/index.jsx b/frontend/src/components/EmbeddingSelection/GenericOpenAiOptions/index.jsx new file mode 100644 index 0000000..e1ee621 --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/GenericOpenAiOptions/index.jsx @@ -0,0 +1,118 @@ +import React, { useState } from "react"; +import { CaretDown, CaretUp } from "@phosphor-icons/react"; + +export default function GenericOpenAiEmbeddingOptions({ settings }) { + const [showAdvancedControls, setShowAdvancedControls] = useState(false); + return ( +
+
+
+ + +
+
+ + +
+
+ + e.target.blur()} + defaultValue={settings?.EmbeddingModelMaxChunkLength} + required={false} + autoComplete="off" + /> +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/LMStudioOptions/index.jsx b/frontend/src/components/EmbeddingSelection/LMStudioOptions/index.jsx new file mode 100644 index 0000000..1fdf396 --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/LMStudioOptions/index.jsx @@ -0,0 +1,200 @@ +import React, { useEffect, useState } from "react"; +import System from "@/models/system"; +import PreLoader from "@/components/Preloader"; +import { LMSTUDIO_COMMON_URLS } from "@/utils/constants"; +import { CaretDown, CaretUp } from "@phosphor-icons/react"; +import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery"; + +export default function LMStudioEmbeddingOptions({ settings }) { + const { + autoDetecting: loading, + basePath, + basePathValue, + showAdvancedControls, + setShowAdvancedControls, + handleAutoDetectClick, + } = useProviderEndpointAutoDiscovery({ + provider: "lmstudio", + initialBasePath: settings?.EmbeddingBasePath, + ENDPOINTS: LMSTUDIO_COMMON_URLS, + }); + + const [maxChunkLength, setMaxChunkLength] = useState( + settings?.EmbeddingModelMaxChunkLength || 8192 + ); + + const handleMaxChunkLengthChange = (e) => { + setMaxChunkLength(Number(e.target.value)); + }; + + return ( +
+
+ +
+ + e.target.blur()} + required={true} + autoComplete="off" + /> +

+ Maximum length of text chunks for embedding. +

+
+
+
+ +
+ + +
+ ); +} + +function LMStudioModelSelection({ settings, basePath = null }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!basePath) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + try { + const { models } = await System.customModels( + "lmstudio", + null, + basePath + ); + setCustomModels(models || []); + } catch (error) { + console.error("Failed to fetch custom models:", error); + setCustomModels([]); + } + setLoading(false); + } + findCustomModels(); + }, [basePath]); + + if (loading || customModels.length == 0) { + return ( +
+ + +

+ Select the LM Studio model for embeddings. Models will load after + entering a valid LM Studio URL. +

+
+ ); + } + + return ( +
+ + +

+ Choose the LM Studio model you want to use for generating embeddings. +

+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/LiteLLMOptions/index.jsx b/frontend/src/components/EmbeddingSelection/LiteLLMOptions/index.jsx new file mode 100644 index 0000000..37ed233 --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/LiteLLMOptions/index.jsx @@ -0,0 +1,186 @@ +import { useEffect, useState } from "react"; +import System from "@/models/system"; +import { Warning } from "@phosphor-icons/react"; +import { Tooltip } from "react-tooltip"; + +export default function LiteLLMOptions({ settings }) { + const [basePathValue, setBasePathValue] = useState(settings?.LiteLLMBasePath); + const [basePath, setBasePath] = useState(settings?.LiteLLMBasePath); + const [apiKeyValue, setApiKeyValue] = useState(settings?.LiteLLMAPIKey); + const [apiKey, setApiKey] = useState(settings?.LiteLLMAPIKey); + + return ( +
+
+
+ + setBasePathValue(e.target.value)} + onBlur={() => setBasePath(basePathValue)} + /> +
+ +
+ + e.target.blur()} + defaultValue={settings?.EmbeddingModelMaxChunkLength} + required={false} + autoComplete="off" + /> +
+
+
+
+
+ +
+ setApiKeyValue(e.target.value)} + onBlur={() => setApiKey(apiKeyValue)} + /> +
+
+
+ ); +} + +function LiteLLMModelSelection({ settings, basePath = null, apiKey = null }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!basePath) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + const { models } = await System.customModels( + "litellm", + typeof apiKey === "boolean" ? null : apiKey, + basePath + ); + setCustomModels(models || []); + setLoading(false); + } + findCustomModels(); + }, [basePath, apiKey]); + + if (loading || customModels.length == 0) { + return ( +
+ + +
+ ); + } + + return ( +
+
+ + +
+ +
+ ); +} + +function EmbeddingModelTooltip() { + return ( +
+ + +

+ Be sure to select a valid embedding model. Chat models are not + embedding models. See{" "} + + this page + {" "} + for more information. +

+
+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/LocalAiOptions/index.jsx b/frontend/src/components/EmbeddingSelection/LocalAiOptions/index.jsx new file mode 100644 index 0000000..ce81ebe --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/LocalAiOptions/index.jsx @@ -0,0 +1,197 @@ +import React, { useEffect, useState } from "react"; +import { CaretDown, CaretUp } from "@phosphor-icons/react"; +import System from "@/models/system"; +import PreLoader from "@/components/Preloader"; +import { LOCALAI_COMMON_URLS } from "@/utils/constants"; +import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery"; + +export default function LocalAiOptions({ settings }) { + const { + autoDetecting: loading, + basePath, + basePathValue, + showAdvancedControls, + setShowAdvancedControls, + handleAutoDetectClick, + } = useProviderEndpointAutoDiscovery({ + provider: "localai", + initialBasePath: settings?.EmbeddingBasePath, + ENDPOINTS: LOCALAI_COMMON_URLS, + }); + const [apiKeyValue, setApiKeyValue] = useState(settings?.LocalAiApiKey); + const [apiKey, setApiKey] = useState(settings?.LocalAiApiKey); + + return ( +
+
+ +
+ + e.target.blur()} + defaultValue={settings?.EmbeddingModelMaxChunkLength} + required={false} + autoComplete="off" + /> +
+
+
+ +
+ setApiKeyValue(e.target.value)} + onBlur={() => setApiKey(apiKeyValue)} + /> +
+
+
+ +
+ +
+ ); +} + +function LocalAIModelSelection({ settings, apiKey = null, basePath = null }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!basePath || !basePath.includes("/v1")) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + const { models } = await System.customModels( + "localai", + typeof apiKey === "boolean" ? null : apiKey, + basePath + ); + setCustomModels(models || []); + setLoading(false); + } + findCustomModels(); + }, [basePath, apiKey]); + + if (loading || customModels.length == 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/MistralAiOptions/index.jsx b/frontend/src/components/EmbeddingSelection/MistralAiOptions/index.jsx new file mode 100644 index 0000000..d95052b --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/MistralAiOptions/index.jsx @@ -0,0 +1,44 @@ +export default function MistralAiOptions({ settings }) { + return ( +
+
+
+ + +
+
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/NativeEmbeddingOptions/index.jsx b/frontend/src/components/EmbeddingSelection/NativeEmbeddingOptions/index.jsx new file mode 100644 index 0000000..e3f974b --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/NativeEmbeddingOptions/index.jsx @@ -0,0 +1,12 @@ +import { useTranslation } from "react-i18next"; + +export default function NativeEmbeddingOptions() { + const { t } = useTranslation(); + return ( +
+

+ {t("embedding.provider.description")} +

+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/OllamaOptions/index.jsx b/frontend/src/components/EmbeddingSelection/OllamaOptions/index.jsx new file mode 100644 index 0000000..10c20b3 --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/OllamaOptions/index.jsx @@ -0,0 +1,199 @@ +import React, { useEffect, useState } from "react"; +import System from "@/models/system"; +import PreLoader from "@/components/Preloader"; +import { OLLAMA_COMMON_URLS } from "@/utils/constants"; +import { CaretDown, CaretUp } from "@phosphor-icons/react"; +import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery"; + +export default function OllamaEmbeddingOptions({ settings }) { + const { + autoDetecting: loading, + basePath, + basePathValue, + showAdvancedControls, + setShowAdvancedControls, + handleAutoDetectClick, + } = useProviderEndpointAutoDiscovery({ + provider: "ollama", + initialBasePath: settings?.EmbeddingBasePath, + ENDPOINTS: OLLAMA_COMMON_URLS, + }); + + const [maxChunkLength, setMaxChunkLength] = useState( + settings?.EmbeddingModelMaxChunkLength || 8192 + ); + + const handleMaxChunkLengthChange = (e) => { + setMaxChunkLength(Number(e.target.value)); + }; + + return ( +
+
+ +
+ + e.target.blur()} + required={true} + autoComplete="off" + /> +

+ Maximum length of text chunks for embedding. +

+
+
+
+ +
+ + +
+ ); +} + +function OllamaEmbeddingModelSelection({ settings, basePath = null }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!basePath) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + try { + const { models } = await System.customModels("ollama", null, basePath); + setCustomModels(models || []); + } catch (error) { + console.error("Failed to fetch custom models:", error); + setCustomModels([]); + } + setLoading(false); + } + findCustomModels(); + }, [basePath]); + + if (loading || customModels.length == 0) { + return ( +
+ + +

+ Select the Ollama model for embeddings. Models will load after + entering a valid Ollama URL. +

+
+ ); + } + + return ( +
+ + +

+ Choose the Ollama model you want to use for generating embeddings. +

+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/OpenAiOptions/index.jsx b/frontend/src/components/EmbeddingSelection/OpenAiOptions/index.jsx new file mode 100644 index 0000000..4199843 --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/OpenAiOptions/index.jsx @@ -0,0 +1,51 @@ +export default function OpenAiOptions({ settings }) { + return ( +
+
+
+ + +
+
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/EmbeddingSelection/VoyageAiOptions/index.jsx b/frontend/src/components/EmbeddingSelection/VoyageAiOptions/index.jsx new file mode 100644 index 0000000..a2ae1c4 --- /dev/null +++ b/frontend/src/components/EmbeddingSelection/VoyageAiOptions/index.jsx @@ -0,0 +1,56 @@ +export default function VoyageAiOptions({ settings }) { + return ( +
+
+
+ + +
+
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/Footer/index.jsx b/frontend/src/components/Footer/index.jsx new file mode 100644 index 0000000..fa4a0a9 --- /dev/null +++ b/frontend/src/components/Footer/index.jsx @@ -0,0 +1,146 @@ +import System from "@/models/system"; +import paths from "@/utils/paths"; +import { + BookOpen, + DiscordLogo, + GithubLogo, + Briefcase, + Envelope, + Globe, + HouseLine, + Info, + LinkSimple, +} from "@phosphor-icons/react"; +import React, { useEffect, useState } from "react"; +import SettingsButton from "../SettingsButton"; +import { isMobile } from "react-device-detect"; +import { Tooltip } from "react-tooltip"; +import { Link } from "react-router-dom"; + +export const MAX_ICONS = 3; +export const ICON_COMPONENTS = { + BookOpen: BookOpen, + DiscordLogo: DiscordLogo, + GithubLogo: GithubLogo, + Envelope: Envelope, + LinkSimple: LinkSimple, + HouseLine: HouseLine, + Globe: Globe, + Briefcase: Briefcase, + Info: Info, +}; + +export default function Footer() { + const [footerData, setFooterData] = useState(false); + + useEffect(() => { + async function fetchFooterData() { + const { footerData } = await System.fetchCustomFooterIcons(); + setFooterData(footerData); + } + fetchFooterData(); + }, []); + + // wait for some kind of non-false response from footer data first + // to prevent pop-in. + if (footerData === false) return null; + + if (!Array.isArray(footerData) || footerData.length === 0) { + return ( +
+
+
+ + + +
+
+ + + +
+
+ + + +
+ {!isMobile && } +
+ +
+ ); + } + + return ( + + ); +} diff --git a/frontend/src/components/LLMSelection/AnthropicAiOptions/index.jsx b/frontend/src/components/LLMSelection/AnthropicAiOptions/index.jsx new file mode 100644 index 0000000..a745114 --- /dev/null +++ b/frontend/src/components/LLMSelection/AnthropicAiOptions/index.jsx @@ -0,0 +1,57 @@ +export default function AnthropicAiOptions({ settings }) { + return ( +
+
+
+ + +
+ + {!settings?.credentialsOnly && ( +
+ + +
+ )} +
+
+ ); +} diff --git a/frontend/src/components/LLMSelection/ApiPieOptions/index.jsx b/frontend/src/components/LLMSelection/ApiPieOptions/index.jsx new file mode 100644 index 0000000..dd8162f --- /dev/null +++ b/frontend/src/components/LLMSelection/ApiPieOptions/index.jsx @@ -0,0 +1,101 @@ +import System from "@/models/system"; +import { useState, useEffect } from "react"; + +export default function ApiPieLLMOptions({ settings }) { + return ( +
+
+
+ + +
+ {!settings?.credentialsOnly && ( + + )} +
+
+ ); +} + +function APIPieModelSelection({ settings }) { + const [groupedModels, setGroupedModels] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models } = await System.customModels("apipie"); + if (models?.length > 0) { + const modelsByOrganization = models.reduce((acc, model) => { + acc[model.organization] = acc[model.organization] || []; + acc[model.organization].push(model); + return acc; + }, {}); + + setGroupedModels(modelsByOrganization); + } + + setLoading(false); + } + findCustomModels(); + }, []); + + if (loading || Object.keys(groupedModels).length === 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/AwsBedrockLLMOptions/index.jsx b/frontend/src/components/LLMSelection/AwsBedrockLLMOptions/index.jsx new file mode 100644 index 0000000..779d487 --- /dev/null +++ b/frontend/src/components/LLMSelection/AwsBedrockLLMOptions/index.jsx @@ -0,0 +1,191 @@ +import { ArrowSquareOut, Info } from "@phosphor-icons/react"; +import { AWS_REGIONS } from "./regions"; +import { useState } from "react"; + +export default function AwsBedrockLLMOptions({ settings }) { + const [useSessionToken, setUseSessionToken] = useState( + settings?.AwsBedrockLLMConnectionMethod === "sessionToken" + ); + + return ( +
+ {!settings?.credentialsOnly && ( +
+
+ +

+ You should use a properly defined IAM user for inferencing. +
+ + Read more on how to use AWS Bedrock in AnythingLLM + + +

+
+
+ )} + +
+ +
+ +

+ Select the method to authenticate with AWS Bedrock. +

+
+
+ + IAM + + + + Session Token + +
+
+ +
+
+ + +
+
+ + +
+ {useSessionToken && ( +
+ + +
+ )} +
+ + +
+
+ +
+ {!settings?.credentialsOnly && ( + <> +
+ + +
+
+ + e.target.blur()} + defaultValue={settings?.AwsBedrockLLMTokenLimit} + required={true} + autoComplete="off" + /> +
+ + )} +
+
+ ); +} diff --git a/frontend/src/components/LLMSelection/AwsBedrockLLMOptions/regions.js b/frontend/src/components/LLMSelection/AwsBedrockLLMOptions/regions.js new file mode 100644 index 0000000..b490a07 --- /dev/null +++ b/frontend/src/components/LLMSelection/AwsBedrockLLMOptions/regions.js @@ -0,0 +1,204 @@ +export const AWS_REGIONS = [ + { + name: "N. Virginia", + full_name: "US East (N. Virginia)", + code: "us-east-1", + public: true, + zones: [ + "us-east-1a", + "us-east-1b", + "us-east-1c", + "us-east-1d", + "us-east-1e", + "us-east-1f", + ], + }, + { + name: "Ohio", + full_name: "US East (Ohio)", + code: "us-east-2", + public: true, + zones: ["us-east-2a", "us-east-2b", "us-east-2c"], + }, + { + name: "N. California", + full_name: "US West (N. California)", + code: "us-west-1", + public: true, + zone_limit: 2, + zones: ["us-west-1a", "us-west-1b", "us-west-1c"], + }, + { + name: "Oregon", + full_name: "US West (Oregon)", + code: "us-west-2", + public: true, + zones: ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"], + }, + { + name: "GovCloud West", + full_name: "AWS GovCloud (US)", + code: "us-gov-west-1", + public: false, + zones: ["us-gov-west-1a", "us-gov-west-1b", "us-gov-west-1c"], + }, + { + name: "GovCloud East", + full_name: "AWS GovCloud (US-East)", + code: "us-gov-east-1", + public: false, + zones: ["us-gov-east-1a", "us-gov-east-1b", "us-gov-east-1c"], + }, + { + name: "Canada", + full_name: "Canada (Central)", + code: "ca-central-1", + public: true, + zones: ["ca-central-1a", "ca-central-1b", "ca-central-1c", "ca-central-1d"], + }, + { + name: "Stockholm", + full_name: "EU (Stockholm)", + code: "eu-north-1", + public: true, + zones: ["eu-north-1a", "eu-north-1b", "eu-north-1c"], + }, + { + name: "Ireland", + full_name: "EU (Ireland)", + code: "eu-west-1", + public: true, + zones: ["eu-west-1a", "eu-west-1b", "eu-west-1c"], + }, + { + name: "London", + full_name: "EU (London)", + code: "eu-west-2", + public: true, + zones: ["eu-west-2a", "eu-west-2b", "eu-west-2c"], + }, + { + name: "Paris", + full_name: "EU (Paris)", + code: "eu-west-3", + public: true, + zones: ["eu-west-3a", "eu-west-3b", "eu-west-3c"], + }, + { + name: "Frankfurt", + full_name: "EU (Frankfurt)", + code: "eu-central-1", + public: true, + zones: ["eu-central-1a", "eu-central-1b", "eu-central-1c"], + }, + { + name: "Milan", + full_name: "EU (Milan)", + code: "eu-south-1", + public: true, + zones: ["eu-south-1a", "eu-south-1b", "eu-south-1c"], + }, + { + name: "Cape Town", + full_name: "Africa (Cape Town)", + code: "af-south-1", + public: true, + zones: ["af-south-1a", "af-south-1b", "af-south-1c"], + }, + { + name: "Tokyo", + full_name: "Asia Pacific (Tokyo)", + code: "ap-northeast-1", + public: true, + zone_limit: 3, + zones: [ + "ap-northeast-1a", + "ap-northeast-1b", + "ap-northeast-1c", + "ap-northeast-1d", + ], + }, + { + name: "Seoul", + full_name: "Asia Pacific (Seoul)", + code: "ap-northeast-2", + public: true, + zones: [ + "ap-northeast-2a", + "ap-northeast-2b", + "ap-northeast-2c", + "ap-northeast-2d", + ], + }, + { + name: "Osaka", + full_name: "Asia Pacific (Osaka-Local)", + code: "ap-northeast-3", + public: true, + zones: ["ap-northeast-3a", "ap-northeast-3b", "ap-northeast-3c"], + }, + { + name: "Singapore", + full_name: "Asia Pacific (Singapore)", + code: "ap-southeast-1", + public: true, + zones: ["ap-southeast-1a", "ap-southeast-1b", "ap-southeast-1c"], + }, + { + name: "Sydney", + full_name: "Asia Pacific (Sydney)", + code: "ap-southeast-2", + public: true, + zones: ["ap-southeast-2a", "ap-southeast-2b", "ap-southeast-2c"], + }, + { + name: "Jakarta", + full_name: "Asia Pacific (Jakarta)", + code: "ap-southeast-3", + public: true, + zones: ["ap-southeast-3a", "ap-southeast-3b", "ap-southeast-3c"], + }, + { + name: "Hong Kong", + full_name: "Asia Pacific (Hong Kong)", + code: "ap-east-1", + public: true, + zones: ["ap-east-1a", "ap-east-1b", "ap-east-1c"], + }, + { + name: "Mumbai", + full_name: "Asia Pacific (Mumbai)", + code: "ap-south-1", + public: true, + zones: ["ap-south-1a", "ap-south-1b", "ap-south-1c"], + }, + { + name: "São Paulo", + full_name: "South America (São Paulo)", + code: "sa-east-1", + public: true, + zone_limit: 2, + zones: ["sa-east-1a", "sa-east-1b", "sa-east-1c"], + }, + { + name: "Bahrain", + full_name: "Middle East (Bahrain)", + code: "me-south-1", + public: true, + zones: ["me-south-1a", "me-south-1b", "me-south-1c"], + }, + { + name: "Beijing", + full_name: "China (Beijing)", + code: "cn-north-1", + public: false, + zones: ["cn-north-1a", "cn-north-1b", "cn-north-1c"], + }, + { + name: "Ningxia", + full_name: "China (Ningxia)", + code: "cn-northwest-1", + public: false, + zones: ["cn-northwest-1a", "cn-northwest-1b", "cn-northwest-1c"], + }, +]; diff --git a/frontend/src/components/LLMSelection/AzureAiOptions/index.jsx b/frontend/src/components/LLMSelection/AzureAiOptions/index.jsx new file mode 100644 index 0000000..0274175 --- /dev/null +++ b/frontend/src/components/LLMSelection/AzureAiOptions/index.jsx @@ -0,0 +1,92 @@ +export default function AzureAiOptions({ settings }) { + return ( +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/LLMSelection/CohereAiOptions/index.jsx b/frontend/src/components/LLMSelection/CohereAiOptions/index.jsx new file mode 100644 index 0000000..a0bd333 --- /dev/null +++ b/frontend/src/components/LLMSelection/CohereAiOptions/index.jsx @@ -0,0 +1,49 @@ +export default function CohereAiOptions({ settings }) { + return ( +
+
+
+ + +
+
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/LLMSelection/DeepSeekOptions/index.jsx b/frontend/src/components/LLMSelection/DeepSeekOptions/index.jsx new file mode 100644 index 0000000..ababd74 --- /dev/null +++ b/frontend/src/components/LLMSelection/DeepSeekOptions/index.jsx @@ -0,0 +1,100 @@ +import { useState, useEffect } from "react"; +import System from "@/models/system"; + +export default function DeepSeekOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.DeepSeekApiKey); + const [deepSeekApiKey, setDeepSeekApiKey] = useState( + settings?.DeepSeekApiKey + ); + + return ( +
+
+ + setInputValue(e.target.value)} + onBlur={() => setDeepSeekApiKey(inputValue)} + /> +
+ {!settings?.credentialsOnly && ( + + )} +
+ ); +} + +function DeepSeekModelSelection({ apiKey, settings }) { + const [models, setModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!apiKey) { + setModels([]); + setLoading(true); + return; + } + + setLoading(true); + const { models } = await System.customModels( + "deepseek", + typeof apiKey === "boolean" ? null : apiKey + ); + setModels(models || []); + setLoading(false); + } + findCustomModels(); + }, [apiKey]); + + if (loading) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/FireworksAiOptions/index.jsx b/frontend/src/components/LLMSelection/FireworksAiOptions/index.jsx new file mode 100644 index 0000000..cc5b290 --- /dev/null +++ b/frontend/src/components/LLMSelection/FireworksAiOptions/index.jsx @@ -0,0 +1,99 @@ +import System from "@/models/system"; +import { useState, useEffect } from "react"; + +export default function FireworksAiOptions({ settings }) { + return ( +
+
+ + +
+ {!settings?.credentialsOnly && ( + + )} +
+ ); +} +function FireworksAiModelSelection({ settings }) { + const [groupedModels, setGroupedModels] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models } = await System.customModels("fireworksai"); + + if (models?.length > 0) { + const modelsByOrganization = models.reduce((acc, model) => { + acc[model.organization] = acc[model.organization] || []; + acc[model.organization].push(model); + return acc; + }, {}); + + setGroupedModels(modelsByOrganization); + } + + setLoading(false); + } + findCustomModels(); + }, []); + + if (loading || Object.keys(groupedModels).length === 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx b/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx new file mode 100644 index 0000000..dc2b248 --- /dev/null +++ b/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx @@ -0,0 +1,133 @@ +import System from "@/models/system"; +import { useEffect, useState } from "react"; + +export default function GeminiLLMOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.GeminiLLMApiKey); + const [geminiApiKey, setGeminiApiKey] = useState(settings?.GeminiLLMApiKey); + + return ( +
+
+
+ + setInputValue(e.target.value)} + onBlur={() => setGeminiApiKey(inputValue)} + /> +
+ + {!settings?.credentialsOnly && ( + <> + +
+ + +
+ + )} +
+
+ ); +} + +function GeminiModelSelection({ apiKey, settings }) { + const [groupedModels, setGroupedModels] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models } = await System.customModels("gemini", apiKey); + + if (models?.length > 0) { + const modelsByOrganization = models.reduce((acc, model) => { + acc[model.experimental ? "Experimental" : "Stable"] = + acc[model.experimental ? "Experimental" : "Stable"] || []; + acc[model.experimental ? "Experimental" : "Stable"].push(model); + return acc; + }, {}); + setGroupedModels(modelsByOrganization); + } + setLoading(false); + } + findCustomModels(); + }, [apiKey]); + + if (loading) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/GenericOpenAiOptions/index.jsx b/frontend/src/components/LLMSelection/GenericOpenAiOptions/index.jsx new file mode 100644 index 0000000..47d7dd1 --- /dev/null +++ b/frontend/src/components/LLMSelection/GenericOpenAiOptions/index.jsx @@ -0,0 +1,85 @@ +export default function GenericOpenAiOptions({ settings }) { + return ( +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + e.target.blur()} + defaultValue={settings?.GenericOpenAiTokenLimit} + required={true} + autoComplete="off" + /> +
+
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/LLMSelection/GroqAiOptions/index.jsx b/frontend/src/components/LLMSelection/GroqAiOptions/index.jsx new file mode 100644 index 0000000..43c8f00 --- /dev/null +++ b/frontend/src/components/LLMSelection/GroqAiOptions/index.jsx @@ -0,0 +1,114 @@ +import { useState, useEffect } from "react"; +import System from "@/models/system"; + +export default function GroqAiOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.GroqApiKey); + const [apiKey, setApiKey] = useState(settings?.GroqApiKey); + + return ( +
+
+ + setInputValue(e.target.value)} + onBlur={() => setApiKey(inputValue)} + /> +
+ + {!settings?.credentialsOnly && ( + + )} +
+ ); +} + +function GroqAIModelSelection({ apiKey, settings }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!apiKey) { + setCustomModels([]); + setLoading(true); + return; + } + + try { + setLoading(true); + const { models } = await System.customModels("groq", apiKey); + setCustomModels(models || []); + } catch (error) { + console.error("Failed to fetch custom models:", error); + setCustomModels([]); + } finally { + setLoading(false); + } + } + findCustomModels(); + }, [apiKey]); + + if (loading) { + return ( +
+ + +

+ Enter a valid API key to view all available models for your account. +

+
+ ); + } + + return ( +
+ + +

+ Select the GroqAI model you want to use for your conversations. +

+
+ ); +} diff --git a/frontend/src/components/LLMSelection/HuggingFaceOptions/index.jsx b/frontend/src/components/LLMSelection/HuggingFaceOptions/index.jsx new file mode 100644 index 0000000..369cfa3 --- /dev/null +++ b/frontend/src/components/LLMSelection/HuggingFaceOptions/index.jsx @@ -0,0 +1,56 @@ +export default function HuggingFaceOptions({ settings }) { + return ( +
+
+
+ + +
+
+ + +
+
+ + e.target.blur()} + defaultValue={settings?.HuggingFaceLLMTokenLimit} + required={true} + autoComplete="off" + /> +
+
+
+ ); +} diff --git a/frontend/src/components/LLMSelection/KoboldCPPOptions/index.jsx b/frontend/src/components/LLMSelection/KoboldCPPOptions/index.jsx new file mode 100644 index 0000000..ac4db91 --- /dev/null +++ b/frontend/src/components/LLMSelection/KoboldCPPOptions/index.jsx @@ -0,0 +1,197 @@ +import { useEffect, useState } from "react"; +import System from "@/models/system"; +import PreLoader from "@/components/Preloader"; +import { KOBOLDCPP_COMMON_URLS } from "@/utils/constants"; +import { CaretDown, CaretUp } from "@phosphor-icons/react"; +import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery"; + +export default function KoboldCPPOptions({ settings }) { + const { + autoDetecting: loading, + basePath, + basePathValue, + showAdvancedControls, + setShowAdvancedControls, + handleAutoDetectClick, + } = useProviderEndpointAutoDiscovery({ + provider: "koboldcpp", + initialBasePath: settings?.KoboldCPPBasePath, + ENDPOINTS: KOBOLDCPP_COMMON_URLS, + }); + + const [tokenLimit, setTokenLimit] = useState( + settings?.KoboldCPPTokenLimit || 4096 + ); + + const handleTokenLimitChange = (e) => { + setTokenLimit(Number(e.target.value)); + }; + + return ( +
+
+ +
+ + e.target.blur()} + required={true} + autoComplete="off" + /> +

+ Maximum number of tokens for context and response. +

+
+
+
+ +
+ + +
+ ); +} + +function KoboldCPPModelSelection({ settings, basePath = null }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!basePath || !basePath.includes("/v1")) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + try { + const { models } = await System.customModels( + "koboldcpp", + null, + basePath + ); + setCustomModels(models || []); + } catch (error) { + console.error("Failed to fetch custom models:", error); + setCustomModels([]); + } + setLoading(false); + } + findCustomModels(); + }, [basePath]); + + if (loading || customModels.length === 0) { + return ( +
+ + +

+ Select the KoboldCPP model you want to use. Models will load after + entering a valid KoboldCPP URL. +

+
+ ); + } + + return ( +
+ + +

+ Choose the KoboldCPP model you want to use for your conversations. +

+
+ ); +} diff --git a/frontend/src/components/LLMSelection/LLMItem/index.jsx b/frontend/src/components/LLMSelection/LLMItem/index.jsx new file mode 100644 index 0000000..ef773ff --- /dev/null +++ b/frontend/src/components/LLMSelection/LLMItem/index.jsx @@ -0,0 +1,37 @@ +export default function LLMItem({ + name, + value, + image, + description, + checked, + onClick, +}) { + return ( +
onClick(value)} + className={`w-full p-2 rounded-md hover:cursor-pointer hover:bg-theme-bg-secondary ${ + checked ? "bg-theme-bg-secondary" : "" + }`} + > + +
+ {`${name} +
+
{name}
+
{description}
+
+
+
+ ); +} diff --git a/frontend/src/components/LLMSelection/LLMProviderOption/index.jsx b/frontend/src/components/LLMSelection/LLMProviderOption/index.jsx new file mode 100644 index 0000000..3c3ed44 --- /dev/null +++ b/frontend/src/components/LLMSelection/LLMProviderOption/index.jsx @@ -0,0 +1,37 @@ +export default function LLMProviderOption({ + name, + link, + description, + value, + image, + checked = false, + onClick, +}) { + return ( +
onClick(value)}> + + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx b/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx new file mode 100644 index 0000000..4f8545f --- /dev/null +++ b/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx @@ -0,0 +1,219 @@ +import { useEffect, useState } from "react"; +import { Info, CaretDown, CaretUp } from "@phosphor-icons/react"; +import paths from "@/utils/paths"; +import System from "@/models/system"; +import PreLoader from "@/components/Preloader"; +import { LMSTUDIO_COMMON_URLS } from "@/utils/constants"; +import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery"; + +export default function LMStudioOptions({ settings, showAlert = false }) { + const { + autoDetecting: loading, + basePath, + basePathValue, + showAdvancedControls, + setShowAdvancedControls, + handleAutoDetectClick, + } = useProviderEndpointAutoDiscovery({ + provider: "lmstudio", + initialBasePath: settings?.LMStudioBasePath, + ENDPOINTS: LMSTUDIO_COMMON_URLS, + }); + + const [maxTokens, setMaxTokens] = useState( + settings?.LMStudioTokenLimit || 4096 + ); + + const handleMaxTokensChange = (e) => { + setMaxTokens(Number(e.target.value)); + }; + + return ( +
+ {showAlert && ( +
+
+ +

+ LMStudio as your LLM requires you to set an embedding service to + use. +

+
+ + Manage embedding → + +
+ )} +
+ +
+ + e.target.blur()} + required={true} + autoComplete="off" + /> +

+ Maximum number of tokens for context and response. +

+
+
+
+ +
+ + +
+ ); +} + +function LMStudioModelSelection({ settings, basePath = null }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!basePath) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + try { + const { models } = await System.customModels( + "lmstudio", + null, + basePath + ); + setCustomModels(models || []); + } catch (error) { + console.error("Failed to fetch custom models:", error); + setCustomModels([]); + } + setLoading(false); + } + findCustomModels(); + }, [basePath]); + + if (loading || customModels.length == 0) { + return ( +
+ + +

+ Select the LM Studio model you want to use. Models will load after + entering a valid LM Studio URL. +

+
+ ); + } + + return ( +
+ + +

+ Choose the LM Studio model you want to use for your conversations. +

+
+ ); +} diff --git a/frontend/src/components/LLMSelection/LiteLLMOptions/index.jsx b/frontend/src/components/LLMSelection/LiteLLMOptions/index.jsx new file mode 100644 index 0000000..7f62f62 --- /dev/null +++ b/frontend/src/components/LLMSelection/LiteLLMOptions/index.jsx @@ -0,0 +1,148 @@ +import { useEffect, useState } from "react"; +import System from "@/models/system"; + +export default function LiteLLMOptions({ settings }) { + const [basePathValue, setBasePathValue] = useState(settings?.LiteLLMBasePath); + const [basePath, setBasePath] = useState(settings?.LiteLLMBasePath); + const [apiKeyValue, setApiKeyValue] = useState(settings?.LiteLLMAPIKey); + const [apiKey, setApiKey] = useState(settings?.LiteLLMAPIKey); + + return ( +
+
+
+ + setBasePathValue(e.target.value)} + onBlur={() => setBasePath(basePathValue)} + /> +
+ +
+ + e.target.blur()} + defaultValue={settings?.LiteLLMTokenLimit} + required={true} + autoComplete="off" + /> +
+
+
+
+
+ +
+ setApiKeyValue(e.target.value)} + onBlur={() => setApiKey(apiKeyValue)} + /> +
+
+
+ ); +} + +function LiteLLMModelSelection({ settings, basePath = null, apiKey = null }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!basePath) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + const { models } = await System.customModels( + "litellm", + typeof apiKey === "boolean" ? null : apiKey, + basePath + ); + setCustomModels(models || []); + setLoading(false); + } + findCustomModels(); + }, [basePath, apiKey]); + + if (loading || customModels.length == 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/LocalAiOptions/index.jsx b/frontend/src/components/LLMSelection/LocalAiOptions/index.jsx new file mode 100644 index 0000000..3e7b081 --- /dev/null +++ b/frontend/src/components/LLMSelection/LocalAiOptions/index.jsx @@ -0,0 +1,219 @@ +import React, { useEffect, useState } from "react"; +import { Info, CaretDown, CaretUp } from "@phosphor-icons/react"; +import paths from "@/utils/paths"; +import System from "@/models/system"; +import PreLoader from "@/components/Preloader"; +import { LOCALAI_COMMON_URLS } from "@/utils/constants"; +import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery"; + +export default function LocalAiOptions({ settings, showAlert = false }) { + const { + autoDetecting: loading, + basePath, + basePathValue, + showAdvancedControls, + setShowAdvancedControls, + handleAutoDetectClick, + } = useProviderEndpointAutoDiscovery({ + provider: "localai", + initialBasePath: settings?.LocalAiBasePath, + ENDPOINTS: LOCALAI_COMMON_URLS, + }); + const [apiKeyValue, setApiKeyValue] = useState(settings?.LocalAiApiKey); + const [apiKey, setApiKey] = useState(settings?.LocalAiApiKey); + + return ( +
+ {showAlert && ( +
+
+ +

+ LocalAI as your LLM requires you to set an embedding service to + use. +

+
+ + Manage embedding → + +
+ )} +
+ {!settings?.credentialsOnly && ( + <> + +
+ + e.target.blur()} + defaultValue={settings?.LocalAiTokenLimit} + required={true} + autoComplete="off" + /> +
+ + )} +
+
+ +
+ setApiKeyValue(e.target.value)} + onBlur={() => setApiKey(apiKeyValue)} + /> +
+
+
+ +
+ +
+ ); +} + +function LocalAIModelSelection({ settings, basePath = null, apiKey = null }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!basePath || !basePath.includes("/v1")) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + const { models } = await System.customModels( + "localai", + typeof apiKey === "boolean" ? null : apiKey, + basePath + ); + setCustomModels(models || []); + setLoading(false); + } + findCustomModels(); + }, [basePath, apiKey]); + + if (loading || customModels.length == 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/MistralOptions/index.jsx b/frontend/src/components/LLMSelection/MistralOptions/index.jsx new file mode 100644 index 0000000..a081895 --- /dev/null +++ b/frontend/src/components/LLMSelection/MistralOptions/index.jsx @@ -0,0 +1,105 @@ +import { useState, useEffect } from "react"; +import System from "@/models/system"; + +export default function MistralOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.MistralApiKey); + const [mistralKey, setMistralKey] = useState(settings?.MistralApiKey); + + return ( +
+
+ + setInputValue(e.target.value)} + onBlur={() => setMistralKey(inputValue)} + /> +
+ {!settings?.credentialsOnly && ( + + )} +
+ ); +} + +function MistralModelSelection({ apiKey, settings }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!apiKey) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + const { models } = await System.customModels( + "mistral", + typeof apiKey === "boolean" ? null : apiKey + ); + setCustomModels(models || []); + setLoading(false); + } + findCustomModels(); + }, [apiKey]); + + if (loading || customModels.length == 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/NovitaLLMOptions/index.jsx b/frontend/src/components/LLMSelection/NovitaLLMOptions/index.jsx new file mode 100644 index 0000000..0f122b6 --- /dev/null +++ b/frontend/src/components/LLMSelection/NovitaLLMOptions/index.jsx @@ -0,0 +1,145 @@ +import System from "@/models/system"; +import { CaretDown, CaretUp } from "@phosphor-icons/react"; +import { useState, useEffect } from "react"; + +export default function NovitaLLMOptions({ settings }) { + return ( +
+
+
+ + +
+ {!settings?.credentialsOnly && ( + + )} +
+ +
+ ); +} + +function AdvancedControls({ settings }) { + const [showAdvancedControls, setShowAdvancedControls] = useState(false); + + return ( +
+
+ +
+ +
+ ); +} + +function NovitaModelSelection({ settings }) { + const [groupedModels, setGroupedModels] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models } = await System.customModels("novita"); + if (models?.length > 0) { + const modelsByOrganization = models.reduce((acc, model) => { + acc[model.organization] = acc[model.organization] || []; + acc[model.organization].push(model); + return acc; + }, {}); + setGroupedModels(modelsByOrganization); + } + setLoading(false); + } + findCustomModels(); + }, []); + + if (loading || Object.keys(groupedModels).length === 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/NvidiaNimOptions/index.jsx b/frontend/src/components/LLMSelection/NvidiaNimOptions/index.jsx new file mode 100644 index 0000000..ed8747a --- /dev/null +++ b/frontend/src/components/LLMSelection/NvidiaNimOptions/index.jsx @@ -0,0 +1,11 @@ +import RemoteNvidiaNimOptions from "./remote"; +import ManagedNvidiaNimOptions from "./managed"; + +export default function NvidiaNimOptions({ settings }) { + const version = "remote"; // static to "remote" when in docker version. + return version === "remote" ? ( + + ) : ( + + ); +} diff --git a/frontend/src/components/LLMSelection/NvidiaNimOptions/managed.jsx b/frontend/src/components/LLMSelection/NvidiaNimOptions/managed.jsx new file mode 100644 index 0000000..0dce898 --- /dev/null +++ b/frontend/src/components/LLMSelection/NvidiaNimOptions/managed.jsx @@ -0,0 +1,7 @@ +/** + * This component is used to select, start, and manage NVIDIA NIM + * containers and images via docker management tools. + */ +export default function ManagedNvidiaNimOptions({ settings }) { + return null; +} diff --git a/frontend/src/components/LLMSelection/NvidiaNimOptions/remote.jsx b/frontend/src/components/LLMSelection/NvidiaNimOptions/remote.jsx new file mode 100644 index 0000000..9237cd7 --- /dev/null +++ b/frontend/src/components/LLMSelection/NvidiaNimOptions/remote.jsx @@ -0,0 +1,130 @@ +import PreLoader from "@/components/Preloader"; +import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery"; +import System from "@/models/system"; +import { NVIDIA_NIM_COMMON_URLS } from "@/utils/constants"; +import { useState, useEffect } from "react"; + +/** + * This component is used to select a remote NVIDIA NIM model endpoint + * This is the default component and way to connect to NVIDIA NIM + * as the "managed" provider can only work in the Desktop context. + */ +export default function RemoteNvidiaNimOptions({ settings }) { + const { + autoDetecting: loading, + basePath, + basePathValue, + handleAutoDetectClick, + } = useProviderEndpointAutoDiscovery({ + provider: "nvidia-nim", + initialBasePath: settings?.NvidiaNimLLMBasePath, + ENDPOINTS: NVIDIA_NIM_COMMON_URLS, + }); + + return ( +
+
+
+ + {loading ? ( + + ) : ( + <> + {!basePathValue.value && ( + + )} + + )} +
+ +

+ Enter the URL where NVIDIA NIM is running. +

+
+ {!settings?.credentialsOnly && ( + + )} +
+ ); +} +function NvidiaNimModelSelection({ settings, basePath }) { + const [models, setModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models } = await System.customModels( + "nvidia-nim", + null, + basePath + ); + setModels(models); + setLoading(false); + } + findCustomModels(); + }, [basePath]); + + if (loading || models.length === 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/OllamaLLMOptions/index.jsx b/frontend/src/components/LLMSelection/OllamaLLMOptions/index.jsx new file mode 100644 index 0000000..d04f7cb --- /dev/null +++ b/frontend/src/components/LLMSelection/OllamaLLMOptions/index.jsx @@ -0,0 +1,277 @@ +import React, { useEffect, useState } from "react"; +import System from "@/models/system"; +import PreLoader from "@/components/Preloader"; +import { OLLAMA_COMMON_URLS } from "@/utils/constants"; +import { CaretDown, CaretUp, Info } from "@phosphor-icons/react"; +import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery"; +import { Tooltip } from "react-tooltip"; + +export default function OllamaLLMOptions({ settings }) { + const { + autoDetecting: loading, + basePath, + basePathValue, + showAdvancedControls, + setShowAdvancedControls, + handleAutoDetectClick, + } = useProviderEndpointAutoDiscovery({ + provider: "ollama", + initialBasePath: settings?.OllamaLLMBasePath, + ENDPOINTS: OLLAMA_COMMON_URLS, + }); + const [performanceMode, setPerformanceMode] = useState( + settings?.OllamaLLMPerformanceMode || "base" + ); + const [maxTokens, setMaxTokens] = useState( + settings?.OllamaLLMTokenLimit || 4096 + ); + + return ( +
+
+ +
+ + setMaxTokens(Number(e.target.value))} + onScroll={(e) => e.target.blur()} + required={true} + autoComplete="off" + /> +

+ Maximum number of tokens for context and response. +

+
+
+
+ +
+ + +
+ ); +} + +function OllamaLLMModelSelection({ settings, basePath = null }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!basePath) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + try { + const { models } = await System.customModels("ollama", null, basePath); + setCustomModels(models || []); + } catch (error) { + console.error("Failed to fetch custom models:", error); + setCustomModels([]); + } + setLoading(false); + } + findCustomModels(); + }, [basePath]); + + if (loading || customModels.length == 0) { + return ( +
+ + +

+ Select the Ollama model you want to use. Models will load after + entering a valid Ollama URL. +

+
+ ); + } + + return ( +
+ + +

+ Choose the Ollama model you want to use for your conversations. +

+
+ ); +} diff --git a/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx b/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx new file mode 100644 index 0000000..70bfc8c --- /dev/null +++ b/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx @@ -0,0 +1,107 @@ +import { useState, useEffect } from "react"; +import System from "@/models/system"; + +export default function OpenAiOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.OpenAiKey); + const [openAIKey, setOpenAIKey] = useState(settings?.OpenAiKey); + + return ( +
+
+ + setInputValue(e.target.value)} + onBlur={() => setOpenAIKey(inputValue)} + /> +
+ {!settings?.credentialsOnly && ( + + )} +
+ ); +} + +function OpenAIModelSelection({ apiKey, settings }) { + const [groupedModels, setGroupedModels] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models } = await System.customModels( + "openai", + typeof apiKey === "boolean" ? null : apiKey + ); + + if (models?.length > 0) { + const modelsByOrganization = models.reduce((acc, model) => { + acc[model.organization] = acc[model.organization] || []; + acc[model.organization].push(model); + return acc; + }, {}); + setGroupedModels(modelsByOrganization); + } + + setLoading(false); + } + findCustomModels(); + }, [apiKey]); + + if (loading) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx b/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx new file mode 100644 index 0000000..dc595c8 --- /dev/null +++ b/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx @@ -0,0 +1,142 @@ +import System from "@/models/system"; +import { CaretDown, CaretUp } from "@phosphor-icons/react"; +import { useState, useEffect } from "react"; + +export default function OpenRouterOptions({ settings }) { + return ( +
+
+
+ + +
+ {!settings?.credentialsOnly && ( + + )} +
+ +
+ ); +} + +function AdvancedControls({ settings }) { + const [showAdvancedControls, setShowAdvancedControls] = useState(false); + + return ( +
+ + +
+ ); +} + +function OpenRouterModelSelection({ settings }) { + const [groupedModels, setGroupedModels] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models } = await System.customModels("openrouter"); + if (models?.length > 0) { + const modelsByOrganization = models.reduce((acc, model) => { + acc[model.organization] = acc[model.organization] || []; + acc[model.organization].push(model); + return acc; + }, {}); + + setGroupedModels(modelsByOrganization); + } + + setLoading(false); + } + findCustomModels(); + }, []); + + if (loading || Object.keys(groupedModels).length === 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/PerplexityOptions/index.jsx b/frontend/src/components/LLMSelection/PerplexityOptions/index.jsx new file mode 100644 index 0000000..352fcaa --- /dev/null +++ b/frontend/src/components/LLMSelection/PerplexityOptions/index.jsx @@ -0,0 +1,90 @@ +import System from "@/models/system"; +import { useState, useEffect } from "react"; + +export default function PerplexityOptions({ settings }) { + return ( +
+
+ + +
+ {!settings?.credentialsOnly && ( + + )} +
+ ); +} + +function PerplexityModelSelection({ settings }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models } = await System.customModels("perplexity"); + setCustomModels(models || []); + setLoading(false); + } + findCustomModels(); + }, []); + + if (loading || customModels.length == 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/TextGenWebUIOptions/index.jsx b/frontend/src/components/LLMSelection/TextGenWebUIOptions/index.jsx new file mode 100644 index 0000000..efa7995 --- /dev/null +++ b/frontend/src/components/LLMSelection/TextGenWebUIOptions/index.jsx @@ -0,0 +1,51 @@ +export default function TextGenWebUIOptions({ settings }) { + return ( +
+
+ + +
+
+ + e.target.blur()} + defaultValue={settings?.TextGenWebUITokenLimit} + required={true} + autoComplete="off" + /> +
+
+ + +
+
+ ); +} diff --git a/frontend/src/components/LLMSelection/TogetherAiOptions/index.jsx b/frontend/src/components/LLMSelection/TogetherAiOptions/index.jsx new file mode 100644 index 0000000..c9a9108 --- /dev/null +++ b/frontend/src/components/LLMSelection/TogetherAiOptions/index.jsx @@ -0,0 +1,114 @@ +import System from "@/models/system"; +import { useState, useEffect } from "react"; + +export default function TogetherAiOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.TogetherAiApiKey); + const [apiKey, setApiKey] = useState(settings?.TogetherAiApiKey); + + return ( +
+
+ + setInputValue(e.target.value)} + onBlur={() => setApiKey(inputValue)} + /> +
+ {!settings?.credentialsOnly && ( + + )} +
+ ); +} + +function TogetherAiModelSelection({ settings, apiKey }) { + const [groupedModels, setGroupedModels] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + try { + const key = apiKey === "*".repeat(20) ? null : apiKey; + const { models } = await System.customModels("togetherai", key); + if (models?.length > 0) { + const modelsByOrganization = models.reduce((acc, model) => { + if (model.type !== "chat") return acc; // Only show chat models in dropdown + const org = model.organization || "Unknown"; + acc[org] = acc[org] || []; + acc[org].push({ + id: model.id, + name: model.name || model.id, + organization: org, + maxLength: model.maxLength, + }); + return acc; + }, {}); + setGroupedModels(modelsByOrganization); + } + } catch (error) { + console.error("Error fetching Together AI models:", error); + } + setLoading(false); + } + findCustomModels(); + }, [apiKey]); + + if (loading || Object.keys(groupedModels).length === 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/LLMSelection/XAiLLMOptions/index.jsx b/frontend/src/components/LLMSelection/XAiLLMOptions/index.jsx new file mode 100644 index 0000000..21bc1b1 --- /dev/null +++ b/frontend/src/components/LLMSelection/XAiLLMOptions/index.jsx @@ -0,0 +1,114 @@ +import { useState, useEffect } from "react"; +import System from "@/models/system"; + +export default function XAILLMOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.XAIApiKey); + const [apiKey, setApiKey] = useState(settings?.XAIApiKey); + + return ( +
+
+ + setInputValue(e.target.value)} + onBlur={() => setApiKey(inputValue)} + /> +
+ + {!settings?.credentialsOnly && ( + + )} +
+ ); +} + +function XAIModelSelection({ apiKey, settings }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!apiKey) { + setCustomModels([]); + setLoading(true); + return; + } + + try { + setLoading(true); + const { models } = await System.customModels("xai", apiKey); + setCustomModels(models || []); + } catch (error) { + console.error("Failed to fetch custom models:", error); + setCustomModels([]); + } finally { + setLoading(false); + } + } + findCustomModels(); + }, [apiKey]); + + if (loading) { + return ( +
+ + +

+ Enter a valid API key to view all available models for your account. +

+
+ ); + } + + return ( +
+ + +

+ Select the xAI model you want to use for your conversations. +

+
+ ); +} diff --git a/frontend/src/components/ModalWrapper/index.jsx b/frontend/src/components/ModalWrapper/index.jsx new file mode 100644 index 0000000..7c45c22 --- /dev/null +++ b/frontend/src/components/ModalWrapper/index.jsx @@ -0,0 +1,32 @@ +import { createPortal } from "react-dom"; +/** + * @typedef {Object} ModalWrapperProps + * @property {import("react").ReactComponentElement} children - The DOM/JSX to render + * @property {boolean} isOpen - Option that renders the modal + * @property {boolean} noPortal - (default: false) Used for creating sub-DOM modals that need to be rendered as a child element instead of a modal placed at the root + * Note: This can impact the bg-overlay presentation due to conflicting DOM positions so if using this property you should + double check it renders as desired. + */ + +/** + * @param {ModalWrapperProps} props - ModalWrapperProps to pass + * @returns {import("react").ReactNode} + */ +export default function ModalWrapper({ children, isOpen, noPortal = false }) { + if (!isOpen) return null; + + if (noPortal) { + return ( +
+ {children} +
+ ); + } + + return createPortal( +
+ {children} +
, + document.getElementById("root") + ); +} diff --git a/frontend/src/components/Modals/DisplayRecoveryCodeModal/index.jsx b/frontend/src/components/Modals/DisplayRecoveryCodeModal/index.jsx new file mode 100644 index 0000000..0cd9a26 --- /dev/null +++ b/frontend/src/components/Modals/DisplayRecoveryCodeModal/index.jsx @@ -0,0 +1,91 @@ +import showToast from "@/utils/toast"; +import { DownloadSimple, Key } from "@phosphor-icons/react"; +import { saveAs } from "file-saver"; +import { useState } from "react"; +import ModalWrapper from "@/components/ModalWrapper"; + +export default function RecoveryCodeModal({ + recoveryCodes, + onDownloadComplete, + onClose, +}) { + const [downloadClicked, setDownloadClicked] = useState(false); + + const downloadRecoveryCodes = () => { + const blob = new Blob([recoveryCodes.join("\n")], { type: "text/plain" }); + saveAs(blob, "recovery_codes.txt"); + setDownloadClicked(true); + }; + + const handleClose = () => { + if (downloadClicked) { + onDownloadComplete(); + onClose(); + } + }; + + const handleCopyToClipboard = () => { + navigator.clipboard.writeText(recoveryCodes.join(",\n")).then(() => { + showToast("Recovery codes copied to clipboard", "success", { + clear: true, + }); + }); + }; + + return ( + +
+
+
+ +

+ Recovery Codes +

+
+
+
+
+

+ In order to reset your password in the future, you will need these + recovery codes. Download or copy your recovery codes to save them.{" "} +
+ These recovery codes are only shown once! +

+
+
    + {recoveryCodes.map((code, index) => ( +
  • + {code} +
  • + ))} +
+
+
+
+ +
+
+
+
+ ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/ConnectorOption/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/ConnectorOption/index.jsx new file mode 100644 index 0000000..b2ce3d4 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/ConnectorOption/index.jsx @@ -0,0 +1,25 @@ +export default function ConnectorOption({ + slug, + selectedConnector, + setSelectedConnector, + image, + name, + description, +}) { + return ( + + ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Confluence/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Confluence/index.jsx new file mode 100644 index 0000000..5f63812 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Confluence/index.jsx @@ -0,0 +1,272 @@ +import { useState } from "react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; +import { Warning } from "@phosphor-icons/react"; +import { Tooltip } from "react-tooltip"; + +export default function ConfluenceOptions() { + const [loading, setLoading] = useState(false); + const [accessType, setAccessType] = useState("username"); + + const handleSubmit = async (e) => { + e.preventDefault(); + const form = new FormData(e.target); + + try { + setLoading(true); + showToast( + "Fetching all pages for Confluence space - this may take a while.", + "info", + { + clear: true, + autoClose: false, + } + ); + const { data, error } = await System.dataConnectors.confluence.collect({ + baseUrl: form.get("baseUrl"), + spaceKey: form.get("spaceKey"), + username: form.get("username"), + accessToken: form.get("accessToken"), + cloud: form.get("isCloud") === "true", + personalAccessToken: form.get("personalAccessToken"), + }); + + if (!!error) { + showToast(error, "error", { clear: true }); + setLoading(false); + return; + } + + showToast( + `Pages collected from Confluence space ${data.spaceKey}. Output folder is ${data.destination}.`, + "success", + { clear: true } + ); + e.target.reset(); + setLoading(false); + } catch (e) { + console.error(e); + showToast(e.message, "error", { clear: true }); + setLoading(false); + } + }; + + return ( +
+
+
+
+
+
+
+ +

+ Determine if your Confluence instance is hosted on Atlassian + cloud or self-hosted. +

+
+ +
+ +
+
+ +

+ This is the base URL of your Confluence space. +

+
+ +
+
+
+ +

+ This is the spaces key of your confluence instance that will + be used. Usually begins with ~ +

+
+ +
+
+
+ +

+ Your Confluence authentication type, eg: username and access + token / personal access token. +

+
+ +
+ {accessType === "username" && ( + <> +
+
+ +

+ Your Confluence username. +

+
+ +
+
+
+ +

+ Access token for authentication. +

+
+ +
+ + )} + {accessType === "personalToken" && ( +
+
+ +

+ Your Confluence personal access token. +

+
+ +
+ )} +
+
+ +
+ + {loading && ( +

+ Once complete, all pages will be available for embedding into + workspaces. +

+ )} +
+
+
+
+ ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Github/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Github/index.jsx new file mode 100644 index 0000000..8bcc00b --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Github/index.jsx @@ -0,0 +1,311 @@ +import React, { useEffect, useState } from "react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; +import pluralize from "pluralize"; +import { TagsInput } from "react-tag-input-component"; +import { Info, Warning } from "@phosphor-icons/react"; +import { Tooltip } from "react-tooltip"; + +const DEFAULT_BRANCHES = ["main", "master"]; +export default function GithubOptions() { + const [loading, setLoading] = useState(false); + const [repo, setRepo] = useState(null); + const [accessToken, setAccessToken] = useState(null); + const [ignores, setIgnores] = useState([]); + + const [settings, setSettings] = useState({ + repo: null, + accessToken: null, + }); + + const handleSubmit = async (e) => { + e.preventDefault(); + const form = new FormData(e.target); + + try { + setLoading(true); + showToast( + "Fetching all files for repo - this may take a while.", + "info", + { clear: true, autoClose: false } + ); + const { data, error } = await System.dataConnectors.github.collect({ + repo: form.get("repo"), + accessToken: form.get("accessToken"), + branch: form.get("branch"), + ignorePaths: ignores, + }); + + if (!!error) { + showToast(error, "error", { clear: true }); + setLoading(false); + return; + } + + showToast( + `${data.files} ${pluralize("file", data.files)} collected from ${ + data.author + }/${data.repo}:${data.branch}. Output folder is ${data.destination}.`, + "success", + { clear: true } + ); + e.target.reset(); + setLoading(false); + return; + } catch (e) { + console.error(e); + showToast(e.message, "error", { clear: true }); + setLoading(false); + } + }; + + return ( +
+
+
+
+
+
+
+ +

+ Url of the GitHub repo you wish to collect. +

+
+ setRepo(e.target.value)} + onBlur={() => setSettings({ ...settings, repo })} + spellCheck={false} + /> +
+
+
+ +

+ Access Token to prevent rate limiting. +

+
+ setAccessToken(e.target.value)} + onBlur={() => setSettings({ ...settings, accessToken })} + /> +
+ +
+ +
+
+ +

+ List in .gitignore format to ignore specific files during + collection. Press enter after each entry you want to save. +

+
+ +
+
+ +
+ + + {loading && ( +

+ Once complete, all files will be available for embedding into + workspaces in the document picker. +

+ )} +
+
+
+
+ ); +} + +function GitHubBranchSelection({ repo, accessToken }) { + const [allBranches, setAllBranches] = useState(DEFAULT_BRANCHES); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchAllBranches() { + if (!repo) { + setAllBranches(DEFAULT_BRANCHES); + setLoading(false); + return; + } + + setLoading(true); + const { branches } = await System.dataConnectors.github.branches({ + repo, + accessToken, + }); + setAllBranches(branches.length > 0 ? branches : DEFAULT_BRANCHES); + setLoading(false); + } + fetchAllBranches(); + }, [repo, accessToken]); + + if (loading) { + return ( +
+
+ +

+ Branch you wish to collect files from. +

+
+ +
+ ); + } + + return ( +
+
+ +

+ Branch you wish to collect files from. +

+
+ +
+ ); +} + +function PATAlert({ accessToken }) { + if (!!accessToken) return null; + return ( +
+
+ +

+ Without filling out the Github Access Token this data connector + will only be able to collect the top-level files of the repo + due to GitHub's public API rate-limits. +
+
+ e.stopPropagation()} + > + {" "} + Get a free Personal Access Token with a GitHub account here. + +

+
+
+ ); +} + +function PATTooltip({ accessToken }) { + if (!!accessToken) return null; + return ( + <> + {!accessToken && ( + + )} + +

+ Without a{" "} + e.stopPropagation()} + > + Personal Access Token + + , the GitHub API may limit the number of files that can be collected + due to rate limits. You can{" "} + e.stopPropagation()} + > + create a temporary Access Token + {" "} + to avoid this issue. +

+
+ + ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Gitlab/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Gitlab/index.jsx new file mode 100644 index 0000000..646a9cc --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Gitlab/index.jsx @@ -0,0 +1,335 @@ +import React, { useEffect, useState } from "react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; +import pluralize from "pluralize"; +import { TagsInput } from "react-tag-input-component"; +import { Info, Warning } from "@phosphor-icons/react"; +import { Tooltip } from "react-tooltip"; + +const DEFAULT_BRANCHES = ["main", "master"]; +export default function GitlabOptions() { + const [loading, setLoading] = useState(false); + const [repo, setRepo] = useState(null); + const [accessToken, setAccessToken] = useState(null); + const [ignores, setIgnores] = useState([]); + const [settings, setSettings] = useState({ + repo: null, + accessToken: null, + }); + + const handleSubmit = async (e) => { + e.preventDefault(); + const form = new FormData(e.target); + + try { + setLoading(true); + showToast( + "Fetching all files for repo - this may take a while.", + "info", + { clear: true, autoClose: false } + ); + const { data, error } = await System.dataConnectors.gitlab.collect({ + repo: form.get("repo"), + accessToken: form.get("accessToken"), + branch: form.get("branch"), + ignorePaths: ignores, + fetchIssues: form.get("fetchIssues"), + }); + + if (!!error) { + showToast(error, "error", { clear: true }); + setLoading(false); + return; + } + + showToast( + `${data.files} ${pluralize("file", data.files)} collected from ${ + data.author + }/${data.repo}:${data.branch}. Output folder is ${data.destination}.`, + "success", + { clear: true } + ); + e.target.reset(); + setLoading(false); + return; + } catch (e) { + console.error(e); + showToast(e.message, "error", { clear: true }); + setLoading(false); + } + }; + + return ( +
+
+
+
+
+
+
+ +

+ URL of the GitLab repo you wish to collect. +

+
+ setRepo(e.target.value)} + onBlur={() => setSettings({ ...settings, repo })} + spellCheck={false} + /> +
+
+
+ +

+ Access Token to prevent rate limiting. +

+
+ setAccessToken(e.target.value)} + onBlur={() => setSettings({ ...settings, accessToken })} + /> +
+
+
+ +

+ Select additional entities to fetch from the GitLab API. +

+
+
+ +
+
+ +
+ +
+
+ +

+ List in .gitignore format to ignore specific files during + collection. Press enter after each entry you want to save. +

+
+ +
+
+ +
+ + + {loading && ( +

+ Once complete, all files will be available for embedding into + workspaces in the document picker. +

+ )} +
+
+
+
+ ); +} + +function GitLabBranchSelection({ repo, accessToken }) { + const [allBranches, setAllBranches] = useState(DEFAULT_BRANCHES); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchAllBranches() { + if (!repo) { + setAllBranches(DEFAULT_BRANCHES); + setLoading(false); + return; + } + + setLoading(true); + const { branches } = await System.dataConnectors.gitlab.branches({ + repo, + accessToken, + }); + setAllBranches(branches.length > 0 ? branches : DEFAULT_BRANCHES); + setLoading(false); + } + fetchAllBranches(); + }, [repo, accessToken]); + + if (loading) { + return ( +
+
+ +

+ Branch you wish to collect files from. +

+
+ +
+ ); + } + + return ( +
+
+ +

+ Branch you wish to collect files from. +

+
+ +
+ ); +} + +function PATAlert({ accessToken }) { + if (!!accessToken) return null; + return ( +
+
+ +

+ Without filling out the GitLab Access Token this data connector + will only be able to collect the top-level files of the repo + due to GitLab's public API rate-limits. +
+
+ e.stopPropagation()} + > + {" "} + Get a free Personal Access Token with a GitLab account here. + +

+
+
+ ); +} + +function PATTooltip({ accessToken }) { + if (!!accessToken) return null; + return ( + <> + {!accessToken && ( + + )} + +

+ Without a{" "} + e.stopPropagation()} + > + Personal Access Token + + , the GitLab API may limit the number of files that can be collected + due to rate limits. You can{" "} + e.stopPropagation()} + > + create a temporary Access Token + {" "} + to avoid this issue. +

+
+ + ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/WebsiteDepth/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/WebsiteDepth/index.jsx new file mode 100644 index 0000000..85ee4f7 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/WebsiteDepth/index.jsx @@ -0,0 +1,134 @@ +import React, { useState } from "react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; +import pluralize from "pluralize"; + +export default function WebsiteDepthOptions() { + const [loading, setLoading] = useState(false); + + const handleSubmit = async (e) => { + e.preventDefault(); + const form = new FormData(e.target); + + try { + setLoading(true); + showToast("Scraping website - this may take a while.", "info", { + clear: true, + autoClose: false, + }); + + const { data, error } = await System.dataConnectors.websiteDepth.scrape({ + url: form.get("url"), + depth: parseInt(form.get("depth")), + maxLinks: parseInt(form.get("maxLinks")), + }); + + if (!!error) { + showToast(error, "error", { clear: true }); + setLoading(false); + return; + } + + showToast( + `Successfully scraped ${data.length} ${pluralize( + "page", + data.length + )}!`, + "success", + { clear: true } + ); + e.target.reset(); + setLoading(false); + } catch (e) { + console.error(e); + showToast(e.message, "error", { clear: true }); + setLoading(false); + } + }; + + return ( +
+
+
+
+
+
+
+ +

+ URL of the website you want to scrape. +

+
+ +
+
+
+ +

+ This is the number of child-links that the worker should + follow from the origin URL. +

+
+ +
+
+
+ +

+ Maximum number of links to scrape. +

+
+ +
+
+
+ +
+ + {loading && ( +

+ Once complete, all scraped pages will be available for embedding + into workspaces in the document picker. +

+ )} +
+
+
+
+ ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Youtube/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Youtube/index.jsx new file mode 100644 index 0000000..3f162ac --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Youtube/index.jsx @@ -0,0 +1,91 @@ +import React, { useState } from "react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; + +export default function YoutubeOptions() { + const [loading, setLoading] = useState(false); + + const handleSubmit = async (e) => { + e.preventDefault(); + const form = new FormData(e.target); + + try { + setLoading(true); + showToast("Fetching transcript for YouTube video.", "info", { + clear: true, + autoClose: false, + }); + + const { data, error } = await System.dataConnectors.youtube.transcribe({ + url: form.get("url"), + }); + + if (!!error) { + showToast(error, "error", { clear: true }); + setLoading(false); + return; + } + + showToast( + `${data.title} by ${data.author} transcription completed. Output folder is ${data.destination}.`, + "success", + { clear: true } + ); + e.target.reset(); + setLoading(false); + return; + } catch (e) { + console.error(e); + showToast(e.message, "error", { clear: true }); + setLoading(false); + } + }; + + return ( +
+
+
+
+
+
+
+ +

+ URL of the YouTube video you wish to transcribe. +

+
+ +
+
+
+ +
+ + {loading && ( +

+ Once complete, the transcription will be available for embedding + into workspaces in the document picker. +

+ )} +
+
+
+
+ ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/index.jsx new file mode 100644 index 0000000..fdbef07 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/index.jsx @@ -0,0 +1,99 @@ +import ConnectorImages from "@/components/DataConnectorOption/media"; +import { MagnifyingGlass } from "@phosphor-icons/react"; +import GithubOptions from "./Connectors/Github"; +import GitlabOptions from "./Connectors/Gitlab"; +import YoutubeOptions from "./Connectors/Youtube"; +import ConfluenceOptions from "./Connectors/Confluence"; +import { useState } from "react"; +import ConnectorOption from "./ConnectorOption"; +import WebsiteDepthOptions from "./Connectors/WebsiteDepth"; + +export const DATA_CONNECTORS = { + github: { + name: "GitHub Repo", + image: ConnectorImages.github, + description: + "Import an entire public or private GitHub repository in a single click.", + options: , + }, + gitlab: { + name: "GitLab Repo", + image: ConnectorImages.gitlab, + description: + "Import an entire public or private GitLab repository in a single click.", + options: , + }, + "youtube-transcript": { + name: "YouTube Transcript", + image: ConnectorImages.youtube, + description: + "Import the transcription of an entire YouTube video from a link.", + options: , + }, + "website-depth": { + name: "Bulk Link Scraper", + image: ConnectorImages.websiteDepth, + description: "Scrape a website and its sub-links up to a certain depth.", + options: , + }, + confluence: { + name: "Confluence", + image: ConnectorImages.confluence, + description: "Import an entire Confluence page in a single click.", + options: , + }, +}; + +export default function DataConnectors() { + const [selectedConnector, setSelectedConnector] = useState("github"); + const [searchQuery, setSearchQuery] = useState(""); + + const filteredConnectors = Object.keys(DATA_CONNECTORS).filter((slug) => + DATA_CONNECTORS[slug].name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + return ( +
+
+
+ + setSearchQuery(e.target.value)} + /> +
+
+ {filteredConnectors.length > 0 ? ( + filteredConnectors.map((slug, index) => ( + + )) + ) : ( +
+ No data connectors found. +
+ )} +
+
+
+
+ {DATA_CONNECTORS[selectedConnector].options} +
+
+ ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/ContextMenu/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/ContextMenu/index.jsx new file mode 100644 index 0000000..c093982 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/ContextMenu/index.jsx @@ -0,0 +1,79 @@ +import { useRef, useEffect } from "react"; + +export default function ContextMenu({ + contextMenu, + closeContextMenu, + files, + selectedItems, + setSelectedItems, +}) { + const contextMenuRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event) => { + if ( + contextMenuRef.current && + !contextMenuRef.current.contains(event.target) + ) { + closeContextMenu(); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [closeContextMenu]); + + const isAllSelected = () => { + const allItems = files.items.flatMap((folder) => [ + folder.name, + ...folder.items.map((file) => file.id), + ]); + return allItems.every((item) => selectedItems[item]); + }; + + const toggleSelectAll = () => { + if (isAllSelected()) { + setSelectedItems({}); + } else { + const newSelectedItems = {}; + files.items.forEach((folder) => { + newSelectedItems[folder.name] = true; + folder.items.forEach((file) => { + newSelectedItems[file.id] = true; + }); + }); + setSelectedItems(newSelectedItems); + } + closeContextMenu(); + }; + + if (!contextMenu.visible) return null; + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FileRow/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FileRow/index.jsx new file mode 100644 index 0000000..9dccbb2 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FileRow/index.jsx @@ -0,0 +1,53 @@ +import React from "react"; +import { + formatDate, + getFileExtension, + middleTruncate, +} from "@/utils/directories"; +import { File } from "@phosphor-icons/react"; + +export default function FileRow({ item, selected, toggleSelection }) { + return ( + toggleSelection(item)} + className={`text-theme-text-primary text-xs grid grid-cols-12 py-2 pl-3.5 pr-8 hover:bg-theme-file-picker-hover cursor-pointer file-row ${ + selected ? "selected light:text-white" : "" + }`} + > +
+
+ {selected &&
} +
+ +

+ {middleTruncate(item.title, 55)} +

+
+
+ {item?.cached && ( +
+

Cached

+
+ )} +
+ + ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FolderRow/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FolderRow/index.jsx new file mode 100644 index 0000000..320c2b1 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FolderRow/index.jsx @@ -0,0 +1,81 @@ +import { useState } from "react"; +import FileRow from "../FileRow"; +import { CaretDown, FolderNotch } from "@phosphor-icons/react"; +import { middleTruncate } from "@/utils/directories"; + +export default function FolderRow({ + item, + selected, + onRowClick, + toggleSelection, + isSelected, + autoExpanded = false, +}) { + const [expanded, setExpanded] = useState(autoExpanded); + + const handleExpandClick = (event) => { + event.stopPropagation(); + setExpanded(!expanded); + }; + + return ( + <> + +
+
{ + event.stopPropagation(); + toggleSelection(item); + }} + > + {selected &&
} +
+
+ +
+ +

+ {middleTruncate(item.name, 35)} +

+
+

+

+ + {expanded && ( + <> + {item.items.map((fileItem) => ( + + ))} + + )} + + ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FolderSelectionPopup/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FolderSelectionPopup/index.jsx new file mode 100644 index 0000000..5261b89 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FolderSelectionPopup/index.jsx @@ -0,0 +1,24 @@ +import { middleTruncate } from "@/utils/directories"; + +export default function FolderSelectionPopup({ folders, onSelect, onClose }) { + const handleFolderSelect = (folder) => { + onSelect(folder); + onClose(); + }; + + return ( +

+
    + {folders.map((folder) => ( +
  • handleFolderSelect(folder)} + className="px-4 py-2 text-xs text-gray-700 hover:bg-gray-200 rounded-lg cursor-pointer whitespace-nowrap" + > + {middleTruncate(folder.name, 25)} +
  • + ))} +
+
+ ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/MoveToFolderIcon.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/MoveToFolderIcon.jsx new file mode 100644 index 0000000..3916fc7 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/MoveToFolderIcon.jsx @@ -0,0 +1,44 @@ +export default function MoveToFolderIcon({ + className, + width = 18, + height = 18, +}) { + return ( + + + + + + + ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/NewFolderModal/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/NewFolderModal/index.jsx new file mode 100644 index 0000000..bd740c8 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/NewFolderModal/index.jsx @@ -0,0 +1,91 @@ +import React, { useState } from "react"; +import { X } from "@phosphor-icons/react"; +import Document from "@/models/document"; + +export default function NewFolderModal({ closeModal, files, setFiles }) { + const [error, setError] = useState(null); + const [folderName, setFolderName] = useState(""); + + const handleCreate = async (e) => { + e.preventDefault(); + setError(null); + if (folderName.trim() !== "") { + const newFolder = { + name: folderName, + type: "folder", + items: [], + }; + const { success } = await Document.createFolder(folderName); + if (success) { + setFiles({ + ...files, + items: [...files.items, newFolder], + }); + closeModal(); + } else { + setError("Failed to create folder"); + } + } + }; + + return ( +
+
+
+
+

+ Create New Folder +

+
+ +
+
+
+
+
+ + setFolderName(e.target.value)} + /> +
+ {error &&

Error: {error}

} +
+
+ + +
+
+
+
+
+ ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/index.jsx new file mode 100644 index 0000000..d29e893 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/index.jsx @@ -0,0 +1,369 @@ +import UploadFile from "../UploadFile"; +import PreLoader from "@/components/Preloader"; +import { memo, useEffect, useState } from "react"; +import FolderRow from "./FolderRow"; +import System from "@/models/system"; +import { MagnifyingGlass, Plus, Trash } from "@phosphor-icons/react"; +import Document from "@/models/document"; +import showToast from "@/utils/toast"; +import FolderSelectionPopup from "./FolderSelectionPopup"; +import MoveToFolderIcon from "./MoveToFolderIcon"; +import { useModal } from "@/hooks/useModal"; +import NewFolderModal from "./NewFolderModal"; +import debounce from "lodash.debounce"; +import { filterFileSearchResults } from "./utils"; +import ContextMenu from "./ContextMenu"; +import { Tooltip } from "react-tooltip"; +import { safeJsonParse } from "@/utils/request"; + +function Directory({ + files, + setFiles, + loading, + setLoading, + workspace, + fetchKeys, + selectedItems, + setSelectedItems, + setHighlightWorkspace, + moveToWorkspace, + setLoadingMessage, + loadingMessage, +}) { + const [amountSelected, setAmountSelected] = useState(0); + const [showFolderSelection, setShowFolderSelection] = useState(false); + const [searchTerm, setSearchTerm] = useState(""); + const { + isOpen: isFolderModalOpen, + openModal: openFolderModal, + closeModal: closeFolderModal, + } = useModal(); + const [contextMenu, setContextMenu] = useState({ + visible: false, + x: 0, + y: 0, + }); + + useEffect(() => { + setAmountSelected(Object.keys(selectedItems).length); + }, [selectedItems]); + + const deleteFiles = async (event) => { + event.stopPropagation(); + if ( + !window.confirm( + "Are you sure you want to delete these files and folders?\nThis will remove the files from the system and remove them from any existing workspaces automatically.\nThis action is not reversible." + ) + ) { + return false; + } + + try { + const toRemove = []; + const foldersToRemove = []; + + for (const itemId of Object.keys(selectedItems)) { + for (const folder of files.items) { + const foundItem = folder.items.find((file) => file.id === itemId); + if (foundItem) { + toRemove.push(`${folder.name}/${foundItem.name}`); + break; + } + } + } + for (const folder of files.items) { + if (folder.name === "custom-documents") { + continue; + } + + if (isSelected(folder.id, folder)) { + foldersToRemove.push(folder.name); + } + } + + setLoading(true); + setLoadingMessage( + `Removing ${toRemove.length} documents and ${foldersToRemove.length} folders. Please wait.` + ); + await System.deleteDocuments(toRemove); + for (const folderName of foldersToRemove) { + await System.deleteFolder(folderName); + } + + await fetchKeys(true); + setSelectedItems({}); + } catch (error) { + console.error("Failed to delete files and folders:", error); + } finally { + setLoading(false); + setSelectedItems({}); + } + }; + + const toggleSelection = (item) => { + setSelectedItems((prevSelectedItems) => { + const newSelectedItems = { ...prevSelectedItems }; + if (item.type === "folder") { + // select all files in the folder + if (newSelectedItems[item.name]) { + delete newSelectedItems[item.name]; + item.items.forEach((file) => delete newSelectedItems[file.id]); + } else { + newSelectedItems[item.name] = true; + item.items.forEach((file) => (newSelectedItems[file.id] = true)); + } + } else { + // single file selections + if (newSelectedItems[item.id]) { + delete newSelectedItems[item.id]; + } else { + newSelectedItems[item.id] = true; + } + } + + return newSelectedItems; + }); + }; + + // check if item is selected based on selectedItems state + const isSelected = (id, item) => { + if (item && item.type === "folder") { + if (!selectedItems[item.name]) { + return false; + } + return item.items.every((file) => selectedItems[file.id]); + } + + return !!selectedItems[id]; + }; + + const moveToFolder = async (folder) => { + const toMove = []; + for (const itemId of Object.keys(selectedItems)) { + for (const currentFolder of files.items) { + const foundItem = currentFolder.items.find( + (file) => file.id === itemId + ); + if (foundItem) { + toMove.push({ ...foundItem, folderName: currentFolder.name }); + break; + } + } + } + setLoading(true); + setLoadingMessage(`Moving ${toMove.length} documents. Please wait.`); + const { success, message } = await Document.moveToFolder( + toMove, + folder.name + ); + if (!success) { + showToast(`Error moving files: ${message}`, "error"); + setLoading(false); + return; + } + + if (success && message) { + // show info if some files were not moved due to being embedded + showToast(message, "info"); + } else { + showToast(`Successfully moved ${toMove.length} documents.`, "success"); + } + await fetchKeys(true); + setSelectedItems({}); + setLoading(false); + }; + + const handleSearch = debounce((e) => { + const searchValue = e.target.value; + setSearchTerm(searchValue); + }, 500); + + const filteredFiles = filterFileSearchResults(files, searchTerm); + + const handleContextMenu = (event) => { + event.preventDefault(); + setContextMenu({ visible: true, x: event.clientX, y: event.clientY }); + }; + + const closeContextMenu = () => { + setContextMenu({ visible: false, x: 0, y: 0 }); + }; + + return ( + <> +
+
+
+

My Documents

+
+ + +
+ +
+ +
+
+

Name

+
+ +
+ {loading ? ( +
+ +

+ {loadingMessage} +

+
+ ) : filteredFiles.length > 0 ? ( + filteredFiles.map( + (item, index) => + item.type === "folder" && ( + toggleSelection(item)} + toggleSelection={toggleSelection} + isSelected={isSelected} + autoExpanded={index === 0} + /> + ) + ) + ) : ( +
+

+ No Documents +

+
+ )} +
+ {amountSelected !== 0 && ( +
+
+
+ +
+ + {showFolderSelection && ( + item.type === "folder" + )} + onSelect={moveToFolder} + onClose={() => setShowFolderSelection(false)} + /> + )} +
+ +
+
+
+ )} +
+ + +
+ {isFolderModalOpen && ( +
+ +
+ )} + +
+ + + ); +} + +/** + * Tooltips for the directory components. Renders when the directory is shown + * or updated so that tooltips are attached as the items are changed. + */ +function DirectoryTooltips() { + return ( + { + const data = safeJsonParse(content, null); + if (!data) return null; + return ( +
+

{data.title}

+
+

+ Date: {data.date} +

+

+ Type: {data.extension} +

+
+
+ ); + }} + /> + ); +} + +export default memo(Directory); diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/utils.js b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/utils.js new file mode 100644 index 0000000..3cce544 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/utils.js @@ -0,0 +1,62 @@ +import strDistance from "js-levenshtein"; + +const LEVENSHTEIN_MIN = 2; + +// Regular expression pattern to match the v4 UUID and the ending .json +const uuidPattern = + /-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/; +const jsonPattern = /\.json$/; + +// Function to strip UUID v4 and JSON from file names as that will impact search results. +export const stripUuidAndJsonFromString = (input = "") => { + return input + ?.replace(uuidPattern, "") // remove v4 uuid + ?.replace(jsonPattern, "") // remove trailing .json + ?.replace("-", " "); // turn slugged names into spaces +}; + +export function filterFileSearchResults(files = [], searchTerm = "") { + if (!searchTerm) return files?.items || []; + + const normalizedSearchTerm = searchTerm.toLowerCase().trim(); + + const searchResult = []; + for (const folder of files?.items) { + const folderNameNormalized = folder.name.toLowerCase(); + + // Check for exact match first, then fuzzy match + if (folderNameNormalized.includes(normalizedSearchTerm)) { + searchResult.push(folder); + continue; + } + + // Check children for matches + const fileSearchResults = []; + for (const file of folder?.items) { + const fileNameNormalized = stripUuidAndJsonFromString( + file.name + ).toLowerCase(); + + // Exact match check + if (fileNameNormalized.includes(normalizedSearchTerm)) { + fileSearchResults.push(file); + } + // Fuzzy match only if no exact matches found + else if ( + fileSearchResults.length === 0 && + strDistance(fileNameNormalized, normalizedSearchTerm) <= LEVENSHTEIN_MIN + ) { + fileSearchResults.push(file); + } + } + + if (fileSearchResults.length > 0) { + searchResult.push({ + ...folder, + items: fileSearchResults, + }); + } + } + + return searchResult; +} diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/UploadFile/FileUploadProgress/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/UploadFile/FileUploadProgress/index.jsx new file mode 100644 index 0000000..e066fe9 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/UploadFile/FileUploadProgress/index.jsx @@ -0,0 +1,151 @@ +import React, { useState, useEffect, memo } from "react"; +import truncate from "truncate"; +import { CheckCircle, XCircle } from "@phosphor-icons/react"; +import Workspace from "../../../../../../models/workspace"; +import { humanFileSize, milliToHms } from "../../../../../../utils/numbers"; +import PreLoader from "../../../../../Preloader"; + +function FileUploadProgressComponent({ + slug, + uuid, + file, + setFiles, + rejected = false, + reason = null, + onUploadSuccess, + onUploadError, + setLoading, + setLoadingMessage, +}) { + const [timerMs, setTimerMs] = useState(10); + const [status, setStatus] = useState("pending"); + const [error, setError] = useState(""); + const [isFadingOut, setIsFadingOut] = useState(false); + + const fadeOut = (cb) => { + setIsFadingOut(true); + cb?.(); + }; + + const beginFadeOut = () => { + setIsFadingOut(false); + setFiles((prev) => { + return prev.filter((item) => item.uid !== uuid); + }); + }; + + useEffect(() => { + async function uploadFile() { + setLoading(true); + setLoadingMessage("Uploading file..."); + const start = Number(new Date()); + const formData = new FormData(); + formData.append("file", file, file.name); + const timer = setInterval(() => { + setTimerMs(Number(new Date()) - start); + }, 100); + + // Chunk streaming not working in production so we just sit and wait + const { response, data } = await Workspace.uploadFile(slug, formData); + if (!response.ok) { + setStatus("failed"); + clearInterval(timer); + onUploadError(data.error); + setError(data.error); + } else { + setLoading(false); + setLoadingMessage(""); + setStatus("complete"); + clearInterval(timer); + onUploadSuccess(); + } + + // Begin fadeout timer to clear uploader queue. + setTimeout(() => { + fadeOut(() => setTimeout(() => beginFadeOut(), 300)); + }, 5000); + } + !!file && !rejected && uploadFile(); + }, []); + + if (rejected) { + return ( +
+
+ +
+
+

+ {truncate(file.name, 30)} +

+

+ {reason || "this file failed to upload"} +

+
+
+ ); + } + + if (status === "failed") { + return ( +
+
+ +
+
+

+ {truncate(file.name, 30)} +

+

+ {error} +

+
+
+ ); + } + + return ( +
+
+ {status !== "complete" ? ( +
+ +
+ ) : ( + + )} +
+
+

+ {truncate(file.name, 30)} +

+

+ {humanFileSize(file.size)} | {milliToHms(timerMs)} +

+
+
+ ); +} + +export default memo(FileUploadProgressComponent); diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/UploadFile/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/UploadFile/index.jsx new file mode 100644 index 0000000..4dd04b0 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/UploadFile/index.jsx @@ -0,0 +1,159 @@ +import { CloudArrowUp } from "@phosphor-icons/react"; +import { useEffect, useState } from "react"; +import showToast from "../../../../../utils/toast"; +import System from "../../../../../models/system"; +import { useDropzone } from "react-dropzone"; +import { v4 } from "uuid"; +import FileUploadProgress from "./FileUploadProgress"; +import Workspace from "../../../../../models/workspace"; +import debounce from "lodash.debounce"; + +export default function UploadFile({ + workspace, + fetchKeys, + setLoading, + setLoadingMessage, +}) { + const [ready, setReady] = useState(false); + const [files, setFiles] = useState([]); + const [fetchingUrl, setFetchingUrl] = useState(false); + + const handleSendLink = async (e) => { + e.preventDefault(); + setLoading(true); + setLoadingMessage("Scraping link..."); + setFetchingUrl(true); + const formEl = e.target; + const form = new FormData(formEl); + const { response, data } = await Workspace.uploadLink( + workspace.slug, + form.get("link") + ); + if (!response.ok) { + showToast(`Error uploading link: ${data.error}`, "error"); + } else { + fetchKeys(true); + showToast("Link uploaded successfully", "success"); + formEl.reset(); + } + setLoading(false); + setFetchingUrl(false); + }; + + // Queue all fetchKeys calls through the same debouncer to prevent spamming the server. + // either a success or error will trigger a fetchKeys call so the UI is not stuck loading. + const debouncedFetchKeys = debounce(() => fetchKeys(true), 1000); + const handleUploadSuccess = () => debouncedFetchKeys(); + const handleUploadError = () => debouncedFetchKeys(); + + const onDrop = async (acceptedFiles, rejections) => { + const newAccepted = acceptedFiles.map((file) => { + return { + uid: v4(), + file, + }; + }); + const newRejected = rejections.map((file) => { + return { + uid: v4(), + file: file.file, + rejected: true, + reason: file.errors[0].code, + }; + }); + setFiles([...newAccepted, ...newRejected]); + }; + + useEffect(() => { + async function checkProcessorOnline() { + const online = await System.checkDocumentProcessorOnline(); + setReady(online); + } + checkProcessorOnline(); + }, []); + + const { getRootProps, getInputProps } = useDropzone({ + onDrop, + disabled: !ready, + }); + + return ( +
+
+ + {ready === false ? ( +
+ +
+ Document Processor Unavailable +
+
+ We can't upload your files right now because the document + processor is offline. Please try again later. +
+
+ ) : files.length === 0 ? ( +
+ +
+ Click to upload or drag and drop +
+
+ supports text files, csv's, spreadsheets, audio files, and more! +
+
+ ) : ( +
+ {files.map((file) => ( + + ))} +
+ )} +
+
+ or submit a link +
+
+ + +
+
+ These files will be uploaded to the document processor running on this + AnythingLLM instance. These files are not sent or shared with a third + party. +
+
+ ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx new file mode 100644 index 0000000..f31a6e9 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx @@ -0,0 +1,260 @@ +import { memo, useState } from "react"; +import { + formatDate, + getFileExtension, + middleTruncate, +} from "@/utils/directories"; +import { ArrowUUpLeft, Eye, File, PushPin } from "@phosphor-icons/react"; +import Workspace from "@/models/workspace"; +import showToast from "@/utils/toast"; +import System from "@/models/system"; + +export default function WorkspaceFileRow({ + item, + folderName, + workspace, + setLoading, + setLoadingMessage, + fetchKeys, + hasChanges, + movedItems, + selected, + toggleSelection, + disableSelection, + setSelectedItems, +}) { + const onRemoveClick = async (e) => { + e.stopPropagation(); + setLoading(true); + + try { + setLoadingMessage(`Removing file from workspace`); + await Workspace.modifyEmbeddings(workspace.slug, { + adds: [], + deletes: [`${folderName}/${item.name}`], + }); + await fetchKeys(true); + } catch (error) { + console.error("Failed to remove document:", error); + } + setSelectedItems({}); + setLoadingMessage(""); + setLoading(false); + }; + + function toggleRowSelection(e) { + if (disableSelection) return; + e.stopPropagation(); + toggleSelection(); + } + + function handleRowSelection(e) { + e.stopPropagation(); + toggleSelection(); + } + + const isMovedItem = movedItems?.some((movedItem) => movedItem.id === item.id); + return ( +
+
+
+ {!disableSelection ? ( +
+ {selected &&
} +
+ ) : null} +
+ +

+ {middleTruncate(item.title, 50)} +

+
+
+ {hasChanges ? ( +
+ ) : ( +
+ + + +
+ )} +
+
+ ); +} + +const PinItemToWorkspace = memo(({ workspace, docPath, item }) => { + const [pinned, setPinned] = useState( + item?.pinnedWorkspaces?.includes(workspace.id) || false + ); + const [hover, setHover] = useState(false); + const pinEvent = new CustomEvent("pinned_document"); + + const updatePinStatus = async (e) => { + try { + e.stopPropagation(); + if (!pinned) window.dispatchEvent(pinEvent); + const success = await Workspace.setPinForDocument( + workspace.slug, + docPath, + !pinned + ); + + if (!success) { + showToast(`Failed to ${!pinned ? "pin" : "unpin"} document.`, "error", { + clear: true, + }); + return; + } + + showToast( + `Document ${!pinned ? "pinned to" : "unpinned from"} workspace`, + "success", + { clear: true } + ); + setPinned(!pinned); + } catch (error) { + showToast(`Failed to pin document. ${error.message}`, "error", { + clear: true, + }); + return; + } + }; + + if (!item) return
; + + return ( +
setHover(true)} + onMouseLeave={() => setHover(false)} + className="flex gap-x-2 items-center hover:bg-theme-file-picker-hover p-[2px] rounded ml-2" + > + +
+ ); +}); + +const WatchForChanges = memo(({ workspace, docPath, item }) => { + const [watched, setWatched] = useState(item?.watched || false); + const [hover, setHover] = useState(false); + const watchEvent = new CustomEvent("watch_document_for_changes"); + + const updateWatchStatus = async () => { + try { + if (!watched) window.dispatchEvent(watchEvent); + const success = + await System.experimentalFeatures.liveSync.setWatchStatusForDocument( + workspace.slug, + docPath, + !watched + ); + + if (!success) { + showToast( + `Failed to ${!watched ? "watch" : "unwatch"} document.`, + "error", + { + clear: true, + } + ); + return; + } + + showToast( + `Document ${ + !watched + ? "will be watched for changes" + : "will no longer be watched for changes" + }.`, + "success", + { clear: true } + ); + setWatched(!watched); + } catch (error) { + showToast(`Failed to watch document. ${error.message}`, "error", { + clear: true, + }); + return; + } + }; + + if (!item || !item.canWatch) return
; + + return ( +
setHover(true)} + onMouseLeave={() => setHover(false)} + className="flex gap-x-2 items-center hover:bg-theme-file-picker-hover p-[2px] rounded ml-2" + > + +
+ ); +}); + +const RemoveItemFromWorkspace = ({ item, onClick }) => { + return ( +
+ +
+ ); +}; diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx new file mode 100644 index 0000000..18a73de --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx @@ -0,0 +1,456 @@ +import PreLoader from "@/components/Preloader"; +import { dollarFormat } from "@/utils/numbers"; +import WorkspaceFileRow from "./WorkspaceFileRow"; +import { memo, useEffect, useState } from "react"; +import ModalWrapper from "@/components/ModalWrapper"; +import { Eye, PushPin, X } from "@phosphor-icons/react"; +import { SEEN_DOC_PIN_ALERT, SEEN_WATCH_ALERT } from "@/utils/constants"; +import paths from "@/utils/paths"; +import { Link } from "react-router-dom"; +import Workspace from "@/models/workspace"; +import { Tooltip } from "react-tooltip"; +import { safeJsonParse } from "@/utils/request"; + +function WorkspaceDirectory({ + workspace, + files, + highlightWorkspace, + loading, + loadingMessage, + setLoadingMessage, + setLoading, + fetchKeys, + hasChanges, + saveChanges, + embeddingCosts, + movedItems, +}) { + const [selectedItems, setSelectedItems] = useState({}); + + const toggleSelection = (item) => { + setSelectedItems((prevSelectedItems) => { + const newSelectedItems = { ...prevSelectedItems }; + if (newSelectedItems[item.id]) { + delete newSelectedItems[item.id]; + } else { + newSelectedItems[item.id] = true; + } + return newSelectedItems; + }); + }; + + const toggleSelectAll = () => { + const allItems = files.items.flatMap((folder) => folder.items); + const allSelected = allItems.every((item) => selectedItems[item.id]); + if (allSelected) { + setSelectedItems({}); + } else { + const newSelectedItems = {}; + allItems.forEach((item) => { + newSelectedItems[item.id] = true; + }); + setSelectedItems(newSelectedItems); + } + }; + + const removeSelectedItems = async () => { + setLoading(true); + setLoadingMessage("Removing selected files from workspace"); + + const itemsToRemove = Object.keys(selectedItems).map((itemId) => { + const folder = files.items.find((f) => + f.items.some((i) => i.id === itemId) + ); + const item = folder.items.find((i) => i.id === itemId); + return `${folder.name}/${item.name}`; + }); + + try { + await Workspace.modifyEmbeddings(workspace.slug, { + adds: [], + deletes: itemsToRemove, + }); + await fetchKeys(true); + setSelectedItems({}); + } catch (error) { + console.error("Failed to remove documents:", error); + } + + setLoadingMessage(""); + setLoading(false); + }; + + const handleSaveChanges = (e) => { + setSelectedItems({}); + saveChanges(e); + }; + + if (loading) { + return ( +
+
+

+ {workspace.name} +

+
+
+
+
+
+

Name

+
+

+

+
+ +

+ {loadingMessage} +

+
+
+
+ ); + } + + return ( + <> +
+
+

+ {workspace.name} +

+
+
+
+
+
+
+ {!hasChanges && + files.items.some((folder) => folder.items.length > 0) ? ( +
sum + folder.items.length, + 0 + ) + } + tabIndex={0} + onClick={toggleSelectAll} + > + {Object.keys(selectedItems).length === + files.items.reduce( + (sum, folder) => sum + folder.items.length, + 0 + ) &&
} +
+ ) : ( +
+ )} +

Name

+
+

+

+
+ {files.items.some((folder) => folder.items.length > 0) || + movedItems.length > 0 ? ( + + {({ item, folder }) => ( + toggleSelection(item)} + disableSelection={hasChanges} + setSelectedItems={setSelectedItems} + /> + )} + + ) : ( +
+

+ No Documents +

+
+ )} +
+ + {Object.keys(selectedItems).length > 0 && !hasChanges && ( +
+
+
+ + +
+
+
+ )} +
+
+ {hasChanges && ( +
+
+

+ {embeddingCosts === 0 + ? "" + : `Estimated Cost: ${ + embeddingCosts < 0.01 + ? `< $0.01` + : dollarFormat(embeddingCosts) + }`} +

+ +
+ + +
+ )} +
+ + + + + ); +} + +const PinAlert = memo(() => { + const [showAlert, setShowAlert] = useState(false); + function dismissAlert() { + setShowAlert(false); + window.localStorage.setItem(SEEN_DOC_PIN_ALERT, "1"); + window.removeEventListener(handlePinEvent); + } + + function handlePinEvent() { + if (!!window?.localStorage?.getItem(SEEN_DOC_PIN_ALERT)) return; + setShowAlert(true); + } + + useEffect(() => { + if (!window || !!window?.localStorage?.getItem(SEEN_DOC_PIN_ALERT)) return; + window?.addEventListener("pinned_document", handlePinEvent); + }, []); + + return ( + +
+
+
+ +

+ What is document pinning? +

+
+
+
+
+

+ When you pin a document in AnythingLLM we will inject the + entire content of the document into your prompt window for your + LLM to fully comprehend. +

+

+ This works best with large-context models or small files + that are critical to its knowledge-base. +

+

+ If you are not getting the answers you desire from AnythingLLM by + default then pinning is a great way to get higher quality answers + in a click. +

+
+
+
+ +
+
+
+ ); +}); + +const DocumentWatchAlert = memo(() => { + const [showAlert, setShowAlert] = useState(false); + function dismissAlert() { + setShowAlert(false); + window.localStorage.setItem(SEEN_WATCH_ALERT, "1"); + window.removeEventListener(handlePinEvent); + } + + function handlePinEvent() { + if (!!window?.localStorage?.getItem(SEEN_WATCH_ALERT)) return; + setShowAlert(true); + } + + useEffect(() => { + if (!window || !!window?.localStorage?.getItem(SEEN_WATCH_ALERT)) return; + window?.addEventListener("watch_document_for_changes", handlePinEvent); + }, []); + + return ( + +
+
+
+ +

+ What does watching a document do? +

+
+
+
+
+

+ When you watch a document in AnythingLLM we will{" "} + automatically sync your document content from it's original + source on regular intervals. This will automatically update the + content in every workspace where this file is managed. +

+

+ This feature currently supports online-based content and will not + be available for manually uploaded documents. +

+

+ You can manage what documents are watched from the{" "} + + File manager + {" "} + admin view. +

+
+
+
+ +
+
+
+ ); +}); + +function RenderFileRows({ files, movedItems, children }) { + function sortMovedItemsAndFiles(a, b) { + const aIsMovedItem = movedItems.some((movedItem) => movedItem.id === a.id); + const bIsMovedItem = movedItems.some((movedItem) => movedItem.id === b.id); + if (aIsMovedItem && !bIsMovedItem) return -1; + if (!aIsMovedItem && bIsMovedItem) return 1; + return 0; + } + + return files.items + .flatMap((folder) => folder.items) + .sort(sortMovedItemsAndFiles) + .map((item) => { + const folder = files.items.find((f) => f.items.includes(item)); + return children({ item, folder }); + }); +} + +/** + * Tooltips for the workspace directory components. Renders when the workspace directory is shown + * or updated so that tooltips are attached as the items are changed. + */ +function WorkspaceDocumentTooltips() { + return ( + <> + { + const data = safeJsonParse(content, null); + if (!data) return null; + return ( +
+

+ {data.title} +

+
+

+ Date: {data.date} +

+

+ Type: {data.extension} +

+
+
+ ); + }} + /> + + + + + ); +} + +export default memo(WorkspaceDirectory); diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/index.jsx new file mode 100644 index 0000000..98244d5 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/index.jsx @@ -0,0 +1,230 @@ +import { ArrowsDownUp } from "@phosphor-icons/react"; +import { useEffect, useState } from "react"; +import Workspace from "../../../../models/workspace"; +import System from "../../../../models/system"; +import showToast from "../../../../utils/toast"; +import Directory from "./Directory"; +import WorkspaceDirectory from "./WorkspaceDirectory"; + +// OpenAI Cost per token +// ref: https://openai.com/pricing#:~:text=%C2%A0/%201K%20tokens-,Embedding%20models,-Build%20advanced%20search + +const MODEL_COSTS = { + "text-embedding-ada-002": 0.0000001, // $0.0001 / 1K tokens + "text-embedding-3-small": 0.00000002, // $0.00002 / 1K tokens + "text-embedding-3-large": 0.00000013, // $0.00013 / 1K tokens +}; + +export default function DocumentSettings({ workspace, systemSettings }) { + const [highlightWorkspace, setHighlightWorkspace] = useState(false); + const [availableDocs, setAvailableDocs] = useState([]); + const [loading, setLoading] = useState(true); + const [workspaceDocs, setWorkspaceDocs] = useState([]); + const [selectedItems, setSelectedItems] = useState({}); + const [hasChanges, setHasChanges] = useState(false); + const [movedItems, setMovedItems] = useState([]); + const [embeddingsCost, setEmbeddingsCost] = useState(0); + const [loadingMessage, setLoadingMessage] = useState(""); + + async function fetchKeys(refetchWorkspace = false) { + setLoading(true); + const localFiles = await System.localFiles(); + const currentWorkspace = refetchWorkspace + ? await Workspace.bySlug(workspace.slug) + : workspace; + + const documentsInWorkspace = + currentWorkspace.documents.map((doc) => doc.docpath) || []; + + // Documents that are not in the workspace + const availableDocs = { + ...localFiles, + items: localFiles.items.map((folder) => { + if (folder.items && folder.type === "folder") { + return { + ...folder, + items: folder.items.filter( + (file) => + file.type === "file" && + !documentsInWorkspace.includes(`${folder.name}/${file.name}`) + ), + }; + } else { + return folder; + } + }), + }; + + // Documents that are already in the workspace + const workspaceDocs = { + ...localFiles, + items: localFiles.items.map((folder) => { + if (folder.items && folder.type === "folder") { + return { + ...folder, + items: folder.items.filter( + (file) => + file.type === "file" && + documentsInWorkspace.includes(`${folder.name}/${file.name}`) + ), + }; + } else { + return folder; + } + }), + }; + + setAvailableDocs(availableDocs); + setWorkspaceDocs(workspaceDocs); + setLoading(false); + } + + useEffect(() => { + fetchKeys(true); + }, []); + + const updateWorkspace = async (e) => { + e.preventDefault(); + setLoading(true); + showToast("Updating workspace...", "info", { autoClose: false }); + setLoadingMessage("This may take a while for large documents"); + + const changesToSend = { + adds: movedItems.map((item) => `${item.folderName}/${item.name}`), + }; + + setSelectedItems({}); + setHasChanges(false); + setHighlightWorkspace(false); + await Workspace.modifyEmbeddings(workspace.slug, changesToSend) + .then((res) => { + if (!!res.message) { + showToast(`Error: ${res.message}`, "error", { clear: true }); + return; + } + showToast("Workspace updated successfully.", "success", { + clear: true, + }); + }) + .catch((error) => { + showToast(`Workspace update failed: ${error}`, "error", { + clear: true, + }); + }); + + setMovedItems([]); + await fetchKeys(true); + setLoading(false); + setLoadingMessage(""); + }; + + const moveSelectedItemsToWorkspace = () => { + setHighlightWorkspace(false); + setHasChanges(true); + + const newMovedItems = []; + + for (const itemId of Object.keys(selectedItems)) { + for (const folder of availableDocs.items) { + const foundItem = folder.items.find((file) => file.id === itemId); + if (foundItem) { + newMovedItems.push({ ...foundItem, folderName: folder.name }); + break; + } + } + } + + let totalTokenCount = 0; + newMovedItems.forEach((item) => { + const { cached, token_count_estimate } = item; + if (!cached) { + totalTokenCount += token_count_estimate; + } + }); + + // Do not do cost estimation unless the embedding engine is OpenAi. + if (systemSettings?.EmbeddingEngine === "openai") { + const COST_PER_TOKEN = + MODEL_COSTS[ + systemSettings?.EmbeddingModelPref || "text-embedding-ada-002" + ]; + + const dollarAmount = (totalTokenCount / 1000) * COST_PER_TOKEN; + setEmbeddingsCost(dollarAmount); + } + + setMovedItems([...movedItems, ...newMovedItems]); + + let newAvailableDocs = JSON.parse(JSON.stringify(availableDocs)); + let newWorkspaceDocs = JSON.parse(JSON.stringify(workspaceDocs)); + + for (const itemId of Object.keys(selectedItems)) { + let foundItem = null; + let foundFolderIndex = null; + + newAvailableDocs.items = newAvailableDocs.items.map( + (folder, folderIndex) => { + const remainingItems = folder.items.filter((file) => { + const match = file.id === itemId; + if (match) { + foundItem = { ...file }; + foundFolderIndex = folderIndex; + } + return !match; + }); + + return { + ...folder, + items: remainingItems, + }; + } + ); + + if (foundItem) { + newWorkspaceDocs.items[foundFolderIndex].items.push(foundItem); + } + } + + setAvailableDocs(newAvailableDocs); + setWorkspaceDocs(newWorkspaceDocs); + setSelectedItems({}); + }; + + return ( +
+ +
+ +
+ +
+ ); +} diff --git a/frontend/src/components/Modals/ManageWorkspace/index.jsx b/frontend/src/components/Modals/ManageWorkspace/index.jsx new file mode 100644 index 0000000..33c9fa8 --- /dev/null +++ b/frontend/src/components/Modals/ManageWorkspace/index.jsx @@ -0,0 +1,172 @@ +import React, { useState, useEffect, memo } from "react"; +import { X } from "@phosphor-icons/react"; +import { useParams } from "react-router-dom"; +import Workspace from "../../../models/workspace"; +import System from "../../../models/system"; +import { isMobile } from "react-device-detect"; +import useUser from "../../../hooks/useUser"; +import DocumentSettings from "./Documents"; +import DataConnectors from "./DataConnectors"; +import ModalWrapper from "@/components/ModalWrapper"; + +const noop = () => {}; +const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => { + const { slug } = useParams(); + const { user } = useUser(); + const [workspace, setWorkspace] = useState(null); + const [settings, setSettings] = useState({}); + const [selectedTab, setSelectedTab] = useState("documents"); + + useEffect(() => { + async function getSettings() { + const _settings = await System.keys(); + setSettings(_settings ?? {}); + } + getSettings(); + }, []); + + useEffect(() => { + async function fetchWorkspace() { + const workspace = await Workspace.bySlug(providedSlug ?? slug); + setWorkspace(workspace); + } + fetchWorkspace(); + }, [providedSlug, slug]); + + if (!workspace) return null; + + if (isMobile) { + return ( + +
+
+
+

+ Editing "{workspace.name}" +

+
+ +
+
+
+

+ Editing these settings are only available on a desktop device. + Please access this page on your desktop to continue. +

+
+
+
+ +
+
+
+ ); + } + + return ( +
+
+
+
+
+ +
+ + {user?.role !== "default" && ( + + )} + + {selectedTab === "documents" ? ( + + ) : ( + + )} +
+
+
+ ); +}; + +export default memo(ManageWorkspace); + +const ModalTabSwitcher = ({ selectedTab, setSelectedTab }) => { + return ( +
+
+ + +
+
+ ); +}; + +export function useManageWorkspaceModal() { + const { user } = useUser(); + const [showing, setShowing] = useState(false); + + function showModal() { + if (user?.role !== "default") { + setShowing(true); + } + } + + function hideModal() { + setShowing(false); + } + + useEffect(() => { + function onEscape(event) { + if (!showing || event.key !== "Escape") return; + setShowing(false); + } + + document.addEventListener("keydown", onEscape); + return () => { + document.removeEventListener("keydown", onEscape); + }; + }, [showing]); + + return { showing, showModal, hideModal }; +} diff --git a/frontend/src/components/Modals/NewWorkspace.jsx b/frontend/src/components/Modals/NewWorkspace.jsx new file mode 100644 index 0000000..5eed377 --- /dev/null +++ b/frontend/src/components/Modals/NewWorkspace.jsx @@ -0,0 +1,97 @@ +import React, { useRef, useState } from "react"; +import { X } from "@phosphor-icons/react"; +import Workspace from "@/models/workspace"; +import paths from "@/utils/paths"; +import { useTranslation } from "react-i18next"; +import ModalWrapper from "@/components/ModalWrapper"; + +const noop = () => false; +export default function NewWorkspaceModal({ hideModal = noop }) { + const formEl = useRef(null); + const [error, setError] = useState(null); + const { t } = useTranslation(); + const handleCreate = async (e) => { + setError(null); + e.preventDefault(); + const data = {}; + const form = new FormData(formEl.current); + for (var [key, value] of form.entries()) data[key] = value; + const { workspace, message } = await Workspace.new(data); + if (!!workspace) { + window.location.href = paths.workspace.chat(workspace.slug); + } + setError(message); + }; + + return ( + +
+
+
+

+ {t("new-workspace.title")} +

+
+ +
+
+
+
+
+
+ + +
+ {error && ( +

Error: {error}

+ )} +
+
+
+ +
+
+
+
+
+ ); +} + +export function useNewWorkspaceModal() { + const [showing, setShowing] = useState(false); + const showModal = () => { + setShowing(true); + }; + const hideModal = () => { + setShowing(false); + }; + + return { showing, showModal, hideModal }; +} diff --git a/frontend/src/components/Modals/Password/MultiUserAuth.jsx b/frontend/src/components/Modals/Password/MultiUserAuth.jsx new file mode 100644 index 0000000..30036ff --- /dev/null +++ b/frontend/src/components/Modals/Password/MultiUserAuth.jsx @@ -0,0 +1,357 @@ +import React, { useEffect, useState } from "react"; +import System from "../../../models/system"; +import { AUTH_TOKEN, AUTH_USER } from "../../../utils/constants"; +import paths from "../../../utils/paths"; +import showToast from "@/utils/toast"; +import ModalWrapper from "@/components/ModalWrapper"; +import { useModal } from "@/hooks/useModal"; +import RecoveryCodeModal from "@/components/Modals/DisplayRecoveryCodeModal"; +import { useTranslation } from "react-i18next"; +import { t } from "i18next"; + +const RecoveryForm = ({ onSubmit, setShowRecoveryForm }) => { + const [username, setUsername] = useState(""); + const [recoveryCodeInputs, setRecoveryCodeInputs] = useState( + Array(2).fill("") + ); + + const handleRecoveryCodeChange = (index, value) => { + const updatedCodes = [...recoveryCodeInputs]; + updatedCodes[index] = value; + setRecoveryCodeInputs(updatedCodes); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + const recoveryCodes = recoveryCodeInputs.filter( + (code) => code.trim() !== "" + ); + onSubmit(username, recoveryCodes); + }; + + return ( +
+
+
+

+ {t("login.password-reset.title")} +

+

+ {t("login.password-reset.description")} +

+
+
+
+
+
+ + setUsername(e.target.value)} + className="border-none bg-theme-settings-input-bg text-theme-text-primary placeholder:text-theme-settings-input-placeholder focus:outline-primary-button active:outline-primary-button outline-none text-sm rounded-md p-2.5 w-full h-[48px] md:w-[300px] md:h-[34px]" + required + /> +
+
+ + {recoveryCodeInputs.map((code, index) => ( +
+ + handleRecoveryCodeChange(index, e.target.value) + } + className="border-none bg-theme-settings-input-bg text-theme-text-primary placeholder:text-theme-settings-input-placeholder focus:outline-primary-button active:outline-primary-button outline-none text-sm rounded-md p-2.5 w-full h-[48px] md:w-[300px] md:h-[34px]" + required + /> +
+ ))} +
+
+
+
+ + +
+
+ ); +}; + +const ResetPasswordForm = ({ onSubmit }) => { + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + + const handleSubmit = (e) => { + e.preventDefault(); + onSubmit(newPassword, confirmPassword); + }; + + return ( +
+
+
+

+ Reset Password +

+

+ Enter your new password. +

+
+
+
+
+
+ setNewPassword(e.target.value)} + className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5" + required + /> +
+
+ setConfirmPassword(e.target.value)} + className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5" + required + /> +
+
+
+
+ +
+
+ ); +}; + +export default function MultiUserAuth() { + const { t } = useTranslation(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [recoveryCodes, setRecoveryCodes] = useState([]); + const [downloadComplete, setDownloadComplete] = useState(false); + const [user, setUser] = useState(null); + const [token, setToken] = useState(null); + const [showRecoveryForm, setShowRecoveryForm] = useState(false); + const [showResetPasswordForm, setShowResetPasswordForm] = useState(false); + const [customAppName, setCustomAppName] = useState(null); + + const { + isOpen: isRecoveryCodeModalOpen, + openModal: openRecoveryCodeModal, + closeModal: closeRecoveryCodeModal, + } = useModal(); + + const handleLogin = async (e) => { + setError(null); + setLoading(true); + e.preventDefault(); + const data = {}; + const form = new FormData(e.target); + for (var [key, value] of form.entries()) data[key] = value; + const { valid, user, token, message, recoveryCodes } = + await System.requestToken(data); + if (valid && !!token && !!user) { + setUser(user); + setToken(token); + + if (recoveryCodes) { + setRecoveryCodes(recoveryCodes); + openRecoveryCodeModal(); + } else { + window.localStorage.setItem(AUTH_USER, JSON.stringify(user)); + window.localStorage.setItem(AUTH_TOKEN, token); + window.location = paths.home(); + } + } else { + setError(message); + setLoading(false); + } + setLoading(false); + }; + + const handleDownloadComplete = () => setDownloadComplete(true); + const handleResetPassword = () => setShowRecoveryForm(true); + const handleRecoverySubmit = async (username, recoveryCodes) => { + const { success, resetToken, error } = await System.recoverAccount( + username, + recoveryCodes + ); + + if (success && resetToken) { + window.localStorage.setItem("resetToken", resetToken); + setShowRecoveryForm(false); + setShowResetPasswordForm(true); + } else { + showToast(error, "error", { clear: true }); + } + }; + + const handleResetSubmit = async (newPassword, confirmPassword) => { + const resetToken = window.localStorage.getItem("resetToken"); + + if (resetToken) { + const { success, error } = await System.resetPassword( + resetToken, + newPassword, + confirmPassword + ); + + if (success) { + window.localStorage.removeItem("resetToken"); + setShowResetPasswordForm(false); + showToast("Password reset successful", "success", { clear: true }); + } else { + showToast(error, "error", { clear: true }); + } + } else { + showToast("Invalid reset token", "error", { clear: true }); + } + }; + + useEffect(() => { + if (downloadComplete && user && token) { + window.localStorage.setItem(AUTH_USER, JSON.stringify(user)); + window.localStorage.setItem(AUTH_TOKEN, token); + window.location = paths.home(); + } + }, [downloadComplete, user, token]); + + useEffect(() => { + const fetchCustomAppName = async () => { + const { appName } = await System.fetchCustomAppName(); + setCustomAppName(appName || ""); + setLoading(false); + }; + fetchCustomAppName(); + }, []); + + if (showRecoveryForm) { + return ( + + ); + } + + if (showResetPasswordForm) + return ; + return ( + <> +
+
+
+
+
+

+ {t("login.multi-user.welcome")} +

+

+ {customAppName || "AnythingLLM"} +

+
+

+ {t("login.sign-in.start")} {customAppName || "AnythingLLM"}{" "} + {t("login.sign-in.end")} +

+
+
+
+
+
+ +
+
+ +
+ {error &&

Error: {error}

} +
+
+
+ + +
+
+
+ + + + + + ); +} diff --git a/frontend/src/components/Modals/Password/SingleUserAuth.jsx b/frontend/src/components/Modals/Password/SingleUserAuth.jsx new file mode 100644 index 0000000..06e3d78 --- /dev/null +++ b/frontend/src/components/Modals/Password/SingleUserAuth.jsx @@ -0,0 +1,128 @@ +import React, { useEffect, useState } from "react"; +import System from "../../../models/system"; +import { AUTH_TOKEN } from "../../../utils/constants"; +import paths from "../../../utils/paths"; +import ModalWrapper from "@/components/ModalWrapper"; +import { useModal } from "@/hooks/useModal"; +import RecoveryCodeModal from "@/components/Modals/DisplayRecoveryCodeModal"; +import { useTranslation } from "react-i18next"; + +export default function SingleUserAuth() { + const { t } = useTranslation(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [recoveryCodes, setRecoveryCodes] = useState([]); + const [downloadComplete, setDownloadComplete] = useState(false); + const [token, setToken] = useState(null); + const [customAppName, setCustomAppName] = useState(null); + + const { + isOpen: isRecoveryCodeModalOpen, + openModal: openRecoveryCodeModal, + closeModal: closeRecoveryCodeModal, + } = useModal(); + + const handleLogin = async (e) => { + setError(null); + setLoading(true); + e.preventDefault(); + const data = {}; + const form = new FormData(e.target); + for (var [key, value] of form.entries()) data[key] = value; + const { valid, token, message, recoveryCodes } = + await System.requestToken(data); + if (valid && !!token) { + setToken(token); + if (recoveryCodes) { + setRecoveryCodes(recoveryCodes); + openRecoveryCodeModal(); + } else { + window.localStorage.setItem(AUTH_TOKEN, token); + window.location = paths.home(); + } + } else { + setError(message); + setLoading(false); + } + setLoading(false); + }; + + const handleDownloadComplete = () => { + setDownloadComplete(true); + }; + + useEffect(() => { + if (downloadComplete && token) { + window.localStorage.setItem(AUTH_TOKEN, token); + window.location = paths.home(); + } + }, [downloadComplete, token]); + + useEffect(() => { + const fetchCustomAppName = async () => { + const { appName } = await System.fetchCustomAppName(); + setCustomAppName(appName || ""); + setLoading(false); + }; + fetchCustomAppName(); + }, []); + + return ( + <> +
+
+
+
+
+

+ {t("login.multi-user.welcome")} +

+

+ {customAppName || "AnythingLLM"} +

+
+

+ {t("login.sign-in.start")} {customAppName || "AnythingLLM"}{" "} + {t("login.sign-in.end")} +

+
+
+
+
+
+ +
+ {error &&

Error: {error}

} +
+
+
+ +
+
+
+ + + + + + ); +} diff --git a/frontend/src/components/Modals/Password/index.jsx b/frontend/src/components/Modals/Password/index.jsx new file mode 100644 index 0000000..6f3f24e --- /dev/null +++ b/frontend/src/components/Modals/Password/index.jsx @@ -0,0 +1,152 @@ +import React, { useState, useEffect } from "react"; +import System from "../../../models/system"; +import SingleUserAuth from "./SingleUserAuth"; +import MultiUserAuth from "./MultiUserAuth"; +import { + AUTH_TOKEN, + AUTH_USER, + AUTH_TIMESTAMP, +} from "../../../utils/constants"; +import useLogo from "../../../hooks/useLogo"; +import illustration from "@/media/illustrations/login-illustration.svg"; + +export default function PasswordModal({ mode = "single" }) { + const { loginLogo } = useLogo(); + return ( +
+
+
+ login illustration +
+
+ Logo + {mode === "single" ? : } +
+
+ ); +} + +export function usePasswordModal(notry = false) { + const [auth, setAuth] = useState({ + loading: true, + requiresAuth: false, + mode: "single", + }); + + useEffect(() => { + async function checkAuthReq() { + if (!window) return; + + // If the last validity check is still valid + // we can skip the loading. + if (!System.needsAuthCheck() && notry === false) { + setAuth({ + loading: false, + requiresAuth: false, + mode: "multi", + }); + return; + } + + const settings = await System.keys(); + if (settings?.MultiUserMode) { + const currentToken = window.localStorage.getItem(AUTH_TOKEN); + if (!!currentToken) { + const valid = notry ? false : await System.checkAuth(currentToken); + if (!valid) { + setAuth({ + loading: false, + requiresAuth: true, + mode: "multi", + }); + window.localStorage.removeItem(AUTH_USER); + window.localStorage.removeItem(AUTH_TOKEN); + window.localStorage.removeItem(AUTH_TIMESTAMP); + return; + } else { + setAuth({ + loading: false, + requiresAuth: false, + mode: "multi", + }); + return; + } + } else { + setAuth({ + loading: false, + requiresAuth: true, + mode: "multi", + }); + return; + } + } else { + // Running token check in single user Auth mode. + // If Single user Auth is disabled - skip check + const requiresAuth = settings?.RequiresAuth || false; + if (!requiresAuth) { + setAuth({ + loading: false, + requiresAuth: false, + mode: "single", + }); + return; + } + + const currentToken = window.localStorage.getItem(AUTH_TOKEN); + if (!!currentToken) { + const valid = notry ? false : await System.checkAuth(currentToken); + if (!valid) { + setAuth({ + loading: false, + requiresAuth: true, + mode: "single", + }); + window.localStorage.removeItem(AUTH_TOKEN); + window.localStorage.removeItem(AUTH_USER); + window.localStorage.removeItem(AUTH_TIMESTAMP); + return; + } else { + setAuth({ + loading: false, + requiresAuth: false, + mode: "single", + }); + return; + } + } else { + setAuth({ + loading: false, + requiresAuth: true, + mode: "single", + }); + return; + } + } + } + checkAuthReq(); + }, []); + + return auth; +} diff --git a/frontend/src/components/Preloader.jsx b/frontend/src/components/Preloader.jsx new file mode 100644 index 0000000..ce17582 --- /dev/null +++ b/frontend/src/components/Preloader.jsx @@ -0,0 +1,18 @@ +export default function PreLoader({ size = "16" }) { + return ( +
+ ); +} + +export function FullScreenLoader() { + return ( +
+
+
+ ); +} diff --git a/frontend/src/components/PrivateRoute/index.jsx b/frontend/src/components/PrivateRoute/index.jsx new file mode 100644 index 0000000..98308fe --- /dev/null +++ b/frontend/src/components/PrivateRoute/index.jsx @@ -0,0 +1,145 @@ +import { useEffect, useState } from "react"; +import { Navigate } from "react-router-dom"; +import { FullScreenLoader } from "../Preloader"; +import validateSessionTokenForUser from "@/utils/session"; +import paths from "@/utils/paths"; +import { AUTH_TIMESTAMP, AUTH_TOKEN, AUTH_USER } from "@/utils/constants"; +import { userFromStorage } from "@/utils/request"; +import System from "@/models/system"; +import UserMenu from "../UserMenu"; + +// Used only for Multi-user mode only as we permission specific pages based on auth role. +// When in single user mode we just bypass any authchecks. +function useIsAuthenticated() { + const [isAuthd, setIsAuthed] = useState(null); + const [shouldRedirectToOnboarding, setShouldRedirectToOnboarding] = + useState(false); + const [multiUserMode, setMultiUserMode] = useState(false); + + useEffect(() => { + const validateSession = async () => { + const { + MultiUserMode, + RequiresAuth, + LLMProvider = null, + VectorDB = null, + } = await System.keys(); + + setMultiUserMode(MultiUserMode); + + // Check for the onboarding redirect condition + if ( + !MultiUserMode && + !RequiresAuth && // Not in Multi-user AND no password set. + !LLMProvider && + !VectorDB + ) { + setShouldRedirectToOnboarding(true); + setIsAuthed(true); + return; + } + + if (!MultiUserMode && !RequiresAuth) { + setIsAuthed(true); + return; + } + + // Single User password mode check + if (!MultiUserMode && RequiresAuth) { + const localAuthToken = localStorage.getItem(AUTH_TOKEN); + if (!localAuthToken) { + setIsAuthed(false); + return; + } + + const isValid = await validateSessionTokenForUser(); + setIsAuthed(isValid); + return; + } + + const localUser = localStorage.getItem(AUTH_USER); + const localAuthToken = localStorage.getItem(AUTH_TOKEN); + if (!localUser || !localAuthToken) { + setIsAuthed(false); + return; + } + + const isValid = await validateSessionTokenForUser(); + if (!isValid) { + localStorage.removeItem(AUTH_USER); + localStorage.removeItem(AUTH_TOKEN); + localStorage.removeItem(AUTH_TIMESTAMP); + setIsAuthed(false); + return; + } + + setIsAuthed(true); + }; + validateSession(); + }, []); + + return { isAuthd, shouldRedirectToOnboarding, multiUserMode }; +} + +// Allows only admin to access the route and if in single user mode, +// allows all users to access the route +export function AdminRoute({ Component, hideUserMenu = false }) { + const { isAuthd, shouldRedirectToOnboarding, multiUserMode } = + useIsAuthenticated(); + if (isAuthd === null) return ; + + if (shouldRedirectToOnboarding) { + return ; + } + + const user = userFromStorage(); + return isAuthd && (user?.role === "admin" || !multiUserMode) ? ( + hideUserMenu ? ( + + ) : ( + + + + ) + ) : ( + + ); +} + +// Allows manager and admin to access the route and if in single user mode, +// allows all users to access the route +export function ManagerRoute({ Component }) { + const { isAuthd, shouldRedirectToOnboarding, multiUserMode } = + useIsAuthenticated(); + if (isAuthd === null) return ; + + if (shouldRedirectToOnboarding) { + return ; + } + + const user = userFromStorage(); + return isAuthd && (user?.role !== "default" || !multiUserMode) ? ( + + + + ) : ( + + ); +} + +export default function PrivateRoute({ Component }) { + const { isAuthd, shouldRedirectToOnboarding } = useIsAuthenticated(); + if (isAuthd === null) return ; + + if (shouldRedirectToOnboarding) { + return ; + } + + return isAuthd ? ( + + + + ) : ( + + ); +} diff --git a/frontend/src/components/SettingsButton/index.jsx b/frontend/src/components/SettingsButton/index.jsx new file mode 100644 index 0000000..98140bc --- /dev/null +++ b/frontend/src/components/SettingsButton/index.jsx @@ -0,0 +1,50 @@ +import useUser from "@/hooks/useUser"; +import paths from "@/utils/paths"; +import { ArrowUUpLeft, Wrench } from "@phosphor-icons/react"; +import { Link } from "react-router-dom"; +import { useMatch } from "react-router-dom"; + +export default function SettingsButton() { + const isInSettings = !!useMatch("/settings/*"); + const { user } = useUser(); + + if (user && user?.role === "default") return null; + + if (isInSettings) + return ( +
+ + + +
+ ); + + return ( +
+ + + +
+ ); +} diff --git a/frontend/src/components/SettingsSidebar/MenuOption/index.jsx b/frontend/src/components/SettingsSidebar/MenuOption/index.jsx new file mode 100644 index 0000000..7b2e429 --- /dev/null +++ b/frontend/src/components/SettingsSidebar/MenuOption/index.jsx @@ -0,0 +1,187 @@ +import React, { useEffect, useState } from "react"; +import { CaretRight } from "@phosphor-icons/react"; +import { Link, useLocation } from "react-router-dom"; + +export default function MenuOption({ + btnText, + icon, + href, + childOptions = [], + flex = false, + user = null, + roles = [], + hidden = false, + isChild = false, +}) { + const storageKey = generateStorageKey({ key: btnText }); + const location = useLocation(); + const hasChildren = childOptions.length > 0; + const hasVisibleChildren = hasVisibleOptions(user, childOptions); + const { isExpanded, setIsExpanded } = useIsExpanded({ + storageKey, + hasVisibleChildren, + childOptions, + location: location.pathname, + }); + + if (hidden) return null; + + // If this option is a parent level option + if (!isChild) { + // and has no children then use its flex props and roles prop directly + if (!hasChildren) { + if (!flex && !roles.includes(user?.role)) return null; + if (flex && !!user && !roles.includes(user?.role)) return null; + } + + // if has children and no visible children - remove it. + if (hasChildren && !hasVisibleChildren) return null; + } else { + // is a child so we use it's permissions + if (!flex && !roles.includes(user?.role)) return null; + if (flex && !!user && !roles.includes(user?.role)) return null; + } + + const isActive = hasChildren + ? (!isExpanded && + childOptions.some((child) => child.href === location.pathname)) || + location.pathname === href + : location.pathname === href; + + const handleClick = (e) => { + if (hasChildren) { + e.preventDefault(); + const newExpandedState = !isExpanded; + setIsExpanded(newExpandedState); + localStorage.setItem(storageKey, JSON.stringify(newExpandedState)); + } + }; + + return ( +
+
+ + {icon} +

+ {btnText} +

+ + {hasChildren && ( + + )} +
+ {isExpanded && hasChildren && ( +
+ {childOptions.map((childOption, index) => ( + + ))} +
+ )} +
+ ); +} + +function useIsExpanded({ + storageKey = "", + hasVisibleChildren = false, + childOptions = [], + location = null, +}) { + const [isExpanded, setIsExpanded] = useState(() => { + if (hasVisibleChildren) { + const storedValue = localStorage.getItem(storageKey); + if (storedValue !== null) { + return JSON.parse(storedValue); + } + return childOptions.some((child) => child.href === location); + } + return false; + }); + + useEffect(() => { + if (hasVisibleChildren) { + const shouldExpand = childOptions.some( + (child) => child.href === location + ); + if (shouldExpand && !isExpanded) { + setIsExpanded(true); + localStorage.setItem(storageKey, JSON.stringify(true)); + } + } + }, [location]); + + return { isExpanded, setIsExpanded }; +} + +/** + * Checks if the child options are visible to the user. + * This hides the top level options if the child options are not visible + * for either the users permissions or the child options hidden prop is set to true by other means. + * If all child options return false for `isVisible` then the parent option will not be visible as well. + * @param {object} user - The user object. + * @param {array} childOptions - The child options. + * @returns {boolean} - True if the child options are visible, false otherwise. + */ +function hasVisibleOptions(user = null, childOptions = []) { + if (!Array.isArray(childOptions) || childOptions?.length === 0) return false; + + function isVisible({ + roles = [], + user = null, + flex = false, + hidden = false, + }) { + if (hidden) return false; + if (!flex && !roles.includes(user?.role)) return false; + if (flex && !!user && !roles.includes(user?.role)) return false; + return true; + } + + return childOptions.some((opt) => + isVisible({ roles: opt.roles, user, flex: opt.flex, hidden: opt.hidden }) + ); +} + +function generateStorageKey({ key = "" }) { + const _key = key.replace(/\s+/g, "_").toLowerCase(); + return `anything_llm_menu_${_key}_expanded`; +} diff --git a/frontend/src/components/SettingsSidebar/index.jsx b/frontend/src/components/SettingsSidebar/index.jsx new file mode 100644 index 0000000..a20c2fe --- /dev/null +++ b/frontend/src/components/SettingsSidebar/index.jsx @@ -0,0 +1,436 @@ +import React, { useEffect, useRef, useState } from "react"; +import paths from "@/utils/paths"; +import useLogo from "@/hooks/useLogo"; +import { + House, + List, + Robot, + Flask, + Gear, + UserCircleGear, + PencilSimpleLine, + Nut, + Toolbox, + Globe, +} from "@phosphor-icons/react"; +import useUser from "@/hooks/useUser"; +import { isMobile } from "react-device-detect"; +import Footer from "../Footer"; +import { Link } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import showToast from "@/utils/toast"; +import System from "@/models/system"; +import Option from "./MenuOption"; +import { CanViewChatHistoryProvider } from "../CanViewChatHistory"; + +export default function SettingsSidebar() { + const { t } = useTranslation(); + const { logo } = useLogo(); + const { user } = useUser(); + const sidebarRef = useRef(null); + const [showSidebar, setShowSidebar] = useState(false); + const [showBgOverlay, setShowBgOverlay] = useState(false); + + useEffect(() => { + function handleBg() { + if (showSidebar) { + setTimeout(() => { + setShowBgOverlay(true); + }, 300); + } else { + setShowBgOverlay(false); + } + } + handleBg(); + }, [showSidebar]); + + if (isMobile) { + return ( + <> +
+ +
+ Logo +
+
+
+
+
setShowSidebar(false)} + /> +
+
+ {/* Header Information */} +
+
+ Logo +
+
+ + + +
+
+ + {/* Primary Body */} +
+
+
+ +
+ + + {t("settings.privacy")} + +
+
+
+
+
+
+
+
+
+ + ); + } + + return ( + <> +
+ + Logo + +
+
+
+ {t("settings.title")} +
+
+
+
+ +
+ + + {t("settings.privacy")} + +
+
+
+
+
+
+
+
+
+ + ); +} + +function SupportEmail() { + const [supportEmail, setSupportEmail] = useState(paths.mailToMintplex()); + const { t } = useTranslation(); + + useEffect(() => { + const fetchSupportEmail = async () => { + const supportEmail = await System.fetchSupportEmail(); + setSupportEmail( + supportEmail?.email + ? `mailto:${supportEmail.email}` + : paths.mailToMintplex() + ); + }; + fetchSupportEmail(); + }, []); + + return ( + + {t("settings.contact")} + + ); +} + +const SidebarOptions = ({ user = null, t }) => ( + + {({ viewable: canViewChatHistory }) => ( + <> + +); + +function HoldToReveal({ children, holdForMs = 3_000 }) { + let timeout = null; + const [showing, setShowing] = useState( + window.localStorage.getItem( + "anythingllm_experimental_feature_preview_unlocked" + ) + ); + + useEffect(() => { + const onPress = (e) => { + if (!["Control", "Meta"].includes(e.key) || timeout !== null) return; + timeout = setTimeout(() => { + setShowing(true); + // Setting toastId prevents hook spam from holding control too many times or the event not detaching + showToast("Experimental feature previews unlocked!"); + window.localStorage.setItem( + "anythingllm_experimental_feature_preview_unlocked", + "enabled" + ); + window.removeEventListener("keypress", onPress); + window.removeEventListener("keyup", onRelease); + clearTimeout(timeout); + }, holdForMs); + }; + const onRelease = (e) => { + if (!["Control", "Meta"].includes(e.key)) return; + if (showing) { + window.removeEventListener("keypress", onPress); + window.removeEventListener("keyup", onRelease); + clearTimeout(timeout); + return; + } + clearTimeout(timeout); + }; + + if (!showing) { + window.addEventListener("keydown", onPress); + window.addEventListener("keyup", onRelease); + } + return () => { + window.removeEventListener("keydown", onPress); + window.removeEventListener("keyup", onRelease); + }; + }, []); + + if (!showing) return null; + return children; +} diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx new file mode 100644 index 0000000..4c12b05 --- /dev/null +++ b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx @@ -0,0 +1,269 @@ +import Workspace from "@/models/workspace"; +import paths from "@/utils/paths"; +import showToast from "@/utils/toast"; +import { + ArrowCounterClockwise, + DotsThree, + PencilSimple, + Trash, + X, +} from "@phosphor-icons/react"; +import { useEffect, useRef, useState } from "react"; +import { useParams } from "react-router-dom"; +import truncate from "truncate"; + +const THREAD_CALLOUT_DETAIL_WIDTH = 26; +export default function ThreadItem({ + idx, + activeIdx, + isActive, + workspace, + thread, + onRemove, + toggleMarkForDeletion, + hasNext, + ctrlPressed = false, +}) { + const { slug, threadSlug = null } = useParams(); + const optionsContainer = useRef(null); + const [showOptions, setShowOptions] = useState(false); + const linkTo = !thread.slug + ? paths.workspace.chat(slug) + : paths.workspace.thread(slug, thread.slug); + + return ( +
+ {/* Curved line Element and leader if required */} +
+ {/* Downstroke border for next item */} + {hasNext && ( +
+ )} + + {/* Curved line inline placeholder for spacing - not visible */} +
+
+ {thread.deleted ? ( +
+
+

+ deleted thread +

+
+ {ctrlPressed && ( + + )} +
+ ) : ( + +

+ {truncate(thread.name, 25)} +

+
+ )} + {!!thread.slug && !thread.deleted && ( +
+ {" "} + {/* Added flex and items-center */} + {ctrlPressed ? ( + + ) : ( +
+ +
+ )} + {showOptions && ( + setShowOptions(false)} + currentThreadSlug={threadSlug} + /> + )} +
+ )} +
+
+ ); +} + +function OptionsMenu({ + containerRef, + workspace, + thread, + onRemove, + close, + currentThreadSlug, +}) { + const menuRef = useRef(null); + + // Ref menu options + const outsideClick = (e) => { + if (!menuRef.current) return false; + if ( + !menuRef.current?.contains(e.target) && + !containerRef.current?.contains(e.target) + ) + close(); + return false; + }; + + const isEsc = (e) => { + if (e.key === "Escape" || e.key === "Esc") close(); + }; + + function cleanupListeners() { + window.removeEventListener("click", outsideClick); + window.removeEventListener("keyup", isEsc); + } + // end Ref menu options + + useEffect(() => { + function setListeners() { + if (!menuRef?.current || !containerRef.current) return false; + window.document.addEventListener("click", outsideClick); + window.document.addEventListener("keyup", isEsc); + } + + setListeners(); + return cleanupListeners; + }, [menuRef.current, containerRef.current]); + + const renameThread = async () => { + const name = window + .prompt("What would you like to rename this thread to?") + ?.trim(); + if (!name || name.length === 0) { + close(); + return; + } + + const { message } = await Workspace.threads.update( + workspace.slug, + thread.slug, + { name } + ); + if (!!message) { + showToast(`Thread could not be updated! ${message}`, "error", { + clear: true, + }); + close(); + return; + } + + thread.name = name; + close(); + }; + + const handleDelete = async () => { + if ( + !window.confirm( + "Are you sure you want to delete this thread? All of its chats will be deleted. You cannot undo this." + ) + ) + return; + const success = await Workspace.threads.delete(workspace.slug, thread.slug); + if (!success) { + showToast("Thread could not be deleted!", "error", { clear: true }); + return; + } + if (success) { + showToast("Thread deleted successfully!", "success", { clear: true }); + onRemove(thread.id); + // Redirect if deleting the active thread + if (currentThreadSlug === thread.slug) { + window.location.href = paths.workspace.chat(workspace.slug); + } + return; + } + }; + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx new file mode 100644 index 0000000..f9c0ea4 --- /dev/null +++ b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx @@ -0,0 +1,233 @@ +import Workspace from "@/models/workspace"; +import paths from "@/utils/paths"; +import showToast from "@/utils/toast"; +import { Plus, CircleNotch, Trash } from "@phosphor-icons/react"; +import { useEffect, useState } from "react"; +import ThreadItem from "./ThreadItem"; +import { useParams } from "react-router-dom"; +export const THREAD_RENAME_EVENT = "renameThread"; + +export default function ThreadContainer({ workspace }) { + const { threadSlug = null } = useParams(); + const [threads, setThreads] = useState([]); + const [loading, setLoading] = useState(true); + const [ctrlPressed, setCtrlPressed] = useState(false); + + useEffect(() => { + const chatHandler = (event) => { + const { threadSlug, newName } = event.detail; + setThreads((prevThreads) => + prevThreads.map((thread) => { + if (thread.slug === threadSlug) { + return { ...thread, name: newName }; + } + return thread; + }) + ); + }; + + window.addEventListener(THREAD_RENAME_EVENT, chatHandler); + + return () => { + window.removeEventListener(THREAD_RENAME_EVENT, chatHandler); + }; + }, []); + + useEffect(() => { + async function fetchThreads() { + if (!workspace.slug) return; + const { threads } = await Workspace.threads.all(workspace.slug); + setLoading(false); + setThreads(threads); + } + fetchThreads(); + }, [workspace.slug]); + + // Enable toggling of bulk-deletion by holding meta-key (ctrl on win and cmd/fn on others) + useEffect(() => { + const handleKeyDown = (event) => { + if (["Control", "Meta"].includes(event.key)) { + setCtrlPressed(true); + } + }; + + const handleKeyUp = (event) => { + if (["Control", "Meta"].includes(event.key)) { + setCtrlPressed(false); + // when toggling, unset bulk progress so + // previously marked threads that were never deleted + // come back to life. + setThreads((prev) => + prev.map((t) => { + return { ...t, deleted: false }; + }) + ); + } + }; + + window.addEventListener("keydown", handleKeyDown); + window.addEventListener("keyup", handleKeyUp); + + return () => { + window.removeEventListener("keydown", handleKeyDown); + window.removeEventListener("keyup", handleKeyUp); + }; + }, []); + + const toggleForDeletion = (id) => { + setThreads((prev) => + prev.map((t) => { + if (t.id !== id) return t; + return { ...t, deleted: !t.deleted }; + }) + ); + }; + + const handleDeleteAll = async () => { + const slugs = threads.filter((t) => t.deleted === true).map((t) => t.slug); + await Workspace.threads.deleteBulk(workspace.slug, slugs); + setThreads((prev) => prev.filter((t) => !t.deleted)); + + // Only redirect if current thread is being deleted + if (slugs.includes(threadSlug)) { + window.location.href = paths.workspace.chat(workspace.slug); + } + }; + + function removeThread(threadId) { + setThreads((prev) => + prev.map((_t) => { + if (_t.id !== threadId) return _t; + return { ..._t, deleted: true }; + }) + ); + + // Show thread was deleted, but then remove from threads entirely so it will + // not appear in bulk-selection. + setTimeout(() => { + setThreads((prev) => prev.filter((t) => !t.deleted)); + }, 500); + } + + if (loading) { + return ( +
+

loading threads....

+
+ ); + } + + const activeThreadIdx = !!threads.find( + (thread) => thread?.slug === threadSlug + ) + ? threads.findIndex((thread) => thread?.slug === threadSlug) + 1 + : 0; + + return ( +
+ 0} + /> + {threads.map((thread, i) => ( + + ))} + + +
+ ); +} + +function NewThreadButton({ workspace }) { + const [loading, setLoading] = useState(false); + const onClick = async () => { + setLoading(true); + const { thread, error } = await Workspace.threads.new(workspace.slug); + if (!!error) { + showToast(`Could not create thread - ${error}`, "error", { clear: true }); + setLoading(false); + return; + } + window.location.replace( + paths.workspace.thread(workspace.slug, thread.slug) + ); + }; + + return ( + + ); +} + +function DeleteAllThreadButton({ ctrlPressed, threads, onDelete }) { + if (!ctrlPressed || threads.filter((t) => t.deleted).length === 0) + return null; + return ( + + ); +} diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx new file mode 100644 index 0000000..5e9c9fb --- /dev/null +++ b/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx @@ -0,0 +1,151 @@ +import React, { useState, useEffect } from "react"; +import * as Skeleton from "react-loading-skeleton"; +import "react-loading-skeleton/dist/skeleton.css"; +import Workspace from "@/models/workspace"; +import ManageWorkspace, { + useManageWorkspaceModal, +} from "../../Modals/ManageWorkspace"; +import paths from "@/utils/paths"; +import { useParams } from "react-router-dom"; +import { GearSix, SquaresFour, UploadSimple } from "@phosphor-icons/react"; +import useUser from "@/hooks/useUser"; +import ThreadContainer from "./ThreadContainer"; +import { Link, useMatch } from "react-router-dom"; + +export default function ActiveWorkspaces() { + const { slug } = useParams(); + const [loading, setLoading] = useState(true); + const [workspaces, setWorkspaces] = useState([]); + const [selectedWs, setSelectedWs] = useState(null); + const { showing, showModal, hideModal } = useManageWorkspaceModal(); + const { user } = useUser(); + const isInWorkspaceSettings = !!useMatch("/workspace/:slug/settings/:tab"); + + useEffect(() => { + async function getWorkspaces() { + const workspaces = await Workspace.all(); + setLoading(false); + setWorkspaces(workspaces); + } + getWorkspaces(); + }, []); + + if (loading) { + return ( + + ); + } + + return ( + + ); +} diff --git a/frontend/src/components/Sidebar/SidebarToggle/index.jsx b/frontend/src/components/Sidebar/SidebarToggle/index.jsx new file mode 100644 index 0000000..a0f47b6 --- /dev/null +++ b/frontend/src/components/Sidebar/SidebarToggle/index.jsx @@ -0,0 +1,105 @@ +import React, { useEffect, useState } from "react"; +import { SidebarSimple } from "@phosphor-icons/react"; +import paths from "@/utils/paths"; +import { Tooltip } from "react-tooltip"; +const SIDEBAR_TOGGLE_STORAGE_KEY = "anythingllm_sidebar_toggle"; + +/** + * Returns the previous state of the sidebar from localStorage. + * If the sidebar was closed, returns false. + * If the sidebar was open, returns true. + * If the sidebar state is not set, returns true. + * @returns {boolean} + */ +function previousSidebarState() { + const previousState = window.localStorage.getItem(SIDEBAR_TOGGLE_STORAGE_KEY); + if (previousState === "closed") return false; + return true; +} + +export function useSidebarToggle() { + const [showSidebar, setShowSidebar] = useState(previousSidebarState()); + const [canToggleSidebar, setCanToggleSidebar] = useState(true); + + useEffect(() => { + function checkPath() { + const currentPath = window.location.pathname; + const isVisible = + currentPath === paths.home() || + /^\/workspace\/[^\/]+$/.test(currentPath) || + /^\/workspace\/[^\/]+\/t\/[^\/]+$/.test(currentPath); + setCanToggleSidebar(isVisible); + } + checkPath(); + }, [window.location.pathname]); + + useEffect(() => { + function toggleSidebar(e) { + if (!canToggleSidebar) return; + if ( + (e.ctrlKey || e.metaKey) && + e.shiftKey && + e.key.toLowerCase() === "s" + ) { + setShowSidebar((prev) => { + const newState = !prev; + window.localStorage.setItem( + SIDEBAR_TOGGLE_STORAGE_KEY, + newState ? "open" : "closed" + ); + return newState; + }); + } + } + window.addEventListener("keydown", toggleSidebar); + return () => { + window.removeEventListener("keydown", toggleSidebar); + }; + }, []); + + useEffect(() => { + window.localStorage.setItem( + SIDEBAR_TOGGLE_STORAGE_KEY, + showSidebar ? "open" : "closed" + ); + }, [showSidebar]); + + return { showSidebar, setShowSidebar, canToggleSidebar }; +} + +export function ToggleSidebarButton({ showSidebar, setShowSidebar }) { + const isMac = navigator.userAgent.includes("Mac"); + const shortcut = isMac ? "⌘ + Shift + S" : "Ctrl + Shift + S"; + + return ( + <> + + + + ); +} diff --git a/frontend/src/components/Sidebar/index.jsx b/frontend/src/components/Sidebar/index.jsx new file mode 100644 index 0000000..04a7fe4 --- /dev/null +++ b/frontend/src/components/Sidebar/index.jsx @@ -0,0 +1,206 @@ +import React, { useEffect, useRef, useState } from "react"; +import { Plus, List } from "@phosphor-icons/react"; +import NewWorkspaceModal, { + useNewWorkspaceModal, +} from "../Modals/NewWorkspace"; +import ActiveWorkspaces from "./ActiveWorkspaces"; +import useLogo from "@/hooks/useLogo"; +import useUser from "@/hooks/useUser"; +import Footer from "../Footer"; +import SettingsButton from "../SettingsButton"; +import { Link } from "react-router-dom"; +import paths from "@/utils/paths"; +import { useTranslation } from "react-i18next"; +import { useSidebarToggle, ToggleSidebarButton } from "./SidebarToggle"; + +export default function Sidebar() { + const { user } = useUser(); + const { logo } = useLogo(); + const sidebarRef = useRef(null); + const { showSidebar, setShowSidebar, canToggleSidebar } = useSidebarToggle(); + const { + showing: showingNewWsModal, + showModal: showNewWsModal, + hideModal: hideNewWsModal, + } = useNewWorkspaceModal(); + const { t } = useTranslation(); + + return ( + <> +
+
+
+ + Logo + + {canToggleSidebar && ( + + )} +
+
+
+
+
+
+
+
+ {(!user || user?.role !== "default") && ( + + )} +
+ +
+
+
+
+
+
+
+
+ {showingNewWsModal && } +
+ + ); +} + +export function SidebarMobileHeader() { + const { logo } = useLogo(); + const sidebarRef = useRef(null); + const [showSidebar, setShowSidebar] = useState(false); + const [showBgOverlay, setShowBgOverlay] = useState(false); + const { + showing: showingNewWsModal, + showModal: showNewWsModal, + hideModal: hideNewWsModal, + } = useNewWorkspaceModal(); + const { user } = useUser(); + const { t } = useTranslation(); + + useEffect(() => { + // Darkens the rest of the screen + // when sidebar is open. + function handleBg() { + if (showSidebar) { + setTimeout(() => { + setShowBgOverlay(true); + }, 300); + } else { + setShowBgOverlay(false); + } + } + handleBg(); + }, [showSidebar]); + + return ( + <> +
+ +
+ Logo +
+
+
+
+
setShowSidebar(false)} + /> +
+
+ {/* Header Information */} +
+
+ Logo +
+ {(!user || user?.role !== "default") && ( +
+ +
+ )} +
+ + {/* Primary Body */} +
+
+
+
+ {(!user || user?.role !== "default") && ( + + )} +
+ +
+
+
+
+
+
+
+
+ {showingNewWsModal && } +
+ + ); +} diff --git a/frontend/src/components/SpeechToText/BrowserNative/index.jsx b/frontend/src/components/SpeechToText/BrowserNative/index.jsx new file mode 100644 index 0000000..1e9bcb3 --- /dev/null +++ b/frontend/src/components/SpeechToText/BrowserNative/index.jsx @@ -0,0 +1,9 @@ +export default function BrowserNative() { + return ( +
+

+ There is no configuration needed for this provider. +

+
+ ); +} diff --git a/frontend/src/components/TextToSpeech/BrowserNative/index.jsx b/frontend/src/components/TextToSpeech/BrowserNative/index.jsx new file mode 100644 index 0000000..1e9bcb3 --- /dev/null +++ b/frontend/src/components/TextToSpeech/BrowserNative/index.jsx @@ -0,0 +1,9 @@ +export default function BrowserNative() { + return ( +
+

+ There is no configuration needed for this provider. +

+
+ ); +} diff --git a/frontend/src/components/TextToSpeech/ElevenLabsOptions/index.jsx b/frontend/src/components/TextToSpeech/ElevenLabsOptions/index.jsx new file mode 100644 index 0000000..f3ea178 --- /dev/null +++ b/frontend/src/components/TextToSpeech/ElevenLabsOptions/index.jsx @@ -0,0 +1,109 @@ +import { useState, useEffect } from "react"; +import System from "@/models/system"; + +export default function ElevenLabsOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.TTSElevenLabsKey); + const [elevenLabsKey, setElevenLabsKey] = useState( + settings?.TTSElevenLabsKey + ); + + return ( +
+
+ + setInputValue(e.target.value)} + onBlur={() => setElevenLabsKey(inputValue)} + /> +
+ {!settings?.credentialsOnly && ( + + )} +
+ ); +} + +function ElevenLabsModelSelection({ apiKey, settings }) { + const [groupedModels, setGroupedModels] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models } = await System.customModels( + "elevenlabs-tts", + typeof apiKey === "boolean" ? null : apiKey + ); + + if (models?.length > 0) { + const modelsByOrganization = models.reduce((acc, model) => { + acc[model.organization] = acc[model.organization] || []; + acc[model.organization].push(model); + return acc; + }, {}); + setGroupedModels(modelsByOrganization); + } + + setLoading(false); + } + findCustomModels(); + }, [apiKey]); + + if (loading) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/TextToSpeech/OpenAiGenericOptions/index.jsx b/frontend/src/components/TextToSpeech/OpenAiGenericOptions/index.jsx new file mode 100644 index 0000000..9517c14 --- /dev/null +++ b/frontend/src/components/TextToSpeech/OpenAiGenericOptions/index.jsx @@ -0,0 +1,69 @@ +import React from "react"; + +export default function OpenAiGenericTextToSpeechOptions({ settings }) { + return ( +
+
+
+
+ +
+ +

+ This should be the base URL of the OpenAI compatible TTS service you + will generate TTS responses from. +

+
+ +
+ + +

+ Some TTS services require an API key to generate TTS responses - + this is optional if your service does not require one. +

+
+
+ + +

+ Most TTS services will have several voice models available, this is + the identifier for the voice model you want to use. +

+
+
+
+ ); +} diff --git a/frontend/src/components/TextToSpeech/OpenAiOptions/index.jsx b/frontend/src/components/TextToSpeech/OpenAiOptions/index.jsx new file mode 100644 index 0000000..7b52f38 --- /dev/null +++ b/frontend/src/components/TextToSpeech/OpenAiOptions/index.jsx @@ -0,0 +1,49 @@ +function toProperCase(string) { + return string.replace(/\w\S*/g, function (txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); +} + +export default function OpenAiTextToSpeechOptions({ settings }) { + const apiKey = settings?.TTSOpenAIKey; + + return ( +
+
+ + +
+
+ + +
+
+ ); +} diff --git a/frontend/src/components/TextToSpeech/PiperTTSOptions/index.jsx b/frontend/src/components/TextToSpeech/PiperTTSOptions/index.jsx new file mode 100644 index 0000000..1bdd283 --- /dev/null +++ b/frontend/src/components/TextToSpeech/PiperTTSOptions/index.jsx @@ -0,0 +1,220 @@ +import { useState, useEffect, useRef } from "react"; +import PiperTTSClient from "@/utils/piperTTS"; +import { titleCase } from "text-case"; +import { humanFileSize } from "@/utils/numbers"; +import showToast from "@/utils/toast"; +import { CircleNotch, PauseCircle, PlayCircle } from "@phosphor-icons/react"; + +export default function PiperTTSOptions({ settings }) { + return ( + <> +

+ All PiperTTS models will run in your browser locally. This can be + resource intensive on lower-end devices. +

+
+ +
+ + ); +} + +function voicesByLanguage(voices = []) { + const voicesByLanguage = voices.reduce((acc, voice) => { + const langName = voice?.language?.name_english ?? "Unlisted"; + acc[langName] = acc[langName] || []; + acc[langName].push(voice); + return acc; + }, {}); + return Object.entries(voicesByLanguage); +} + +function voiceDisplayName(voice) { + const { is_stored, name, quality, files } = voice; + const onnxFileKey = Object.keys(files).find((key) => key.endsWith(".onnx")); + const fileSize = files?.[onnxFileKey]?.size_bytes || 0; + return `${is_stored ? "✔ " : ""}${titleCase(name)}-${quality === "low" ? "Low" : "HQ"} (${humanFileSize(fileSize)})`; +} + +function PiperTTSModelSelection({ settings }) { + const [loading, setLoading] = useState(true); + const [voices, setVoices] = useState([]); + const [selectedVoice, setSelectedVoice] = useState( + settings?.TTSPiperTTSVoiceModel + ); + + function flushVoices() { + PiperTTSClient.flush() + .then(() => + showToast("All voices flushed from browser storage", "info", { + clear: true, + }) + ) + .catch((e) => console.error(e)); + } + + useEffect(() => { + PiperTTSClient.voices() + .then((voices) => { + if (voices?.length !== 0) return setVoices(voices); + throw new Error("Could not fetch voices from web worker."); + }) + .catch((e) => { + console.error(e); + }) + .finally(() => setLoading(false)); + }, []); + + if (loading) { + return ( +
+ + +
+ ); + } + + return ( +
+
+ +
+ + +
+

+ The "✔" indicates this model is already stored locally and does not + need to be downloaded when run. +

+
+ {!!voices.find((voice) => voice.is_stored) && ( + + )} +
+ ); +} + +function DemoVoiceSample({ voiceId }) { + const playerRef = useRef(null); + const [speaking, setSpeaking] = useState(false); + const [loading, setLoading] = useState(false); + const [audioSrc, setAudioSrc] = useState(null); + + async function speakMessage(e) { + e.preventDefault(); + if (speaking) { + playerRef?.current?.pause(); + return; + } + + try { + if (!audioSrc) { + setLoading(true); + const client = new PiperTTSClient({ voiceId }); + const blobUrl = await client.getAudioBlobForText( + "Hello, welcome to AnythingLLM!" + ); + setAudioSrc(blobUrl); + setLoading(false); + client.worker?.terminate(); + PiperTTSClient._instance = null; + } else { + playerRef.current.play(); + } + } catch (e) { + console.error(e); + setLoading(false); + setSpeaking(false); + } + } + + useEffect(() => { + function setupPlayer() { + if (!playerRef?.current) return; + playerRef.current.addEventListener("play", () => { + setSpeaking(true); + }); + + playerRef.current.addEventListener("pause", () => { + playerRef.current.currentTime = 0; + setSpeaking(false); + setAudioSrc(null); + }); + } + setupPlayer(); + }, []); + + return ( + + ); +} diff --git a/frontend/src/components/TranscriptionSelection/NativeTranscriptionOptions/index.jsx b/frontend/src/components/TranscriptionSelection/NativeTranscriptionOptions/index.jsx new file mode 100644 index 0000000..2a8d8c9 --- /dev/null +++ b/frontend/src/components/TranscriptionSelection/NativeTranscriptionOptions/index.jsx @@ -0,0 +1,88 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Gauge } from "@phosphor-icons/react"; + +export default function NativeTranscriptionOptions({ settings }) { + const { t } = useTranslation(); + const [model, setModel] = useState(settings?.WhisperModelPref); + + return ( +
+ +
+
+ + +
+
+
+ ); +} + +function LocalWarning({ model }) { + switch (model) { + case "Xenova/whisper-small": + return ; + case "Xenova/whisper-large": + return ; + default: + return ; + } +} + +function WhisperSmall() { + const { t } = useTranslation(); + + return ( +
+
+ +

+ {t("transcription.warn-start")} +
+ {t("transcription.warn-recommend")} +
+
+ {t("transcription.warn-end")} (250mb) +

+
+
+ ); +} + +function WhisperLarge() { + const { t } = useTranslation(); + + return ( +
+
+ +

+ {t("transcription.warn-start")} +
+ {t("transcription.warn-recommend")} +
+
+ {t("transcription.warn-end")} (1.56GB) +

+
+
+ ); +} diff --git a/frontend/src/components/TranscriptionSelection/OpenAiOptions/index.jsx b/frontend/src/components/TranscriptionSelection/OpenAiOptions/index.jsx new file mode 100644 index 0000000..615eb23 --- /dev/null +++ b/frontend/src/components/TranscriptionSelection/OpenAiOptions/index.jsx @@ -0,0 +1,41 @@ +import { useState } from "react"; + +export default function OpenAiWhisperOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.OpenAiKey); + const [_openAIKey, setOpenAIKey] = useState(settings?.OpenAiKey); + + return ( +
+
+ + setInputValue(e.target.value)} + onBlur={() => setOpenAIKey(inputValue)} + /> +
+
+ + +
+
+ ); +} diff --git a/frontend/src/components/UserIcon/index.jsx b/frontend/src/components/UserIcon/index.jsx new file mode 100644 index 0000000..2e40709 --- /dev/null +++ b/frontend/src/components/UserIcon/index.jsx @@ -0,0 +1,42 @@ +import React, { memo } from "react"; +import usePfp from "../../hooks/usePfp"; +import UserDefaultPfp from "./user.svg"; +import WorkspaceDefaultPfp from "./workspace.svg"; + +const UserIcon = memo(({ role }) => { + const { pfp } = usePfp(); + + return ( +
+ {role === "user" && } + {role !== "user" && ( + system profile picture + )} +
+ ); +}); + +function RenderUserPfp({ pfp }) { + if (!pfp) + return ( + User profile picture + ); + + return ( + User profile picture + ); +} + +export default UserIcon; diff --git a/frontend/src/components/UserIcon/user.svg b/frontend/src/components/UserIcon/user.svg new file mode 100644 index 0000000..b0812cd --- /dev/null +++ b/frontend/src/components/UserIcon/user.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/components/UserIcon/workspace.svg b/frontend/src/components/UserIcon/workspace.svg new file mode 100644 index 0000000..6a24de5 --- /dev/null +++ b/frontend/src/components/UserIcon/workspace.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/components/UserMenu/AccountModal/index.jsx b/frontend/src/components/UserMenu/AccountModal/index.jsx new file mode 100644 index 0000000..9de8689 --- /dev/null +++ b/frontend/src/components/UserMenu/AccountModal/index.jsx @@ -0,0 +1,253 @@ +import { useLanguageOptions } from "@/hooks/useLanguageOptions"; +import usePfp from "@/hooks/usePfp"; +import System from "@/models/system"; +import { AUTH_USER } from "@/utils/constants"; +import showToast from "@/utils/toast"; +import { Plus, X } from "@phosphor-icons/react"; +import ModalWrapper from "@/components/ModalWrapper"; +import { useTheme } from "@/hooks/useTheme"; + +export default function AccountModal({ user, hideModal }) { + const { pfp, setPfp } = usePfp(); + + const handleFileUpload = async (event) => { + const file = event.target.files[0]; + if (!file) return false; + + const formData = new FormData(); + formData.append("file", file); + const { success, error } = await System.uploadPfp(formData); + if (!success) { + showToast(`Failed to upload profile picture: ${error}`, "error"); + return; + } + + const pfpUrl = await System.fetchPfp(user.id); + setPfp(pfpUrl); + showToast("Profile picture uploaded.", "success"); + }; + + const handleRemovePfp = async () => { + const { success, error } = await System.removePfp(); + if (!success) { + showToast(`Failed to remove profile picture: ${error}`, "error"); + return; + } + + setPfp(null); + }; + + const handleUpdate = async (e) => { + e.preventDefault(); + + const data = {}; + const form = new FormData(e.target); + for (var [key, value] of form.entries()) { + if (!value || value === null) continue; + data[key] = value; + } + + const { success, error } = await System.updateUser(data); + if (success) { + let storedUser = JSON.parse(localStorage.getItem(AUTH_USER)); + + if (storedUser) { + storedUser.username = data.username; + localStorage.setItem(AUTH_USER, JSON.stringify(storedUser)); + } + showToast("Profile updated.", "success", { clear: true }); + hideModal(); + } else { + showToast(`Failed to update user: ${error}`, "error"); + } + }; + + return ( + +
+
+
+

+ Edit Account +

+
+ +
+
+
+
+
+ + {pfp && ( + + )} +
+
+
+
+ + +

+ Username must be only contain lowercase letters, numbers, + underscores, and hyphens with no spaces +

+
+
+ + +

+ Password must be at least 8 characters long +

+
+
+ + +
+
+
+ + +
+
+
+
+
+ ); +} + +function LanguagePreference() { + const { + currentLanguage, + supportedLanguages, + getLanguageName, + changeLanguage, + } = useLanguageOptions(); + + return ( +
+ + +
+ ); +} + +function ThemePreference() { + const { theme, setTheme, availableThemes } = useTheme(); + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/UserMenu/UserButton/index.jsx b/frontend/src/components/UserMenu/UserButton/index.jsx new file mode 100644 index 0000000..af1a04a --- /dev/null +++ b/frontend/src/components/UserMenu/UserButton/index.jsx @@ -0,0 +1,129 @@ +import useLoginMode from "@/hooks/useLoginMode"; +import usePfp from "@/hooks/usePfp"; +import useUser from "@/hooks/useUser"; +import System from "@/models/system"; +import paths from "@/utils/paths"; +import { userFromStorage } from "@/utils/request"; +import { Person } from "@phosphor-icons/react"; +import { useEffect, useRef, useState } from "react"; +import AccountModal from "../AccountModal"; +import { AUTH_TIMESTAMP, AUTH_TOKEN, AUTH_USER } from "@/utils/constants"; + +export default function UserButton() { + const mode = useLoginMode(); + const { user } = useUser(); + const menuRef = useRef(); + const buttonRef = useRef(); + const [showMenu, setShowMenu] = useState(false); + const [showAccountSettings, setShowAccountSettings] = useState(false); + const [supportEmail, setSupportEmail] = useState(""); + + const handleClose = (event) => { + if ( + menuRef.current && + !menuRef.current.contains(event.target) && + !buttonRef.current.contains(event.target) + ) { + setShowMenu(false); + } + }; + + const handleOpenAccountModal = () => { + setShowAccountSettings(true); + setShowMenu(false); + }; + + useEffect(() => { + if (showMenu) { + document.addEventListener("mousedown", handleClose); + } + return () => document.removeEventListener("mousedown", handleClose); + }, [showMenu]); + + useEffect(() => { + const fetchSupportEmail = async () => { + const supportEmail = await System.fetchSupportEmail(); + setSupportEmail( + supportEmail?.email + ? `mailto:${supportEmail.email}` + : paths.mailToMintplex() + ); + }; + fetchSupportEmail(); + }, []); + + if (mode === null) return null; + return ( +
+ + + {showMenu && ( +
+
+ {mode === "multi" && !!user && ( + + )} + + Support + + +
+
+ )} + {user && showAccountSettings && ( + setShowAccountSettings(false)} + /> + )} +
+ ); +} + +function UserDisplay() { + const { pfp } = usePfp(); + const user = userFromStorage(); + + if (pfp) { + return ( +
+ User profile picture +
+ ); + } + + return user?.username?.slice(0, 2) || "AA"; +} diff --git a/frontend/src/components/UserMenu/index.jsx b/frontend/src/components/UserMenu/index.jsx new file mode 100644 index 0000000..4773096 --- /dev/null +++ b/frontend/src/components/UserMenu/index.jsx @@ -0,0 +1,10 @@ +import UserButton from "./UserButton"; + +export default function UserMenu({ children }) { + return ( +
+ + {children} +
+ ); +} diff --git a/frontend/src/components/VectorDBSelection/AstraDBOptions/index.jsx b/frontend/src/components/VectorDBSelection/AstraDBOptions/index.jsx new file mode 100644 index 0000000..9db5d29 --- /dev/null +++ b/frontend/src/components/VectorDBSelection/AstraDBOptions/index.jsx @@ -0,0 +1,41 @@ +export default function AstraDBOptions({ settings }) { + return ( +
+
+
+ + +
+ +
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/VectorDBSelection/ChromaDBOptions/index.jsx b/frontend/src/components/VectorDBSelection/ChromaDBOptions/index.jsx new file mode 100644 index 0000000..cdcda5c --- /dev/null +++ b/frontend/src/components/VectorDBSelection/ChromaDBOptions/index.jsx @@ -0,0 +1,51 @@ +export default function ChromaDBOptions({ settings }) { + return ( +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/VectorDBSelection/LanceDBOptions/index.jsx b/frontend/src/components/VectorDBSelection/LanceDBOptions/index.jsx new file mode 100644 index 0000000..d8845bb --- /dev/null +++ b/frontend/src/components/VectorDBSelection/LanceDBOptions/index.jsx @@ -0,0 +1,11 @@ +import { useTranslation } from "react-i18next"; +export default function LanceDBOptions() { + const { t } = useTranslation(); + return ( +
+

+ {t("vector.provider.description")} +

+
+ ); +} diff --git a/frontend/src/components/VectorDBSelection/MilvusDBOptions/index.jsx b/frontend/src/components/VectorDBSelection/MilvusDBOptions/index.jsx new file mode 100644 index 0000000..cc30868 --- /dev/null +++ b/frontend/src/components/VectorDBSelection/MilvusDBOptions/index.jsx @@ -0,0 +1,52 @@ +export default function MilvusDBOptions({ settings }) { + return ( +
+
+
+ + +
+ +
+ + +
+
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/VectorDBSelection/PineconeDBOptions/index.jsx b/frontend/src/components/VectorDBSelection/PineconeDBOptions/index.jsx new file mode 100644 index 0000000..58031ae --- /dev/null +++ b/frontend/src/components/VectorDBSelection/PineconeDBOptions/index.jsx @@ -0,0 +1,38 @@ +export default function PineconeDBOptions({ settings }) { + return ( +
+
+
+ + +
+
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/VectorDBSelection/QDrantDBOptions/index.jsx b/frontend/src/components/VectorDBSelection/QDrantDBOptions/index.jsx new file mode 100644 index 0000000..0f17d34 --- /dev/null +++ b/frontend/src/components/VectorDBSelection/QDrantDBOptions/index.jsx @@ -0,0 +1,38 @@ +export default function QDrantDBOptions({ settings }) { + return ( +
+
+
+ + +
+ +
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/VectorDBSelection/VectorDBItem/index.jsx b/frontend/src/components/VectorDBSelection/VectorDBItem/index.jsx new file mode 100644 index 0000000..3de81ac --- /dev/null +++ b/frontend/src/components/VectorDBSelection/VectorDBItem/index.jsx @@ -0,0 +1,37 @@ +export default function VectorDBItem({ + name, + value, + image, + description, + checked, + onClick, +}) { + return ( +
onClick(value)} + className={`w-full p-2 rounded-md hover:cursor-pointer hover:bg-theme-bg-secondary ${ + checked ? "bg-theme-bg-secondary" : "" + }`} + > + +
+ {`${name} +
+
{name}
+
{description}
+
+
+
+ ); +} diff --git a/frontend/src/components/VectorDBSelection/WeaviateDBOptions/index.jsx b/frontend/src/components/VectorDBSelection/WeaviateDBOptions/index.jsx new file mode 100644 index 0000000..84c7d4a --- /dev/null +++ b/frontend/src/components/VectorDBSelection/WeaviateDBOptions/index.jsx @@ -0,0 +1,38 @@ +export default function WeaviateDBOptions({ settings }) { + return ( +
+
+
+ + +
+ +
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/VectorDBSelection/ZillizCloudOptions/index.jsx b/frontend/src/components/VectorDBSelection/ZillizCloudOptions/index.jsx new file mode 100644 index 0000000..b489653 --- /dev/null +++ b/frontend/src/components/VectorDBSelection/ZillizCloudOptions/index.jsx @@ -0,0 +1,38 @@ +export default function ZillizCloudOptions({ settings }) { + return ( +
+
+
+ + +
+ +
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/CustomCell.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/CustomCell.jsx new file mode 100644 index 0000000..b0e9439 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/CustomCell.jsx @@ -0,0 +1,50 @@ +export default function CustomCell({ ...props }) { + const { + root, + depth, + x, + y, + width, + height, + index, + payload, + colors, + rank, + name, + } = props; + return ( + + + {depth === 1 ? ( + + {name} + + ) : null} + {depth === 1 ? ( + + {index + 1} + + ) : null} + + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/CustomTooltip.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/CustomTooltip.jsx new file mode 100644 index 0000000..2222090 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/CustomTooltip.jsx @@ -0,0 +1,89 @@ +import { Tooltip as RechartsTooltip } from "recharts"; + +// Given a hex, convert to the opposite highest-contrast color +// and if `bw` is enabled, force it to be black/white to normalize +// interface. +function invertColor(hex, bw) { + if (hex.indexOf("#") === 0) { + hex = hex.slice(1); + } + // convert 3-digit hex to 6-digits. + if (hex.length === 3) { + hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; + } + if (hex.length !== 6) { + throw new Error("Invalid HEX color."); + } + var r = parseInt(hex.slice(0, 2), 16), + g = parseInt(hex.slice(2, 4), 16), + b = parseInt(hex.slice(4, 6), 16); + if (bw) { + // https://stackoverflow.com/a/3943023/112731 + return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? "#FFFFFF" : "#000000"; + // : '#FFFFFF'; + } + // invert color components + r = (255 - r).toString(16); + g = (255 - g).toString(16); + b = (255 - b).toString(16); + // pad each with zeros and return + return "#" + padZero(r) + padZero(g) + padZero(b); +} + +function padZero(str, len) { + len = len || 2; + var zeros = new Array(len).join("0"); + return (zeros + str).slice(-len); +} + +export default function Tooltip({ legendColor, ...props }) { + return ( + { + return active && payload ? ( +
+
+

{label}

+
+
+ {payload.map(({ value, name }, idx) => ( +
+
+ +

+ {value} +

+
+

+ {name} +

+
+ ))} +
+
+ ) : null; + }} + /> + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/chart-utils.js b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/chart-utils.js new file mode 100644 index 0000000..53e501f --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/chart-utils.js @@ -0,0 +1,98 @@ +export const Colors = { + blue: "#3b82f6", + sky: "#0ea5e9", + cyan: "#06b6d4", + teal: "#14b8a6", + emerald: "#10b981", + green: "#22c55e", + lime: "#84cc16", + yellow: "#eab308", + amber: "#f59e0b", + orange: "#f97316", + red: "#ef4444", + rose: "#f43f5e", + pink: "#ec4899", + fuchsia: "#d946ef", + purple: "#a855f7", + violet: "#8b5cf6", + indigo: "#6366f1", + neutral: "#737373", + stone: "#78716c", + gray: "#6b7280", + slate: "#64748b", + zinc: "#71717a", +}; + +export function getTremorColor(color) { + switch (color) { + case "blue": + return Colors.blue; + case "sky": + return Colors.sky; + case "cyan": + return Colors.cyan; + case "teal": + return Colors.teal; + case "emerald": + return Colors.emerald; + case "green": + return Colors.green; + case "lime": + return Colors.lime; + case "yellow": + return Colors.yellow; + case "amber": + return Colors.amber; + case "orange": + return Colors.orange; + case "red": + return Colors.red; + case "rose": + return Colors.rose; + case "pink": + return Colors.pink; + case "fuchsia": + return Colors.fuchsia; + case "purple": + return Colors.purple; + case "violet": + return Colors.violet; + case "indigo": + return Colors.indigo; + case "neutral": + return Colors.neutral; + case "stone": + return Colors.stone; + case "gray": + return Colors.gray; + case "slate": + return Colors.slate; + case "zinc": + return Colors.zinc; + } +} + +export const themeColorRange = [ + "slate", + "gray", + "zinc", + "neutral", + "stone", + "red", + "orange", + "amber", + "yellow", + "lime", + "green", + "emerald", + "teal", + "cyan", + "sky", + "blue", + "indigo", + "violet", + "purple", + "fuchsia", + "pink", + "rose", +]; diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/index.jsx new file mode 100644 index 0000000..f47c585 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Chartable/index.jsx @@ -0,0 +1,487 @@ +import { v4 } from "uuid"; +import { + AreaChart, + BarChart, + DonutChart, + Legend, + LineChart, +} from "@tremor/react"; +import { + Bar, + CartesianGrid, + ComposedChart, + Funnel, + FunnelChart, + Line, + PolarAngleAxis, + PolarGrid, + PolarRadiusAxis, + Radar, + RadarChart, + RadialBar, + RadialBarChart, + Scatter, + ScatterChart, + Treemap, + XAxis, + YAxis, +} from "recharts"; +import { Colors, getTremorColor } from "./chart-utils.js"; +import CustomCell from "./CustomCell.jsx"; +import Tooltip from "./CustomTooltip.jsx"; +import { safeJsonParse } from "@/utils/request.js"; +import renderMarkdown from "@/utils/chat/markdown.js"; +import { WorkspaceProfileImage } from "../PromptReply/index.jsx"; +import { memo, useCallback, useState } from "react"; +import { saveAs } from "file-saver"; +import { useGenerateImage } from "recharts-to-png"; +import { CircleNotch, DownloadSimple } from "@phosphor-icons/react"; + +const dataFormatter = (number) => { + return Intl.NumberFormat("us").format(number).toString(); +}; + +export function Chartable({ props, workspace }) { + const [getDivJpeg, { ref }] = useGenerateImage({ + quality: 1, + type: "image/jpeg", + options: { + backgroundColor: "#393d43", + padding: 20, + }, + }); + const handleDownload = useCallback(async () => { + const jpeg = await getDivJpeg(); + if (jpeg) saveAs(jpeg, `chart-${v4().split("-")[0]}.jpg`); + }, []); + + const color = null; + const showLegend = true; + const content = + typeof props.content === "string" + ? safeJsonParse(props.content, null) + : props.content; + if (content === null) return null; + + const chartType = content?.type?.toLowerCase(); + const data = + typeof content.dataset === "string" + ? safeJsonParse(content.dataset, []) + : content.dataset; + const value = data.length > 0 ? Object.keys(data[0])[1] : "value"; + const title = content?.title; + + const renderChart = () => { + switch (chartType) { + case "area": + return ( +
+

+ {title} +

+ +
+ ); + case "bar": + return ( +
+

+ {title} +

+ +
+ ); + case "line": + return ( +
+

+ {title} +

+ +
+ ); + case "composed": + return ( +
+

+ {title} +

+ {showLegend && ( + + )} + + + + + + + + +
+ ); + case "scatter": + return ( +
+

+ {title} +

+ {showLegend && ( +
+ +
+ )} + + + + + + + +
+ ); + case "pie": + return ( +
+

+ {title} +

+ +
+ ); + case "radar": + return ( +
+

+ {title} +

+ {showLegend && ( +
+ +
+ )} + + + + + + + +
+ ); + case "radialbar": + return ( +
+

+ {title} +

+ {showLegend && ( +
+ +
+ )} + + + + +
+ ); + case "treemap": + return ( +
+

+ {title} +

+ {showLegend && ( +
+ +
+ )} + } + > + + +
+ ); + case "funnel": + return ( +
+

+ {title} +

+ {showLegend && ( +
+ +
+ )} + + + + +
+ ); + default: + return

Unsupported chart type.

; + } + }; + + if (!!props.chatId) { + return ( +
+
+
+ +
+ +
{renderChart()}
+ +
+
+
+
+ ); + } + + return ( +
+
+
+ +
{renderChart()}
+
+
+ +
+
+
+ ); +} + +const customTooltip = (props) => { + const { payload, active } = props; + if (!active || !payload) return null; + const categoryPayload = payload?.[0]; + if (!categoryPayload) return null; + return ( +
+
+
+
+
+

+ {categoryPayload.name} +

+

+ {categoryPayload.value} +

+
+
+
+
+ ); +}; + +function DownloadGraph({ onClick }) { + const [loading, setLoading] = useState(false); + const handleClick = async () => { + setLoading(true); + await onClick?.(); + setLoading(false); + }; + + return ( +
+
+
+ {loading ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default memo(Chartable); diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Citation/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Citation/index.jsx new file mode 100644 index 0000000..b2a6f73 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Citation/index.jsx @@ -0,0 +1,248 @@ +import { memo, useState } from "react"; +import { v4 } from "uuid"; +import { decode as HTMLDecode } from "he"; +import truncate from "truncate"; +import ModalWrapper from "@/components/ModalWrapper"; +import { middleTruncate } from "@/utils/directories"; +import { + CaretRight, + FileText, + Info, + ArrowSquareOut, + GithubLogo, + Link, + X, + YoutubeLogo, +} from "@phosphor-icons/react"; +import ConfluenceLogo from "@/media/dataConnectors/confluence.png"; +import { toPercentString } from "@/utils/numbers"; + +function combineLikeSources(sources) { + const combined = {}; + sources.forEach((source) => { + const { id, title, text, chunkSource = "", score = null } = source; + if (combined.hasOwnProperty(title)) { + combined[title].chunks.push({ id, text, chunkSource, score }); + combined[title].references += 1; + } else { + combined[title] = { + title, + chunks: [{ id, text, chunkSource, score }], + references: 1, + }; + } + }); + return Object.values(combined); +} + +export default function Citations({ sources = [] }) { + if (sources.length === 0) return null; + const [open, setOpen] = useState(false); + const [selectedSource, setSelectedSource] = useState(null); + + return ( +
+ + {open && ( +
+ {combineLikeSources(sources).map((source) => ( + setSelectedSource(source)} + /> + ))} +
+ )} + {selectedSource && ( + setSelectedSource(null)} + /> + )} +
+ ); +} + +const Citation = memo(({ source, onClick }) => { + const { title } = source; + if (!title) return null; + const chunkSourceInfo = parseChunkSource(source); + const truncatedTitle = chunkSourceInfo?.text ?? middleTruncate(title, 25); + const CitationIcon = ICONS.hasOwnProperty(chunkSourceInfo?.icon) + ? ICONS[chunkSourceInfo.icon] + : ICONS.file; + + return ( +
+ +

{truncatedTitle}

+
+ ); +}); + +function omitChunkHeader(text) { + if (!text.startsWith("")) return text; + return text.split("")[1].trim(); +} + +function CitationDetailModal({ source, onClose }) { + const { references, title, chunks } = source; + const { isUrl, text: webpageUrl, href: linkTo } = parseChunkSource(source); + + return ( + +
+
+
+ {isUrl ? ( + +

+ {webpageUrl} + +

+
+ ) : ( +

+ {truncate(title, 45)} +

+ )} +
+ {references > 1 && ( +

+ Referenced {references} times. +

+ )} + +
+
+
+ {chunks.map(({ text, score }, idx) => ( + <> +
+
+

+ {HTMLDecode(omitChunkHeader(text))} +

+ + {!!score && ( +
+
+ +

{toPercentString(score)} match

+
+
+ )} +
+
+ {idx !== chunks.length - 1 && ( +
+ )} + + ))} +
+
+
+
+
+ ); +} + +// Show the correct title and/or display text for citations +// which contain valid outbound links that can be clicked by the +// user when viewing a citation. Optionally allows various icons +// to show distinct types of sources. +function parseChunkSource({ title = "", chunks = [] }) { + const nullResponse = { + isUrl: false, + text: null, + href: null, + icon: "file", + }; + + if ( + !chunks.length || + (!chunks[0].chunkSource?.startsWith("link://") && + !chunks[0].chunkSource?.startsWith("confluence://") && + !chunks[0].chunkSource?.startsWith("github://")) + ) + return nullResponse; + try { + const url = new URL( + chunks[0].chunkSource.split("link://")[1] || + chunks[0].chunkSource.split("confluence://")[1] || + chunks[0].chunkSource.split("github://")[1] + ); + let text = url.host + url.pathname; + let icon = "link"; + + if (url.host.includes("youtube.com")) { + text = title; + icon = "youtube"; + } + + if (url.host.includes("github.com")) { + text = title; + icon = "github"; + } + + if (url.host.includes("atlassian.net")) { + text = title; + icon = "confluence"; + } + + return { + isUrl: true, + href: url.toString(), + text, + icon, + }; + } catch {} + return nullResponse; +} + +// Patch to render Confluence icon as a element like we do with Phosphor +const ConfluenceIcon = ({ ...props }) => ( + +); + +const ICONS = { + file: FileText, + link: Link, + youtube: YoutubeLogo, + github: GithubLogo, + confluence: ConfluenceIcon, +}; diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/ActionMenu/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/ActionMenu/index.jsx new file mode 100644 index 0000000..f95cae8 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/ActionMenu/index.jsx @@ -0,0 +1,70 @@ +import React, { useState, useEffect, useRef } from "react"; +import { Trash, DotsThreeVertical, TreeView } from "@phosphor-icons/react"; + +function ActionMenu({ chatId, forkThread, isEditing, role }) { + const [open, setOpen] = useState(false); + const menuRef = useRef(null); + + const toggleMenu = () => setOpen(!open); + + const handleFork = () => { + forkThread(chatId); + setOpen(false); + }; + + const handleDelete = () => { + window.dispatchEvent( + new CustomEvent("delete-message", { detail: { chatId } }) + ); + setOpen(false); + }; + + useEffect(() => { + const handleClickOutside = (event) => { + if (menuRef.current && !menuRef.current.contains(event.target)) { + setOpen(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + if (!chatId || isEditing || role === "user") return null; + + return ( +
+ + {open && ( +
+ + +
+ )} +
+ ); +} + +export default ActionMenu; diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx new file mode 100644 index 0000000..1e9518e --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx @@ -0,0 +1,58 @@ +import { useState, useEffect } from "react"; +import { Trash } from "@phosphor-icons/react"; +import Workspace from "@/models/workspace"; + +const DELETE_EVENT = "delete-message"; + +export function useWatchDeleteMessage({ chatId = null, role = "user" }) { + const [isDeleted, setIsDeleted] = useState(false); + const [completeDelete, setCompleteDelete] = useState(false); + + useEffect(() => { + function listenForEvent() { + if (!chatId) return; + window.addEventListener(DELETE_EVENT, onDeleteEvent); + } + listenForEvent(); + return () => { + window.removeEventListener(DELETE_EVENT, onDeleteEvent); + }; + }, [chatId]); + + function onEndAnimation() { + if (!isDeleted) return; + setCompleteDelete(true); + } + + async function onDeleteEvent(e) { + if (e.detail.chatId === chatId) { + setIsDeleted(true); + // Do this to prevent double-emission of the PUT/DELETE api call + // because then there will be a race condition and it will make an error log for nothing + // as one call will complete and the other will fail. + if (role === "assistant") await Workspace.deleteChat(chatId); + return false; + } + } + + return { isDeleted, completeDelete, onEndAnimation }; +} + +export function DeleteMessage({ chatId, isEditing, role }) { + if (!chatId || isEditing || role === "user") return null; + + function emitDeleteEvent() { + window.dispatchEvent(new CustomEvent(DELETE_EVENT, { detail: { chatId } })); + } + + return ( + + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/EditMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/EditMessage/index.jsx new file mode 100644 index 0000000..3c1fd16 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/EditMessage/index.jsx @@ -0,0 +1,122 @@ +import { Pencil } from "@phosphor-icons/react"; +import { useState, useEffect, useRef } from "react"; + +const EDIT_EVENT = "toggle-message-edit"; + +export function useEditMessage({ chatId, role }) { + const [isEditing, setIsEditing] = useState(false); + + function onEditEvent(e) { + if (e.detail.chatId !== chatId || e.detail.role !== role) { + setIsEditing(false); + return false; + } + setIsEditing((prev) => !prev); + } + + useEffect(() => { + function listenForEdits() { + if (!chatId || !role) return; + window.addEventListener(EDIT_EVENT, onEditEvent); + } + listenForEdits(); + return () => { + window.removeEventListener(EDIT_EVENT, onEditEvent); + }; + }, [chatId, role]); + + return { isEditing, setIsEditing }; +} + +export function EditMessageAction({ chatId = null, role, isEditing }) { + function handleEditClick() { + window.dispatchEvent( + new CustomEvent(EDIT_EVENT, { detail: { chatId, role } }) + ); + } + + if (!chatId || isEditing) return null; + return ( +
+ +
+ ); +} + +export function EditMessageForm({ + role, + chatId, + message, + attachments = [], + adjustTextArea, + saveChanges, +}) { + const formRef = useRef(null); + function handleSaveMessage(e) { + e.preventDefault(); + const form = new FormData(e.target); + const editedMessage = form.get("editedMessage"); + saveChanges({ editedMessage, chatId, role, attachments }); + window.dispatchEvent( + new CustomEvent(EDIT_EVENT, { detail: { chatId, role, attachments } }) + ); + } + + function cancelEdits() { + window.dispatchEvent( + new CustomEvent(EDIT_EVENT, { detail: { chatId, role, attachments } }) + ); + return false; + } + + useEffect(() => { + if (!formRef || !formRef.current) return; + formRef.current.focus(); + adjustTextArea({ target: formRef.current }); + }, [formRef]); + + return ( +
+ +
+
+ + +
+
+
+
+ + +
+ +
+
+ + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/EditPresetModal.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/EditPresetModal.jsx new file mode 100644 index 0000000..3d66102 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/EditPresetModal.jsx @@ -0,0 +1,155 @@ +import { useState, useEffect } from "react"; +import { X } from "@phosphor-icons/react"; +import ModalWrapper from "@/components/ModalWrapper"; +import { CMD_REGEX } from "."; + +export default function EditPresetModal({ + isOpen, + onClose, + onSave, + onDelete, + preset, +}) { + const [command, setCommand] = useState(""); + const [deleting, setDeleting] = useState(false); + + useEffect(() => { + if (preset && isOpen) { + setCommand(preset.command?.slice(1) || ""); + } + }, [preset, isOpen]); + + const handleSubmit = (e) => { + e.preventDefault(); + const form = new FormData(e.target); + const sanitizedCommand = command.replace(CMD_REGEX, ""); + onSave({ + id: preset.id, + command: `/${sanitizedCommand}`, + prompt: form.get("prompt"), + description: form.get("description"), + }); + }; + + const handleCommandChange = (e) => { + const value = e.target.value.replace(CMD_REGEX, ""); + setCommand(value); + }; + + const handleDelete = async () => { + if (!window.confirm("Are you sure you want to delete this preset?")) return; + + setDeleting(true); + await onDelete(preset.id); + setDeleting(false); + onClose(); + }; + + return ( + +
+
+
+

+ Edit Preset +

+
+ +
+
+
+
+
+
+ +
+ / + +
+
+
+ + +
+
+ + +
+
+
+
+ +
+ + +
+
+
+
+
+
+ ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx new file mode 100644 index 0000000..e7e5536 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx @@ -0,0 +1,136 @@ +import { useEffect, useState } from "react"; +import { useIsAgentSessionActive } from "@/utils/chat/agent"; +import AddPresetModal from "./AddPresetModal"; +import EditPresetModal from "./EditPresetModal"; +import { useModal } from "@/hooks/useModal"; +import System from "@/models/system"; +import { DotsThree, Plus } from "@phosphor-icons/react"; +import showToast from "@/utils/toast"; + +export const CMD_REGEX = new RegExp(/[^a-zA-Z0-9_-]/g); +export default function SlashPresets({ setShowing, sendCommand }) { + const isActiveAgentSession = useIsAgentSessionActive(); + const { + isOpen: isAddModalOpen, + openModal: openAddModal, + closeModal: closeAddModal, + } = useModal(); + const { + isOpen: isEditModalOpen, + openModal: openEditModal, + closeModal: closeEditModal, + } = useModal(); + const [presets, setPresets] = useState([]); + const [selectedPreset, setSelectedPreset] = useState(null); + + useEffect(() => { + fetchPresets(); + }, []); + if (isActiveAgentSession) return null; + + const fetchPresets = async () => { + const presets = await System.getSlashCommandPresets(); + setPresets(presets); + }; + + const handleSavePreset = async (preset) => { + const { error } = await System.createSlashCommandPreset(preset); + if (!!error) { + showToast(error, "error"); + return false; + } + + fetchPresets(); + closeAddModal(); + return true; + }; + + const handleEditPreset = (preset) => { + setSelectedPreset(preset); + openEditModal(); + }; + + const handleUpdatePreset = async (updatedPreset) => { + const { error } = await System.updateSlashCommandPreset( + updatedPreset.id, + updatedPreset + ); + + if (!!error) { + showToast(error, "error"); + return; + } + + fetchPresets(); + closeEditModalAndResetPreset(); + }; + + const handleDeletePreset = async (presetId) => { + await System.deleteSlashCommandPreset(presetId); + fetchPresets(); + closeEditModalAndResetPreset(); + }; + + const closeEditModalAndResetPreset = () => { + closeEditModal(); + setSelectedPreset(null); + }; + + return ( + <> + {presets.map((preset) => ( + + + ))} + + + {selectedPreset && ( + + )} + + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/endAgentSession.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/endAgentSession.jsx new file mode 100644 index 0000000..6a8d70f --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/endAgentSession.jsx @@ -0,0 +1,23 @@ +import { useIsAgentSessionActive } from "@/utils/chat/agent"; + +export default function EndAgentSession({ setShowing, sendCommand }) { + const isActiveAgentSession = useIsAgentSessionActive(); + if (!isActiveAgentSession) return null; + + return ( + + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/icons/SlashCommandIcon.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/icons/SlashCommandIcon.jsx new file mode 100644 index 0000000..4805233 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/icons/SlashCommandIcon.jsx @@ -0,0 +1,28 @@ +export default function SlashCommandIcon(props) { + return ( + + + + + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/index.jsx new file mode 100644 index 0000000..26f1958 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/index.jsx @@ -0,0 +1,69 @@ +import { useEffect, useRef, useState } from "react"; +import SlashCommandIcon from "./icons/SlashCommandIcon"; +import { Tooltip } from "react-tooltip"; +import ResetCommand from "./reset"; +import EndAgentSession from "./endAgentSession"; +import SlashPresets from "./SlashPresets"; + +export default function SlashCommandsButton({ showing, setShowSlashCommand }) { + return ( +
setShowSlashCommand(!showing)} + className={`flex justify-center items-center cursor-pointer ${ + showing ? "!opacity-100" : "" + }`} + > + + +
+ ); +} + +export function SlashCommands({ showing, setShowing, sendCommand }) { + const cmdRef = useRef(null); + useEffect(() => { + function listenForOutsideClick() { + if (!showing || !cmdRef.current) return false; + document.addEventListener("click", closeIfOutside); + } + listenForOutsideClick(); + }, [showing, cmdRef.current]); + + const closeIfOutside = ({ target }) => { + if (target.id === "slash-cmd-btn") return; + const isOutside = !cmdRef?.current?.contains(target); + if (!isOutside) return; + setShowing(false); + }; + + return ( + + ); +} + +export function useSlashCommands() { + const [showSlashCommand, setShowSlashCommand] = useState(false); + return { showSlashCommand, setShowSlashCommand }; +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/reset.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/reset.jsx new file mode 100644 index 0000000..f75468a --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/reset.jsx @@ -0,0 +1,23 @@ +import { useIsAgentSessionActive } from "@/utils/chat/agent"; + +export default function ResetCommand({ setShowing, sendCommand }) { + const isActiveAgentSession = useIsAgentSessionActive(); + if (isActiveAgentSession) return null; // cannot reset during active agent chat + + return ( + + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SpeechToText/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SpeechToText/index.jsx new file mode 100644 index 0000000..f4991b4 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SpeechToText/index.jsx @@ -0,0 +1,120 @@ +import { useEffect, useCallback } from "react"; +import { Microphone } from "@phosphor-icons/react"; +import { Tooltip } from "react-tooltip"; +import _regeneratorRuntime from "regenerator-runtime"; +import SpeechRecognition, { + useSpeechRecognition, +} from "react-speech-recognition"; +import { PROMPT_INPUT_EVENT } from "../../PromptInput"; + +let timeout; +const SILENCE_INTERVAL = 3_200; // wait in seconds of silence before closing. +export default function SpeechToText({ sendCommand }) { + const { + transcript, + listening, + resetTranscript, + browserSupportsSpeechRecognition, + browserSupportsContinuousListening, + isMicrophoneAvailable, + } = useSpeechRecognition({ + clearTranscriptOnListen: true, + }); + + function startSTTSession() { + if (!isMicrophoneAvailable) { + alert( + "AnythingLLM does not have access to microphone. Please enable for this site to use this feature." + ); + return; + } + + resetTranscript(); + SpeechRecognition.startListening({ + continuous: browserSupportsContinuousListening, + language: window?.navigator?.language ?? "en-US", + }); + } + + function endTTSSession() { + SpeechRecognition.stopListening(); + if (transcript.length > 0) { + sendCommand(transcript, true); + } + + resetTranscript(); + clearTimeout(timeout); + } + + const handleKeyPress = useCallback( + (event) => { + if (event.ctrlKey && event.keyCode === 77) { + if (listening) { + endTTSSession(); + } else { + startSTTSession(); + } + } + }, + [listening, endTTSSession, startSTTSession] + ); + + function handlePromptUpdate(e) { + if (!e?.detail && timeout) { + endTTSSession(); + clearTimeout(timeout); + } + } + + useEffect(() => { + document.addEventListener("keydown", handleKeyPress); + return () => { + document.removeEventListener("keydown", handleKeyPress); + }; + }, [handleKeyPress]); + + useEffect(() => { + if (!!window) + window.addEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate); + return () => + window?.removeEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate); + }, []); + + useEffect(() => { + if (transcript?.length > 0 && listening) { + sendCommand(transcript, false); + clearTimeout(timeout); + timeout = setTimeout(() => { + endTTSSession(); + }, SILENCE_INTERVAL); + } + }, [transcript, listening]); + + if (!browserSupportsSpeechRecognition) return null; + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/StopGenerationButton/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/StopGenerationButton/index.jsx new file mode 100644 index 0000000..bce3c68 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/StopGenerationButton/index.jsx @@ -0,0 +1,53 @@ +import { ABORT_STREAM_EVENT } from "@/utils/chat"; +import { Tooltip } from "react-tooltip"; + +export default function StopGenerationButton() { + function emitHaltEvent() { + window.dispatchEvent(new CustomEvent(ABORT_STREAM_EVENT)); + } + + return ( + <> + + + + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/StopGenerationButton/stop.svg b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/StopGenerationButton/stop.svg new file mode 100644 index 0000000..ab98895 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/StopGenerationButton/stop.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/TextSizeMenu/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/TextSizeMenu/index.jsx new file mode 100644 index 0000000..9ca5be7 --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/TextSizeMenu/index.jsx @@ -0,0 +1,131 @@ +import { useState, useRef, useEffect } from "react"; +import { TextT } from "@phosphor-icons/react"; +import { Tooltip } from "react-tooltip"; + +export default function TextSizeButton() { + const [showTextSizeMenu, setShowTextSizeMenu] = useState(false); + const buttonRef = useRef(null); + + return ( + <> +
setShowTextSizeMenu(!showTextSizeMenu)} + className={`border-none relative flex justify-center items-center opacity-60 hover:opacity-100 light:opacity-100 light:hover:opacity-60 cursor-pointer ${ + showTextSizeMenu ? "!opacity-100" : "" + }`} + > + + +
+ + + ); +} + +function TextSizeMenu({ showing, setShowing, buttonRef }) { + const formRef = useRef(null); + const [selectedSize, setSelectedSize] = useState( + window.localStorage.getItem("anythingllm_text_size") || "normal" + ); + + useEffect(() => { + function listenForOutsideClick() { + if (!showing || !formRef.current) return false; + document.addEventListener("click", closeIfOutside); + } + listenForOutsideClick(); + }, [showing, formRef.current]); + + const closeIfOutside = ({ target }) => { + if (target.id === "text-size-btn") return; + const isOutside = !formRef?.current?.contains(target); + if (!isOutside) return; + setShowing(false); + }; + + const handleTextSizeChange = (size) => { + setSelectedSize(size); + window.localStorage.setItem("anythingllm_text_size", size); + window.dispatchEvent(new CustomEvent("textSizeChange", { detail: size })); + }; + + if (!buttonRef.current) return null; + + return ( + + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx new file mode 100644 index 0000000..a0b5d7f --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx @@ -0,0 +1,327 @@ +import React, { useState, useRef, useEffect } from "react"; +import SlashCommandsButton, { + SlashCommands, + useSlashCommands, +} from "./SlashCommands"; +import debounce from "lodash.debounce"; +import { PaperPlaneRight } from "@phosphor-icons/react"; +import StopGenerationButton from "./StopGenerationButton"; +import AvailableAgentsButton, { + AvailableAgents, + useAvailableAgents, +} from "./AgentMenu"; +import TextSizeButton from "./TextSizeMenu"; +import SpeechToText from "./SpeechToText"; +import { Tooltip } from "react-tooltip"; +import AttachmentManager from "./Attachments"; +import AttachItem from "./AttachItem"; +import { PASTE_ATTACHMENT_EVENT } from "../DnDWrapper"; +import useTextSize from "@/hooks/useTextSize"; + +export const PROMPT_INPUT_EVENT = "set_prompt_input"; +const MAX_EDIT_STACK_SIZE = 100; + +export default function PromptInput({ + submit, + onChange, + inputDisabled, + buttonDisabled, + sendCommand, + attachments = [], +}) { + const [promptInput, setPromptInput] = useState(""); + const { showAgents, setShowAgents } = useAvailableAgents(); + const { showSlashCommand, setShowSlashCommand } = useSlashCommands(); + const formRef = useRef(null); + const textareaRef = useRef(null); + const [_, setFocused] = useState(false); + const undoStack = useRef([]); + const redoStack = useRef([]); + const { textSizeClass } = useTextSize(); + + /** + * To prevent too many re-renders we remotely listen for updates from the parent + * via an event cycle. Otherwise, using message as a prop leads to a re-render every + * change on the input. + * @param {Event} e + */ + function handlePromptUpdate(e) { + setPromptInput(e?.detail ?? ""); + } + + function resetTextAreaHeight() { + if (!textareaRef.current) return; + textareaRef.current.style.height = "auto"; + } + + useEffect(() => { + if (!!window) + window.addEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate); + return () => + window?.removeEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate); + }, []); + + useEffect(() => { + if (!inputDisabled && textareaRef.current) textareaRef.current.focus(); + resetTextAreaHeight(); + }, [inputDisabled]); + + /** + * Save the current state before changes + * @param {number} adjustment + */ + function saveCurrentState(adjustment = 0) { + if (undoStack.current.length >= MAX_EDIT_STACK_SIZE) + undoStack.current.shift(); + undoStack.current.push({ + value: promptInput, + cursorPositionStart: textareaRef.current.selectionStart + adjustment, + cursorPositionEnd: textareaRef.current.selectionEnd + adjustment, + }); + } + const debouncedSaveState = debounce(saveCurrentState, 250); + + function handleSubmit(e) { + setFocused(false); + submit(e); + } + + function resetTextAreaHeight() { + if (!textareaRef.current) return; + textareaRef.current.style.height = "auto"; + } + + function checkForSlash(e) { + const input = e.target.value; + if (input === "/") setShowSlashCommand(true); + if (showSlashCommand) setShowSlashCommand(false); + return; + } + const watchForSlash = debounce(checkForSlash, 300); + + function checkForAt(e) { + const input = e.target.value; + if (input === "@") return setShowAgents(true); + if (showAgents) return setShowAgents(false); + } + const watchForAt = debounce(checkForAt, 300); + + /** + * Capture enter key press to handle submission, redo, or undo + * via keyboard shortcuts + * @param {KeyboardEvent} event + */ + function captureEnterOrUndo(event) { + // Is simple enter key press w/o shift key + if (event.keyCode === 13 && !event.shiftKey) { + event.preventDefault(); + return submit(event); + } + + // Is undo with Ctrl+Z or Cmd+Z + Shift key = Redo + if ( + (event.ctrlKey || event.metaKey) && + event.key === "z" && + event.shiftKey + ) { + event.preventDefault(); + if (redoStack.current.length === 0) return; + + const nextState = redoStack.current.pop(); + if (!nextState) return; + + undoStack.current.push({ + value: promptInput, + cursorPositionStart: textareaRef.current.selectionStart, + cursorPositionEnd: textareaRef.current.selectionEnd, + }); + setPromptInput(nextState.value); + setTimeout(() => { + textareaRef.current.setSelectionRange( + nextState.cursorPositionStart, + nextState.cursorPositionEnd + ); + }, 0); + } + + // Undo with Ctrl+Z or Cmd+Z + if ( + (event.ctrlKey || event.metaKey) && + event.key === "z" && + !event.shiftKey + ) { + if (undoStack.current.length === 0) return; + const lastState = undoStack.current.pop(); + if (!lastState) return; + + redoStack.current.push({ + value: promptInput, + cursorPositionStart: textareaRef.current.selectionStart, + cursorPositionEnd: textareaRef.current.selectionEnd, + }); + setPromptInput(lastState.value); + setTimeout(() => { + textareaRef.current.setSelectionRange( + lastState.cursorPositionStart, + lastState.cursorPositionEnd + ); + }, 0); + } + } + + function adjustTextArea(event) { + const element = event.target; + element.style.height = "auto"; + element.style.height = `${element.scrollHeight}px`; + } + + function handlePasteEvent(e) { + e.preventDefault(); + if (e.clipboardData.items.length === 0) return false; + + // paste any clipboard items that are images. + for (const item of e.clipboardData.items) { + if (item.type.startsWith("image/")) { + const file = item.getAsFile(); + window.dispatchEvent( + new CustomEvent(PASTE_ATTACHMENT_EVENT, { + detail: { files: [file] }, + }) + ); + continue; + } + + // handle files specifically that are not images as uploads + if (item.kind === "file") { + const file = item.getAsFile(); + window.dispatchEvent( + new CustomEvent(PASTE_ATTACHMENT_EVENT, { + detail: { files: [file] }, + }) + ); + continue; + } + } + + const pasteText = e.clipboardData.getData("text/plain"); + if (pasteText) { + const textarea = textareaRef.current; + const start = textarea.selectionStart; + const end = textarea.selectionEnd; + const newPromptInput = + promptInput.substring(0, start) + + pasteText + + promptInput.substring(end); + setPromptInput(newPromptInput); + onChange({ target: { value: newPromptInput } }); + + // Set the cursor position after the pasted text + // we need to use setTimeout to prevent the cursor from being set to the end of the text + setTimeout(() => { + textarea.selectionStart = textarea.selectionEnd = + start + pasteText.length; + }, 0); + } + return; + } + + function handleChange(e) { + debouncedSaveState(-1); + onChange(e); + watchForSlash(e); + watchForAt(e); + adjustTextArea(e); + setPromptInput(e.target.value); + } + + return ( +
+ + +
+
+
+ +
+